3D激光SLAM--A-LOAM :前端lidar点特征提取部分代码解读

举报
月照银海似蛟龙 发表于 2022/07/30 00:54:50 2022/07/30
【摘要】 3D激光SLAM--A-LOAM :前端lidar点特征提取部分代码解读 A-LOAM代码的结构Code A-LOAM代码的结构 A-LOAM的cpp有四个,其中 kittiHelper....

3D激光SLAM--A-LOAM :前端lidar点特征提取部分代码解读

A-LOAM代码的结构

A-LOAM的cpp有四个,其中 kittiHelper.cpp 的作用是将kitti数据集转为rosbag
剩下的三个是作为 slam 的 部分,分别是:

  • laserMappin.cpp ++++ 当前帧到地图的优化
  • laserOdometry.cpp ++++ 帧间里程计
  • scanRegistration.cpp ++++ 前端lidar点预处理及特征提取

本片主要解读 前端lidar点特征提取部分的代码

之前要做点的预处理,这部分的代码在之前分析过了
链接

Code

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    //计算曲率
    for (int i = 5; i < cloudSize - 5; i++)
    { 
        //计算x维度的曲率
        float diffX = laserCloud->points[i - 5].x + laserCloud->points[i - 4].x + laserCloud->points[i - 3].x + laserCloud->points[i - 2].x + laserCloud->points[i - 1].x - 10 * laserCloud->points[i].x + laserCloud->points[i + 1].x + laserCloud->points[i + 2].x + laserCloud->points[i + 3].x + laserCloud->points[i + 4].x + laserCloud->points[i + 5].x;
        float diffY = laserCloud->points[i - 5].y + laserCloud->points[i - 4].y + laserCloud->points[i - 3].y + laserCloud->points[i - 2].y + laserCloud->points[i - 1].y - 10 * laserCloud->points[i].y + laserCloud->points[i + 1].y + laserCloud->points[i + 2].y + laserCloud->points[i + 3].y + laserCloud->points[i + 4].y + laserCloud->points[i + 5].y;
        float diffZ = laserCloud->points[i - 5].z + laserCloud->points[i - 4].z + laserCloud->points[i - 3].z + laserCloud->points[i - 2].z + laserCloud->points[i - 1].z - 10 * laserCloud->points[i].z + laserCloud->points[i + 1].z + laserCloud->points[i + 2].z + laserCloud->points[i + 3].z + laserCloud->points[i + 4].z + laserCloud->points[i + 5].z;

        //存储曲率,索引
        cloudCurvature[i] = diffX * diffX + diffY * diffY + diffZ * diffZ;
        //保存 原来点的索引   因为后面要排序 
        cloudSortInd[i] = i;
        //这俩个是标志位
        cloudNeighborPicked[i] = 0;//此标志位置1时代表这个点被选位特征点了
        cloudLabel[i] = 0;//特征点的标签
    }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

计算每个点的曲率
并将每个点的曲率保存在一个向量中
将每个点的索引保存在一个向量中
初始化的点标签向量

这里的计算曲率就是对应的论文中的这个公式
在这里插入图片描述
在代码里就是分别求一个轴维度的曲率,以x轴为例,就是
前五个点x的值+后五个点x的值-10*当前点x的值
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    //声明 特征点的点云
    pcl::PointCloud<PointType> cornerPointsSharp;   //角点特征 点云
    pcl::PointCloud<PointType> cornerPointsLessSharp;  //弱角点特征 点云
    pcl::PointCloud<PointType> surfPointsFlat;//面点特征 点云
    pcl::PointCloud<PointType> surfPointsLessFlat;//弱面点特征 点云

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

声明 特征点的点云
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
注意:下面代码是一个大的嵌套循环
按每个scan一个循环
每个scan等分6份,每份一个循环

    //声明 特征点的点云
    pcl::PointCloud<PointType> cornerPointsSharp;   //角点特征 点云
    pcl::PointCloud<PointType> cornerPointsLessSharp;  //弱角点特征 点云
    pcl::PointCloud<PointType> surfPointsFlat;//面点特征 点云
    pcl::PointCloud<PointType> surfPointsLessFlat;//弱面点特征 点云 经过体素滤波处理

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

声明 特征点的点云

    //遍历每个scan
    for (int i = 0; i < N_SCANS; i++)
    {        
    // 没有有效的点了,就continue
        if( scanEndInd[i] - scanStartInd[i] < 6)
            continue;

        //用来存储不太平整的点  不经过体素滤波  
        pcl::PointCloud<PointType>::Ptr surfPointsLessFlatScan(new pcl::PointCloud<PointType>);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

遍历每个scan
当这个scan上面的 起始点和结束点的id差小于6 ,也就是 有效点过少,则不处理这个scan直接continue
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
注意这个循环是嵌套在上面的那个里面的

        // 将每个scan等分成6等分   分别计算里面的特征点
        for (int j = 0; j < 6; j++)
        {
            // 计算等分 的 每个起始点 和 结束点 的 id
            int sp = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * j / 6;  //起始点的id
            int ep = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * (j + 1) / 6 - 1;//结束点的id

            TicToc t_tmp;//算时间用的

            //对点云按照曲率进行排序,小的在前,大的在后
            std::sort (cloudSortInd + sp, cloudSortInd + ep + 1, comp);

            t_q_sort += t_tmp.toc();//计算时间

            int largestPickedNum = 0;//挑选最大的曲率特征点的个数  即角点的个数

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这个地方涉及到数组的处理了,要好好看下

A-LOAM算法为了让特征点分部均匀,将每一条scan分成6份,在每一份中分部提取曲率最大的和最小的几个点作为角点和面点

在这里要把前面的数据给总结下了,要不基本是乱的

laserCloud 是依次放的由 scan1 到 scan16的点云
图
cloudCurvature 存放的上面的每个点对应的 曲率值
图
cloudSortInd 存放的点对应的id ,一开始 每个元素是由 5~n,后面会根据曲率大小重新排
图
scanStartInd[i] 是第i个scan的起始点的id scanEndInd[i] 是第i个scan的结束点的id
图

sp和ep分别是一条scan上面等分6份,中一份的 起始点id和结束点id
所以计算sp的时候是这样的:
sp = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * j / 6
计算ep的时候是这样的:
ep = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * (j + 1) / 6 - 1;

然后下面的代码 sort那部分,是利用std的排序方法,将cloudSortInd 对应的那部分 将点云的id进行了重新排序,曲率小的在前面,曲率大的在后面
图

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 		int largestPickedNum = 0;//挑选最大的曲率特征点的个数  即角点的个数
            //挑选曲率比较大的部分
            for (int k = ep; k >= sp; k--)
            {
                //排序后顺序就乱了,  用之前存的 索引值 取到 点的id 
                int ind = cloudSortInd[k]; 

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

重新排序后,cloudSortInd 上的ep位置就是存放的曲率最大点的id,取出来这个id

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                //1 看这个点是否是有效点,同时曲率是否大于阈值
                if (cloudNeighborPicked[ind] == 0 &&
                    cloudCurvature[ind] > 0.1)
                {
                    //2 满足要求,则选择的 角点个数 加1
                    largestPickedNum++;

                    // 每段选择2个曲率最大的点
                    if (largestPickedNum <= 2)
                    {
                        //3 label 为2 是曲率大的标记                        
                        cloudLabel[ind] = 2;
                        //4 cornerPointsSharp存放曲率大的点
                        cornerPointsSharp.push_back(laserCloud->points[ind]);
                        //cornerPointsLessSharp 存放曲率稍微大的点
                        cornerPointsLessSharp.push_back(laserCloud->points[ind]);
                    }
                    //5 以及20个曲率稍微大些的点
                    else if (largestPickedNum <= 20)
                    {  
                        // 6 label 为1 是曲率稍微大的标记                       
                        cloudLabel[ind] = 1; 
                        //7
                        cornerPointsLessSharp.push_back(laserCloud->points[ind]);
                    }
                    //超过20个的就不要了
                    else
                    {
                        break;
                    }

                    //8 这个点被选中后 pick标志位置 1
                    cloudNeighborPicked[ind] = 1; 
                    
                    //9 为了保证特征点不过度集中,将选中的点周围5个点都置1 避免后续会选到
                    for (int l = 1; l <= 5; l++)
                    {
                        //查看相邻点距离是否差异过大,如果差异过大,说明点云在此不连续,是特征边缘,就是新的特征,就不置位了
                        float diffX = laserCloud->points[ind + l].x - laserCloud->points[ind + l - 1].x;
                        float diffY = laserCloud->points[ind + l].y - laserCloud->points[ind + l - 1].y;
                        float diffZ = laserCloud->points[ind + l].z - laserCloud->points[ind + l - 1].z;


                        if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05)
                        {
                            break;
                        }

                        cloudNeighborPicked[ind + l] = 1;
                    }
                    for (int l = -1; l >= -5; l--)
                    {
                        float diffX = laserCloud->points[ind + l].x - laserCloud->points[ind + l + 1].x;
                        float diffY = laserCloud->points[ind + l].y - laserCloud->points[ind + l + 1].y;
                        float diffZ = laserCloud->points[ind + l].z - laserCloud->points[ind + l + 1].z;
                        if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05)
                        {
                            break;
                        }

                        cloudNeighborPicked[ind + l] = 1;
                    }
                }
            }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

上面的代码对照注释依次做了如下事情,注释里都标了对应的序号:

  1. cloudNeighborPicked[ind] == 0 代表这个点可以挑选,然后曲率大于0.1 则满足要求
  2. 满足要求则选择的点个数+1
  3. 将最大的两个曲率点标签置为2
  4. 然后将这两个点存入cornerPointsSharp
  5. 然后再选较大的20个点,
  6. 标签置位1
  7. 然后存入cornerPointsLessSharp
  8. 被选为 角点和弱角点后,点的选取标志位置为1,则这个点就不能在后面被选取了,因为一开始有那个判断
  9. 为了保证特征点不过度集中,将选中的点周围5个点都置1 避免后续会选到

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

            int smallestPickedNum = 0;
            for (int k = sp; k <= ep; k++)
            {
                int ind = cloudSortInd[k];

                if (cloudNeighborPicked[ind] == 0 &&
                    cloudCurvature[ind] < 0.1)
                {

                    cloudLabel[ind] = -1; 
                    surfPointsFlat.push_back(laserCloud->points[ind]);

                    smallestPickedNum++;

                    //选择4个面点
                    if (smallestPickedNum >= 4)
                    { 
                        break;//够数了则跳出循环
                    }

                    cloudNeighborPicked[ind] = 1;
                    for (int l = 1; l <= 5; l++)
                    { 
                        float diffX = laserCloud->points[ind + l].x - laserCloud->points[ind + l - 1].x;
                        float diffY = laserCloud->points[ind + l].y - laserCloud->points[ind + l - 1].y;
                        float diffZ = laserCloud->points[ind + l].z - laserCloud->points[ind + l - 1].z;
                        if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05)
                        {
                            break;
                        }

                        cloudNeighborPicked[ind + l] = 1;
                    }
                    for (int l = -1; l >= -5; l--)
                    {
                        float diffX = laserCloud->points[ind + l].x - laserCloud->points[ind + l + 1].x;
                        float diffY = laserCloud->points[ind + l].y - laserCloud->points[ind + l + 1].y;
                        float diffZ = laserCloud->points[ind + l].z - laserCloud->points[ind + l + 1].z;
                        if (diffX * diffX + diffY * diffY + diffZ * diffZ > 0.05)
                        {
                            break;
                        }

                        cloudNeighborPicked[ind + l] = 1;
                    }
                }
            }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

选择面点的时候和角点基本一致,不再细写.
有区别的地方就是:
只选4个面点,没有像角点一样选几个弱面点,这部分在下面

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

            //除了角点和弱角点的都是弱面点
            for (int k = sp; k <= ep; k++)
            {
                if (cloudLabel[k] <= 0)
                {
                    surfPointsLessFlatScan->push_back(laserCloud->points[k]);
                }
            }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

弱面点的处理就在接着的下面
通过标签判断,前面的标签已将打上了 角点为-2 ,弱角点为-1 ,面点为1 .剩下的均为弱面点

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        //将弱面点 进行体素滤波  因为剩下的都是弱面点了,点就很多了
        pcl::PointCloud<PointType> surfPointsLessFlatScanDS;
        pcl::VoxelGrid<PointType> downSizeFilter;
        downSizeFilter.setInputCloud(surfPointsLessFlatScan);
        downSizeFilter.setLeafSize(0.2, 0.2, 0.2);
        downSizeFilter.filter(surfPointsLessFlatScanDS);

        surfPointsLessFlat += surfPointsLessFlatScanDS;

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

将弱面点 进行体素滤波 因为剩下的都是弱面点了,点就很多了

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    //发布所有的点云
    sensor_msgs::PointCloud2 laserCloudOutMsg;
    pcl::toROSMsg(*laserCloud, laserCloudOutMsg);
    laserCloudOutMsg.header.stamp = laserCloudMsg->header.stamp;
    laserCloudOutMsg.header.frame_id = "/camera_init";
    pubLaserCloud.publish(laserCloudOutMsg);

    //发布角点
    sensor_msgs::PointCloud2 cornerPointsSharpMsg;
    pcl::toROSMsg(cornerPointsSharp, cornerPointsSharpMsg);
    cornerPointsSharpMsg.header.stamp = laserCloudMsg->header.stamp;
    cornerPointsSharpMsg.header.frame_id = "/camera_init";
    pubCornerPointsSharp.publish(cornerPointsSharpMsg);

    //发布弱角点
    sensor_msgs::PointCloud2 cornerPointsLessSharpMsg;
    pcl::toROSMsg(cornerPointsLessSharp, cornerPointsLessSharpMsg);
    cornerPointsLessSharpMsg.header.stamp = laserCloudMsg->header.stamp;
    cornerPointsLessSharpMsg.header.frame_id = "/camera_init";
    pubCornerPointsLessSharp.publish(cornerPointsLessSharpMsg);


    //发布面点
    sensor_msgs::PointCloud2 surfPointsFlat2;
    pcl::toROSMsg(surfPointsFlat, surfPointsFlat2);
    surfPointsFlat2.header.stamp = laserCloudMsg->header.stamp;
    surfPointsFlat2.header.frame_id = "/camera_init";
    pubSurfPointsFlat.publish(surfPointsFlat2);

    //发布弱面点
    sensor_msgs::PointCloud2 surfPointsLessFlat2;
    pcl::toROSMsg(surfPointsLessFlat, surfPointsLessFlat2);
    surfPointsLessFlat2.header.stamp = laserCloudMsg->header.stamp;
    surfPointsLessFlat2.header.frame_id = "/camera_init";
    pubSurfPointsLessFlat.publish(surfPointsLessFlat2);


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

将上面整理的特征点转为ros的格式,然后发布topic出去
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

以上为A-LOAM 特征点提取部分的代码解读
之前要做点的预处理,这部分的代码在之前分析过了
链接

文章来源: blog.csdn.net,作者:月照银海似蛟龙,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_32761549/article/details/120664672

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。