激光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方向索引过于边界后的栅格位置调整
  • 根据前帧的栅格索引,取出周围一定数量的栅格中的点云,构成角度地图和面点地图
  • 进行角点地图和面点地图体素滤波处理
相关文章
|
存储 传感器 编解码
turtlebot3 在gazebo仿真下 通过 gmapping slam 建立二维平面地图——全过程
turtlebot3 在gazebo仿真下 通过 gmapping slam 建立二维平面地图——全过程
turtlebot3 在gazebo仿真下 通过 gmapping slam 建立二维平面地图——全过程
|
7月前
|
数据采集 定位技术
R语言geodetector包基于栅格图像实现地理探测器操作
R语言geodetector包基于栅格图像实现地理探测器操作
166 1
|
7月前
|
算法
基于ENVI的遥感影像栅格图层手动地理配准方法
基于ENVI的遥感影像栅格图层手动地理配准方法
|
缓存 数据可视化 vr&ar
医学影像PACS源码 三维多平面重建、三维容积重建
持所见即所得报告书写方式; •  报告单预览功能(在书写、审核、打印时都可随时预览报告); •  在书写报告过程中可随时切换报告单样式; •  相关检查功能:在书写报告时可查看患者相关检查的图像及报告信息; •  常用词汇管理,支持报告模板管理;
|
存储 数据可视化 数据管理
处理RGB-D图像数据以构建室内环境地图并估计相机的轨迹
视觉同步定位和映射 (vSLAM) 是指计算摄像机相对于周围环境的位置和方向,同时映射环境的过程。 您可以使用单眼摄像头执行 vSLAM。但是,深度无法准确计算,估计的轨迹未知,并且随着时间的推移而漂移。要生成无法从第一帧开始三角测量的初始地图,必须使用单眼相机的多个视图。更好、更可靠的解决方案是使用 RGB-D 相机,它由一个 RGB 彩色图像和一个深度图像组成。
196 0
|
传感器 存储 编解码
使用激光雷达数据构建地图并使用SLAM算法估计车辆轨迹
使用激光雷达数据构建地图并使用SLAM算法估计车辆轨迹。
227 0
|
存储
医院PACS系统源码(三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜)
PACS部分主要提供医学影像获取、影像信息网络传递、大容量数据存储、影像显示和处理、影像打印等功能。RIS主要提供分诊登记、叫号、检查报告生成和打印等功能。本套影像存储与传输系统将二者进行无缝对接,提供了一个完整的集患者登记、图像采集、图像存储、报告产生的影像检查诊疗业务流程系统。
236 0
医院PACS系统源码(三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜)
|
机器学习/深度学习 传感器 算法
【栅格地图路径规划】基于动态衡量启发式A星算法实现机器人栅格地图路径规划附matlab代码
【栅格地图路径规划】基于动态衡量启发式A星算法实现机器人栅格地图路径规划附matlab代码
|
机器学习/深度学习 传感器 算法
【机器人栅格地图】基于双向A星算法实现栅格地图机器人动态路径规划附matlab代码
【机器人栅格地图】基于双向A星算法实现栅格地图机器人动态路径规划附matlab代码
|
数据可视化 API
【视觉高级篇】21 # 如何添加相机,用透视原理对物体进行投影?
【视觉高级篇】21 # 如何添加相机,用透视原理对物体进行投影?
204 0
【视觉高级篇】21 # 如何添加相机,用透视原理对物体进行投影?