1 刻字机尖角补偿原理
刀具切割直线段过渡方法在文章旋转偏心裁切刀切向跟踪及半径补偿 已经有过说明。刻字机由于刀具半径的影响,切割直角时会不直会比较圆滑,而且在闭合曲线的下刀点会容易不闭合。使用尖角补偿可以克服这些问题。
如上图所示,切割俩条相邻线段AB和BC时,刀心需要走的轨迹是从A' --> B' --> B'' -->C'。由于刻字机使用的刻刀刀尖半径都比较小,而且刀具也是固定没有转动轴控制转动,所以从B'过渡到B''时使用圆弧进行过渡。这里B2和B1都是圆弧上的过渡点。示意图中从B'B''采用小线段B'B2、B2B1、B1B''进行圆弧拟合过渡。
AB的矢量角α,则A'点坐标为(Xa+r*cosα,Ya+r*sinα)。BC的矢量角为β,则B''的坐标为(Xb+r*cosβ,Yb+r*sinβ)。C'的坐标为(Xc+r*cosβ,Yc+r*sinβ)。AB的转角为β-α。
2 尖角补偿python程序实现
import numpy
import cv2
import math
const_ratio = 10
WIDTH = 100
HEIGHT = 60
KNIFE_CIR = 40 #plt中40个单位=1mm
ANGLE_STEP = 30 #圆弧过渡时插补间隔30度
def show_img(window,img):
cv2.namedWindow(window,0)
cv2.resizeWindow(window,int(img.shape[1]),int(img.shape[0]))
cv2.imshow(window,img)
def proc_line(line):
i = 0
signZ = 1
if len(line) == 0:
return None
while line[i]<'0' or line[i]>'9':
if line[i] == 'U':
signZ = 0
if line[i] == '-':
break
i = i+1
if i == len(line):
return None
line = line[i:]
if len(line) == 0:
return None
strs = line.split(',')
if len(strs) != 2:
return None
axis_y = int(strs[0])
axis_x = int(strs[1])
if (axis_x is None) or (axis_y is None):
return None
return {'x':axis_x,'y':axis_y,'z':signZ}
def plot_plt(cv_img,data,print_width,print_height):
height_total = print_height
width_total = print_width
line = ""
comma = 0
points = []
for i in range(len(data)):
try:
ch = chr(data[i])
except:
ch = data[i]
if ch == ';':
point = proc_line(line)
if point is not None:
points.append(point)
line = ""
comma = 0
else:
line = line+ch
if ch==',' or ch==' ':
comma = comma+1
if comma == 2:
point = proc_line(line)
if point is not None:
points.append(point)
line = ""
comma = 0
max_x = points[0]['x']
max_y = points[0]['y']
min_x = points[0]['x']
min_y = points[0]['y']
for point in points:
if point['x']>max_x:
max_x = point['x']
if point['y']>max_y:
max_y = point['y']
if point['x']<min_x:
min_x = point['x']
if point['y']<min_y:
min_y = point['y']
print(max_x,max_y,min_x,min_y)
pre_point = points[0]
black = (0,255,0)
offset_x = 10*const_ratio
offset_y = 10*const_ratio
for point in points:
x = int((point['x'])*const_ratio/40+offset_x)
y = int((point['y'])*const_ratio/40+offset_y)
if point['z'] == 1:
cv2.line(cv_img,(pre_point['x'],pre_point['y']),(width_total*const_ratio-x,height_total*const_ratio-y),black,lineType=cv2.LINE_AA)
pre_point = {'x':width_total*const_ratio-x,'y':height_total*const_ratio-y}
def plot_file(img,filepath):
with open(filepath) as f:
data = f.read()
plot_plt(img,data,WIDTH,HEIGHT)
def plot_plt_comp(cv_img,data,print_width,print_height):
height_total = print_height
width_total = print_width
line = ""
comma = 0
points = []
for i in range(len(data)):
try:
ch = chr(data[i])
except:
ch = data[i]
if ch == ';':
point = proc_line(line)
if point is not None:
points.append(point)
line = ""
comma = 0
else:
line = line+ch
if ch==',' or ch==' ':
comma = comma+1
if comma == 2:
point = proc_line(line)
if point is not None:
points.append(point)
line = ""
comma = 0
max_x = points[0]['x']
max_y = points[0]['y']
min_x = points[0]['x']
min_y = points[0]['y']
for point in points:
if point['x']>max_x:
max_x = point['x']
if point['y']>max_y:
max_y = point['y']
if point['x']<min_x:
min_x = point['x']
if point['y']<min_y:
min_y = point['y']
print(max_x,max_y,min_x,min_y)
#这里开始执行尖角补偿插补计算
angleArr = []
pointNum = len(points)
pre_point = {'x':0,'y':0,'z':0}
pre_angle = 0
downPoint = None
downAngle = 0
off_x = 0
off_y = 0
pointsInterpArr = []
for i in range(pointNum):
point = points[i]
angle = math.atan2(point['y']-pre_point['y'],point['x']-pre_point['x'])
length = math.sqrt((point['y']-pre_point['y'])*(point['y']-pre_point['y'])+(point['x']-pre_point['x'])*(point['x']-pre_point['x']))
angleArr.append(angle)
angle_delta = (angle-pre_angle)*180/math.pi
if angle_delta >= 180:
angle_delta = angle_delta-360
elif angle_delta <= -180:
angle_delta = angle_delta+360
if angle_delta>0:
angle_step = ANGLE_STEP
else:
angle_step = -ANGLE_STEP
print(angle_delta,length)
if length == 0:
continue
elif point['z'] == 0: #抬刀时不处理
pointsInterpArr.append(point)
elif pre_point['z'] == 0:#由抬刀变为下刀保存下刀点坐标和角度,并进行起点和终点偏移
downPoint = pre_point
downAngle = angle
x1=pre_point['x']+KNIFE_CIR*math.cos(angle)
y1=pre_point['y']+KNIFE_CIR*math.sin(angle)
pointsInterpArr.append({'x':int(x1),'y':int(y1),'z':0})
x2 = point['x']+KNIFE_CIR*math.cos(angle)
y2 = point['y']+KNIFE_CIR*math.sin(angle)
pointsInterpArr.append({'x':int(x2),'y':int(y2),'z':1})
off_x = KNIFE_CIR*math.cos(angle)
off_y = KNIFE_CIR*math.sin(angle)
elif abs(angle_delta)>30:#下刀切割时线段转角大于30度时进行圆弧过渡
count = math.floor(angle_delta/angle_step)
remain = angle_delta-count*angle_step
for j in range(0,count):
x = pre_point['x']+KNIFE_CIR*math.cos(pre_angle+angle_step*(j+1)*math.pi/180)
y = pre_point['y']+KNIFE_CIR*math.sin(pre_angle+angle_step*(j+1)*math.pi/180)
pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
if abs(remain) > 0.1:
x = pre_point['x']+KNIFE_CIR*math.cos(angle)
y = pre_point['y']+KNIFE_CIR*math.sin(angle)
pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
delta_x = KNIFE_CIR*math.cos(angle)
delta_y = KNIFE_CIR*math.sin(angle)
pointsInterpArr.append({'x':int(point['x']+delta_x),'y':int(point['y']+delta_y),'z':point['z']})
off_x = KNIFE_CIR*math.cos(angle)
off_y = KNIFE_CIR*math.sin(angle)
else:#转角小于30度直接过渡
delta_x = KNIFE_CIR*math.cos(angle)
delta_y = KNIFE_CIR*math.sin(angle)
pointsInterpArr.append({'x':int(point['x']+delta_x),'y':int(point['y']+delta_y),'z':point['z']})
#发现是下刀点坐标时代表曲线段闭合,进行闭合圆弧过渡
if downPoint != None and length > 0 and point['x'] == downPoint['x'] and point['y'] == downPoint['y']:
angle_delta = (downAngle-angle)*180/math.pi
if angle_delta >= 180:
angle_delta = angle_delta-360
elif angle_delta <= -180:
angle_delta = angle_delta+360
if angle_delta>0:
angle_step = ANGLE_STEP
else:
angle_step = -ANGLE_STEP
count = math.floor(angle_delta/angle_step)
remain = angle_delta-count*angle_step
for j in range(0,count):
x = point['x']+KNIFE_CIR*math.cos(angle+angle_step*(j+1)*math.pi/180)
y = point['y']+KNIFE_CIR*math.sin(angle+angle_step*(j+1)*math.pi/180)
pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
if abs(remain) > 0.1:
x = point['x']+KNIFE_CIR*math.cos(downAngle)
y = point['y']+KNIFE_CIR*math.sin(downAngle)
pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
pre_point = point
pre_angle = angle
pre_point = pointsInterpArr[0]
black = (0,255,0)
offset_x = 10*const_ratio
offset_y = 10*const_ratio
max_x = pointsInterpArr[0]['x']
max_y = pointsInterpArr[0]['y']
min_x = pointsInterpArr[0]['x']
min_y = pointsInterpArr[0]['y']
for point in pointsInterpArr:
if point['x']>max_x:
max_x = point['x']
if point['y']>max_y:
max_y = point['y']
if point['x']<min_x:
min_x = point['x']
if point['y']<min_y:
min_y = point['y']
print(max_x,max_y,min_x,min_y)
for point in pointsInterpArr:
x = int((point['x'])*const_ratio/40+offset_x)
y = int((point['y'])*const_ratio/40+offset_y)
if point['z'] == 1:
cv2.line(cv_img,(pre_point['x'],pre_point['y']),(width_total*const_ratio-x,height_total*const_ratio-y),black,lineType=cv2.LINE_AA)
pre_point = {'x':width_total*const_ratio-x,'y':height_total*const_ratio-y}
def plot_file_comp(img,filepath):
with open(filepath) as f:
data = f.read()
plot_plt_comp(img,data,WIDTH,HEIGHT)
cv_img = numpy.ones((HEIGHT*const_ratio,WIDTH*const_ratio),dtype=numpy.uint8)
cv_img = cv2.bitwise_not(cv_img)
cv2.rectangle(cv_img,(0,0),(WIDTH*const_ratio-1,HEIGHT*const_ratio-1),(0,0,0))
file = 'C:/Users/liuzj/Desktop/plt/rec.plt'
plot_file(cv_img,file)
show_img('img1',cv_img)
cv_img2 = numpy.ones((HEIGHT*const_ratio,WIDTH*const_ratio),dtype=numpy.uint8)
cv_img2 = cv2.bitwise_not(cv_img2)
cv2.rectangle(cv_img2,(0,0),(WIDTH*const_ratio-1,HEIGHT*const_ratio-1),(0,0,0))
plot_file_comp(cv_img2,file)
show_img('img2',cv_img2)
cv2.waitKey(0)
正方形尖角补偿轨迹:
三角形尖角补偿轨迹:
方圆尖角补偿轨迹: