4. 点云预处理:点云滤波、点云降采样、点云去噪、点云分割

各位同学,咱们今天聊聊点云预处理。说实话,这是整个高精地图构建里最“脏”最“累”的活,但也是最重要的一步。我经常跟团队里的新人说:“预处理做不好,后面定位算法再牛也白搭。”

你想想看,激光雷达扫出来的原始点云,那叫一个“原生态”。有飞鸟、有扬尘、有路面反光造成的噪点,甚至还有雷达自己产生的鬼影。你要是直接拿这些数据去建图或者做定位,那结果肯定是一团糟。所以,咱们得先给点云“洗个澡”。

核心目标:把原始点云变成干净、均匀、结构清晰的“可用数据”。

点云预处理核心流程 原始点云数据 点云滤波 直通/体素/统计滤波 点云降采样 体素网格/随机采样 点云去噪 半径滤波/统计滤波 点云分割 地面/物体/区域分割 预处理后的干净点云

4.1 点云滤波:把“垃圾”挡在门外

滤波,说白了就是“筛子”。我们根据点云的空间位置、距离、强度等属性,把不想要的部分直接过滤掉。

直通滤波(PassThrough)是我最常用的。比如我只关心车前50米范围内的障碍物,那就直接把50米以外的点全部砍掉。代码很简单,但效果立竿见影。

// C++ 示例:直通滤波
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");      // 沿Z轴过滤
pass.setFilterLimits(-1.5, 3.0);   // 保留高度 -1.5m 到 3.0m 的点
pass.filter(*cloud_filtered);

我的小技巧:做直通滤波时,别只想着过滤距离。我习惯先过滤掉头顶以上的点(比如高于3米),因为那些大多是树枝、路灯杆,对地面定位没啥用,还能减少计算量。

4.2 点云降采样:给点云“减肥”

64线激光雷达一秒钟能扫出130万个点。你想想看,这么多点全拿去算匹配,CPU得累死。所以得降采样,也就是“减肥”。

体素网格滤波(VoxelGrid)是业界标配。它把空间切成一个个小立方体(体素),每个立方体里只保留一个重心点。体素大小设成0.1米,点云量能减少90%以上,但几何结构基本不变。

// C++ 示例:体素网格降采样
pcl::VoxelGrid<pcl::PointXYZ> voxel;
voxel.setInputCloud(cloud);
voxel.setLeafSize(0.1f, 0.1f, 0.1f);  // 体素边长10cm
voxel.filter(*cloud_downsampled);

注意:体素大小不是越小越好。设成0.05米,点云还是太密;设成0.5米,细节全丢了。我一般根据场景来调:城市道路用0.1米,高速公路用0.2米。

4.3 点云去噪:把“脏东西”洗掉

去噪和滤波有啥区别?滤波是“一刀切”,去噪是“精准打击”。噪点通常是一些孤立点,比如空气中的灰尘、雷达的反射杂波。

统计滤波(StatisticalOutlierRemoval)是我最推荐的。它计算每个点周围K个邻居的平均距离,如果某个点离邻居太远,就判定为噪点干掉。

// C++ 示例:统计滤波去噪
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50);          // 考虑50个邻居
sor.setStddevMulThresh(1.0); // 标准差倍数阈值
sor.filter(*cloud_denoised);

我曾经在一个隧道场景里吃过亏。隧道里粉尘特别大,统计滤波的参数没调好,把墙上的点也给误删了。后来我把 setMeanK 从20改到80,效果才稳定下来。所以参数一定要根据实际场景调。

4.4 点云分割:把“整块”拆成“零件”

分割,就是把点云按语义拆开。比如地面是一块,路边的车是一块,建筑物是一块。为什么要分割?因为后续的定位算法只关心地面和静态结构,动态物体(车、人)反而会干扰匹配。

地面分割是最基础也最重要的。我常用RANSAC平面拟合来提取地面。代码大概长这样:

// C++ 示例:RANSAC地面分割
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE);
seg.setMethodType(pcl::SAC_RANSAC);
seg.setDistanceThreshold(0.2);  // 距离平面20cm以内的点算地面
seg.setInputCloud(cloud);
seg.segment(*inliers, *coefficients);

避坑指南:我曾经在坡道上做地面分割,RANSAC直接崩了。因为坡道不是一个平面,而是一个斜面。后来我改用“区域生长”算法,先找种子点,再向四周扩散,才把坡道完整分割出来。

除了地面分割,还有欧几里得聚类用于分割独立物体。比如把一辆车、一棵树分别提取出来。这个在动态物体剔除时特别有用。

4.5 预处理流程总结

在实际项目中,这四个步骤通常是串起来用的。我个人的习惯顺序是:

  1. 先滤波:用直通滤波砍掉远距离和无关高度区域
  2. 再去噪:用统计滤波干掉孤立噪点
  3. 后分割:提取地面和静态结构
  4. 最后降采样:把干净的点云“减肥”,给后续算法用

为什么降采样放最后?因为分割和去噪需要尽量保留原始点的密度,才能准确判断邻居关系。等分割完了再降采样,精度损失最小。

步骤 常用方法 核心参数 我的建议值
滤波 直通滤波 过滤范围 Z轴:-1.5~3.0m
降采样 体素网格 体素大小 城市0.1m,高速0.2m
去噪 统计滤波 邻居数/标准差倍数 50个邻居,1.0倍
分割 RANSAC/区域生长 距离阈值 0.2m(平面)

嗯,点云预处理这块就聊到这儿。说白了,预处理没有银弹,每个场景都得调参数。但只要你理解了每个步骤“为什么做”、“怎么做”,上手就快了。

最后说一句:别小看预处理。我见过太多项目,定位精度上不去,最后查来查去,发现是点云预处理没做好。把基础打牢,后面才能走得稳。

可可精品资料库