自动驾驶系列(3)ALOAM源码解析-scanRegistration.cpp

ALOM节点图

在这里插入图片描述

1.特征点提取原理

根据曲率来提取两种特征点,分别为角点与面点
主函数

2.实现

2.1主函数

功能:初始化ROS节点,订阅激光点云数据,发布处理后的点云数据。代码的主要功能如下:

  1. 初始化ROS节点,设置节点名称和参数。
  2. 设置扫描线数量(N_SCANS),最小距离(MINIMUM_RANGE)。
  3. 打印扫描线数量和最小距离。
  4. 检查扫描线数量是否支持16、32或64条扫描线,否则输出提示信息并退出程序。
  5. 订阅激光点云数据(/points_raw),并设置回调函数laserCloudHandler。
  6. 发布处理后的点云数据(/velodyne_cloud_2),(/laser_cloud_sharp),(/laser_cloud_less_sharp),(/laser_cloud_flat),(/laser_cloud_less_flat),(/laser_remove_points)。
  7. 如果PUB_EACH_LINE设置为true,则为每个扫描线发布单独的点云数据。
  8. 运行ROS事件循环。
int main(int argc, char **argv)
{
    // 初始化节点
    ros::init(argc, argv, "scanRegistration");
    // 获取节点句柄
    ros::NodeHandle nh;

    // 获取参数:扫描线数量
    nh.param<int>("scan_line", N_SCANS, 16);

    // 获取参数:最小距离
    nh.param<double>("minimum_range", MINIMUM_RANGE, 0.1);

    // 打印参数:扫描线数量
    printf("scan line number %d \n", N_SCANS);

    // 判断扫描线数量是否支持
    if(N_SCANS != 16 && N_SCANS != 32 && N_SCANS != 64)
    {
        printf("only support velodyne with 16, 32 or 64 scan line!");
        return 0;
    }

    // 订阅激光点云
    ros::Subscriber subLaserCloud = nh.subscribe<sensor_msgs::PointCloud2>("/points_raw", 100, laserCloudHandler);

    // 发布激光点云
    pubLaserCloud = nh.advertise<sensor_msgs::PointCloud2>("/velodyne_cloud_2", 100);

    // 发布角点点云(锐角)
    pubCornerPointsSharp = nh.advertise<sensor_msgs::PointCloud2>("/laser_cloud_sharp", 100);

    // 发布角点点云(稍锐角)
    pubCornerPointsLessSharp = nh.advertise<sensor_msgs::PointCloud2>("/laser_cloud_less_sharp", 100);

    // 发布平面点云(平坦)
    pubSurfPointsFlat = nh.advertise<sensor_msgs::PointCloud2>("/laser_cloud_flat", 100);

    // 发布平面点云(稍平坦)
    pubSurfPointsLessFlat = nh.advertise<sensor_msgs::PointCloud2>("/laser_cloud_less_flat", 100);

    // 发布移除点云
    pubRemovePoints = nh.advertise<sensor_msgs::PointCloud2>("/laser_remove_points", 100);

    // 判断是否发布每个扫描线
    if(PUB_EACH_LINE)
    {
        // 发布每个扫描线
        for(int i = 0; i < N_SCANS; i++)
        {
            ros::Publisher tmp = nh.advertise<sensor_msgs::PointCloud2>("/laser_scanid_" + std::to_string(i), 100);
            pubEachScan.push_back(tmp);
        }
    }
    // 订阅
    ros::spin();

    return 0;
}

2.2 laserCloudHandler 函数

订阅LiDAR点云数据,并对其进行处理,最后将处理后的点云数据发布出去。处理过程包括去噪、过滤、投影、提取角点、分割平面等。

1.定义了一些变量和结构体,如点云类型、点云尺寸等。

2.定义了一个名为laserCloudHandler的回调函数,该函数会在接收到LiDAR点云数据时被调用。

3.在laserCloudHandler函数中,首先检查系统是否初始化,如果没有,则计数器自增,直到达到系统延迟时间。然后,计算预处理时间。

4.对LiDAR点云数据进行预处理,包括去噪、过滤等。预处理后的点云数据存储在laserCloudIn变量中。

5.计算处理时间。

6.遍历预处理后的点云数据,提取角点、分割平面等。

7.将处理后的点云数据发布出去。

2.3 removeClosedPointCloud函数

接收两个pcl::PointCloud类型的参数:cloud_in(输入点云)和cloud_out(输出点云)。函数的目的是从输入点云中移除那些距离原点(坐标系原点)小于特定阈值的点。

原理:是遍历输入点云中的每个点,检查其三维空间坐标(x、y和z)是否满足条件。如果满足条件,将该点复制到输出点云中,否则跳过该点。最后,调整输出点云的大小以匹配有效点的数量。

注意:

输入和输出点云必须是指向同一内存空间的指针,否则会导致未定义行为。
函数使用三维空间中的点距离公式(平方和)来检查点是否满足条件。
函数使用size_t类型来表示点cloud_in中的索引和cloud_out中的有效点数。

void removeClosedPointCloud(const pcl::PointCloud<PointT> &cloud_in,
                              pcl::PointCloud<PointT> &cloud_out, float thres)
{
    // 如果输入和输出点云不是同一内存空间,则抛出异常或采取其他措施
    if (&cloud_in != &cloud_out)
    {
        // 复制输入点云的头部信息到输出点云
        cloud_out.header = cloud_in.header;
        // 调整输出点云的大小以匹配有效点数
        cloud_out.points.resize(cloud_in.points.size());
    }

    // 初始化输出点云的有效点数
    size_t j = 0;

    // 遍历输入点云中的每个点
    for (size_t i = 0; i < cloud_in.points.size(); ++i)
    {
        // 检查该点的三维空间坐标是否满足条件
        if (cloud_in.points[i].x * cloud_in.points[i].x + cloud_in.points[i].y * cloud_in.points[i].y + cloud_in.points[i].z * cloud_in.points[i].z < thres * thres)
            continue;
        // 将该点复制到输出点云中
        cloud_out.points[j] = cloud_in.points[i];
        // 有效点数加一
        j++;
    }
    // 调整输出点云的大小以匹配有效点数
    if (j != cloud_in.points.size())
    {
        cloud_out.points.resize(j);
    }

    // 设置输出点云的属性
    cloud_out.height = 1;
    cloud_out.width = static_cast<uint32_t>(j);
    cloud_out.is_dense = true;
}

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我明白了。这是一道关于数据结构中顺序栈的编程题目。我们可以按照以下步骤进行: 1. 首先,我们需要定义顺序栈的结构体和一些基本操作函数,比如栈的初始化、入栈、出栈、取栈顶元素等。下面是一个简单的顺序栈的实现代码,文件名为SqStack.cpp: ```c++ #define MaxSize 50 typedef int ElemType; typedef struct { ElemType data[MaxSize]; int top; }SqStack; void InitStack(SqStack &S){ S.top = -1; } bool StackEmpty(SqStack S){ if (S.top == -1) return true; else return false; } bool Push(SqStack &S, ElemType x){ if (S.top == MaxSize - 1) return false; S.top++; S.data[S.top] = x; return true; } bool Pop(SqStack &S, ElemType &x){ if (S.top == -1) return false; x = S.data[S.top]; S.top--; return true; } bool GetTop(SqStack S, ElemType &x){ if (S.top == -1) return false; x = S.data[S.top]; return true; } ``` 2. 接下来,我们需要编写一个测试代码test3-1.cpp,来测试顺序栈的各项操作是否正确。下面是一个简单的测试代码: ```c++ #include <iostream> #include "SqStack.cpp" using namespace std; int main(){ SqStack s; InitStack(s); for (int i = 0; i < 10; i++){ Push(s, i); } ElemType x; while (!StackEmpty(s)){ Pop(s, x); cout << x << " "; } return 0; } ``` 上面的测试代码实现了将0~9的数字依次入栈,并将它们全部出栈并输出。我们可以运行这段代码来测试我们的顺序栈实现是否正确。 注意,为了测试方便,我们将SqStack.cpp文件包含在了test3-1.cpp中。在实际编写中,我们应该将SqStack.cpp和test3-1.cpp分别保存为两个不同的文件。 希望我的回答能够帮助到你,如果有问题可以继续提问哦。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值