3. 统计滤波(Statistical Outlier Removal):原理、均值与标准差、PCL实现与参数调优
3.1 为什么要做统计滤波?
点云数据里总有些「不速之客」。比如激光雷达打到空气中的灰尘,或者深度相机边缘的噪点。这些离群点看着不多,但影响很大——建图会飘,配准会偏。
我个人习惯,拿到原始点云的第一件事,就是做统计滤波。为什么?因为它简单、有效,而且参数很直观。说白了,就是看每个点跟邻居们「合不合群」。
3.2 原理:邻居关系与高斯分布
统计滤波的核心思想其实很朴素:
- 对每个点,计算它到所有邻居的平均距离
- 这些平均距离应该服从高斯分布
- 那些距离均值太远的点,就是离群点
你想想看,一个正常的点,周围总有几个小伙伴。如果它孤零零的,周围啥也没有,那大概率是噪声。
具体步骤是这样的:
- 设定邻居数量 k(比如 50 个)
- 对每个点,找最近的 k 个邻居
- 计算到这些邻居的平均距离 d_i
- 所有点的 d_i 构成一个分布,计算均值 μ 和标准差 σ
- 设定阈值:如果 d_i > μ + std_mult × σ,就剔除
这里的 std_mult 就是标准差倍数,也是我们调参的关键。
核心公式:
保留条件:d_i ≤ μ + std_mult × σ
其中 μ 是所有 d_i 的均值,σ 是标准差
3.3 均值与标准差:到底在算什么?
我记得刚入行时,总觉得「均值」和「标准差」是数学课上的东西。直到我在一个隧道场景里做滤波,才发现这两个值太重要了。
举个例子:
| 场景 | 均值 μ | 标准差 σ | 说明 |
|---|---|---|---|
| 室内平整墙面 | 0.05 m | 0.02 m | 点云很均匀 |
| 室外灌木丛 | 0.15 m | 0.08 m | 点云分布散 |
| 有飞点的场景 | 0.12 m | 0.25 m | 标准差突然变大 |
看到没?当场景里有飞点时,标准差会急剧增大。这时候如果还用固定的阈值,就会把很多正常点也滤掉。
我曾经在一个果园项目里吃过这个亏。果树叶子稀疏,标准差本来就大,结果我把阈值设得太死,把树干上的点都干掉了。后来我学乖了——先看统计直方图,再决定参数。
3.4 PCL实现:代码怎么写?
PCL 里已经封装好了,直接用 StatisticalOutlierRemoval 滤波器。代码不长,但有几个坑要注意。
#include <pcl/filters/statistical_outlier_removal.h>
// 输入点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
// 假设 cloud 已经加载了数据
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
// 关键参数设置
sor.setMeanK(50); // 邻居数量 k
sor.setStddevMulThresh(1.0); // 标准差倍数
// 执行滤波
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
sor.filter(*cloud_filtered);
// 如果想看看被剔除的点
sor.setNegative(true);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_removed(new pcl::PointCloud<pcl::PointXYZ>);
sor.filter(*cloud_removed);
嗯,这里要注意:setNegative(true) 会返回被剔除的点,调试时很有用。我一般会先跑一遍,看看剔除的都是些什么货色。
3.5 参数调优:k 和 std_mult 怎么选?
这是大家最头疼的问题。我直接给经验值:
- k(邻居数量):一般取 20~100。点云越密,k 可以越大。室内场景我常用 50,室外稀疏场景用 20 就够了。
- std_mult(标准差倍数):一般取 0.5~3.0。值越小,滤得越狠。保守点用 2.0,激进点用 1.0。
调参小技巧:
先设 std_mult=3.0,看看剔除效果。如果还有明显飞点,逐步降低到 2.0、1.5。如果正常点被误删了,就调高 std_mult 或增大 k。
我曾经在一个工厂自动化项目里,点云里有大量反光造成的飞点。std_mult 设到 0.8 才勉强干净,但代价是边缘点也被削掉了一层。后来我改用半径滤波配合统计滤波,效果才好。
避坑指南:
我曾经在低密度点云上用过 k=100,结果每个点都找不到那么多邻居,算法直接崩了。记住:k 不能超过点云总点数。
3.6 核心逻辑流程图
下面这张图总结了统计滤波的完整流程,我建议你保存下来,调参时对照着看:
3.7 实战经验总结
最后分享几个我踩过的坑:
- 场景不同,参数不同:室内和室外不要用同一套参数。我习惯给每个场景单独标定一次。
- 先降采样再滤波:点云太密时,统计滤波会很慢。先体素滤波降采样,速度能快 10 倍。
- 不要迷信默认值:PCL 默认 k=50、std_mult=1.0,但这只是通用值。我见过有人直接拿来用,结果把墙上的点都滤没了。
- 可视化验证:调参后一定要用可视化工具看看效果。肉眼比任何指标都靠谱。
一句话总结:
统计滤波就是「看邻居,算距离,砍 outliers」。参数 k 控制邻居范围,std_mult 控制砍的力度。调参时先松后紧,配合可视化,效果不会差。
好了,统计滤波就聊到这儿。下一节我们讲半径滤波,那家伙在某些场景下比统计滤波更狠。