赞
踩
对于最近出来的Point-LIO(鲁棒高带宽激光惯性里程计),本人还是非常该兴趣的,为此花了一些时间重点分析了Point-LIO的代码,并研究了它相较于Fast-LIO2的区别
第一部分就是实现对激光雷达视场角的图像分割。首先定义了一个BoxPointType类型的局部地图(LocalMap_Points)和一个bool类型的变量(Localmap_Initialized),表示是否已经初始化局部地图。然后,在lasermap_fov_segment()函数中,根据激光雷达的姿态计算出激光雷达的位置(pos_LiD),并根据移动阈值(MOV_THRESHOLD)判断是否需要移动局部地图。如果需要移动,则计算新的局部地图边界(New_LocalMap_Points),并将需要移除的框添加到cub_needrm中。最后,根据cub_needrm中的框删除点云,完成图像分割。
- BoxPointType LocalMap_Points;
- bool Localmap_Initialized = false;
- void lasermap_fov_segment() //针对激光雷达视场角来完成图像分割
- {
- cub_needrm.shrink_to_fit(); //将容量设置为容器的长度
-
- V3D pos_LiD;
- if (use_imu_as_input) {
- pos_LiD =
- kf_input.x_.pos + kf_input.x_.rot.normalized() *
- Lidar_T_wrt_IMU; //计算激光雷达在当前位姿下的位置
- } else {
- pos_LiD =
- kf_output.x_.pos + kf_output.x_.rot.normalized() * Lidar_T_wrt_IMU;
- }
- if (!Localmap_Initialized) { //判断是否需要初始化局部地图
- //将局部地图的边界设置为当前位置的正方形区域,并将Localmap_Initialized设置为true
- for (int i = 0; i < 3; i++) {
- LocalMap_Points.vertex_min[i] = pos_LiD(i) - cube_len / 2.0;
- LocalMap_Points.vertex_max[i] = pos_LiD(i) + cube_len / 2.0;
- }
- Localmap_Initialized = true;
- return;
- }
- float dist_to_map_edge[3][2];
- bool need_move = false;
- //如果不需要初始化,则计算激光雷达当前位姿与局部地图边界的距离,并根据移动阈值判断是否需要移动局部地图
- for (int i = 0; i < 3; i++) {
- dist_to_map_edge[i][0] = fabs(pos_LiD(i) - LocalMap_Points.vertex_min[i]);
- dist_to_map_edge[i][1] = fabs(pos_LiD(i) - LocalMap_Points.vertex_max[i]);
- if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE ||
- dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE)
- need_move = true;
- }
- //如果需要移动,则计算新的局部地图边界
- if (!need_move)
- return;
- BoxPointType New_LocalMap_Points, tmp_boxpoints;
- New_LocalMap_Points = LocalMap_Points;
- float mov_dist = max((cube_len - 2.0 * MOV_THRESHOLD * DET_RANGE) * 0.5 * 0.9,
- double(DET_RANGE * (MOV_THRESHOLD - 1)));
- for (int i = 0; i < 3; i++) {
- tmp_boxpoints = LocalMap_Points;
- if (dist_to_map_edge[i][0] <= MOV_THRESHOLD * DET_RANGE) {
- New_LocalMap_Points.vertex_max[i] -= mov_dist;
- New_LocalMap_Points.vertex_min[i] -= mov_dist;
- tmp_boxpoints.vertex_min[i] = LocalMap_Points.vertex_max[i] - mov_dist;
- cub_needrm.emplace_back(tmp_boxpoints); //将需要移除的框添加到cub_needrm中
- } else if (dist_to_map_edge[i][1] <= MOV_THRESHOLD * DET_RANGE) {
- New_LocalMap_Points.vertex_max[i] += mov_dist;
- New_LocalMap_Points.vertex_min[i] += mov_dist;
- tmp_boxpoints.vertex_max[i] = LocalMap_Points.vertex_min[i] + mov_dist;
- cub_needrm.emplace_back(tmp_boxpoints);
- }
- }
- LocalMap_Points = New_LocalMap_Points;
-
- points_cache_collect();
- if (cub_needrm.size() > 0)
- int kdtree_delete_counter = ikdtree.Delete_Point_Boxes(
- cub_needrm); //收集点云缓存,并根据cub_needrm中的框删除点云,返回删除的框数量。
- }
下面我们来看看怎么使用这个函数的,下面的代码主要实现了以下的操作:
1.对激光雷达采集到的点云进行空间下采样和时间压缩;
2.初始化地图kd-tree;
3.使用迭代最近点算法(ICP)和卡尔曼滤波更新地图。其中,ICP主要用于点云配准,卡尔曼滤波则用于对机器人位姿进行估计和更新。
- lasermap_fov_segment();
- /*** downsample the feature points in a scan ***/
- t1 = omp_get_wtime();
- if (space_down_sample) { //空间下采样
- downSizeFilterSurf.setInputCloud(feats_undistort);
- downSizeFilterSurf.filter(*feats_down_body);
- sort(feats_down_body->points.begin(), feats_down_body->points.end(),
- time_list); //按时间排序
- } else {
- feats_down_body = Measures.lidar;
- sort(feats_down_body->points.begin(), feats_down_body->points.end(),
- time_list);
- }
- time_seq = time_compressing<int>(feats_down_body); //时间压缩
- feats_down_size = feats_down_body->points.size(); //点云数量
-
- /*** initialize the map kdtree ***/
- if (!init_map) {
- if (ikdtree.Root_Node == nullptr) //
- // if(feats_down_size > 5)
- {
- ikdtree.set_downsample_param(filter_size_map_min); //设置滤波参数
- }
-
- feats_down_world->resize(feats_down_size); //初始化点云
- for (int i = 0; i < feats_down_size; i++) {
- pointBodyToWorld(&(feats_down_body->points[i]),
- &(feats_down_world->points[i])); //转换到世界坐标系
- }
- for (size_t i = 0; i < feats_down_world->size(); i++) {
- init_feats_world->points.emplace_back(
- feats_down_world
- ->points[i]); //将转换到世界坐标西的点云加入到init_feats_world
- }
- if (init_feats_world->size() < init_map_size) //等待构建地图
- continue;
- ikdtree.Build(init_feats_world->points); //构建地图
- init_map = true;
- publish_init_kdtree(pubLaserCloudMap); //(pubLaserCloudFullRes);
- continue;
- }
- /*** ICP and Kalman filter update ***/
- normvec->resize(feats_down_size);
- feats_down_world->resize(feats_down_size);
-
- Nearest_Points.resize(feats_down_size);
-
- t2 = omp_get_wtime(); //初始化t2为当前时间
-
- /*** iterated state estimation ***/
- crossmat_list.reserve(feats_down_size);
- pbody_list.reserve(feats_down_size);
- // pbody_ext_list.reserve(feats_down_size);
-
- //对于每个点,将其坐标转换为V3D类型的point_this
- for (size_t i = 0; i < feats_down_body->size(); i++) {
- V3D point_this(feats_down_body->points[i].x,
- feats_down_body->points[i].y,
- feats_down_body->points[i].z);
- pbody_list[i] = point_this;
- //如果使用外参估计
- if (extrinsic_est_en) {
- if (!use_imu_as_input) {
- //对于每个点,使用卡尔曼滤波估计出的外参对其进行坐标变换
- point_this = kf_output.x_.offset_R_L_I.normalized() * point_this +
- kf_output.x_.offset_T_L_I;
- } else {
- point_this = kf_input.x_.offset_R_L_I.normalized() * point_this +
- kf_input.x_.offset_T_L_I;
- }
- } else {
- // 使用Lidar_R_wrt_IMU和Lidar_T_wrt_IMU对其进行变换
- point_this = Lidar_R_wrt_IMU * point_this + Lidar_T_wrt_IMU;
- }
- M3D point_crossmat;
- point_crossmat << SKEW_SYM_MATRX(point_this); //根据当前点生成矩阵
- crossmat_list[i] = point_crossmat;
- }
-
- if (!use_imu_as_input) {
- bool imu_upda_cov = false; //是否需要更新imu的协方差
- effct_feat_num = 0;
- /**** point by point update ****/
-
- double pcl_beg_time =
- Measures
- .lidar_beg_time; //首先设置pcl_beg_time为Measures.lidar_beg_time,idx为-1
- idx = -1;
- for (k = 0; k < time_seq.size(); k++) {
- PointType &point_body = feats_down_body->points[idx + time_seq[k]];
-
- time_current =
- point_body.curvature / 1000.0 +
- pcl_beg_time; //找到对应的点并计算出当前时间time_current
-
- if (is_first_frame) {
- if (imu_en) { //如果是第一帧,且启用了IMU,那么需要找到最近的IMU数据
- while (time_current > imu_next.header.stamp.toSec()) {
- imu_last = imu_next;
- imu_next = *(imu_deque.front());
- imu_deque.pop_front();
- // imu_deque.pop();
- }
- //计算出对应的角速度和加速度
- angvel_avr << imu_last.angular_velocity.x,
- imu_last.angular_velocity.y, imu_last.angular_velocity.z;
- acc_avr << imu_last.linear_acceleration.x,
- imu_last.linear_acceleration.y,
- imu_last.linear_acceleration.z;
- }
- is_first_frame = false;
- imu_upda_cov = true;
- time_update_last = time_current;
- time_predict_last_const = time_current;
- }
- if (imu_en) {
- bool imu_comes = time_current > imu_next.header.stamp.toSec();
- // 如果启用了IMU,那么需要在当前时间之前的IMU数据都进行卡尔曼滤波更新
- while (imu_comes) {
- imu_upda_cov = true;
- //将IMU数据中的角速度和线性加速度分别存储到angvel_avr和acc_avr中
- angvel_avr << imu_next.angular_velocity.x,
- imu_next.angular_velocity.y, imu_next.angular_velocity.z;
- acc_avr << imu_next.linear_acceleration.x,
- imu_next.linear_acceleration.y,
- imu_next.linear_acceleration.z;
-
- /*** 对协方差进行更新 ***/
- imu_last = imu_next; //将当前IMU数据存储为imu_last
- imu_next = *(imu_deque.front()); //将下一个IMU数据存储为imu_next
- imu_deque.pop_front();
- double dt = imu_last.header.stamp.toSec() -
- time_predict_last_const; //接着计算时间差dt
- kf_output.predict(dt, Q_output, input_in, true,
- false); //通过kf_output.predict函数进行预测
- time_predict_last_const =
- imu_last.header.stamp.toSec(); // big problem
- imu_comes = time_current > imu_next.header.stamp.toSec();
- // if (!imu_comes)
- {
- double dt_cov = imu_last.header.stamp.toSec() -
- time_update_last; //就计算时间差dt_cov
-
- if (dt_cov > 0.0) {
- time_update_last = imu_last.header.stamp.toSec();
- double propag_imu_start = omp_get_wtime();
-
- kf_output.predict(dt_cov, Q_output, input_in, false,
- true); //行卡尔曼滤波预测
-
- propag_time += omp_get_wtime() - propag_imu_start;
- double solve_imu_start = omp_get_wtime();
- kf_output.update_iterated_dyn_share_IMU(); //进行eskf迭代更新
- solve_time += omp_get_wtime() - solve_imu_start;
- }
- }
- }
- }
-
- double dt = time_current - time_predict_last_const;
- double propag_state_start = omp_get_wtime();
- if (!prop_at_freq_of_imu) {
- double dt_cov = time_current - time_update_last;
- if (dt_cov > 0.0) {
- kf_output.predict(dt_cov, Q_output, input_in, false, true);
- time_update_last = time_current;
- }
- }
- kf_output.predict(dt, Q_output, input_in, true, false);
- propag_time += omp_get_wtime() - propag_state_start;
- time_predict_last_const = time_current;
- // if(k == 0)
- // {
- // fout_imu_pbp << Measures.lidar_last_time - first_lidar_time <<
- // " " << imu_last.angular_velocity.x << " " <<
- // imu_last.angular_velocity.y << " " <<
- // imu_last.angular_velocity.z \ // << " " << imu_last.linear_acceleration.x << " " <<
- // imu_last.linear_acceleration.y << " " <<
- // imu_last.linear_acceleration.z << endl;
- // }
-
- double t_update_start = omp_get_wtime();
-
- if (feats_down_size < 1) {
- ROS_WARN("No point, skip this scan!\n");
- idx += time_seq[k];
- continue;
- }
- if (!kf_output.update_iterated_dyn_share_modified()) {
- idx = idx + time_seq[k];
- continue;
- }
-
- if (prop_at_freq_of_imu) {
- double dt_cov = time_current - time_update_last;
- if (!imu_en &&
- (dt_cov >=
- imu_time_inte)) // //需要在当前时间之前的时间间隔大于imu_time_inte时进行卡尔曼滤波协方差更新
- {
- double propag_cov_start = omp_get_wtime();
- kf_output.predict(
- dt_cov, Q_output, input_in, false,
- true); //对于每个时间片中的点,进行卡尔曼滤波更新,并将点转换到世界坐标系中
- imu_upda_cov = false;
- time_update_last = time_current;
- propag_time += omp_get_wtime() - propag_cov_start;
- }
- }
-
- solve_start = omp_get_wtime();
点击代码浅析Point-LIO - 古月居 可查看全文
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。