3. 统计滤波(Statistical Outlier Removal):原理详解、PCL实现、参数调优
各位做SLAM的朋友,你们有没有遇到过这种情况?建好的地图里,莫名其妙飘着几个孤立的点,就像照片上的噪点一样碍眼。这些离群点,说白了就是传感器在某个瞬间抽风了,或者被什么反光材质晃了一下。我刚开始做激光SLAM时,就被这些点坑过——明明闭环检测做得挺好,地图就是不平滑,后来才发现是这些“野点”在捣乱。
今天要讲的统计滤波,就是专门收拾这些捣蛋鬼的。它不跟你讲什么复杂数学,核心逻辑就一句话:如果一个点跟周围邻居离得太远,那它大概率是个错误。
3.1 原理:邻居们怎么看你的?
想象一下,你站在一群人中间。如果周围的人都离你半米远,那很正常。但如果所有人都离你十米远,那你肯定站错地方了。统计滤波就是这个思路。
具体做法分三步:
- 找邻居:对每个点,用K-D树搜索它最近的k个邻居。
- 算距离:计算这个点到所有邻居的平均距离。
- 做判断:如果这个平均距离,比全局平均值大太多(超过标准差倍数),就干掉它。
这里有个关键点——全局平均值。算法会先算所有点的“邻居平均距离”,得到一个分布。这个分布通常符合高斯分布。然后设定一个阈值:
阈值 = 全局平均距离 + 标准差倍数 × 全局标准差
超过这个阈值的点,就被判定为离群点。嗯,这里要注意,标准差倍数设得越大,保留的点越多,滤波越温和。
我画了一张图,帮你理解这个流程:
3.2 PCL实现:三行代码搞定
在PCL里,统计滤波封装得特别干净。我直接上代码:
#include <pcl/filters/statistical_outlier_removal.h>
// 创建滤波器对象
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
// 设置输入点云
sor.setInputCloud(cloud_in);
// 设置参数
sor.setMeanK(50); // 邻居点数:50
sor.setStddevMulThresh(1.0); // 标准差倍数:1.0
// 执行滤波
sor.filter(*cloud_out);
你看,核心就三个函数调用。但参数怎么设,才是真正的学问。
3.3 参数调优:邻居点数与标准差倍数
这两个参数,是统计滤波的灵魂。我分别说说我的经验。
邻居点数(setMeanK)
这个参数决定了每个点要参考多少个邻居。设小了,判断不准确;设大了,计算慢,而且会把边缘点误删。
| 点云类型 | 推荐邻居点数 | 说明 |
|---|---|---|
| 稀疏点云(如16线激光雷达) | 10 - 30 | 邻居太少,统计意义不大 |
| 中等密度(如32线) | 30 - 80 | 平衡效果和速度 |
| 稠密点云(如64线、固态激光雷达) | 50 - 200 | 邻居越多,判断越稳 |
我记得有一次做室内建图,用的是16线雷达。点云本来就稀稀拉拉的,我把邻居点数设成100,结果把墙角的点全删了,地图直接缺了一块。后来改成20,效果就好了。
标准差倍数(setStddevMulThresh)
这个参数控制滤波的严格程度。倍数越小,删得越多;倍数越大,保留得越多。
- 0.5 - 1.0:严格滤波。适合噪声明显的场景,比如雨雪天。但小心别把有效点删太多。
- 1.0 - 2.0:中等滤波。日常建图最常用的范围。我个人习惯用1.2,效果比较均衡。
- 2.0 - 3.0:宽松滤波。只删那些特别离谱的离群点。适合点云质量本身就不错的情况。
3.4 实战建议:什么时候用统计滤波?
根据我的经验,统计滤波最适合以下场景:
- 预处理阶段:在体素滤波之后,用统计滤波清理残留的离群点。
- 动态物体过滤:如果场景里有偶尔出现的行人、车辆,统计滤波能有效去除。
- 多帧融合前:在拼接多帧点云之前,先每帧单独滤波,能减少累积误差。
但要注意,统计滤波对边缘点不太友好。物体边缘的点,本来邻居就少,容易被误判为离群点。如果你要保留边缘细节,可以把标准差倍数设大一点,比如2.0以上。
好了,统计滤波就讲到这里。说白了,它就是点云世界里的“社交距离检测器”——离群太远的,直接请出去。参数调优的关键,就是根据你的点云密度,找到那个“既不放过噪声,也不误伤好人”的平衡点。
可可精品资料库