3. 点云预处理
拿到一帧原始点云,你直接扔进分割算法?我劝你别这么干。原始点云里什么都有:离群噪点、冗余点、远处的杂波,甚至还有传感器自带的反射鬼影。不做预处理,分割结果基本没法看。
这一章,我带你过一遍点云预处理最常用的四个滤波器。说白了,就是给点云「洗个澡、剃个毛、剪个边」。每个滤波器解决一类问题,组合起来用,效果翻倍。
3.1 直通滤波:先切一刀
直通滤波是最简单的滤波器,没有之一。你指定一个轴(比如 Z 轴),设一个范围,范围外的点全砍掉。为什么先做它?因为很多场景下,我们只关心某个距离范围内的目标。
举个例子。车载激光雷达扫一圈,能扫到 100 米开外。但你要检测的是前方 30 米内的行人。那 30 米外的点留着干嘛?徒增计算量而已。
// PCL 直通滤波示例
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("z"); // 沿 Z 轴过滤
pass.setFilterLimits(0.0, 30.0); // 保留 0~30 米范围
// pass.setFilterLimitsNegative(true); // 设为 true 则保留范围外的点
pass.filter(*cloud_filtered);
参数就两个:轴和范围。但有个坑——坐标系方向。我记得有一次,同事把 Z 轴当成高度,结果滤完发现地面没了。后来一查,他用的传感器坐标系里 Z 轴是朝前的。所以,先搞清楚你的点云坐标系再动手。
3.2 体素滤波降采样:给点云「减肥」
体素滤波是降采样的主力。它的原理很简单:把空间划分成一个个小立方体(体素),每个体素内只保留一个点(通常是重心或中心点)。
你想想看,一片平坦的地面,可能有几万个点。但实际分割时,几百个点就够描述地面形状了。体素滤波就是干这个的——用更少的点,保留同样的几何结构。
| 体素大小 | 点数保留比例 | 适用场景 |
|---|---|---|
| 0.01 m | ~80% | 精细建模、小目标检测 |
| 0.05 m | ~30% | 一般分割、地面提取 |
| 0.10 m | ~10% | 大场景、快速预览 |
// PCL 体素滤波示例
pcl::VoxelGrid<pcl::PointXYZ> voxel;
voxel.setInputCloud(cloud_filtered);
voxel.setLeafSize(0.05f, 0.05f, 0.05f); // 5cm 体素
voxel.filter(*cloud_downsampled);
体素滤波还有一个隐藏好处:均匀化点密度。原始点云近处密、远处疏,体素滤波能把这个差异抹平。后续做特征计算时,不会因为密度不均导致误判。
3.3 统计滤波去噪:干掉「孤魂野鬼」
统计滤波去噪,是我用得最多的去噪方法。它的原理基于一个观察:真实物体表面的点,邻域内点很多;而离群噪点周围,点很少。
具体做法是:对每个点,计算它到 k 个最近邻的平均距离。如果这个距离超过全局均值 + 若干倍标准差,就判定为离群点,干掉。
// PCL 统计滤波示例
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud_downsampled);
sor.setMeanK(50); // 邻域点数
sor.setStddevMulThresh(1.0); // 标准差倍数
sor.filter(*cloud_denoised);
两个关键参数:MeanK 和 StddevMulThresh。
- MeanK:邻域点数。设太小,统计不稳定;设太大,边缘点容易被误杀。我一般设 20~50。
- StddevMulThresh:标准差倍数。设 1.0 比较严格,能去掉大部分噪点;设 2.0 更宽松,只去掉极端离群点。
3.4 半径滤波去噪:简单粗暴但有效
半径滤波和统计滤波有点像,但更直接。它给每个点画一个半径为 R 的球,如果球内的点数少于阈值 N,就干掉这个点。
什么时候用半径滤波?当你的噪点分布比较均匀,或者你想保留特定密度的区域时。比如,我只想保留每平方米至少有 10 个点的区域,半径滤波就很好用。
// PCL 半径滤波示例
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud_denoised);
ror.setRadiusSearch(0.1); // 搜索半径 0.1 米
ror.setMinNeighborsInRadius(5); // 至少 5 个点
ror.filter(*cloud_clean);
| 滤波器 | 核心参数 | 适用场景 | 缺点 |
|---|---|---|---|
| 统计滤波 | MeanK, StddevMulThresh | 稀疏离群点、传感器噪点 | 密度不均时误杀 |
| 半径滤波 | Radius, MinNeighbors | 均匀噪点、密度阈值控制 | 对半径敏感 |
3.5 预处理流程实战建议
说了这么多,到底怎么组合?我给出一个通用流程,你根据实际场景微调:
- 直通滤波:先切出感兴趣区域,减少数据量。这一步能省 50%~80% 的点。
- 体素滤波:降采样到可接受密度。目标大就用大体素,目标小就用小体素。
- 统计滤波:去掉稀疏离群点。标准参数 MeanK=50, StddevMulThresh=1.0。
- 半径滤波:可选步骤,用于进一步清理小簇噪点。
嗯,这里要注意:每一步的参数都不是固定的。我习惯先跑一遍默认参数,看看效果,再微调。比如体素大小,我会从 0.05 开始,如果发现目标点被削得太厉害,就改小到 0.03。