当前位置:   article > 正文

python做的多激光雷达外参标定程序(初版本 完整版见专栏)_python 外参校准算法

python 外参校准算法

查阅了一番资料和现有的代码后发现,现在的多个激光雷达之间的标定程序都是ROS框架下面的,并且都是C++代码,需要安装的依赖也比较复杂,于是自己写了一个python版本的标定程序,依赖非常简单,Windows系统也可以运行。并且代码简单,扩展性比较好,后续想要同时标定2个以上的雷达也很方便拓展;

 

它能够解决上述的三个问题:

  1. 通过手动标定加自动配准可以更精确的标定
  2. 标定的参数包含了各个方向的角度和位移
  3. 可以实时手动调整点云的位姿,效率较高

 

 

标定流程为:

  1. 抽取每个雷达的点云数据的一帧数据保存为PCD文件,作为标定的原始数据
  2. 先手动标定两个雷达的点云,可以连续任意角度旋转,平移,效率较高,得到标定矩阵的初始值
  3. 根据标定的初始值,利用ICP算法找到最优的标定矩阵,使得B点云与A点云完全重合,得到最优的外参标定矩阵

 

 

 

# 通过以下按键来调整点云位姿:

# q,a  偏航角调整

# w,s  roll 翻滚角调整

# e,d  pitch 俯仰角调整

# r,f    X 轴调整

# t,g    Y 轴调整

# y,h    Z 轴调整

 

a05e42919f1347ae8cc2ce96f3593062.png

原始未标定数据

 

 

ac116ce2612d43dfa5bcfec3274a84ae.png

 

标定后的数据

 

 

程序如下,只放出了手动标定的部分

 

  1. # coding:utf-8
  2. import open3d as o3d
  3. import numpy as np
  4. import math
  5. import copy
  6. # 第一版本使用角度转换的旋转矩阵,
  7. # 然后把每次的旋转角度加起来,累计;
  8. # 或者把每次的旋转矩阵累乘,都不是最后的正确结果,很奇怪
  9. # 第二版本改为使用矩阵的逆矩阵,没问题了,应该是矩阵的旋转顺序有影响
  10. # 通过以下按键来调整点云位姿:
  11. # q,a 偏航角调整
  12. # w,s roll 翻滚角调整
  13. # e,d pitch 俯仰角调整
  14. # r,f X 轴调整
  15. # t,g Y 轴调整
  16. # y,h Z 轴调整
  17. def eulerAnglesToRotationMatrix(theta):
  18. rx = np.asarray([[1.0, 0.0, 0.0],
  19. [0.0, math.cos(theta[0]), -math.sin(theta[0])],
  20. [0.0, math.sin(theta[0]), math.cos(theta[0])]])
  21. ry = np.asarray([[math.cos(theta[1]), 0.0, math.sin(theta[1])],
  22. [0.0, 1.0, 0.0],
  23. [-math.sin(theta[1]), 0.0, math.cos(theta[1])]])
  24. rz = np.asarray([[math.cos(theta[2]), -math.sin(theta[2]), 0.0],
  25. [math.sin(theta[2]), math.cos(theta[2]), 0.0],
  26. [0.0, 0.0, 1.0]])
  27. # print(rx)
  28. # print(ry)
  29. # print(rz)
  30. # temp = np.matmul(rz,ry)
  31. # print(temp)
  32. # r = np.matmul(temp,rx)
  33. r = rz@ry@rx
  34. # print(r)
  35. return r
  36. # r = np.matmul(np.matmul(rz,ry),rx)
  37. def key_call(x):
  38. global to_reset,yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,distance_pixel
  39. # a = keyboard.KeyboardEvent('down',28,'enter')
  40. if x.event_type == 'down' and x.name == 'q':
  41. yaw_deg = yaw_deg + angel_pixel
  42. # // 转化为弧度
  43. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  44. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  45. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  46. print("你按下了 q 键",yaw_deg, roll_deg, pitch_deg)
  47. print("你按下了 q 键",roll_arc, pitch_arc, yaw_arc)
  48. rr = eulerAnglesToRotationMatrix([roll_arc, pitch_arc, yaw_arc])
  49. trans_init[0:3,0:3] = rr
  50. # print(trans_init)
  51. pcd_sourse.transform(trans_init)
  52. # vis.update_geometry(pcd_sourse)
  53. # if to_reset:
  54. # vis.reset_view_point(True)
  55. # to_reset = False
  56. # vis.poll_events()
  57. # vis.update_renderer()
  58. # print('to_reset ',to_reset)
  59. o3d.visualization.draw_geometries([pcd_target, pcd_sourse])
  60. def key_q(vis):
  61. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  62. pcd_sourse.rotate(temp_r)
  63. yaw_deg = yaw_deg + angel_pixel
  64. # // 转化为弧度
  65. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  66. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  67. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  68. print("你按下了 q 键",yaw_deg, roll_deg, pitch_deg)
  69. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  70. # r2 = o3d.geometry.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  71. pcd_sourse.rotate(R1) # 不指定旋转中心
  72. print("旋转矩阵:\n", R1)
  73. vis.update_geometry(pcd_sourse)
  74. # R1 = pcd_sourse.get_rotation_matrix_from_xyz((-roll_arc, -pitch_arc, -yaw_arc))
  75. R1 = np.linalg.inv(R1)
  76. temp_r = R1
  77. def key_a(vis):
  78. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  79. pcd_sourse.rotate(temp_r)
  80. yaw_deg = yaw_deg - angel_pixel
  81. # // 转化为弧度
  82. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  83. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  84. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  85. print("你按下了 a 键",yaw_deg, roll_deg, pitch_deg)
  86. # print("你按下了 q 键",roll_arc, pitch_arc, yaw_arc)
  87. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  88. pcd_sourse.rotate(R1) # 不指定旋转中心
  89. print("旋转矩阵:\n", R1)
  90. vis.update_geometry(pcd_sourse)
  91. # R1 = pcd_sourse.get_rotation_matrix_from_zyx((-roll_arc, -pitch_arc, -yaw_arc))
  92. R1 = np.linalg.inv(R1)
  93. temp_r = R1
  94. def key_w(vis):
  95. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  96. pcd_sourse.rotate(temp_r)
  97. roll_deg = roll_deg + angel_pixel
  98. # // 转化为弧度
  99. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  100. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  101. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  102. print("你按下了 w 键",yaw_deg, roll_deg, pitch_deg)
  103. # print("你按下了 q 键",roll_arc, pitch_arc, yaw_arc)
  104. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  105. pcd_sourse.rotate(R1) # 不指定旋转中心
  106. print("旋转矩阵:\n", R1)
  107. vis.update_geometry(pcd_sourse)
  108. # R1 = pcd_sourse.get_rotation_matrix_from_zyx((-roll_arc, -pitch_arc, -yaw_arc))
  109. R1 = np.linalg.inv(R1)
  110. temp_r = R1
  111. def key_s(vis):
  112. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  113. pcd_sourse.rotate(temp_r)
  114. roll_deg = roll_deg - angel_pixel
  115. # // 转化为弧度
  116. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  117. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  118. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  119. print("你按下了 s 键",yaw_deg, roll_deg, pitch_deg)
  120. # print("你按下了 q 键",roll_arc, pitch_arc, yaw_arc)
  121. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  122. pcd_sourse.rotate(R1) # 不指定旋转中心
  123. print("旋转矩阵:\n", R1)
  124. vis.update_geometry(pcd_sourse)
  125. # R1 = pcd_sourse.get_rotation_matrix_from_zyx((-roll_arc, -pitch_arc, -yaw_arc))
  126. R1 = np.linalg.inv(R1)
  127. temp_r = R1
  128. def key_e(vis):
  129. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  130. pcd_sourse.rotate(temp_r)
  131. pitch_deg = pitch_deg + angel_pixel
  132. # // 转化为弧度
  133. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  134. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  135. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  136. print("你按下了 e 键",yaw_deg, roll_deg, pitch_deg)
  137. # print("你按下了 q 键",roll_arc, pitch_arc, yaw_arc)
  138. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  139. pcd_sourse.rotate(R1) # 不指定旋转中心
  140. print("旋转矩阵:\n", R1)
  141. vis.update_geometry(pcd_sourse)
  142. # R1 = pcd_sourse.get_rotation_matrix_from_zyx((-roll_arc, -pitch_arc, -yaw_arc))
  143. R1 = np.linalg.inv(R1)
  144. temp_r = R1
  145. def key_d(vis):
  146. global yaw_deg,roll_deg,pitch_deg,trans_init,angel_pixel,temp_r
  147. pcd_sourse.rotate(temp_r)
  148. pitch_deg = pitch_deg - angel_pixel
  149. # // 转化为弧度
  150. roll_arc = roll_deg * DEG_TO_ARC; # // 绕X轴
  151. pitch_arc = pitch_deg * DEG_TO_ARC; # // 绕Y轴
  152. yaw_arc = yaw_deg * DEG_TO_ARC; # // 绕Z轴
  153. print("你按下了 d 键",yaw_deg, roll_deg, pitch_deg)
  154. R1 = pcd_sourse.get_rotation_matrix_from_xyz((roll_arc, pitch_arc, yaw_arc))
  155. pcd_sourse.rotate(R1) # 不指定旋转中心
  156. print("旋转矩阵:\n", R1)
  157. vis.update_geometry(pcd_sourse)
  158. # R1 = pcd_sourse.get_rotation_matrix_from_zyx((-roll_arc, -pitch_arc, -yaw_arc))
  159. # pcd_sourse.rotate(R1) # 不指定旋转中心
  160. R1 = np.linalg.inv(R1)
  161. temp_r = R1
  162. # xyz move
  163. def key_r(vis):
  164. global xd,yd,zd,trans_init,temp_t,distance_pixel
  165. # pcd_sourse.transform(trans_init)
  166. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  167. xd = xd + distance_pixel
  168. print("你按下了 r 键; xyz偏移为 ", xd,yd,zd)
  169. pcd_sourse.translate((xd, yd, zd))
  170. vis.update_geometry(pcd_sourse)
  171. temp_t[0] = -xd
  172. temp_t[1] = -yd
  173. temp_t[2] = -zd
  174. def key_f(vis):
  175. global xd,yd,zd,trans_init,temp_t,distance_pixel
  176. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  177. xd = xd - distance_pixel
  178. print("你按下了 f 键; xyz偏移为 ", xd,yd,zd)
  179. pcd_sourse.translate((xd, yd, zd))
  180. vis.update_geometry(pcd_sourse)
  181. temp_t[0] = -xd
  182. temp_t[1] = -yd
  183. temp_t[2] = -zd
  184. def key_t(vis):
  185. global xd,yd,zd,trans_init,temp_t,distance_pixel
  186. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  187. yd = yd + distance_pixel
  188. print("你按下了 t 键; xyz偏移为 ", xd,yd,zd)
  189. pcd_sourse.translate((xd, yd, zd))
  190. vis.update_geometry(pcd_sourse)
  191. temp_t[0] = -xd
  192. temp_t[1] = -yd
  193. temp_t[2] = -zd
  194. def key_g(vis):
  195. global xd,yd,zd,trans_init,temp_t,distance_pixel
  196. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  197. yd = yd - distance_pixel
  198. print("你按下了 g 键; xyz偏移为 ", xd,yd,zd)
  199. pcd_sourse.translate((xd, yd, zd))
  200. vis.update_geometry(pcd_sourse)
  201. temp_t[0] = -xd
  202. temp_t[1] = -yd
  203. temp_t[2] = -zd
  204. def key_y(vis):
  205. global xd,yd,zd,trans_init,temp_t,distance_pixel
  206. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  207. zd = zd + distance_pixel
  208. print("你按下了 y 键; xyz偏移为 ", xd,yd,zd)
  209. pcd_sourse.translate((xd, yd, zd))
  210. vis.update_geometry(pcd_sourse)
  211. temp_t[0] = -xd
  212. temp_t[1] = -yd
  213. temp_t[2] = -zd
  214. def key_h(vis):
  215. global xd,yd,zd,trans_init,temp_t,distance_pixel
  216. pcd_sourse.translate((temp_t[0], temp_t[1], temp_t[2]))
  217. zd = zd - distance_pixel
  218. print("你按下了 h 键; xyz偏移为 ", xd,yd,zd)
  219. pcd_sourse.translate((xd, yd, zd))
  220. vis.update_geometry(pcd_sourse)
  221. temp_t[0] = -xd
  222. temp_t[1] = -yd
  223. temp_t[2] = -zd
  224. def filter_dis(cloud):
  225. tp = np.asarray(cloud.points)
  226. dis = tp[:, 0]*tp[:, 0] + tp[:, 1]*tp[:, 1] + tp[:, 2]*tp[:, 2]
  227. new_data_c = tp[dis >= 5*5]
  228. # 高度过滤
  229. z = new_data_c[:, 2]
  230. new_data_c = new_data_c[z >= -0.6]
  231. # new_cloud = o3d.geometry.PointCloud()
  232. # new_cloud.points = o3d.utility.Vector3dVector(new_data_c)
  233. cloud.points = o3d.utility.Vector3dVector(new_data_c)
  234. # return new_cloud
  235. if __name__ == '__main__':
  236. trans_init = np.asarray([[1.0, 0.0, 0.0, 0.0],
  237. [0.0, 1.0, 0.0, 0.0],
  238. [0.0, 0.0, 1.0, 0.0],
  239. [0.0, 0.0, 0.0, 1.0]]).astype(np.float64)
  240. temp_r = np.asarray([[1.0, 0.0, 0.0],
  241. [0.0, 1.0, 0.0],
  242. [0.0, 0.0, 1.0]]).astype(np.float64)
  243. temp_t = np.asarray([0.0, 0.0, 0.0]).astype(np.float64)
  244. # trans_init = np.asarray([
  245. # [-6.97562996e-02 , 9.97564062e-01 ,1.74532925e-07, 3.6-1.54],
  246. # [-9.97564062e-01 ,- 6.97562996e-02 ,- 1.74532925e-07, -5.04999+2.60],
  247. # [-1.61933003e-07 ,- 1.86282545e-07 , 1.00000000e+00, 0 ],
  248. # [0.0, 0.0, 0.0, 1.0]]).astype(np.float64)
  249. # 分辨率
  250. angel_pixel = 1.
  251. distance_pixel = 0.05
  252. path1 = r"E:\cpp_project\lidar_app_calib\pcldemo\lidar1.pcd"
  253. path2 = r"E:\cpp_project\lidar_app_calib\pcldemo\lidar2.pcd"
  254. pcd_target = o3d.io.read_point_cloud(path1)
  255. pcd_sourse = o3d.io.read_point_cloud(path2)
  256. pcd_target.paint_uniform_color([0, 1, 0])
  257. pcd_sourse.paint_uniform_color([1, 0, 0])
  258. pcd_sourse.transform(trans_init)
  259. vis = o3d.visualization.Visualizer()
  260. ARC_TO_DEG = 57.29577951308238;
  261. DEG_TO_ARC = 0.0174532925199433;
  262. # // 设定车体欧拉角(角度),绕固定轴
  263. roll_deg = 0.00001; # // 绕X轴
  264. pitch_deg = 0.00001; # // 绕Y轴
  265. yaw_deg = 0.00001; # // 绕Z轴
  266. xd = 0.000001;
  267. yd = 0.000001;
  268. zd = 0.000001;
  269. key_to_callback = {}
  270. key_to_callback[ord("Q")] = key_q
  271. key_to_callback[ord("A")] = key_a
  272. key_to_callback[ord("W")] = key_w
  273. key_to_callback[ord("S")] = key_s
  274. key_to_callback[ord("E")] = key_e
  275. key_to_callback[ord("D")] = key_d
  276. key_to_callback[ord("R")] = key_r
  277. key_to_callback[ord("F")] = key_f
  278. key_to_callback[ord("T")] = key_t
  279. key_to_callback[ord("G")] = key_g
  280. key_to_callback[ord("Y")] = key_y
  281. key_to_callback[ord("H")] = key_h
  282. o3d.visualization.draw_geometries_with_key_callbacks([pcd_target, pcd_sourse], key_to_callback)

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/420951
推荐阅读
相关标签
  

闽ICP备14008679号