3. 滤波器基础:一阶低通滤波器的原理与实现,截止频率的选择策略
做控制这么多年,我遇到过最头疼的问题之一,就是传感器信号里的高频噪声。你想想看,明明电机转得好好的,反馈回来的速度却像心电图一样上下乱跳。这时候,一阶低通滤波器就是我们的第一道防线。
说白了,它就是让低频信号通过,把高频噪声滤掉。原理不复杂,但用好了能省很多事。我刚开始做无人机飞控时,就因为没处理好陀螺仪的噪声,飞机悬停时一直在轻微抖动。后来加了个一阶低通滤波器,问题就解决了。
3.1 一阶低通滤波器的数学原理
先看连续域下的传递函数:
G(s) = 1 / (τs + 1)
其中 τ 是时间常数。对应的微分方程是:
τ · dy/dt + y = x
x 是输入,y 是输出。这个方程描述的就是一个惯性环节——输出不会突变,而是以指数形式逼近输入。
在嵌入式系统里,我们没法直接处理连续信号。得把它离散化。最常用的方法是后向差分法:
dy/dt ≈ (y[k] - y[k-1]) / T
代入微分方程,整理一下:
τ · (y[k] - y[k-1]) / T + y[k] = x[k]
y[k] = (T / (τ + T)) · x[k] + (τ / (τ + T)) · y[k-1]
令 α = T / (τ + T),就得到了我们最熟悉的递推形式:
y[k] = α · x[k] + (1 - α) · y[k-1]
α 就是滤波系数,范围在 0 到 1 之间。α 越小,滤波效果越强,但响应也越慢。
核心公式:
y[k] = α · x[k] + (1 - α) · y[k-1]
其中 α = T / (τ + T),T 为采样周期,τ 为时间常数。
3.2 截止频率与 α 的关系
截止频率 f_c 是滤波器增益下降到 -3dB 时的频率。对于一阶低通滤波器:
f_c = 1 / (2π · τ)
结合 α 的表达式,我们可以直接由截止频率算出 α:
α = 2π · T · f_c / (2π · T · f_c + 1)
反过来,已知 α 也能算出截止频率:
f_c = α / (2π · T · (1 - α))
这里有个坑,我踩过好几次。α 的取值和采样周期 T 是绑定的。同样的 α,采样率不同,实际截止频率完全不同。所以别光记 α 值,一定要结合你的采样周期来算。
| 采样周期 T | α 值 | 截止频率 f_c | 适用场景 |
|---|---|---|---|
| 1 ms | 0.1 | 约 17.7 Hz | 电机电流环 |
| 1 ms | 0.05 | 约 8.4 Hz | 速度环 |
| 10 ms | 0.1 | 约 1.77 Hz | 位置环 |
| 10 ms | 0.01 | 约 0.16 Hz | 温度滤波 |
3.3 嵌入式实现:C 代码示例
实际写代码时,我习惯用定点数来算,避免浮点运算拖慢速度。尤其是在没有 FPU 的 MCU 上,浮点运算代价很高。
// 一阶低通滤波器结构体
typedef struct {
float alpha; // 滤波系数
float y_prev; // 上一次输出值
uint8_t first; // 首次运行标志
} lpf1_t;
// 初始化
void lpf1_init(lpf1_t *lpf, float alpha) {
lpf->alpha = alpha;
lpf->y_prev = 0.0f;
lpf->first = 1;
}
// 滤波计算
float lpf1_process(lpf1_t *lpf, float x) {
float y;
if (lpf->first) {
// 首次运行时,直接用输入值初始化
y = x;
lpf->first = 0;
} else {
y = lpf->alpha * x + (1.0f - lpf->alpha) * lpf->y_prev;
}
lpf->y_prev = y;
return y;
}
小技巧:首次运行时直接用输入值初始化输出,可以避免启动时的阶跃响应。否则你会看到输出从 0 慢慢爬到真实值,那几秒钟的数据基本不能用。
如果 MCU 性能紧张,可以用移位运算代替浮点乘法。比如 α 取 1/16、1/32 这样的值:
// 定点数版本,alpha = 1/16
#define ALPHA_SHIFT 4 // 2^4 = 16
int16_t lpf1_fixed(int16_t x, int16_t *y_prev) {
int16_t y;
y = (x + ( (1 << ALPHA_SHIFT) - 1) * (*y_prev) ) >> ALPHA_SHIFT;
*y_prev = y;
return y;
}
嗯,这里要注意。定点数版本虽然快,但精度会损失。如果 α 不是 2 的负整数次幂,误差会累积。我一般只在电流环这种对延迟极度敏感的场景才用定点版本。
3.4 截止频率的选择策略
这是整个滤波器设计里最关键的一步。选高了,滤不掉噪声;选低了,信号延迟太大,系统可能不稳定。
我个人习惯按以下步骤来:
- 先分析噪声频谱——用示波器或逻辑分析仪抓一段原始信号,看看噪声主要分布在哪个频段。
- 确定有用信号的最高频率——比如速度环的带宽是 50 Hz,那有用信号基本都在 50 Hz 以下。
- 在两者之间选一个折中点——通常取有用信号最高频率的 2~5 倍作为截止频率。
举个例子。我之前做四轴飞行器的姿态解算,陀螺仪的噪声主要集中在 30 Hz 以上,而姿态控制的带宽大约 10 Hz。我选了 20 Hz 的截止频率,效果不错。
避坑指南:我曾经在一个伺服驱动器项目里,把截止频率设得太低(1 Hz),结果位置环的响应变得非常迟钝,系统一加速就振荡。后来把截止频率提高到 10 Hz,问题才解决。记住,滤波器的相位延迟会降低系统的相位裕度,选截止频率时一定要留够余量。
不同控制环路的推荐截止频率范围:
| 控制环路 | 典型带宽 | 推荐截止频率 | 说明 |
|---|---|---|---|
| 电流环 | 1~5 kHz | 500 Hz ~ 2 kHz | 延迟敏感,滤波不宜过强 |
| 速度环 | 50~200 Hz | 20~100 Hz | 折中考虑噪声和响应 |
| 位置环 | 5~20 Hz | 2~10 Hz | 可以适当加强滤波 |
| 温度/压力 | < 1 Hz | 0.1~0.5 Hz | 变化缓慢,滤波可以很深 |
3.5 一阶低通滤波器的知识体系
下面这张图把本章的核心逻辑串起来了。从连续域到离散域,从原理到实现,再到工程中的选择策略,你可以把它当作一个快速参考。
3.6 工程中的常见问题
最后聊几个我实际踩过的坑。
问题一:滤波器输出滞后导致系统振荡。
有一次我在调一个两轮平衡车,速度环加了 5 Hz 的滤波,结果车子一直前后晃动。后来发现是滤波器的相位延迟让速度反馈慢了半拍,控制器以为速度不够,继续加速,形成正反馈。解决办法是把截止频率提高到 15 Hz,同时适当降低控制器增益。
问题二:α 值在启动时导致输出跳变。
前面代码里我加了 first 标志位,就是为了解决这个问题。如果不做初始化,滤波器从 0 开始,输出会慢慢爬升,这段时间的控制量全是错的。
问题三:多个滤波器串联时的累积效应。
有些工程师喜欢在电流环、速度环、位置环各加一个滤波器。三个一阶滤波器串联,等效于一个三阶滤波器,相位延迟是单个的三倍。我建议尽量少用串联,如果必须用,每个的截止频率都要适当提高。
我的经验:在调试阶段,先把滤波器关掉,让系统能稳定运行。然后从高截止频率往低调,直到噪声被抑制住为止。这样能避免滤波器掩盖了控制器本身的问题。