本文是我在学习深蓝学院的激光slam课程第四次作业的笔记。本次作业主要是熟悉 imls-icp 的过程以及对 csm 接口调用,除此之外还有对课上讲的四种 icp 算法进行总结以及想一下 icp 的改进思路。
实现两帧之间的 IMLS-ICP 激光匹配
补充代码,实现两帧间的 IMLS-ICP 激光匹配;
前提准备
编程环境: ubuntu 18.04 + ROS medodic
IDE: CLion, 安装官方教程配置:
- 以项目方式打开想要编译的 package 文件夹中的
CMakeLists.txt
- CMake设置中: Generation path 设置成
WORKSPACE_PATH/build
- Cmake Option:
-DCATKIN_DEVEL_PREFIX:PATH=WORKSPACE_PATH/devel
本次作业只给了模块代码,所以可以放在上次给的工作空间/自己的工作空间里编译运行。首先,需要安装好一些需要的模块:
- champion_nav_msgs:这个模块在上一次作业也有用到,如果上次安装了这次就不用了,否则可以根据内部的
readme.md
自行编译安装,注意 ros 版本的差异更改对应文件的对应位置。 - libnabo: 参考这篇教程
- csm:第二次作业有用到,可以根据这篇教程选择自己喜欢的方式安装,安装完成后在
CMakeLists.txt
修改csm的对应路径位置。
另外注意一下 melodic 下安装的 pcl 版本貌似是1.8的,需要在对应 hpp
, CMakeLists.txt
下修改需要的 pcl 版本。
代码结构
老规矩,还是先阅读给定的框架代码,了解代码结构再考虑补全。
main.cpp
:和之前的作业类似,在一个定义好的类 imlsDebug
里面完成所有项目需要的基本函数(数据读取、初始化、话题订阅/发布),用另一个类 imls_icp
完成作业需要的功能(针对这次作业而言,就是激光数据配准)。
imlsDebug
:六个函数和一些成员变量,主要功能如下:
构造函数:初始化两条路径的发布话题信息( imls-icp 生成的和 odom 生成的)、利用
rosbag:View
来读取 rosbag 中的话题信息(激光和里程计数据),并逐条处理,手动进入相应的 callback函数。(rosbag 读取的 api 可以参考官方文档)void odomCallback(...)
和pubPath(const nav_msgs::OdometryConstPtr& msg, ...)
: 简单的接收里程计数据并发布void ConvertChampionLaserScanToEigenPointCloud(...)
:工具函数,将激光扫描数据(距离,角度)换算成2d坐标(x, y)void championLaserScanCallback(...)
:先将激光数据转换成点云,调用imls_icp
中的方法对上一阵点云数据进行匹配,匹配成功的话输出当前帧位姿(通过上一阵位姿乘相对位姿变换)并发布void pubPath(Eigen::Vector3d& pose, nav_msgs::Path &path, ros::Publisher &mcu_path_pub_)
:发布由 imls-icp 生成的位姿
imls_icp
类中功能函数比较多,大部分函数名字和注释都能说明功能,这里不赘述,只介绍一下几个核心的功能函数:
projSourcePtToSurface(...)
:将当前帧点云投影到上一阵点云构造的曲面上(对每个店调用下面的函数),并去除匹配不合格的点,计算输出匹配后的点云和法向量bool ImplicitMLSFunction(Eigen::Vector2d x, double& height)
:将当前激光点投影到上一帧曲面上,计算高度;大致流程是利用 kd 数搜索距离当前点最近的三个点(需要具有代表性)构造平面,进行投影。SolveMotionEstimationProblem()
:已知匹配后的对应点和法向量,计算相对位姿,这部分已经写好了
我们需要完成的有三个函数:computeNormal()
(给定某个点周围的 n 个点,计算法向量),ImplictMLSFunction()
的高度计算部分(参考课上学习的公式),projSourcePtToSurface()
将匹配后的点转换成点云和法向量输出。
代码补充
ComputeNormal
:补充如下,基本参考课上内容,利用 pca 求解法向量
1 | /** |
ImplicitMLSFunction
:这个函数主要是补充高度计算部分,按照论文里的公式计算即可:
1 | //TODO |
projSourcePtToSurface
这个主要是补充计算匹配后的点云yi
,同样按照论文公式计算即可
1 | //TODO |
运行结果
补充完成后运行结果如下所示:红色为根据 imls 算出的位姿轨迹,蓝色为 odom 的位姿轨迹。虽然由于没有进行后端优化导致后面轨迹飘了,但是整体来看大致方向是对的。
用 csm 进行激光匹配比较结果
将第一题 IMLS-ICP 匹配的接口换成第二次作业中 CSM 库的 ICP 匹配接口,并生成激光匹配的轨迹
代码修改
这一题主要是熟悉 csm 的接口并调用,主要是以下几个部分:
添加以下成员变量:
1 | // csm 进行 pl-icp 需要的变量 |
添加 csm 相关的函数,注意部分变量的修改以及 LaserScan
和 ChampionNavLaserScan
之间的差异
1 | // 进行pl-icp的相关函数 |
然后在构造函数中进行 csm 的相关设置:
1 | imlsDebug() |
最后在激光数据的 callback
函数中增加使用 pl-icp 匹配的分支:
1 | if(m_isFirstFrame == true) |
pl-icp 运行结果
运行结果如下所示,使用 pl-icp 的速度明显变快,但由于没有优化结果同样也发生了漂移。
比较各类 ICP 算法的特点以及异同
阅读 ICP 相关论文,总结课上所学的几种 ICP 及其相关变型并简述其异同(ICP,PL-ICL,NICP,
IMLS-ICP)
课上讲解的几种 ICP 算法的基本思路都是类似的:找到前后两帧激光扫描数据的匹配关系,并根据匹配关系迭代找到相对位姿的最优值,主要的差异就是寻找对应点(匹配关系)以及计算误差两方面,几种 ICP 算法的详细说明可以参考我的上篇博客。
ICP 算法改进思路
简答题,开放性答案:现在你已经了解了多种 ICP 算法,你是否也能提出一种改进的 ICP 算法,或能提
升 ICP 总体匹配精度或速度的技巧?请简述你的改进策略。
两个方面:
- 精度上可以采用合理的方法提供初值(目前是人工设置):其他传感器(odom等),用 Ransac 去除 outlier
- 效率上可以对点云采样(提高匹配速度)