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 截止频率的选择策略

这是整个滤波器设计里最关键的一步。选高了,滤不掉噪声;选低了,信号延迟太大,系统可能不稳定。

我个人习惯按以下步骤来:

  1. 先分析噪声频谱——用示波器或逻辑分析仪抓一段原始信号,看看噪声主要分布在哪个频段。
  2. 确定有用信号的最高频率——比如速度环的带宽是 50 Hz,那有用信号基本都在 50 Hz 以下。
  3. 在两者之间选一个折中点——通常取有用信号最高频率的 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 一阶低通滤波器的知识体系

下面这张图把本章的核心逻辑串起来了。从连续域到离散域,从原理到实现,再到工程中的选择策略,你可以把它当作一个快速参考。

一阶低通滤波器知识体系 一阶低通滤波器知识体系 连续域传递函数 G(s) = 1 / (τs + 1) 离散化(后向差分) y[k] = α·x[k] + (1-α)·y[k-1] 参数计算 α = T / (τ + T) 嵌入式实现方式 浮点实现 精度高,通用 定点实现 速度快,无FPU时首选 截止频率选择策略 分析噪声频谱 → 确定信号带宽 → 折中选择 → 留相位裕度 ⚠ 关键权衡 滤波深度 vs 响应延迟

3.6 工程中的常见问题

最后聊几个我实际踩过的坑。

问题一:滤波器输出滞后导致系统振荡。

有一次我在调一个两轮平衡车,速度环加了 5 Hz 的滤波,结果车子一直前后晃动。后来发现是滤波器的相位延迟让速度反馈慢了半拍,控制器以为速度不够,继续加速,形成正反馈。解决办法是把截止频率提高到 15 Hz,同时适当降低控制器增益。

问题二:α 值在启动时导致输出跳变。

前面代码里我加了 first 标志位,就是为了解决这个问题。如果不做初始化,滤波器从 0 开始,输出会慢慢爬升,这段时间的控制量全是错的。

问题三:多个滤波器串联时的累积效应。

有些工程师喜欢在电流环、速度环、位置环各加一个滤波器。三个一阶滤波器串联,等效于一个三阶滤波器,相位延迟是单个的三倍。我建议尽量少用串联,如果必须用,每个的截止频率都要适当提高。

我的经验:在调试阶段,先把滤波器关掉,让系统能稳定运行。然后从高截止频率往低调,直到噪声被抑制住为止。这样能避免滤波器掩盖了控制器本身的问题。