基础滤波算法:均值滤波、中值滤波、滑动窗口滤波的原理与实现
做GNSS定位的工程师,十有八九都跟「漂移」打过架。信号跳变、多径反射、接收机噪声……这些家伙会让定位轨迹像喝醉了酒一样东倒西歪。我刚开始做车载导航那会儿,看着原始定位数据在屏幕上画出的「鬼画符」,心里那个急啊。
后来我明白了——滤波不是把数据变「假」,而是把数据变「真」。今天咱们就聊聊三种最基础的滤波算法:均值滤波、中值滤波、滑动窗口滤波。它们虽然简单,但用好了,能解决80%的漂移问题。
1. 均值滤波:最简单的平滑器
均值滤波的原理,说白了就是「取平均」。你连续采N个点,然后算个平均值,用这个平均值代替当前点的值。
举个例子。假设你收到一串位置数据(单位:米):
原始数据:10.2, 10.5, 11.8, 10.3, 10.1, 10.6, 10.4
如果窗口大小取3,那么第3个点的滤波值就是:
(10.2 + 10.5 + 11.8) / 3 = 10.83
第4个点:
(10.5 + 11.8 + 10.3) / 3 = 10.87
你看,11.8这个异常值被「拉」回来了不少。
核心公式:
y[n] = (x[n] + x[n-1] + ... + x[n-N+1]) / N
其中N是窗口大小,x是原始数据,y是滤波后的数据。
我在项目中遇到过一个问题:窗口大小选3还是选5?选3,噪声抑制不够;选5,信号延迟太大。后来我总结了一个经验——对于GNSS定位数据,窗口大小一般取3~7之间。城市峡谷环境取小一点,开阔环境取大一点。
我的小技巧:均值滤波对高斯噪声效果很好,但对脉冲噪声(比如突然跳变10米)效果一般。因为一个坏点会把整个平均值拉偏。
2. 中值滤波:专治「野点」
中值滤波的思路更直接——把窗口内的数据排序,取中间那个值。它不关心数值大小,只关心「谁在中间」。
还是刚才那组数据:
原始数据:10.2, 10.5, 11.8, 10.3, 10.1, 10.6, 10.4
窗口大小取3,第3个点的窗口数据是 [10.2, 10.5, 11.8],排序后是 [10.2, 10.5, 11.8],中值是10.5。
第4个点的窗口数据是 [10.5, 11.8, 10.3],排序后是 [10.3, 10.5, 11.8],中值是10.5。
你看,11.8这个野点直接被「踢」出去了,根本没影响结果。
核心公式:
y[n] = median(x[n], x[n-1], ..., x[n-N+1])
median表示取中位数。
我曾经在无人机定位项目里吃过亏。无人机在楼宇间飞行时,信号经常被遮挡,导致位置突然跳变几十米。均值滤波根本压不住,但中值滤波一上,轨迹立马稳了。为什么?因为中值滤波对脉冲噪声有天然的免疫力。
注意:中值滤波的窗口大小必须是奇数,否则中位数不好定义。我一般用3或5,太大反而会丢失细节。
3. 滑动窗口滤波:实时场景的利器
前面两种滤波,每次都要等窗口满了才能算。但在GNSS定位里,数据是一帧一帧来的,你不能等。滑动窗口滤波就是为这种场景设计的。
它的思路很简单:维护一个固定大小的队列,新数据进来,旧数据出去,然后对队列里的数据做处理(可以是均值,也可以是中值)。
我画了一张图,帮你理解这个过程:
滑动窗口滤波的代码实现也很简单。我写个C语言的例子:
#define WINDOW_SIZE 5
float buffer[WINDOW_SIZE];
int index = 0;
int count = 0;
float sliding_mean_filter(float new_data) {
float sum = 0;
// 新数据入队
buffer[index] = new_data;
index = (index + 1) % WINDOW_SIZE;
// 窗口未满时,用已有数据计算
if (count < WINDOW_SIZE) count++;
// 计算均值
for (int i = 0; i < count; i++) {
sum += buffer[i];
}
return sum / count;
}
实用建议:滑动窗口滤波的窗口大小决定了「响应速度」和「平滑程度」的平衡。窗口越大,轨迹越平滑,但延迟也越大。我一般建议从5开始调,看效果再增减。
三种滤波的对比
我把三种滤波的特点整理成了表格,方便你对比:
| 特性 | 均值滤波 | 中值滤波 | 滑动窗口滤波 |
|---|---|---|---|
| 噪声类型 | 高斯噪声 | 脉冲噪声 | 两者皆可(取决于内部算法) |
| 计算量 | 低(只需累加) | 中(需要排序) | 低到中 |
| 延迟 | N/2个采样周期 | N/2个采样周期 | 取决于窗口大小 |
| 实时性 | 需等待窗口满 | 需等待窗口满 | 每来一个数据立即输出 |
| 适用场景 | 缓慢变化的信号 | 有突发干扰的信号 | 实时定位系统 |
实战中的选择策略
你可能会问:那我到底该用哪个?
我的建议是分情况讨论:
- 如果信号偶尔跳变(比如过隧道、桥下),用中值滤波。它能干净利落地干掉野点。
- 如果信号持续抖动(比如城市峡谷多径效应),用均值滤波或滑动窗口均值滤波。
- 如果既要实时又要抗干扰,用滑动窗口滤波,内部用中值计算。
我曾经在一个共享单车定位项目里,三种滤波都试过。最后发现——把滑动窗口和中值结合效果最好。窗口大小取5,每来一个数据就更新窗口,然后取中值输出。这样既保证了实时性,又不怕偶尔的跳变。
避坑指南:我曾经犯过一个错误——窗口大小取太大,导致定位轨迹严重滞后。车都转弯了,轨迹还在直行。后来我把窗口从15改到5,延迟问题就解决了。记住:滤波不是越平滑越好,而是要「恰到好处」。
好了,这三种基础滤波算法就讲到这里。它们虽然简单,但却是定位漂移补偿的基石。很多高级算法(比如卡尔曼滤波)也是在这些基础上发展起来的。你先把这三种吃透,后面学复杂算法会轻松很多。
可可精品资料库