激光SLAM:ALOAM---后端lasermapping地图栅格化处理与提取

简介: 不同于前端的scan-to-scan的过程,ALOAM的后端是scan-to-map的算法,具体来说就是把当前帧和地图进行匹配,得到更准确的位姿同时也可以构建更好的地图.由于是scan-to-map的算法,因此计算量会明显高于scan-to-scan的前端,所以后端通常处于一个低频的运行频率,但是由于scan-to-map的精度往往优于scan-to-scan.因此后端也有比前端更高的精度.为了提高后端的处理速度,所以要进行地图的栅格化处理

前言

栅格点云地图处理原因
不同于前端的scan-to-scan的过程,ALOAM的后端是scan-to-map的算法,具体来说就是把当前帧和地图进行匹配,得到更准确的位姿同时也可以构建更好的地图.由于是scan-to-map的算法,因此计算量会明显高于scan-to-scan的前端,所以后端通常处于一个低频的运行频率,但是由于scan-to-map的精度往往优于scan-to-scan.因此后端也有比前端更高的精度.为了提高后端的处理速度,所以要进行地图的栅格化处理

栅格地图构成原理
地图通常是当前帧通过匹配得在地图坐标系下的准确位姿之后拼接而成.如果保留所有拼接的点云,此时随着时间的运行,内存很容易吃不消,为此考虑存储离当前帧比较近的部分地图,同时,为了便于地图更新和调整,在原始LOAM中,使用的是基于栅格的地图存储方式.具体来说,将整个地图分成212111个栅格,每个栅格是一个边长50m的正方体,当地图逐渐累加时,栅格之外的部分就被舍弃,这样可以保证内存空间不会随着程序的运行而爆炸.

水平方向上就是2150=1050m的地图 纵向1150=550m

栅格地图调整
如果当前位姿原理栅格覆盖的范围,则地图就没有意义了,因此栅格地图也需要随着当前位姿动态调整,从而保证我们可以从栅格地图中取出离当前位姿比较近的点云来进行scan-to-map的算法,获得最优位姿估计
当当前位姿即将到达地图栅格边界时,调整栅格地图,具体做法会在下面代码分析中展示

代码解析

具体代码在lasermapping.cpp中

transformAssociateToMap();//初值估计

初值估计, 和接收到里程计的数据的操作一致
将当前帧到里程计坐标系下的位姿,转到当前帧到map坐标系下,根据里程计坐标系和map坐标系的变换关系
里程计回调函数是为了以前端的频率向外发布位姿
这里主要提供一个估计的初值

            int centerCubeI = int((t_w_curr.x() + 25.0) / 50.0) + laserCloudCenWidth;
            int centerCubeJ = int((t_w_curr.y() + 25.0) / 50.0) + laserCloudCenHeight;
            int centerCubeK = int((t_w_curr.z() + 25.0) / 50.0) + laserCloudCenDepth;

t_w_curr就是当前帧的位置(估计初值),因为ALOAM针对机械式雷达,360度扫描,所以没有考虑旋转,如果要是livox的固态雷达,则必须考虑旋转了.
上面代码的功能就是根据初值估计值计算寻找当前位姿在地图中的索引,一个各自边长是50m
当前位置/50然后加上偏移量.
在这里插入图片描述
因为栅格是212111,所以初始是10,10,5,为了上初始时刻,在栅格地图的中心.
加25就是四舍五入的操作.

            if (t_w_curr.x() + 25.0 < 0)
                centerCubeI--;
            if (t_w_curr.y() + 25.0 < 0)
                centerCubeJ--;
            if (t_w_curr.z() + 25.0 < 0)
                centerCubeK--;

同样是取整的操作,C语言的取整是向0取整,所以要自减1

下面是动态调整栅格地图的部分,即处理快要出边界的情况,对栅格地图做动态调整

首先是当x轴,当前帧栅格索引小于三,说明块出边界了,让整体向x方向移动

            while (centerCubeI < 3)
            {
                for (int j = 0; j < laserCloudHeight; j++)
                {
                    for (int k = 0; k < laserCloudDepth; k++)
                    { 

判断当前帧位置索引小于3了,j和k都不动,所以整体for循环
在这里插入图片描述

                        int i = laserCloudWidth - 1;
                        //从x最大值开始
                        pcl::PointCloud<PointType>::Ptr laserCloudCubeCornerPointer =
                            laserCloudCornerArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k]; 
                        pcl::PointCloud<PointType>::Ptr laserCloudCubeSurfPointer =
                            laserCloudSurfArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k];

i值先取最大,从x最大值开始处理.然后取出了最右边的一片点云
[图]

                        //整体右移
                        for (; i >= 1; i--)
                        {
                            //移动地图角点
                            laserCloudCornerArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k] =
                                laserCloudCornerArray[i - 1 + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k];
                            //移动地图面点
                            laserCloudSurfArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k] =
                                laserCloudSurfArray[i - 1 + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k];
                        }

整体向右移
第一次循环

最后一次循环
在这里插入图片描述

                        laserCloudCornerArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k] =
                            laserCloudCubeCornerPointer;
                        laserCloudSurfArray[i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k] =
                            laserCloudCubeSurfPointer;

此时i=0,也就是最左边的格子赋值了之前最右边的格子

                        laserCloudCubeCornerPointer->clear();
                        laserCloudCubeSurfPointer->clear();

该点云清零,由于是指针操作,相当于最左边的格子清空了

                //索引右移
                centerCubeI++;
                laserCloudCenWidth++;
            }

索引右移

while (centerCubeI >= laserCloudWidth - 3)

然后是 当前帧位置接近右边的边界,和上面的类似,相当于整体左移

while (centerCubeJ < 3)
while (centerCubeJ >= laserCloudHeight - 3)
while (centerCubeK < 3)
while (centerCubeK >= laserCloudDepth - 3)

然后就是J方向和K方向的处理,和I类似,不再赘述

上面随着当前帧位置调整完栅格地图后,下面则根据当前位置取出该位置附近的地图

            int laserCloudValidNum = 0;
            int laserCloudSurroundNum = 0;
            // 从氮气格子为中心,选出一定范围内的点云
            for (int i = centerCubeI - 2; i <= centerCubeI + 2; i++)
            {
                for (int j = centerCubeJ - 2; j <= centerCubeJ + 2; j++)
                {
                    for (int k = centerCubeK - 1; k <= centerCubeK + 1; k++)
                    {
                        if (i >= 0 && i < laserCloudWidth &&
                            j >= 0 && j < laserCloudHeight &&
                            k >= 0 && k < laserCloudDepth)
                        {     
                            //把索引记下来
                            laserCloudValidInd[laserCloudValidNum] = i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k;
                            laserCloudValidNum++;
                            laserCloudSurroundInd[laserCloudSurroundNum] = i + laserCloudWidth * j + laserCloudWidth * laserCloudHeight * k;
                            laserCloudSurroundNum++;
                        }
                    }
                }
            }

以当前帧的位置索引为中心,I方向前后取2个格子,J方向前后去2个格子,K方向前后取一个格子.
然后通过循环把格子里面的点的索引记下来.

            //清空上一次从地图出取出的 角点地图和面点地图 
            laserCloudCornerFromMap->clear();
            laserCloudSurfFromMap->clear();
            //构建用来优化当前帧的局部地图,
            for (int i = 0; i < laserCloudValidNum; i++)
            {
                *laserCloudCornerFromMap += *laserCloudCornerArray[laserCloudValidInd[i]];
                *laserCloudSurfFromMap += *laserCloudSurfArray[laserCloudValidInd[i]];
            }
            int laserCloudCornerFromMapNum = laserCloudCornerFromMap->points.size();
            int laserCloudSurfFromMapNum = laserCloudSurfFromMap->points.size();

根据上面获得的需要格子的索引,把角点和面点分别取出来,构成用来优化当前帧的局部地图

            pcl::PointCloud<PointType>::Ptr laserCloudCornerStack(new pcl::PointCloud<PointType>());
            downSizeFilterCorner.setInputCloud(laserCloudCornerLast);
            downSizeFilterCorner.filter(*laserCloudCornerStack);
            int laserCloudCornerStackNum = laserCloudCornerStack->points.size();

            pcl::PointCloud<PointType>::Ptr laserCloudSurfStack(new pcl::PointCloud<PointType>());
            downSizeFilterSurf.setInputCloud(laserCloudSurfLast);
            downSizeFilterSurf.filter(*laserCloudSurfStack);
            int laserCloudSurfStackNum = laserCloudSurfStack->points.size();

之后就是对角点地图和面点地图进行体素滤波

总结

  • 根据前端的结果得到后端一个估计的初值位姿
  • 根据位置计算当前帧的栅格索引
  • 处理当I J K方向索引过于边界后的栅格位置调整
  • 根据前帧的栅格索引,取出周围一定数量的栅格中的点云,构成角度地图和面点地图
  • 进行角点地图和面点地图体素滤波处理
相关文章
|
存储 传感器 编解码
3D激光SLAM:LeGO-LOAM论文解读---完整篇
![在这里插入图片描述](https://img-blog.csdnimg.cn/348d0b4467a24296a22413207566c67e.png) 论文的标题是:**LeGO-LOAM: Lightweight and Ground-Optimized Lidar Odometry and Mapping on Variable Terrain** - 标题给出的应用场景是 **可变地形** - 重点是 **轻量级** 并 利用 **地面优化** - 本质依然是一个 **激光雷达里程计和建图**
3D激光SLAM:LeGO-LOAM论文解读---完整篇
|
机器学习/深度学习 PyTorch 算法框架/工具
【单点知识】基于实例详解PyTorch中的DataLoader类
【单点知识】基于实例详解PyTorch中的DataLoader类
2568 2
|
机器学习/深度学习 数据采集 自然语言处理
OneFlow深度学习框原理、用法、案例和注意事项
OneFlow深度学习框原理、用法、案例和注意事项
|
算法 数据处理
点云地面点滤波(Progressive Morphological Filter)算法介绍(PCL库)
点云地面点滤波(Progressive Morphological Filter)算法介绍(PCL库)
2268 0
点云地面点滤波(Progressive Morphological Filter)算法介绍(PCL库)
|
存储 缓存 Java
面试官:你知道包装类的缓存机制吗?
面试官:你知道包装类的缓存机制吗?
1955 0
|
10月前
|
Ubuntu Linux 网络安全
Linux装软件神器:yum 超详细入门指南
在Linux中安装软件有三种常用方法:源码安装、rpm包安装和yum包管理器安装。其中,**yum**(或Ubuntu的apt)最为便捷,类似于手机应用商店,能自动解决依赖问题,适合新手使用。本文详细介绍了yum的工作原理、软件源配置、常用命令及生态系统的意义,帮助用户快速上手Linux软件安装与管理。
 Linux装软件神器:yum 超详细入门指南
|
机器学习/深度学习 数据采集 人工智能
深入探索人工智能与大数据的融合之路
本文旨在探讨人工智能(AI)与大数据技术如何相互促进,共同推动现代科技的进步。通过分析两者结合的必要性、挑战以及未来趋势,为读者提供一个全面的视角,理解这一领域内的最新发展动态及其对行业的影响。文章不仅回顾了历史背景,还展望了未来可能带来的变革,并提出了几点建议以促进更高效的技术整合。
|
Ubuntu 机器人 定位技术
Loam在Ubuntu 18.04上的一站式安装指南
现在,你已经完成了Loam在Ubuntu 18.04上的一站式安装盛宴。从更新系统清洁,到搭建魔法环境的工作空间,再到召唤和激活Loam精髓的艺术——每步都妙不可言,每步都至关重要,让你在这场技术的饕餮盛宴中大显身手。
296 8
|
机器学习/深度学习 算法 PyTorch
基于图神经网络的大语言模型检索增强生成框架研究:面向知识图谱推理的优化与扩展
本文探讨了图神经网络(GNN)与大型语言模型(LLM)结合在知识图谱问答中的应用。研究首先基于G-Retriever构建了探索性模型,然后深入分析了GNN-RAG架构,通过敏感性研究和架构改进,显著提升了模型的推理能力和答案质量。实验结果表明,改进后的模型在多个评估指标上取得了显著提升,特别是在精确率和召回率方面。最后,文章提出了反思机制和教师网络的概念,进一步增强了模型的推理能力。
1076 4
基于图神经网络的大语言模型检索增强生成框架研究:面向知识图谱推理的优化与扩展
|
并行计算 异构计算
建立Hugging Face模型调用环境
本文介绍了如何在环境中导入transformers库,并从Hugging Face网站下载模型。如果使用镜像网站,需获取access token。部分模型需申请仓库权限,建议使用国外信息填写。有GPU的用户需先配置CUDA和pytorch-gpu。