hjsx.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import numpy as np
  2. import os
  3. import rospy
  4. from math import acos, degrees,tan,atan,sqrt,sin,cos,radians
  5. from scipy.spatial.transform import Rotation as R
  6. #求向量的模
  7. def calculate_distance(point1, point2):
  8. return np.linalg.norm(np.array(point1) - np.array(point2))
  9. #计算焊缝向量与xy平面夹角 用于判断是平缝还是竖缝
  10. def calculate_angle_with_xy_plane(point1,point2):
  11. # 计算方向向量
  12. dir_vector = np.array([point2[0] - point1[0], point2[1] - point1[1], point2[2] - point1[2]])
  13. # 计算方向向量在 xy 平面上的投影
  14. proj_vector = np.array([point2[0] - point1[0], point2[1] - point1[1], 0])
  15. # 计算夹角
  16. angle = acos(np.dot(dir_vector, proj_vector) / (np.linalg.norm(dir_vector) * np.linalg.norm(proj_vector)))
  17. angle_deg = degrees(angle)
  18. return angle_deg
  19. # 从文件中读取焊缝起点和终点,并分为平缝和竖缝
  20. def read_hanfeng_from_point_txt(file_path):
  21. data_ping, data_shu = [],[]
  22. with open(file_path, 'r') as file:
  23. # 逐行读取文件内容
  24. lines = file.readlines()
  25. flag_sequence = 0
  26. for line in lines:
  27. # print(line)
  28. flag_sequence += 1
  29. # 去除行尾的换行符并按'/'分割每一行
  30. points_str = line.strip().split('/')
  31. point1_str = points_str[0].split(',')
  32. point2_str = points_str[1].split(',')
  33. # 转换字符串为浮点数列表,构造三维点
  34. point1 = [float(coord) for coord in point1_str]
  35. point2 = [float(coord) for coord in point2_str]
  36. angle = calculate_angle_with_xy_plane(point1,point2)
  37. if abs(angle) < rospy.get_param("ping_sta"):#用于判断平缝的角度
  38. data_ping.append([point1, point2, flag_sequence])
  39. else:
  40. data_shu.append([*sorted([point1, point2], key=lambda p: p[2]), flag_sequence])
  41. data = data_ping + data_shu
  42. return data_ping, data_shu
  43. def sort_welds_by_distance_ping(data_ping, reference_point):
  44. """根据与参考点的距离对焊缝进行排序"""
  45. sorted_data_ping = []#一定放for外
  46. for i in range(len(data_ping)):
  47. # 计算起点到参考点的距离并排序[[起点与参考点距离,终点与参考点距离,起点坐标,终点坐标,焊缝序号(与points文件中顺序对应)],[],[]....]
  48. data_with_distances = [[calculate_distance(start, reference_point),calculate_distance(end, reference_point), start, end,flag_sequence] for start, end,flag_sequence in data_ping]
  49. for i in range(len(data_with_distances)):
  50. if data_with_distances[i][0] > data_with_distances[i][1]:#若起点比终点离参考点更远 则互换起始点 且将第一个元素变为最近的点(后面用lambda针对第一个元素大小排序)
  51. data_with_distances[i][0] = data_with_distances[i][1]
  52. tem = data_with_distances[i][2]
  53. data_with_distances[i][2] = data_with_distances[i][3]
  54. data_with_distances[i][3] = tem
  55. data_with_distances.sort(key=lambda x: x[0])#将水平焊缝(起点是离机器人近的那一端)按照起点离参考点的距离排序
  56. # 初始化结果和当前终点
  57. sorted_data_ping.append(data_with_distances.pop(0)[-3:]) # 取出data_with_distances第一个元素的 起点坐标 终点坐标 焊缝序号
  58. #reference_point = sorted_data_ping[-1][1] #参考点变更为 sorted_data_ping中的最后一个点 即寻找离当前焊缝终点最近的
  59. data_ping = [[start, end,flag_sequence] for distances1,distances2, start,end,flag_sequence in data_with_distances]#从弹出已经排好的点的数组重新更新被处理列表
  60. #将起始点位置交换回来,保证平缝焊接方向一致
  61. # for i in range(len(sorted_data_ping)):
  62. # sorted_data_ping[i][0], sorted_data_ping[i][1] = sorted_data_ping[i][1], sorted_data_ping[i][0]
  63. return sorted_data_ping #焊接方向最终是由参考点近端向远端焊接 顺序为先找离参考点最近的 然后找其次的
  64. # def sort_welds_by_distance_shu(data_shu, reference_point):
  65. # """根据与参考点的距离对焊缝进行排序"""
  66. # sorted_data_shu = []
  67. # for i in range(len(data_shu)):
  68. # # 计算起点到参考点的距离并排序
  69. # data_with_distances = [[calculate_distance(start, reference_point), start, end ,flag_sequence] for start, end ,flag_sequence in data_shu]
  70. # data_with_distances.sort(key=lambda x: x[0])
  71. # # 初始化结果和当前终点
  72. # sorted_data_shu.append(data_with_distances.pop(0)[-3:])
  73. # reference_point = sorted_data_shu[-1][1]
  74. # data_shu = [[start, end ,flag_sequence] for distances, start,end ,flag_sequence in data_with_distances]
  75. # return sorted_data_shu
  76. def compute_pose_R(fangxiang, hanfeng):
  77. """wg根据计算出的焊接时z轴负方向的向量(fangxiang)和由起点指向终点的向量(hanfeng)计算旋转矩阵""" #得到的是fangxiang向量相对于基坐标系原点的姿态变换矩阵 多了个绕x转180的变换
  78. x, y, z = fangxiang
  79. # 计算旋转角度
  80. rotx = -np.arctan2(y, z)#投影到yz平面的的夹角
  81. roty = np.arctan2(x, np.sqrt(y**2 + z**2))#向量和yz面的夹角
  82. # 构造旋转矩阵 rot x y 先绕x转不影响向量和yz面的夹角?
  83. A = np.array([[1, 0, 0],
  84. [0, np.cos(rotx), -np.sin(rotx)],
  85. [0, np.sin(rotx), np.cos(rotx)]])
  86. B = np.array([[np.cos(roty), 0, np.sin(roty)],
  87. [0, 1, 0],
  88. [-np.sin(roty), 0, np.cos(roty)]])
  89. bb = np.matmul(hanfeng, np.matmul(A, B))#先转到XOZ 再转到X 相当于通过将方向向量转到z轴上的方式 让焊缝向量转到XOY平面 通过方向向量和焊缝向量的正交关系求出向量在Z轴上的转角
  90. # 区分平缝竖缝
  91. rotz = np.arctan2(-bb[1], -bb[0])
  92. # 构造旋转矩阵 rot z x
  93. C = np.array([[np.cos(rotz), -np.sin(rotz), 0],
  94. [np.sin(rotz), np.cos(rotz), 0],
  95. [0, 0, 1]])
  96. D = np.array([[1, 0, 0],
  97. [0, np.cos(np.pi), -np.sin(np.pi)],
  98. [0, np.sin(np.pi), np.cos(np.pi)]])
  99. ANG_Y=-25
  100. E = np.array([[np.cos(radians(ANG_Y)), 0, np.sin(radians(ANG_Y))],
  101. [0, 1, 0],
  102. [-np.sin(radians(ANG_Y)), 0, np.cos(radians(ANG_Y))]])
  103. #A*(B*(C*D)) x*y*z*x(180)
  104. #R_mat = np.matmul(A, np.matmul(B, np.matmul(C, D)))
  105. #R_mat = np.matmul(A, np.matmul(B, C))
  106. R_mat = np.matmul(A, np.matmul(B, np.matmul(C, E)))
  107. return R_mat
  108. def run():
  109. result=[]
  110. # 定义参考点,排序
  111. #reference_point = (1172.85077147, -1.50259925, 668.30298144)
  112. reference_point = (0.0,9000.0, 0.0)
  113. file_path = rospy.get_param("folder_path")
  114. file_path_points = os.path.join(file_path, 'points.txt')
  115. file_path_result = os.path.join(file_path, 'result.txt')
  116. data_ping, data_shu= read_hanfeng_from_point_txt(file_path_points)
  117. # 对焊缝排序
  118. sorted_data_ping = sort_welds_by_distance_ping(data_ping, reference_point)
  119. # if len(data_ping) > 0:
  120. # a = tuple(sorted_data_ping[-1][1])
  121. # else:
  122. # a = reference_point
  123. # sorted_welds_shu = sort_welds_by_distance_shu(data_shu, a)
  124. sorted_welds = sorted_data_ping# + sorted_welds_shu #[[起点,终点,序号],[],[]。。。。]
  125. #传递焊接顺序(points读入时的编号,从零开始)
  126. flag_sequence = [flag_sequence for start,end ,flag_sequence in sorted_welds]
  127. rospy.set_param('welding_sequence',flag_sequence)
  128. sorted_welds_withnoflag = [[start,end]for start,end ,flag_sequence in sorted_welds]
  129. # with open(file_path_points_guihua, "w") as file:
  130. # for start, end in sorted_welds_withnoflag:
  131. # # 将每一对起点和终点转换为字符串,并用逗号连接,最后以换行符分隔不同的焊缝对
  132. # line = "{},{},{}".format(start[0], start[1], start[2]) + "/" + "{},{},{}".format(end[0], end[1], end[2]) + "\n"
  133. # file.write(line)
  134. # rospy.loginfo("Welding sequence calculation completed")
  135. # 计算位姿 ########################################################################################################
  136. if len(sorted_welds_withnoflag)!=2:
  137. rospy.loginfo("焊缝数量错误!!!!")
  138. #print(len(sorted_welds_withnoflag))
  139. # exit(0)
  140. return len(sorted_welds_withnoflag)
  141. else:
  142. point1=sorted_welds_withnoflag[0]
  143. point2=sorted_welds_withnoflag[1]
  144. # middle1=[(point1[0][0]+point1[1][0])/2,(point1[0][1]+point1[1][1])/2,(point1[0][2]+point1[1][2])/2]
  145. # middle2=[(point2[0][0]+point2[1][0])/2,(point2[0][1]+point2[1][1])/2,(point2[0][2]+point2[1][2])/2]
  146. middle1=[(point1[0][0]+point1[1][0])/2,(point1[0][1]+point1[1][1])/2,0]
  147. middle2=[(point2[0][0]+point2[1][0])/2,(point2[0][1]+point2[1][1])/2,0]#不计算z的偏移 就是相对于水平的旋转
  148. hanfeng1=[point1[1][0]-point1[0][0],point1[1][1]-point1[0][1],point1[1][2]-point1[0][2]]
  149. hanfeng2=[point2[1][0]-point2[0][0],point2[1][1]-point2[0][1],point2[1][2]-point2[0][2]]
  150. #sorted_welds #[[起点,终点,序号],[],[]。。。。]
  151. faxiang=[middle2[0]-middle1[0],middle2[1]-middle1[1],middle2[2]-middle1[2]]
  152. #shuzhi=np.cross(faxiang,hanfeng1)#向量叉乘
  153. shuzhi=[0,0,1]
  154. faxiang/= np.linalg.norm(faxiang) # 单位化
  155. shuzhi /= np.linalg.norm(shuzhi) # 单位化
  156. #ang_md=calculate_angle_with_xy_plane(middle1,middle2)
  157. # if(point1[0][0]<0 and point2[0][0]<0):
  158. # fangxiang=faxiang+shuzhi*5.6 #arctan(4)=76度
  159. # else:
  160. # fangxiang=faxiang-shuzhi*5.6 #arctan(4)=76度
  161. ang_deg=35
  162. fangxiang=-faxiang-shuzhi*tan(radians(90-ang_deg))#arctan(4)=76度
  163. mat=compute_pose_R(fangxiang,hanfeng1)
  164. q = R.from_matrix(mat).as_quat()#应该是RPY转四元数
  165. result.append(("s", point1[0],point1[1], q, q))
  166. # if(point1[0][0]<0 and point2[0][0]<0):
  167. # fangxiang=-faxiang+shuzhi*5.6 #arctan(4)=76度
  168. # else:
  169. # fangxiang=-faxiang-shuzhi*5.6 #arctan(4)=76度
  170. fangxiang=faxiang-shuzhi*tan(radians(90-ang_deg)) #arctan(4)=76度
  171. mat=compute_pose_R(fangxiang,hanfeng1)
  172. q = R.from_matrix(mat).as_quat()#应该是RPY转四元数
  173. result.append(("s", point2[0],point2[1], q, q))
  174. # 打开文件准备写入
  175. with open(file_path_result, "w") as file:
  176. for group in result:
  177. # 对于每个数据组,将每个部分用逗号连接,然后在各部分间插入斜杠
  178. line = "/".join([",".join(map(str, sublist)) for sublist in group]) + "\n"
  179. file.write(line)
  180. rospy.loginfo("顺序与姿态规划完毕 0v0")
  181. return len(sorted_welds_withnoflag)
  182. if __name__ == '__main__':
  183. run()