激光SLAM:ALOAM---后端lasermapping数据处理低延时性保障操作

简介: ALOAM方法实现了低的漂移,并且计算的复杂度低,实时性很好.并且不需要高精度的lidar和惯导这个方法的核心思想就是把SLAM问题进行了拆分,通过两个算法来进行.一个是执行高频率的前端里程计但是低精度的运动估计(定位),另一个算法在比定位低一个数量级的频率执行后端建图(建图和校正里程计).

前言

ALOAM方法实现了低的漂移,并且计算的复杂度低,实时性很好.并且不需要高精度的lidar和惯导

这个方法的核心思想就是把SLAM问题进行了拆分,通过两个算法来进行.一个是执行高频率的前端里程计但是低精度的运动估计(定位),另一个算法在比定位低一个数量级的频率执行后端建图(建图和校正里程计).

那么其中需要处理的一个问题就是 低运算频率的后端,如何保障数据处理的实时性的

ALOAM的做法就是去掉过多的数据,仅处理能处理的数据.这样虽然有可能丢掉一部分数据,但是保障了后端的低延时和计算内存.

当运行ALOAM的时候你的终端,打印出里如下信息:
在这里插入图片描述

drop lidar frame in mapping for real time performance

这就意味着,后端的数据处理不过来了,为了保证后端的实时性,将会删掉前面的数据.

本篇博客主要介绍这部分内容在代码中是如何实现的,即如何保证后端的数据处理的低延时性.

代码解析

后端的处理主要在一个主线程 process函数中,其中是一个while的循环

void process()
{
    while(1)
    {
        while (!cornerLastBuf.empty() && !surfLastBuf.empty() &&
            !fullResBuf.empty() && !odometryBuf.empty())
        {

首先判断4个buf里数据是非空的,然后再进入整体循环.

            // 处理odom的数据
            while (!odometryBuf.empty() && odometryBuf.front()->header.stamp.toSec() < cornerLastBuf.front()->header.stamp.toSec())
                odometryBuf.pop();//最前面的数据时间戳小于基准时间则pop
            if (odometryBuf.empty())//pop后为空了
            {
                mBuf.unlock();//线程锁放开,可以继续存数据
                break;//跳出本次循环
            }
            // 处理surfLast的数据
            while (!surfLastBuf.empty() && surfLastBuf.front()->header.stamp.toSec() < cornerLastBuf.front()->header.stamp.toSec())
                surfLastBuf.pop();
            if (surfLastBuf.empty())
            {
                mBuf.unlock();
                break;
            }
            // 处理fullResBuf的数据
            while (!fullResBuf.empty() && fullResBuf.front()->header.stamp.toSec() < cornerLastBuf.front()->header.stamp.toSec())
                fullResBuf.pop();
            if (fullResBuf.empty())
            {
                mBuf.unlock();
                break;
            }

以 cornerLastBuf 的时间戳为基准,把时间小于它的pop出去
对于三个数据的操作类似

            /*取出处理后的数据的 最前面一个数据的 时间戳*/
            timeLaserCloudCornerLast = cornerLastBuf.front()->header.stamp.toSec();
            timeLaserCloudSurfLast = surfLastBuf.front()->header.stamp.toSec();
            timeLaserCloudFullRes = fullResBuf.front()->header.stamp.toSec();
            timeLaserOdometry = odometryBuf.front()->header.stamp.toSec();

取出处理后的数据的 最前面一个数据的 时间戳

            if (timeLaserCloudCornerLast != timeLaserOdometry ||
                timeLaserCloudSurfLast != timeLaserOdometry ||
                timeLaserCloudFullRes != timeLaserOdometry)
            {
                printf("time corner %f surf %f full %f odom %f \n", timeLaserCloudCornerLast, timeLaserCloudSurfLast, timeLaserCloudFullRes, timeLaserOdometry);
                printf("unsync messeage!");
                mBuf.unlock();
                break;
            }

判断时间是否相等,不等则会打印有问题的时间和打印同步失败,并且跳出本次循环

            laserCloudCornerLast->clear();
            pcl::fromROSMsg(*cornerLastBuf.front(), *laserCloudCornerLast);//把本次要处理的数据转成PCL的格式
            cornerLastBuf.pop();//pop掉buf中的本次处理的数据

            laserCloudSurfLast->clear();
            pcl::fromROSMsg(*surfLastBuf.front(), *laserCloudSurfLast);//把本次要处理的数据转成PCL的格式
            surfLastBuf.pop();//pop掉buf中的本次处理的数据

            laserCloudFullRes->clear();
            pcl::fromROSMsg(*fullResBuf.front(), *laserCloudFullRes);//把本次要处理的数据转成PCL的格式
            fullResBuf.pop();//pop掉buf中的本次处理的数据

清空上次操作的上帧点云中的点 把本次要处理的数据转成PCL的格式
op掉buf中的本次处理的数据

            /*取出本次处理数据的里程计信息,转成eigen的形式  当前帧在odom坐标系下的位姿 */
            q_wodom_curr.x() = odometryBuf.front()->pose.pose.orientation.x;
            q_wodom_curr.y() = odometryBuf.front()->pose.pose.orientation.y;
            q_wodom_curr.z() = odometryBuf.front()->pose.pose.orientation.z;
            q_wodom_curr.w() = odometryBuf.front()->pose.pose.orientation.w;
            t_wodom_curr.x() = odometryBuf.front()->pose.pose.position.x;
            t_wodom_curr.y() = odometryBuf.front()->pose.pose.position.y;
            t_wodom_curr.z() = odometryBuf.front()->pose.pose.position.z;
            odometryBuf.pop();//pop掉buf中的本次处理的数据

取出本次处理数据的里程计信息,转成eigen的形式 当前帧在odom坐标系下的位姿

            //考虑到实时性,就把队列里其它的都pop出去,不然可能出现延时的情况
            while(!cornerLastBuf.empty())
            {
                cornerLastBuf.pop();
                printf("drop lidar frame in mapping for real time performance \n");
            }

考虑到实时性,就把队列里其它的都pop出去,不然可能出现延时的情况
计算资源也不会过载

比如 前端1s,可以出5帧数据,后端1s可以处理1帧数据,如果不像上面操作的话,

那么 Buf里的数据会随时间增长一直增加.直到计算资源过载.
并且会出现后端结果延时的情况,运行了5秒之后,后端,还在处理第一秒的数据,25秒之后,后端在处理第5s的数据.这种延时情况随时间增长也会越来越严重.

在ALOAM中,每次把Buf中的对应一帧的四组数据取出来后,则把所有的buf清空.则会丢掉很多帧的数据.但是计算资源和实时性可以得到保证.

测试

前端向后端发布的数据有

  • 上一帧的角点
  • 上一帧的面点
  • 前端里程计位姿
  • 所有的点云

通过终端查看下上一帧的角点发布的频率

rostopic hz /laser_cloud_corner_last

在这里插入图片描述
大概是10hz

后端的处理时间要根据场景和处理器的性能有关系了

由于在台式机上测试的,后端处理的比较快,电脑配置如下:
在这里插入图片描述
在运行ALOAM的时候,终端会打印出后端的处理时间:
在这里插入图片描述
whole mapping time在七八毫秒左右.完全可以处理10hz的数据
所以不会出现处理过多数据的情况,也就打印不出
drop lidar frame in mapping for real time performance

为了测试上面的情况,可以在后端中加个1s的延时,

ros::Duration(1).sleep();//人工加个延时.

则会出现下面的情况
在这里插入图片描述
连续打印了10次.drop lidar

因为前端发布的是10hz的数据,那么在延时1s的时间内,buf里面则出现了10帧处理不过来的数据
pop掉每秒的10帧数据,则连续打印10次drop lidar

相关文章
|
7月前
|
开发工具 git
web后端-IDEA的Git操作
web后端-IDEA的Git操作
|
开发者
氚云丨开发课— 05 后端代码调试与业务对象操作| 学习笔记
快速学习氚云丨开发课— 05 后端代码调试与业务对象操作。
|
前端开发 定位技术 索引
3D激光SLAM:ALOAM---后端 lasermapping构建角点约束与面点约束
后端的构建约束问题和前端不一样。原因就是前端从上一帧上去找,而后端是在局部地图上找,点要多很多,并且没有了线束信息,所以原理上不一样了。 **线特征的提取** 通过kdtree在局部地图中找到5个最近的线特征,为了判断他们是否符合线特征的特性,需要对5个点构成的协方差矩阵进行特征值分解,当上述5个点在一条直线上时,他们只有一个主方向,也就是特征值是一个大特征值,以及两个小特征值,大特征值对应的特征向量就是对应直线的方向向量。 **面特征的提取** 通过kdtree在地图中找到最近的面特征也是5个, 理论上也可以通过特种值分解的方式,最小的特征值对应的特征向量就是平面的法向量, 不过代码里选
3D激光SLAM:ALOAM---后端 lasermapping构建角点约束与面点约束
|
6月前
|
缓存 Java 数据库连接
我们后端程序员不是操作MyBatis的CRUD Boy
大家好,我是南哥。一个对Java程序员进阶成长颇有研究的人,今天我们接着新的一篇Java进阶指南。为啥都戏称后端是CRUD Boy?难道就因为天天怼着数据库CRUD吗?要我说,是这个岗位的位置要的就是你CRUD,你不得不CRUD。哪有公司天天能给你搭建高并发、高可用、大数据框架的活呢,一条业务线总要成长吧,慢慢成熟了就要装修工来缝缝补补、美化美化,也就是CRUD的活。不能妄自菲薄CRUD Boy,我们是后端工程师。今天来指南下操作数据库之MyBatis框架。
136 3
|
6月前
|
Java 应用服务中间件 Maven
浅谈后端整合Springboot框架后操作基础配置
浅谈后端整合Springboot框架后操作基础配置
41 3
|
7月前
|
网络安全 开发工具 git
web后端-GitHub文件夹上传操作
web后端-GitHub文件夹上传操作
|
7月前
|
JSON 小程序 前端开发
史上最详细微信小程序授权登录与后端SprIngBoot交互操作说明!!!附源代码
史上最详细微信小程序授权登录与后端SprIngBoot交互操作说明!!!附源代码
1869 2
|
7月前
|
XML API 数据库
七天.NET 8操作SQLite入门到实战 - 第六天后端班级管理相关接口完善和Swagger自定义配置
七天.NET 8操作SQLite入门到实战 - 第六天后端班级管理相关接口完善和Swagger自定义配置
133 0
|
JSON JavaScript 前端开发
JavaScript中操作后端用ModelAndView返回的List数据
在 JavaWeb 项目中,我们经常使用 ModelAndView 装入请求页面的地址和传入页面的数据,我们在 HTML 中可以用 JSTL 来操作装入在 ModelAndView 中的 List 数据
374 0
|
SQL 关系型数据库 MySQL
【后端随笔】mysql操作语句记录
【后端随笔】mysql操作语句记录

热门文章

最新文章