4、经典滤波算法(一):限幅滤波法、中位值滤波法、算术平均滤波法原理与实现
各位同学,欢迎来到滤波算法的实战环节。
说实话,传感器数据融合这个领域,滤波算法就是基本功。就像练武要先扎马步一样,这些经典算法虽然看起来简单,但我在点钞机项目里,80%的噪声问题都是用它们解决的。今天咱们就聊聊三种最基础、也最实用的滤波方法。
4.1 限幅滤波法:最朴素的防抖手段
先说说限幅滤波。这玩意儿说白了就是:如果这次采样值和上次差太多,我就认为它是干扰,直接扔掉。
我在做点钞机的时候,遇到过一种情况:钞票高速通过时,传感器偶尔会跳出一个离谱的数值,比如厚度突然变成5mm。这明显是机械振动导致的误触发。限幅滤波就是专门治这种毛病的。
原理
设定一个最大允许偏差值 ΔY。每次新采样值 Y(n) 和上次有效值 Y(n-1) 比较:
- 如果 |Y(n) - Y(n-1)| ≤ ΔY,则本次值有效
- 如果 |Y(n) - Y(n-1)| > ΔY,则本次值无效,用上次值代替
核心思想:物理世界的变化是连续的,突变大概率是噪声。
代码实现
// 限幅滤波
#define MAX_DELTA 10 // 最大允许偏差,根据实际传感器调整
int16_t Limiter_Filter(int16_t new_value, int16_t last_valid_value) {
if (abs(new_value - last_valid_value) > MAX_DELTA) {
// 超出范围,丢弃,返回上次值
return last_valid_value;
} else {
return new_value;
}
}
我的经验:ΔY 怎么选?我一般会先让传感器空跑,记录正常波动范围,然后取这个范围的1.5倍作为阈值。太小了容易误判,太大了又起不到滤波效果。
4.2 中位值滤波法:对付脉冲噪声的利器
限幅滤波只能处理单个的跳变,但如果连续出现几个坏点呢?比如点钞机在清点旧钞时,传感器会间歇性地被灰尘遮挡。这时候,中位值滤波就派上用场了。
原理
连续采样 N 次(N 通常取奇数),排序后取中间值作为本次有效值。
为什么取中位数?因为中位数对异常值不敏感。你想想看,就算有3个坏点混在5个数据里,排序后它们要么在最左边,要么在最右边,中间那个大概率是好的。
代码实现
// 中位值滤波
#define FILTER_SIZE 5 // 采样次数,建议3、5、7
int16_t Median_Filter(int16_t *buffer) {
int16_t temp[FILTER_SIZE];
int16_t i, j, tmp;
// 复制数据,避免破坏原数组
for (i = 0; i < FILTER_SIZE; i++) {
temp[i] = buffer[i];
}
// 冒泡排序(数据量小,够用)
for (i = 0; i < FILTER_SIZE - 1; i++) {
for (j = 0; j < FILTER_SIZE - 1 - i; j++) {
if (temp[j] > temp[j+1]) {
tmp = temp[j];
temp[j] = temp[j+1];
temp[j+1] = tmp;
}
}
}
// 返回中位数
return temp[FILTER_SIZE / 2];
}
注意:N 值越大,滤波效果越好,但延迟也越大。点钞机每秒要处理几十张钞票,我一般用5次采样,再多就来不及了。
4.3 算术平均滤波法:平滑噪声的万金油
如果说前两种算法是「特种兵」,那算术平均滤波就是「常规部队」。它不挑场景,几乎所有传感器数据都能用,就是代价有点大——需要存一堆历史数据。
原理
连续采样 N 次,求算术平均值作为本次输出。
公式很简单:Y = (X1 + X2 + ... + XN) / N
但这里有个坑:N 的选择直接影响响应速度。N 越大,曲线越平滑,但信号变化时反应也越慢。
代码实现
// 算术平均滤波
#define AVG_SIZE 10 // 平均次数
int16_t Average_Filter(int16_t *buffer) {
uint32_t sum = 0;
uint8_t i;
for (i = 0; i < AVG_SIZE; i++) {
sum += buffer[i];
}
return (int16_t)(sum / AVG_SIZE);
}
避坑指南:我曾经在点钞机项目里把 AVG_SIZE 设成 100,结果钞票都过去了,滤波后的数据还没跟上。后来我总结了一个经验:N 不要超过信号变化周期的采样点数。比如一张钞票通过传感器需要20个采样点,那 N 最好小于20。
4.4 三种算法的对比与选型
好了,三种算法都讲完了。你可能会问:到底该用哪个?
我的建议是:不要纠结,组合使用。点钞机里我通常是这么干的:
| 算法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 限幅滤波 | 突发性大噪声(如机械振动) | 简单、实时性好 | 无法处理连续噪声 |
| 中位值滤波 | 脉冲噪声、偶发坏点 | 抗干扰能力强 | 有延迟,N 不能太大 |
| 算术平均滤波 | 周期性噪声、随机噪声 | 平滑效果好 | 响应慢,占用内存 |
实际项目中,我经常这样搭配:先用限幅滤波干掉明显的跳变,再用中位值滤波处理残留的脉冲,最后用算术平均滤波把曲线磨平。三层下来,数据基本就干净了。
记住:滤波不是越复杂越好,而是越合适越好。有时候一个简单的限幅滤波,比跑个卡尔曼滤波还管用。
嗯,今天就先聊到这儿。下一节咱们继续讲剩下的经典算法——递推平均滤波和加权递推平均滤波。到时候我会分享一个点钞机里真实踩过的坑,保证让你印象深刻。