4. 点云数据处理:点云滤波、点云配准(ICP、NDT)、点云分割、点云分类
点云数据处理,说白了就是给激光雷达「擦亮眼睛」。
我刚开始做高精地图那会儿,拿到一帧原始点云数据,密密麻麻全是点。但真正有用的信息,其实只占一小部分。剩下的呢?大多是噪声、地面反射杂波,还有那些飞来飞去的飞虫。
所以,点云处理的第一步,永远是滤波。
4.1 点云滤波:去噪与降采样
滤波的目的很简单:去掉没用的,留下有用的。但怎么定义「有用」?这得看场景。
4.1.1 体素滤波(Voxel Grid Filter)
这是我最常用的降采样方法。原理不复杂:把空间切成一个个小立方体(体素),每个体素里只保留一个重心点。
举个例子,一帧64线激光雷达的数据,原始可能有十几万个点。用0.2米的体素一过滤,能降到两三万个点。计算量瞬间就下来了。
// PCL 中的体素滤波示例
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(0.2f, 0.2f, 0.2f); // 体素边长0.2米
vg.filter(*cloud_filtered);
4.1.2 直通滤波(PassThrough Filter)
这个更简单。我只保留某个轴向上的点。比如,我只关心地面以上0.5米到3米之间的点。超出这个范围的,直接扔掉。
为什么?因为高精地图主要关注路面标识、护栏、路沿。天上的树枝、地下的井盖,都不是我想要的。
4.1.3 统计滤波(Statistical Outlier Removal)
这个用来去除离群点。原理是:每个点周围都有邻居。如果某个点离邻居太远,那它大概率是噪声。
我记得有一次在隧道里采集数据,激光雷达打到墙壁上,产生了大量散射点。用统计滤波一跑,干净多了。
4.2 点云配准:ICP与NDT
配准,说白了就是「对齐」。把不同时刻、不同位置扫到的点云,拼到同一个坐标系里。
高精地图的构建,本质上就是不断做配准。你想想看,车在跑,激光雷达在扫,每一帧都是局部数据。怎么把它们拼成一张完整的地图?靠的就是配准算法。
4.2.1 ICP(迭代最近点)
ICP是经典中的经典。原理很直观:找两片点云中距离最近的点对,然后算一个旋转平移矩阵,让这些点对的距离之和最小。
但ICP有个致命弱点:对初始位置敏感。如果两片点云一开始就离得远,它很容易陷入局部最优。
// PCL 中的 ICP 示例
pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
icp.setInputSource(cloud_source);
icp.setInputTarget(cloud_target);
icp.setMaxCorrespondenceDistance(0.5); // 最大匹配距离
icp.setMaximumIterations(50);
icp.align(*cloud_final);
4.2.2 NDT(正态分布变换)
NDT是ICP的替代方案。它不直接匹配点,而是把点云用概率分布来表示。
具体做法是:把空间分成网格,每个网格里计算点的均值和协方差。配准时,找的是让概率密度最大的变换。
我个人更喜欢NDT。为什么?因为它对初始位置不那么敏感,而且速度更快。在城区道路这种特征丰富的场景下,NDT的鲁棒性明显优于ICP。
// PCL 中的 NDT 示例
pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;
ndt.setInputSource(cloud_source);
ndt.setInputTarget(cloud_target);
ndt.setResolution(1.0); // 网格分辨率
ndt.setMaximumIterations(30);
ndt.align(*cloud_final);
4.3 点云分割:提取地面与障碍物
分割,就是把点云分成不同的「物体」。在高精地图里,最重要的分割是:地面分割和障碍物分割。
4.3.1 地面分割
地面点占了点云的大头。如果不先去掉地面,后续的聚类和分类都会受影响。
常用的方法是RANSAC平面拟合。我设定一个高度阈值,把符合平面模型的点都标为地面。
// PCL 中的 RANSAC 平面分割
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.1); // 距离阈值
seg.setInputCloud(cloud);
seg.segment(*inliers, *coefficients);
嗯,这里要注意。如果路面有坡度,RANSAC会误把坡面也当成地面。我一般会结合高度直方图来做二次校验。
4.3.2 欧几里得聚类
去掉地面后,剩下的点就是障碍物了。怎么区分不同的障碍物?用欧几里得聚类。
原理很简单:点与点之间的距离小于某个阈值,就认为它们属于同一个物体。
我记得有一次在停车场,两辆车停得很近。聚类阈值设大了,两辆车被当成一个物体。设小了,一辆车被分成好几块。后来我根据激光雷达的线束密度,动态调整了阈值,效果才好起来。
4.4 点云分类:识别道路元素
分类,是点云处理的最后一环。它要回答的问题是:这个点属于什么?是路沿?是护栏?还是交通标志牌?
4.4.1 基于几何特征的方法
传统方法靠手工特征。比如:
- 高度特征:路沿通常比地面高10~15厘米
- 法向量:墙面法向量是水平的,地面法向量是垂直的
- 曲率:杆状物的曲率变化大,平面物的曲率变化小
我早期做的一个项目,就是用这些特征组合,配合随机森林分类器,实现了90%以上的分类准确率。
4.4.2 基于深度学习的方法
现在主流是用PointNet++或RandLA-Net这类网络。它们能直接处理无序点云,自动学习特征。
但深度学习有个问题:需要大量标注数据。我建议先用传统方法做快速原型验证,再上深度学习做精细化分类。
好了,这一章的内容就到这里。下一章我们会聊点云语义地图的构建,那是把分类结果真正用起来的关键一步。