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 mathconst_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 = 0signZ = 1if len(line) == 0:return Nonewhile line[i]<'0' or line[i]>'9':if line[i] == 'U':signZ = 0if line[i] == '-':breaki = i+1if i == len(line):return Noneline = line[i:]if len(line) == 0:return Nonestrs = line.split(',')if len(strs) != 2:return Noneaxis_y = int(strs[0])axis_x = int(strs[1])if (axis_x is None) or (axis_y is None):return Nonereturn {'x':axis_x,'y':axis_y,'z':signZ}def plot_plt(cv_img,data,print_width,print_height):height_total = print_heightwidth_total = print_widthline = ""comma = 0points = []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 = 0else:line = line+chif ch==',' or ch==' ':comma = comma+1if comma == 2:point = proc_line(line)if point is not None:points.append(point)line = ""comma = 0max_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_ratiooffset_y = 10*const_ratiofor 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_heightwidth_total = print_widthline = ""comma = 0points = []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 = 0else:line = line+chif ch==',' or ch==' ':comma = comma+1if comma == 2:point = proc_line(line)if point is not None:points.append(point)line = ""comma = 0max_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 = 0downPoint = NonedownAngle = 0off_x = 0off_y = 0pointsInterpArr = []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.piif angle_delta >= 180:angle_delta = angle_delta-360elif angle_delta <= -180:angle_delta = angle_delta+360if angle_delta>0:angle_step = ANGLE_STEPelse:angle_step = -ANGLE_STEPprint(angle_delta,length)if length == 0:continueelif point['z'] == 0: #抬刀时不处理pointsInterpArr.append(point)elif pre_point['z'] == 0:#由抬刀变为下刀保存下刀点坐标和角度,并进行起点和终点偏移downPoint = pre_pointdownAngle = anglex1=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_stepfor 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.piif angle_delta >= 180:angle_delta = angle_delta-360elif angle_delta <= -180:angle_delta = angle_delta+360if angle_delta>0:angle_step = ANGLE_STEPelse:angle_step = -ANGLE_STEPcount = math.floor(angle_delta/angle_step)remain = angle_delta-count*angle_stepfor 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 = pointpre_angle = anglepre_point = pointsInterpArr[0]black = (0,255,0)offset_x = 10*const_ratiooffset_y = 10*const_ratiomax_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)
正方形尖角补偿轨迹:
三角形尖角补偿轨迹:
方圆尖角补偿轨迹: