4、信号预处理——滤波去噪:硬件RC滤波设计、软件滑动平均滤波、中值滤波实现
各位同学,咱们接着聊。上一节我们把轮速传感器的原始信号调理成了方波,但说实话,那个方波还远不够干净。你想想看,车轮在路面上跑,颠簸、电磁干扰、传感器本身的抖动,都会在信号上叠加一堆噪声。
这节我们就来解决这个问题——滤波去噪。我个人的经验是,滤波做不好,后面ABS算法写得再漂亮也是白搭。曾经有个项目,轮速信号在低速时频繁跳变,查了三天,最后发现是RC滤波的截止频率选高了,高频噪声全漏进来了。
滤波这件事,通常分两步走:硬件先粗滤,软件再精滤。咱们一个一个说。
4.1 硬件RC低通滤波——第一道防线
硬件滤波,说白了就是在传感器输出和MCU引脚之间,串一个电阻、并一个电容。结构简单,但效果立竿见影。
为什么用RC低通?因为轮速信号的频率本身不高——乘用车车轮转速最高也就2000rpm左右,对应的齿圈信号频率大概在几百赫兹到几千赫兹。而噪声往往是高频的,比如发动机点火干扰、车载逆变器开关噪声。RC低通正好把高频干掉。
电路长这样:
传感器输出 —— R —— MCU引脚
|
C
|
GND
截止频率的计算公式:
f_c = 1 / (2π × R × C)
举个例子。假设齿圈有48个齿,最高车速对应频率2kHz。我一般会把截止频率设在信号最高频率的3~5倍,也就是6kHz~10kHz。选R=1kΩ,C=22nF,算一下:
f_c = 1 / (2 × 3.14 × 1000 × 22×10^-9) ≈ 7.2kHz
嗯,这个值就挺合适。
4.2 软件滑动平均滤波——简单粗暴但有效
硬件滤波之后,信号已经干净了不少。但MCU引脚上可能还有残留的毛刺,或者因为传感器安装间隙变化导致的微小抖动。这时候就该软件上场了。
滑动平均滤波,是我用得最多的方法。原理很简单:维护一个长度为N的窗口,每次采样一个新值,就丢掉最旧的那个,然后对窗口内所有值求平均。
为什么好用?因为它对周期性噪声有很好的抑制效果,而且实现起来几乎不占CPU。你想想看,ABS系统里轮速信号每秒要采样几千次,如果每个点都做复杂运算,MCU扛不住的。
代码实现:
#define WINDOW_SIZE 8
static uint16_t buffer[WINDOW_SIZE] = {0};
static uint8_t index = 0;
static uint32_t sum = 0;
uint16_t moving_average(uint16_t new_sample)
{
// 减去最旧的值
sum -= buffer[index];
// 存入新值
buffer[index] = new_sample;
// 加上新值
sum += new_sample;
// 更新索引
index = (index + 1) % WINDOW_SIZE;
// 返回平均值
return sum / WINDOW_SIZE;
}
这里有个细节:窗口大小N怎么选?
| 窗口大小 | 平滑效果 | 响应速度 | 适用场景 |
|---|---|---|---|
| 4 | 一般 | 快 | 高速行驶,需要快速响应 |
| 8 | 较好 | 中等 | 城市道路,常规工况 |
| 16 | 很好 | 慢 | 低速或静止检测 |
我个人习惯在低速时用大窗口,高速时用小窗口。曾经有个项目,我固定用8点平均,结果低速时信号抖动被平滑掉了,但高速时响应慢了半拍,导致ABS介入延迟。后来改成动态调整窗口大小,问题就解决了。
4.3 中值滤波——专治脉冲噪声
滑动平均滤波对高斯噪声效果好,但遇到脉冲噪声——比如一个突然的尖峰——就有点力不从心了。因为平均会把尖峰的影响「摊」到整个窗口里。
这时候中值滤波就派上用场了。它的原理:对窗口内的数据排序,取中间那个值作为输出。
举个例子。原始数据:[100, 102, 98, 500, 101]。500明显是个异常尖峰。滑动平均会输出(100+102+98+500+101)/5=180.2,被拉高了很多。而中值滤波排序后:[98, 100, 101, 102, 500],取中间值101,完美避开干扰。
代码实现:
#define MEDIAN_WINDOW 5
uint16_t median_filter(uint16_t *buffer, uint8_t len)
{
uint16_t temp[MEDIAN_WINDOW];
uint8_t i, j;
uint16_t tmp;
// 复制数据
for(i = 0; i < len; i++)
temp[i] = buffer[i];
// 冒泡排序(窗口小,够用)
for(i = 0; i < len - 1; i++)
{
for(j = 0; j < len - 1 - i; j++)
{
if(temp[j] > temp[j+1])
{
tmp = temp[j];
temp[j] = temp[j+1];
temp[j+1] = tmp;
}
}
}
// 返回中值
return temp[len / 2];
}
4.4 实际项目中的组合策略
讲到这里,你可能会问:到底用哪种?
我的答案是:组合使用。
在实际的ABS控制器里,信号处理链路通常是这样的:
- 硬件RC滤波:在PCB上直接做,滤掉大部分高频噪声。这是必须的,不能省。
- 软件中值滤波:在MCU中断服务程序里,对ADC采样值做3点中值,干掉脉冲干扰。
- 软件滑动平均:在主循环或定时器里,对中值滤波后的数据做8点滑动平均,进一步平滑。
我曾经在一个项目中,只做了硬件滤波和滑动平均,结果在电磁兼容测试时,辐射干扰导致轮速信号偶尔跳变。后来在中断里加了一级3点中值滤波,问题就再没出现过。
好了,这一节的内容就到这里。滤波去噪是信号处理的基础,做好了,后面的轮速计算和ABS控制才能稳。下一节我们讲轮速的周期测量和速度计算,到时候你会感谢现在把信号滤干净的自己。