当前位置:网站首页>3D激光SLAM:LeGO-LOAM---两步优化的帧间里程计及代码分析
3D激光SLAM:LeGO-LOAM---两步优化的帧间里程计及代码分析
2022-08-04 21:55:00 【月照银海似蛟龙】
前言
LeGO-LOAM的全称是 Lightweight and Ground-Optimized Lidar Odometry and Mapping on Variable Terrain
其中LeGO就是轻量级和利用地面优化,轻量级的实现就是通过两步的优化方式,利用地面优化的部分也在两步优化的第一步中。
和原始LOAM一样,通过前后两帧点云来估计两帧之间的运动,从而累加得到前端里程计的输出,和上述方法使用线面约束同时优化六自由度帧间位姿不同,LeGO-LOAM的前端分成两个步骤,每个步骤估计三自由度的变量。
通过这种方式进行帧间里程计的运算,可以提供运算效率,使得可以在嵌入式平台中运行
在里程计部分,LeGO-LOAM充分利用了前面存储的标签信息,在原本LOAM的里程计部分,其使用的思路更像是ICP,单纯寻找距离最近的点,而并没有考虑其合理性。在这里,LeGO-LOAM通过标签增加了一次筛选,标签相同的点才会用于优化,通过这种方法,能够大幅提高匹配的准确率。
此外,在优化部分,LOAM使用的是直接将所有参数放在一起,进行LM优化,而在LeGO-LOAM中,作者将LM优化分两步进行,每次优化三个参数,在确保准确性的前提下,减小了35%的计算开销。
利用地面点优化
地面点更符合面特征的性质,因此地面点的优化问题就使用点到面的约束来构建
地面点的约束对x,y,yaw这三个自由度是不能估测的。当这个三个自由度的值发生变化,点到面的残差不会发生显著变化,
地面点的优化,只对pitch、roll及z进行约束和优化


白色的为当前帧,绿色的为上一帧,可以看到当前帧发生yaw的旋转,和发生x、y的平移的时候,当前帧的面点到上一帧的距离都没有变,所有不能把这三个自由度的优化放到面点特征上来。
当z轴发生移动,或者发生pitch和roll角的变化,当前帧的面特征点,到上一帧的面特征点的距离即发生变化。
利用角点优化
第一步优化完pitch、roll和z之后,剩下还有三个自由度的变量需要进行估计,
需要提取角点进行优化,由于多线激光雷达提取的角点通常是垂直的边缘特征,因此对,x、y及yaw有着比较好的能观性,通过角点的优化结合上地面点的结果可以得到六自由度的帧间优化结果。
将上一步分优化的z、pitch和roll的三自由度量,和剩下没有估计的三个自由度进行角点的约束优化。
代码部分
LeGO-LOAM的帧间里程计部分在FeatureAssociation.cpp中 。
可以直接找到runFeatureAssociation的函数
adjustDistortion();//去除畸变
calculateSmoothness();//计算曲率
markOccludedPoints();//剔除离散点
extractFeatures();//提取特征
publishCloud(); // cloud for visualization
/** 2. Feature Association */
if (!systemInitedLM) {
checkSystemInitialization();
return;
}
updateInitialGuess();
updateTransformation();//帧间里程计核心部分
integrateTransformation();
publishOdometry();
publishCloudsLast(); // cloud to mapOptimization
在这个函数中进行了
- 去除畸变
- 计算曲率
- 剔除离散点
- 提取特征
- 帧间里程计核心部分
- 发布里程计
- 发布点云
其中帧间里程计核心部分在 updateTransformation() 函数中
void updateTransformation(){
if (laserCloudCornerLastNum < 10 || laserCloudSurfLastNum < 100)
return;
//地面点帧间里程计估计优化
for (int iterCount1 = 0; iterCount1 < 25; iterCount1++) {
laserCloudOri->clear();
coeffSel->clear();
findCorrespondingSurfFeatures(iterCount1);//通过kdtree 寻找对应的点
if (laserCloudOri->points.size() < 10)
continue;
if (calculateTransformationSurf(iterCount1) == false)//进行优化估计
break;
}
//角点帧间里程计估计优化
for (int iterCount2 = 0; iterCount2 < 25; iterCount2++) {
laserCloudOri->clear();
coeffSel->clear();
findCorrespondingCornerFeatures(iterCount2);//通过kdtree 寻找对应的点
if (laserCloudOri->points.size() < 10)
continue;
if (calculateTransformationCorner(iterCount2) == false)//进行优化估计
break;
}
}
从这个函数中可以明显看出分成了两部分
- 第一个for循环是 地面点帧间里程计估计优化
- 第二个for循环是 角点帧间里程计估计优化

从这里可以看出surfPointsFlat中的点都是地面点
然后在findCorrespondingSurfFeatures的函数中,进行当前帧的地面点与上一帧中的地面点进行匹配。
其过程和ALOAM中的原理一致。
在findCorrespondingCornerFeatures的函数中,进行当前帧的角点与上一帧中的角点进行匹配。
其过程和ALOAM中的原理一致。
当前帧角点i,映射到上一帧点云中,并找到距离最近的点j,在与j相同的scan中,寻找次近邻点m。
利用映射后的点i到j和m的距离,进行约束
通过两步优化的位姿变换通过里程计数据的方式发送出来可以进行可视化的操作
具体代码在 publishOdometry() 函数中
当前帧角点i,映射到上一帧点云中,并找到距离最近的点j,并在相同scan中找到次近邻点l,在相邻的scan中再找到近邻点m。
利用映射后的点i到j、l、m平面的距离,进行约束。
//发布前端里程计
void publishOdometry(){
geometry_msgs::Quaternion geoQuat = tf::createQuaternionMsgFromRollPitchYaw(transformSum[2], -transformSum[0], -transformSum[1]);
laserOdometry.header.stamp = cloudHeader.stamp;
laserOdometry.pose.pose.orientation.x = -geoQuat.y;
laserOdometry.pose.pose.orientation.y = -geoQuat.z;
laserOdometry.pose.pose.orientation.z = geoQuat.x;
laserOdometry.pose.pose.orientation.w = geoQuat.w;
laserOdometry.pose.pose.position.x = transformSum[3];
laserOdometry.pose.pose.position.y = transformSum[4];
laserOdometry.pose.pose.position.z = transformSum[5];
pubLaserOdometry.publish(laserOdometry);
laserOdometryTrans.stamp_ = cloudHeader.stamp;
laserOdometryTrans.setRotation(tf::Quaternion(-geoQuat.y, -geoQuat.z, geoQuat.x, geoQuat.w));
laserOdometryTrans.setOrigin(tf::Vector3(transformSum[3], transformSum[4], transformSum[5]));
tfBroadcaster.sendTransform(laserOdometryTrans);
}
其topic的名称为:
gazebo测试

前端的里程计在Z轴方向已经发生了漂移。
但是地图没有飘,这个是后端进行优化的结果,将前端漂移的部分优化了回来
边栏推荐
猜你喜欢

AXI interface application of Zynq Fpga image processing - the use of axi_lite interface

Cocoa Application-test

开发deepstram的自定义插件,使用gst-dseaxmple插件进行扩充,实现deepstream图像输出前的预处理,实现图像自定义绘制图(精四)

MySQL查询为啥慢了?

国内的PMP证书含金量到底如何

ES 数据聚合、数据同步、集群

Codeforces Round #811 (Div. 3)
![[larave]关于laravel使用form submit()不能获取值问题](/img/2f/a7a17b1497390509edd6d096e8e473.png)
[larave]关于laravel使用form submit()不能获取值问题

mysql基础
![[QT] Implementation of callback function](/img/df/500ae0625eccf83f39d9a8ddfd3533.png)
[QT] Implementation of callback function
随机推荐
ES6高级-async的用法
Excel商业智能-Power BI电商数据分析实战
"Jianzhi offer" brush title classification
27. Dimensionality reduction
webmine网页挖矿木马分析与处置
How to solve the problem that the alarm information cannot be transmitted after EasyGBS is connected to the latest version of Hikvision camera?
ROS播包可视化
数电快速入门(一)(BCD码和三种基本逻辑运算的介绍)
数电快速入门(二)(复合逻辑运算和逻辑代数的基本定律的介绍)
ue unreal 虚幻 高分辨率无缩放 编辑器字太小 调整编辑器整体缩放
【线性代数02】AX=b的2种解释和矩阵乘法的5种视角
【QT】回调函数的实现
遍历await方法的区别:以for和forEach为例
docker 搭建mysql 主从复制
多个平台显示IP属地,必须大力推行互联网实名制
Lecture 2 Software Life Cycle
openresty lua-resty-template页面静态化
Analysis and treatment of Ramnit infectious virus
未知点云结构文件转换需求
PyTorch Geometric (PyG) 安装教程