第四节:一阶低通滤波——原理、截止频率设计、代码实现
各位同学好,今天我们聊一聊传感器融合里最基础、也最常用的一个工具——一阶低通滤波。
说实话,我刚入行那会儿,觉得滤波嘛,不就是把数据平滑一下?后来在项目里吃过亏才明白:截止频率设错了,整个系统都会跟着抖。今天我就把这块掰开揉碎了讲清楚。
4.1 一阶低通滤波的原理
先问大家一个问题:为什么传感器数据需要滤波?
你想想看,一个加速度计放在桌面上不动,读出来的数值是不是一直在跳?±0.01g、±0.02g 来回晃。这就是高频噪声。一阶低通滤波,说白了就是让低频信号通过,把高频噪声挡在外面。
它的数学表达式很简单:
y[n] = α * x[n] + (1 - α) * y[n-1]
其中:
y[n]—— 当前滤波后的值x[n]—— 当前原始采样值y[n-1]—— 上一次滤波后的值α—— 滤波系数(0 ~ 1)
这个公式怎么理解?我个人的习惯是把它看作“信任分配”:
α越大,越信任当前测量值,响应快但噪声大α越小,越信任历史平滑值,响应慢但更平滑
核心思想:一阶低通滤波本质上是一个加权滑动平均,只不过权重是指数衰减的。
4.2 截止频率的设计
这里有个坑,我当年踩过。很多人直接把 α 设成 0.1 或 0.01,觉得“越小越平滑”。结果呢?系统响应慢得像蜗牛,手都挥过去了,滤波后的数据还没跟上。
截止频率才是设计的关键。它决定了“多快的信号算高频,会被滤掉”。
一阶低通滤波的截止频率 fc 与 α 的关系如下:
α = 2π * fc * Δt / (1 + 2π * fc * Δt)
其中 Δt 是采样周期(秒)。反过来,已知 α 求截止频率:
fc = α / (2π * Δt * (1 - α))
嗯,这里要注意:这个公式是近似公式,在 fc 远小于采样频率时成立。实际项目中够用了。
我的经验法则:
- 人体运动(手势、步态):截止频率 5~20 Hz
- 车辆姿态(IMU 融合):截止频率 2~10 Hz
- 温度、气压等缓变量:截止频率 0.1~1 Hz
举个例子:采样频率 100 Hz(Δt = 0.01 s),想要截止频率 10 Hz:
α = 2π * 10 * 0.01 / (1 + 2π * 10 * 0.01)
= 0.628 / 1.628
≈ 0.386
你看,α 并不是随便拍脑袋定的,而是有物理含义的。
4.3 代码实现
直接上代码。我习惯用 C 语言写嵌入式滤波库,结构清晰,移植方便。
4.3.1 基础实现
typedef struct {
float alpha; // 滤波系数
float y_prev; // 上一次滤波值
uint8_t first; // 首次运行标志
} LowPassFilter_t;
void LowPass_Init(LowPassFilter_t *filt, float alpha) {
filt->alpha = alpha;
filt->y_prev = 0.0f;
filt->first = 1;
}
float LowPass_Update(LowPassFilter_t *filt, float x) {
if (filt->first) {
filt->y_prev = x; // 首次直接赋值
filt->first = 0;
return x;
}
float y = filt->alpha * x + (1.0f - filt->alpha) * filt->y_prev;
filt->y_prev = y;
return y;
}
注意:首次滤波时,我建议直接输出原始值。否则初始值设为 0,会导致前几个采样点出现跳变。这个细节我曾经在调试无人机姿态时吃过亏,飞控一上电就乱晃,查了半天才发现是滤波器初始值的问题。
4.3.2 带截止频率的初始化
我个人更喜欢直接传截止频率和采样频率,让函数自己算 α:
void LowPass_InitWithFc(LowPassFilter_t *filt, float fc, float fs) {
float dt = 1.0f / fs;
float omega_c = 2.0f * 3.14159265f * fc;
filt->alpha = omega_c * dt / (1.0f + omega_c * dt);
filt->y_prev = 0.0f;
filt->first = 1;
}
这样调用起来就直观多了:
LowPassFilter_t accel_filter;
LowPass_InitWithFc(&accel_filter, 10.0f, 100.0f); // 10Hz 截止,100Hz 采样
// 每次读取传感器后调用
float filtered_accel = LowPass_Update(&accel_filter, raw_accel);
4.4 实际项目中的避坑指南
讲几个我亲身踩过的坑:
- 坑一:采样频率不稳定 —— 如果系统是事件驱动的,采样间隔不固定,直接用固定 α 会导致截止频率漂移。解决方案:用时间戳动态计算 α。
- 坑二:α 太小导致滞后 —— 我曾经在平衡车上把 α 设成 0.01,结果车子反应慢半拍,根本站不稳。后来改成 0.3 左右才正常。
- 坑三:浮点运算性能 —— 在低端 MCU 上,大量浮点乘法可能拖慢主循环。可以用定点数近似,或者用移位运算实现 2 的幂次 α。
一句话总结:一阶低通滤波不是“越小越好”,而是“合适才好”。先算截止频率,再定 α,最后调参微调。
4.5 进阶思考
一阶低通滤波虽然简单,但它是很多高级滤波器的基石。比如:
- 互补滤波 —— 本质上是两个一阶低通滤波的组合
- 卡尔曼滤波 —— 预测更新阶段也包含低通特性
- 滑动窗口滤波 —— 可以看作一阶低通的离散近似
所以,把一阶低通吃透了,后面学其他滤波器会轻松很多。
好,今天就到这里。下一节我们讲滑动平均滤波,看看它和一阶低通有什么区别,各自适合什么场景。
公众号:蓝海资料掘金营,微信deep3321