• 刻字机尖角补偿


    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程序实现

    1. import numpy
    2. import cv2
    3. import math
    4. const_ratio = 10
    5. WIDTH = 100
    6. HEIGHT = 60
    7. KNIFE_CIR = 40 #plt中40个单位=1mm
    8. ANGLE_STEP = 30 #圆弧过渡时插补间隔30度
    9. def show_img(window,img):
    10. cv2.namedWindow(window,0)
    11. cv2.resizeWindow(window,int(img.shape[1]),int(img.shape[0]))
    12. cv2.imshow(window,img)
    13. def proc_line(line):
    14. i = 0
    15. signZ = 1
    16. if len(line) == 0:
    17. return None
    18. while line[i]<'0' or line[i]>'9':
    19. if line[i] == 'U':
    20. signZ = 0
    21. if line[i] == '-':
    22. break
    23. i = i+1
    24. if i == len(line):
    25. return None
    26. line = line[i:]
    27. if len(line) == 0:
    28. return None
    29. strs = line.split(',')
    30. if len(strs) != 2:
    31. return None
    32. axis_y = int(strs[0])
    33. axis_x = int(strs[1])
    34. if (axis_x is None) or (axis_y is None):
    35. return None
    36. return {'x':axis_x,'y':axis_y,'z':signZ}
    37. def plot_plt(cv_img,data,print_width,print_height):
    38. height_total = print_height
    39. width_total = print_width
    40. line = ""
    41. comma = 0
    42. points = []
    43. for i in range(len(data)):
    44. try:
    45. ch = chr(data[i])
    46. except:
    47. ch = data[i]
    48. if ch == ';':
    49. point = proc_line(line)
    50. if point is not None:
    51. points.append(point)
    52. line = ""
    53. comma = 0
    54. else:
    55. line = line+ch
    56. if ch==',' or ch==' ':
    57. comma = comma+1
    58. if comma == 2:
    59. point = proc_line(line)
    60. if point is not None:
    61. points.append(point)
    62. line = ""
    63. comma = 0
    64. max_x = points[0]['x']
    65. max_y = points[0]['y']
    66. min_x = points[0]['x']
    67. min_y = points[0]['y']
    68. for point in points:
    69. if point['x']>max_x:
    70. max_x = point['x']
    71. if point['y']>max_y:
    72. max_y = point['y']
    73. if point['x']
    74. min_x = point['x']
    75. if point['y']
    76. min_y = point['y']
    77. print(max_x,max_y,min_x,min_y)
    78. pre_point = points[0]
    79. black = (0,255,0)
    80. offset_x = 10*const_ratio
    81. offset_y = 10*const_ratio
    82. for point in points:
    83. x = int((point['x'])*const_ratio/40+offset_x)
    84. y = int((point['y'])*const_ratio/40+offset_y)
    85. if point['z'] == 1:
    86. 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)
    87. pre_point = {'x':width_total*const_ratio-x,'y':height_total*const_ratio-y}
    88. def plot_file(img,filepath):
    89. with open(filepath) as f:
    90. data = f.read()
    91. plot_plt(img,data,WIDTH,HEIGHT)
    92. def plot_plt_comp(cv_img,data,print_width,print_height):
    93. height_total = print_height
    94. width_total = print_width
    95. line = ""
    96. comma = 0
    97. points = []
    98. for i in range(len(data)):
    99. try:
    100. ch = chr(data[i])
    101. except:
    102. ch = data[i]
    103. if ch == ';':
    104. point = proc_line(line)
    105. if point is not None:
    106. points.append(point)
    107. line = ""
    108. comma = 0
    109. else:
    110. line = line+ch
    111. if ch==',' or ch==' ':
    112. comma = comma+1
    113. if comma == 2:
    114. point = proc_line(line)
    115. if point is not None:
    116. points.append(point)
    117. line = ""
    118. comma = 0
    119. max_x = points[0]['x']
    120. max_y = points[0]['y']
    121. min_x = points[0]['x']
    122. min_y = points[0]['y']
    123. for point in points:
    124. if point['x']>max_x:
    125. max_x = point['x']
    126. if point['y']>max_y:
    127. max_y = point['y']
    128. if point['x']
    129. min_x = point['x']
    130. if point['y']
    131. min_y = point['y']
    132. print(max_x,max_y,min_x,min_y)
    133. #这里开始执行尖角补偿插补计算
    134. angleArr = []
    135. pointNum = len(points)
    136. pre_point = {'x':0,'y':0,'z':0}
    137. pre_angle = 0
    138. downPoint = None
    139. downAngle = 0
    140. off_x = 0
    141. off_y = 0
    142. pointsInterpArr = []
    143. for i in range(pointNum):
    144. point = points[i]
    145. angle = math.atan2(point['y']-pre_point['y'],point['x']-pre_point['x'])
    146. length = math.sqrt((point['y']-pre_point['y'])*(point['y']-pre_point['y'])+(point['x']-pre_point['x'])*(point['x']-pre_point['x']))
    147. angleArr.append(angle)
    148. angle_delta = (angle-pre_angle)*180/math.pi
    149. if angle_delta >= 180:
    150. angle_delta = angle_delta-360
    151. elif angle_delta <= -180:
    152. angle_delta = angle_delta+360
    153. if angle_delta>0:
    154. angle_step = ANGLE_STEP
    155. else:
    156. angle_step = -ANGLE_STEP
    157. print(angle_delta,length)
    158. if length == 0:
    159. continue
    160. elif point['z'] == 0: #抬刀时不处理
    161. pointsInterpArr.append(point)
    162. elif pre_point['z'] == 0:#由抬刀变为下刀保存下刀点坐标和角度,并进行起点和终点偏移
    163. downPoint = pre_point
    164. downAngle = angle
    165. x1=pre_point['x']+KNIFE_CIR*math.cos(angle)
    166. y1=pre_point['y']+KNIFE_CIR*math.sin(angle)
    167. pointsInterpArr.append({'x':int(x1),'y':int(y1),'z':0})
    168. x2 = point['x']+KNIFE_CIR*math.cos(angle)
    169. y2 = point['y']+KNIFE_CIR*math.sin(angle)
    170. pointsInterpArr.append({'x':int(x2),'y':int(y2),'z':1})
    171. off_x = KNIFE_CIR*math.cos(angle)
    172. off_y = KNIFE_CIR*math.sin(angle)
    173. elif abs(angle_delta)>30:#下刀切割时线段转角大于30度时进行圆弧过渡
    174. count = math.floor(angle_delta/angle_step)
    175. remain = angle_delta-count*angle_step
    176. for j in range(0,count):
    177. x = pre_point['x']+KNIFE_CIR*math.cos(pre_angle+angle_step*(j+1)*math.pi/180)
    178. y = pre_point['y']+KNIFE_CIR*math.sin(pre_angle+angle_step*(j+1)*math.pi/180)
    179. pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
    180. if abs(remain) > 0.1:
    181. x = pre_point['x']+KNIFE_CIR*math.cos(angle)
    182. y = pre_point['y']+KNIFE_CIR*math.sin(angle)
    183. pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
    184. delta_x = KNIFE_CIR*math.cos(angle)
    185. delta_y = KNIFE_CIR*math.sin(angle)
    186. pointsInterpArr.append({'x':int(point['x']+delta_x),'y':int(point['y']+delta_y),'z':point['z']})
    187. off_x = KNIFE_CIR*math.cos(angle)
    188. off_y = KNIFE_CIR*math.sin(angle)
    189. else:#转角小于30度直接过渡
    190. delta_x = KNIFE_CIR*math.cos(angle)
    191. delta_y = KNIFE_CIR*math.sin(angle)
    192. pointsInterpArr.append({'x':int(point['x']+delta_x),'y':int(point['y']+delta_y),'z':point['z']})
    193. #发现是下刀点坐标时代表曲线段闭合,进行闭合圆弧过渡
    194. if downPoint != None and length > 0 and point['x'] == downPoint['x'] and point['y'] == downPoint['y']:
    195. angle_delta = (downAngle-angle)*180/math.pi
    196. if angle_delta >= 180:
    197. angle_delta = angle_delta-360
    198. elif angle_delta <= -180:
    199. angle_delta = angle_delta+360
    200. if angle_delta>0:
    201. angle_step = ANGLE_STEP
    202. else:
    203. angle_step = -ANGLE_STEP
    204. count = math.floor(angle_delta/angle_step)
    205. remain = angle_delta-count*angle_step
    206. for j in range(0,count):
    207. x = point['x']+KNIFE_CIR*math.cos(angle+angle_step*(j+1)*math.pi/180)
    208. y = point['y']+KNIFE_CIR*math.sin(angle+angle_step*(j+1)*math.pi/180)
    209. pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
    210. if abs(remain) > 0.1:
    211. x = point['x']+KNIFE_CIR*math.cos(downAngle)
    212. y = point['y']+KNIFE_CIR*math.sin(downAngle)
    213. pointsInterpArr.append({'x':int(x),'y':int(y),'z':1})
    214. pre_point = point
    215. pre_angle = angle
    216. pre_point = pointsInterpArr[0]
    217. black = (0,255,0)
    218. offset_x = 10*const_ratio
    219. offset_y = 10*const_ratio
    220. max_x = pointsInterpArr[0]['x']
    221. max_y = pointsInterpArr[0]['y']
    222. min_x = pointsInterpArr[0]['x']
    223. min_y = pointsInterpArr[0]['y']
    224. for point in pointsInterpArr:
    225. if point['x']>max_x:
    226. max_x = point['x']
    227. if point['y']>max_y:
    228. max_y = point['y']
    229. if point['x']
    230. min_x = point['x']
    231. if point['y']
    232. min_y = point['y']
    233. print(max_x,max_y,min_x,min_y)
    234. for point in pointsInterpArr:
    235. x = int((point['x'])*const_ratio/40+offset_x)
    236. y = int((point['y'])*const_ratio/40+offset_y)
    237. if point['z'] == 1:
    238. 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)
    239. pre_point = {'x':width_total*const_ratio-x,'y':height_total*const_ratio-y}
    240. def plot_file_comp(img,filepath):
    241. with open(filepath) as f:
    242. data = f.read()
    243. plot_plt_comp(img,data,WIDTH,HEIGHT)
    244. cv_img = numpy.ones((HEIGHT*const_ratio,WIDTH*const_ratio),dtype=numpy.uint8)
    245. cv_img = cv2.bitwise_not(cv_img)
    246. cv2.rectangle(cv_img,(0,0),(WIDTH*const_ratio-1,HEIGHT*const_ratio-1),(0,0,0))
    247. file = 'C:/Users/liuzj/Desktop/plt/rec.plt'
    248. plot_file(cv_img,file)
    249. show_img('img1',cv_img)
    250. cv_img2 = numpy.ones((HEIGHT*const_ratio,WIDTH*const_ratio),dtype=numpy.uint8)
    251. cv_img2 = cv2.bitwise_not(cv_img2)
    252. cv2.rectangle(cv_img2,(0,0),(WIDTH*const_ratio-1,HEIGHT*const_ratio-1),(0,0,0))
    253. plot_file_comp(cv_img2,file)
    254. show_img('img2',cv_img2)
    255. cv2.waitKey(0)

    正方形尖角补偿轨迹:

    三角形尖角补偿轨迹:

    方圆尖角补偿轨迹:

  • 相关阅读:
    Go 语言 设计模式-生成器模式
    NFT Insider #64:电商巨头eBay提交NFT相关商标申请,毕马威将在Web3和元宇宙中投入3000万美元
    CPP-Templates-2nd--第十九章 萃取的实现 19.4-19.5
    基于人工蜂群算法的新型概率密度模型的无人机路径规划(Matlab代码实现)
    PaddleOCR系列-训练模型并部署android手机
    m基于16QAM的自适应波束形成matlab仿真
    计算机网络基础(二):物理层、数据链路层及网络层
    最新SparkAI创作系统V2.6.2/ChatGPT网站系统H5源码+微信公众号版+AI绘画系统源码/支持GPT联网提问/支持Prompt应用
    16.预处理、动态库、静态库
    ZeroTier CentOS7 网关机配置
  • 原文地址:https://blog.csdn.net/liuzhijun301/article/details/132889246