当前位置:   article > 正文

代码浅析Point-LIO_pointlio与fastlio

pointlio与fastlio

0. 简介

对于最近出来的Point-LIO(鲁棒高带宽激光惯性里程计),本人还是非常该兴趣的,为此花了一些时间重点分析了Point-LIO的代码,并研究了它相较于Fast-LIO2的区别

1. laserMapping.cpp

第一部分就是实现对激光雷达视场角的图像分割。首先定义了一个BoxPointType类型的局部地图(LocalMap_Points)和一个bool类型的变量(Localmap_Initialized),表示是否已经初始化局部地图。然后,在lasermap_fov_segment()函数中,根据激光雷达的姿态计算出激光雷达的位置(pos_LiD),并根据移动阈值(MOV_THRESHOLD)判断是否需要移动局部地图。如果需要移动,则计算新的局部地图边界(New_LocalMap_Points),并将需要移除的框添加到cub_needrm中。最后,根据cub_needrm中的框删除点云,完成图像分割。

  1. BoxPointType LocalMap_Points;
  2. bool Localmap_Initialized = false;
  3. void lasermap_fov_segment() //针对激光雷达视场角来完成图像分割
  4. {
  5. cub_needrm.shrink_to_fit(); //将容量设置为容器的长度
  6. V3D pos_LiD;
  7. if (use_imu_as_input) {
  8. pos_LiD =
  9. kf_input.x_.pos + kf_input.x_.rot.normalized() *
  10. Lidar_T_wrt_IMU; //计算激光雷达在当前位姿下的位置
  11. } else {
  12. pos_LiD =
  13. kf_output.x_.pos + kf_output.x_.rot.normalized() * Lidar_T_wrt_IMU;
  14. }
  15. if (!Localmap_Initialized) { //判断是否需要初始化局部地图
  16. //将局部地图的边界设置为当前位置的正方形区域,并将Localmap_Initialized设置为true
  17. for (int i = 0; i < 3; i++) {
  18. LocalMap_Points.vertex_min[i] = pos_LiD(i) - cube_len / 2.0;
  19. LocalMap_Points.vertex_max[i] = pos_LiD(i) + cube_len / 2.0;
  20. }
  21. Localmap_Initialized = true;
  22. return;
  23. }
  24. float dist_to_map_edge[3][2];
  25. bool need_move = false;
  26. //如果不需要初始化,则计算激光雷达当前位姿与局部地图边界的距离,并根据移动阈值判断是否需要移动局部地图
  27. for (int i = 0; i < 3; i++) {
  28. dist_to_map_edge[i][0] = fabs(pos_LiD(i) - LocalMap_Points.vertex_min[i]);
  29. dist_to_map_edge[i][1] = fabs(pos_LiD(i) - LocalMap_Points.vertex_max[i]);
  30. if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE ||
  31. dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE)
  32. need_move = true;
  33. }
  34. //如果需要移动,则计算新的局部地图边界
  35. if (!need_move)
  36. return;
  37. BoxPointType New_LocalMap_Points, tmp_boxpoints;
  38. New_LocalMap_Points = LocalMap_Points;
  39. float mov_dist = max((cube_len - 2.0 * MOV_THRESHOLD * DET_RANGE) * 0.5 * 0.9,
  40. double(DET_RANGE * (MOV_THRESHOLD - 1)));
  41. for (int i = 0; i < 3; i++) {
  42. tmp_boxpoints = LocalMap_Points;
  43. if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE) {
  44. New_LocalMap_Points.vertex_max[i] -= mov_dist;
  45. New_LocalMap_Points.vertex_min[i] -= mov_dist;
  46. tmp_boxpoints.vertex_min[i] = LocalMap_Points.vertex_max[i] - mov_dist;
  47. cub_needrm.emplace_back(tmp_boxpoints); //将需要移除的框添加到cub_needrm中
  48. } else if (dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE) {
  49. New_LocalMap_Points.vertex_max[i] += mov_dist;
  50. New_LocalMap_Points.vertex_min[i] += mov_dist;
  51. tmp_boxpoints.vertex_max[i] = LocalMap_Points.vertex_min[i] + mov_dist;
  52. cub_needrm.emplace_back(tmp_boxpoints);
  53. }
  54. }
  55. LocalMap_Points = New_LocalMap_Points;
  56. points_cache_collect();
  57. if (cub_needrm.size() > 0)
  58. int kdtree_delete_counter = ikdtree.Delete_Point_Boxes(
  59. cub_needrm); //收集点云缓存,并根据cub_needrm中的框删除点云,返回删除的框数量。
  60. }

下面我们来看看怎么使用这个函数的,下面的代码主要实现了以下的操作:

1.对激光雷达采集到的点云进行空间下采样和时间压缩;

2.初始化地图kd-tree;

3.使用迭代最近点算法(ICP)和卡尔曼滤波更新地图。其中,ICP主要用于点云配准,卡尔曼滤波则用于对机器人位姿进行估计和更新。

  1. lasermap_fov_segment();
  2. /*** downsample the feature points in a scan ***/
  3. t1 = omp_get_wtime();
  4. if (space_down_sample) { //空间下采样
  5. downSizeFilterSurf.setInputCloud(feats_undistort);
  6. downSizeFilterSurf.filter(*feats_down_body);
  7. sort(feats_down_body->points.begin(), feats_down_body->points.end(),
  8. time_list); //按时间排序
  9. } else {
  10. feats_down_body = Measures.lidar;
  11. sort(feats_down_body->points.begin(), feats_down_body->points.end(),
  12. time_list);
  13. }
  14. time_seq = time_compressing<int>(feats_down_body); //时间压缩
  15. feats_down_size = feats_down_body->points.size(); //点云数量
  16. /*** initialize the map kdtree ***/
  17. if (!init_map) {
  18. if (ikdtree.Root_Node == nullptr) //
  19. // if(feats_down_size > 5)
  20. {
  21. ikdtree.set_downsample_param(filter_size_map_min); //设置滤波参数
  22. }
  23. feats_down_world->resize(feats_down_size); //初始化点云
  24. for (int i = 0; i < feats_down_size; i++) {
  25. pointBodyToWorld(&(feats_down_body->points[i]),
  26. &(feats_down_world->points[i])); //转换到世界坐标系
  27. }
  28. for (size_t i = 0; i < feats_down_world->size(); i++) {
  29. init_feats_world->points.emplace_back(
  30. feats_down_world
  31. ->points[i]); //将转换到世界坐标西的点云加入到init_feats_world
  32. }
  33. if (init_feats_world->size() < init_map_size) //等待构建地图
  34. continue;
  35. ikdtree.Build(init_feats_world->points); //构建地图
  36. init_map = true;
  37. publish_init_kdtree(pubLaserCloudMap); //(pubLaserCloudFullRes);
  38. continue;
  39. }
  40. /*** ICP and Kalman filter update ***/
  41. normvec->resize(feats_down_size);
  42. feats_down_world->resize(feats_down_size);
  43. Nearest_Points.resize(feats_down_size);
  44. t2 = omp_get_wtime(); //初始化t2为当前时间
  45. /*** iterated state estimation ***/
  46. crossmat_list.reserve(feats_down_size);
  47. pbody_list.reserve(feats_down_size);
  48. // pbody_ext_list.reserve(feats_down_size);
  49. //对于每个点,将其坐标转换为V3D类型的point_this
  50. for (size_t i = 0; i < feats_down_body->size(); i++) {
  51. V3D point_this(feats_down_body->points[i].x,
  52. feats_down_body->points[i].y,
  53. feats_down_body->points[i].z);
  54. pbody_list[i] = point_this;
  55. //如果使用外参估计
  56. if (extrinsic_est_en) {
  57. if (!use_imu_as_input) {
  58. //对于每个点,使用卡尔曼滤波估计出的外参对其进行坐标变换
  59. point_this = kf_output.x_.offset_R_L_I.normalized() * point_this +
  60. kf_output.x_.offset_T_L_I;
  61. } else {
  62. point_this = kf_input.x_.offset_R_L_I.normalized() * point_this +
  63. kf_input.x_.offset_T_L_I;
  64. }
  65. } else {
  66. // 使用Lidar_R_wrt_IMU和Lidar_T_wrt_IMU对其进行变换
  67. point_this = Lidar_R_wrt_IMU * point_this + Lidar_T_wrt_IMU;
  68. }
  69. M3D point_crossmat;
  70. point_crossmat << SKEW_SYM_MATRX(point_this); //根据当前点生成矩阵
  71. crossmat_list[i] = point_crossmat;
  72. }
  73. if (!use_imu_as_input) {
  74. bool imu_upda_cov = false; //是否需要更新imu的协方差
  75. effct_feat_num = 0;
  76. /**** point by point update ****/
  77. double pcl_beg_time =
  78. Measures
  79. .lidar_beg_time; //首先设置pcl_beg_time为Measures.lidar_beg_time,idx为-1
  80. idx = -1;
  81. for (k = 0; k < time_seq.size(); k++) {
  82. PointType &point_body = feats_down_body->points[idx + time_seq[k]];
  83. time_current =
  84. point_body.curvature / 1000.0 +
  85. pcl_beg_time; //找到对应的点并计算出当前时间time_current
  86. if (is_first_frame) {
  87. if (imu_en) { //如果是第一帧,且启用了IMU,那么需要找到最近的IMU数据
  88. while (time_current > imu_next.header.stamp.toSec()) {
  89. imu_last = imu_next;
  90. imu_next = *(imu_deque.front());
  91. imu_deque.pop_front();
  92. // imu_deque.pop();
  93. }
  94. //计算出对应的角速度和加速度
  95. angvel_avr << imu_last.angular_velocity.x,
  96. imu_last.angular_velocity.y, imu_last.angular_velocity.z;
  97. acc_avr << imu_last.linear_acceleration.x,
  98. imu_last.linear_acceleration.y,
  99. imu_last.linear_acceleration.z;
  100. }
  101. is_first_frame = false;
  102. imu_upda_cov = true;
  103. time_update_last = time_current;
  104. time_predict_last_const = time_current;
  105. }
  106. if (imu_en) {
  107. bool imu_comes = time_current > imu_next.header.stamp.toSec();
  108. // 如果启用了IMU,那么需要在当前时间之前的IMU数据都进行卡尔曼滤波更新
  109. while (imu_comes) {
  110. imu_upda_cov = true;
  111. //将IMU数据中的角速度和线性加速度分别存储到angvel_avr和acc_avr中
  112. angvel_avr << imu_next.angular_velocity.x,
  113. imu_next.angular_velocity.y, imu_next.angular_velocity.z;
  114. acc_avr << imu_next.linear_acceleration.x,
  115. imu_next.linear_acceleration.y,
  116. imu_next.linear_acceleration.z;
  117. /*** 对协方差进行更新 ***/
  118. imu_last = imu_next; //将当前IMU数据存储为imu_last
  119. imu_next = *(imu_deque.front()); //将下一个IMU数据存储为imu_next
  120. imu_deque.pop_front();
  121. double dt = imu_last.header.stamp.toSec() -
  122. time_predict_last_const; //接着计算时间差dt
  123. kf_output.predict(dt, Q_output, input_in, true,
  124. false); //通过kf_output.predict函数进行预测
  125. time_predict_last_const =
  126. imu_last.header.stamp.toSec(); // big problem
  127. imu_comes = time_current > imu_next.header.stamp.toSec();
  128. // if (!imu_comes)
  129. {
  130. double dt_cov = imu_last.header.stamp.toSec() -
  131. time_update_last; //就计算时间差dt_cov
  132. if (dt_cov > 0.0) {
  133. time_update_last = imu_last.header.stamp.toSec();
  134. double propag_imu_start = omp_get_wtime();
  135. kf_output.predict(dt_cov, Q_output, input_in, false,
  136. true); //行卡尔曼滤波预测
  137. propag_time += omp_get_wtime() - propag_imu_start;
  138. double solve_imu_start = omp_get_wtime();
  139. kf_output.update_iterated_dyn_share_IMU(); //进行eskf迭代更新
  140. solve_time += omp_get_wtime() - solve_imu_start;
  141. }
  142. }
  143. }
  144. }
  145. double dt = time_current - time_predict_last_const;
  146. double propag_state_start = omp_get_wtime();
  147. if (!prop_at_freq_of_imu) {
  148. double dt_cov = time_current - time_update_last;
  149. if (dt_cov > 0.0) {
  150. kf_output.predict(dt_cov, Q_output, input_in, false, true);
  151. time_update_last = time_current;
  152. }
  153. }
  154. kf_output.predict(dt, Q_output, input_in, true, false);
  155. propag_time += omp_get_wtime() - propag_state_start;
  156. time_predict_last_const = time_current;
  157. // if(k == 0)
  158. // {
  159. // fout_imu_pbp << Measures.lidar_last_time - first_lidar_time <<
  160. // " " << imu_last.angular_velocity.x << " " <<
  161. // imu_last.angular_velocity.y << " " <<
  162. // imu_last.angular_velocity.z \ // << " " << imu_last.linear_acceleration.x << " " <<
  163. // imu_last.linear_acceleration.y << " " <<
  164. // imu_last.linear_acceleration.z << endl;
  165. // }
  166. double t_update_start = omp_get_wtime();
  167. if (feats_down_size < 1) {
  168. ROS_WARN("No point, skip this scan!\n");
  169. idx += time_seq[k];
  170. continue;
  171. }
  172. if (!kf_output.update_iterated_dyn_share_modified()) {
  173. idx = idx + time_seq[k];
  174. continue;
  175. }
  176. if (prop_at_freq_of_imu) {
  177. double dt_cov = time_current - time_update_last;
  178. if (!imu_en &&
  179. (dt_cov >=
  180. imu_time_inte)) // //需要在当前时间之前的时间间隔大于imu_time_inte时进行卡尔曼滤波协方差更新
  181. {
  182. double propag_cov_start = omp_get_wtime();
  183. kf_output.predict(
  184. dt_cov, Q_output, input_in, false,
  185. true); //对于每个时间片中的点,进行卡尔曼滤波更新,并将点转换到世界坐标系中
  186. imu_upda_cov = false;
  187. time_update_last = time_current;
  188. propag_time += omp_get_wtime() - propag_cov_start;
  189. }
  190. }
  191. solve_start = omp_get_wtime();

点击代码浅析Point-LIO - 古月居 可查看全文

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

闽ICP备14008679号