第4章:点云预处理实战:体素滤波、统计滤波、半径滤波,去除离群点与下采样
点云数据拿到手,第一件事是什么?
不是急着做配准,也不是急着做分割。我个人的习惯是——先做预处理。为什么?因为原始点云太“脏”了。传感器噪声、环境干扰、远距离的飘点……这些乱七八糟的东西如果不处理掉,后面所有的算法都会被带偏。
说白了,预处理就是给点云“洗个澡”。洗完之后,数据干净了,计算量也小了,后面的活儿才好干。
4.1 为什么要做点云预处理?
你想想看,一个激光雷达扫一圈,可能产生几十万个点。这里面有多少是真正有用的?
- 离群点:莫名其妙飘在空中的点,可能是传感器误触发的。
- 噪声点:物体边缘的抖动、反射干扰造成的。
- 冗余点:平坦地面上密密麻麻的点,其实几个点就能代表。
我在项目中遇到过最夸张的一次,一个室内扫描的点云,离群点占了将近15%。如果不滤掉,后续的平面拟合直接跑偏。嗯,从那以后我再也不敢跳过预处理了。
核心目标:
- 去除噪声和离群点,提高数据质量
- 降低点云密度,减少计算量
- 保持几何结构不变,不破坏关键特征
4.2 三种滤波方法详解
预处理主要靠三种滤波:体素滤波、统计滤波、半径滤波。它们各管一摊,配合使用效果最好。
4.2.1 体素滤波(Voxel Grid Filter)—— 下采样的主力
体素滤波说白了就是“降采样”。它把空间划分成一个个小立方体(体素),每个体素内只保留一个点(通常是重心点)。
这样做的好处很明显:点云密度均匀了,计算量大幅下降。我习惯把体素大小设为0.01m~0.05m,具体看场景。如果是室内小物体,0.01m就够了;如果是室外大场景,0.05m更合适。
我的经验:体素大小不要设得太小,否则降采样效果不明显。也不要太大,否则会丢失细节。0.02m是个不错的起点。
// C++ PCL 示例:体素滤波
pcl::VoxelGrid<pcl::PointXYZ> voxel_filter;
voxel_filter.setInputCloud(cloud);
voxel_filter.setLeafSize(0.02f, 0.02f, 0.02f); // 体素大小 2cm
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
voxel_filter.filter(*cloud_filtered);
4.2.2 统计滤波(Statistical Outlier Removal)—— 去离群点的利器
统计滤波的原理很简单:计算每个点到它K个近邻的平均距离。如果这个距离超出了全局平均距离的某个倍数(标准差倍数),就判定为离群点。
为什么会这样?因为离群点通常孤零零的,周围没什么邻居,平均距离自然就大。
我记得有一次做室外道路点云处理,统计滤波帮我清掉了所有飘在空中的树叶点。参数设置上,我一般取K=50,标准差倍数设为1.0~2.0。倍数越小,滤掉的点越多。
注意:标准差倍数设得太小(比如0.5),会把正常点也滤掉。设得太大(比如3.0),离群点又滤不干净。建议先试1.0,看效果再调。
// C++ PCL 示例:统计滤波
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50); // 近邻点数
sor.setStddevMulThresh(1.0); // 标准差倍数
sor.filter(*cloud_filtered);
4.2.3 半径滤波(Radius Outlier Removal)—— 另一种去噪思路
半径滤波和统计滤波有点像,但判断标准不同。它检查每个点周围指定半径内是否有足够多的邻居。如果邻居数量少于阈值,就干掉它。
这个滤波器对稀疏的离群点特别有效。我曾经用它处理过激光雷达扫描的墙角点云,那些因为反射角度问题产生的孤立点,半径滤波一把就清干净了。
// C++ PCL 示例:半径滤波
pcl::RadiusOutlierRemoval<pcl::PointXYZ> ror;
ror.setInputCloud(cloud);
ror.setRadiusSearch(0.05); // 搜索半径 5cm
ror.setMinNeighborsInRadius(10); // 最少邻居数
ror.filter(*cloud_filtered);
4.3 三种滤波对比
| 滤波方法 | 主要用途 | 关键参数 | 适用场景 |
|---|---|---|---|
| 体素滤波 | 下采样,降低密度 | 体素大小 | 所有场景,尤其是大场景 |
| 统计滤波 | 去除离群点 | K近邻数、标准差倍数 | 噪声均匀的场景 |
| 半径滤波 | 去除稀疏离群点 | 搜索半径、最少邻居数 | 孤立点较多的场景 |
4.4 实战流程:先滤波,再下采样
在实际项目中,我通常按这个顺序来:
- 先统计滤波或半径滤波:把明显的离群点干掉。
- 再体素滤波:把干净的点云降采样,降低计算量。
为什么要先滤波再下采样?你想想看,如果先下采样,离群点还在里面,只是密度变低了。后面再做滤波,效果会打折扣。我曾经犯过这个错,后来就记住了。
推荐流程:
原始点云 → 统计滤波(去噪) → 体素滤波(降采样) → 干净点云
4.5 知识体系总览
下面这张图把本章的核心逻辑串起来了。你可以看到,预处理不是随便选一个滤波就完事,而是有策略的。
4.6 避坑指南
做点云预处理这些年,我踩过不少坑。分享几个给你:
我曾经犯过的错:
- 体素滤波参数设得太大,结果把物体的棱角都磨平了,后续特征提取全废。
- 统计滤波的标准差倍数设得太小,把正常的地面点也滤掉了,导致地面拟合缺了一块。
- 半径滤波的搜索半径设得太小,离群点一个都没滤掉,白跑一趟。
嗯,这些坑你记住了,就能少走弯路。预处理做好了,后面的配准、分割、识别才能稳稳当当。
我的建议:每次调参后,用可视化工具看一眼效果。别光看数据,眼睛比数字靠谱。