第四节:统计滤波原理

统计滤波,说白了就是利用数据的统计特性来剔除异常点。我刚开始做点云处理时,总觉得滤波就是套个半径或者设个距离阈值,后来发现——嗯,没那么简单。

你想想看,激光雷达扫出来的点云,里面总会有那么几个「不听话」的点。可能是飞鸟,可能是扬尘,也可能是传感器本身的噪声。这些离群点如果不处理,后续的配准、建图、分割都会受影响。我踩过这个坑,所以今天咱们好好聊聊统计滤波的数学原理。

4.1 统计滤波的核心思想

统计滤波的直觉其实很朴素:一个点是不是离群,看它和邻居们的距离就知道了。

具体来说,对于点云中的每个点,我们计算它到所有K个近邻点的平均距离。然后,我们统计所有点的「平均距离」的分布——这个分布通常近似于高斯分布。于是,我们就可以用均值和标准差来划定一个范围,超出这个范围的点,就被判定为离群点。

核心公式:

对于点 pi,计算它到 K 个最近邻的平均距离 di

所有 di 的均值:μ = (1/N) Σ di

所有 di 的标准差:σ = sqrt((1/N) Σ (di - μ)²)

判定阈值:T = μ + α × σ

如果 di > T,则点 pi 被标记为离群点

这里的 α 就是标准差倍数,通常取 1.0 到 3.0 之间。α 越大,滤波越宽松;α 越小,滤波越严格。

4.2 均值与标准差的作用

我经常被问到:「为什么非得用均值和标准差?直接用最大距离不行吗?」

这个问题问得好。咱们来拆解一下:

  • 均值 μ:代表了点云中「正常点」的邻域平均距离。说白了,就是大多数点之间的疏密程度。如果点云很密,μ 就小;如果点云很稀疏,μ 就大。
  • 标准差 σ:代表了点云中「正常点」的邻域距离的波动程度。如果点云分布均匀,σ 就小;如果点云分布不均匀(比如有边缘、有遮挡),σ 就大。

这两个参数配合起来,就能自适应地处理不同密度的点云。举个例子:

场景 均值 μ 标准差 σ 阈值 T
室内密集点云 0.05 m 0.02 m 0.05 + 2×0.02 = 0.09 m
室外稀疏点云 0.30 m 0.10 m 0.30 + 2×0.10 = 0.50 m

你看,同样的 α=2,在室内场景下阈值是 9cm,在室外场景下阈值是 50cm。这就是统计滤波的妙处——它能自动适应点云的密度变化。

我的经验:

我在做自动驾驶路测数据处理时,遇到过一种情况:车辆转弯时,点云密度会剧烈变化。如果只用固定半径滤波,要么把墙上的点误删了,要么把远处的离群点放过了。换成统计滤波后,这个问题就解决了。因为统计滤波的阈值是动态计算的。

4.3 离群点判定阈值

阈值 T = μ + α × σ 这个公式,其实隐含了一个假设:正常点的邻域平均距离服从高斯分布。但实际点云数据往往不是完美的高斯分布,所以 α 的选择就变得很关键。

我个人的习惯是:

  • α = 1.0:非常严格的滤波,会剔除大约 15% 的点(如果数据是高斯分布的话)。适合对精度要求极高的场景,比如工业检测。
  • α = 2.0:中等严格,剔除约 5% 的点。这是最常用的设置,适合大多数 SLAM 和建图场景。
  • α = 3.0:宽松滤波,只剔除极端离群点。适合点云本身噪声较大的情况,比如远距离激光雷达数据。

注意:

α 不是越大越好。我曾经在一个项目中把 α 设成了 5.0,结果离群点一个都没滤掉——因为阈值太大了,所有点都通过了。反过来,α 设成 0.5 的话,会把很多正常点也当成离群点删掉,导致点云变得稀疏。

另外,K 值(近邻数量)也会影响滤波效果。K 太小,统计结果不稳定;K 太大,计算量上去了,而且会模糊局部细节。我一般取 K = 20~50,具体看点云密度。

4.4 统计滤波的完整流程

咱们把整个流程串起来,看看统计滤波到底是怎么工作的:

输入:原始点云 P,近邻数 K,标准差倍数 α
输出:滤波后的点云 P_filtered

1. 构建 KD-Tree 或 Octree,加速近邻搜索
2. 对于每个点 p_i:
   a. 搜索 K 个最近邻
   b. 计算到这些近邻的欧氏距离
   c. 求平均距离 d_i
3. 计算所有 d_i 的均值 μ 和标准差 σ
4. 计算阈值 T = μ + α × σ
5. 对于每个点 p_i:
   if d_i <= T:
      保留 p_i
   else:
      剔除 p_i
6. 返回保留的点集

这里有个细节:第 1 步构建 KD-Tree 是必须的。如果不做加速,直接暴力搜索近邻,时间复杂度是 O(N²),点云一多就卡死了。我见过有人用 10 万点的点云跑暴力搜索,等了 5 分钟还没出结果——嗯,那滋味不好受。

4.5 统计滤波 vs 半径滤波

很多初学者会问:「统计滤波和半径滤波有什么区别?我该用哪个?」

我画了一张图,帮你理解两者的核心差异:

统计滤波 vs 半径滤波 统计滤波 计算每个点的K近邻平均距离 计算全局均值 μ 和标准差 σ 阈值 T = μ + α × σ 剔除 d_i > T 的点 半径滤波 设定固定半径 R 统计每个点半径R内的邻居数 阈值:邻居数 < N_min 则剔除 剔除邻居数不足的点 统计滤波:自适应阈值,适合密度不均匀的点云 半径滤波:固定阈值,适合密度均匀的点云

从图上可以看得很清楚:统计滤波的阈值是「算出来」的,半径滤波的阈值是「设出来」的。这就决定了它们的适用场景不同。

我个人建议:

  • 如果你不确定点云的密度分布,或者点云来自不同距离、不同角度,优先用统计滤波
  • 如果你对场景很熟悉,点云密度基本均匀,半径滤波更简单、更快
  • 如果两者都不满意,可以组合使用——先统计滤波剔除远距离离群点,再半径滤波剔除孤立点

避坑指南:

我曾经在一个项目中,用统计滤波处理车载激光雷达数据。当时没注意,把 K 设成了 5,结果滤波效果很差——因为 K 太小,统计结果受局部噪声影响太大。后来我把 K 调到 30,效果就好多了。所以我的建议是:K 不要小于 10,最好在 20~50 之间。

好了,统计滤波的原理就讲到这里。记住三个关键点:均值描述整体密度,标准差描述局部波动,α 控制滤波强度。下节课咱们聊聊实际代码怎么写,以及怎么调参才能又快又好。

可可精品资料库