一阶低通滤波器(LPF):从模拟RC到数字实现

做电机控制这些年,我遇到最多的一个问题就是:怎么把传感器信号里的高频毛刺去掉,又不影响控制带宽?

答案其实很简单——一阶低通滤波器。但很多人用不好它。要么滤波效果太弱,要么把有用的信号也滤没了。今天我就把这块彻底讲透。

模拟RC低通:理解本质

先看模拟电路。一个电阻R串联,一个电容C对地并联,这就是最经典的RC低通滤波器。

它的传递函数长这样:

H(s) = 1 / (1 + s * R * C)

这里有个关键参数——时间常数 τ = R * C。τ越大,滤波越强,但响应越慢。

我刚开始做电机电流采样滤波时,就吃过这个亏。选了个很大的电容,结果电流环带宽直接掉了一半,电机跑起来嗡嗡响。嗯,这就是滤波过度了。

截止频率的计算

截止频率 f_c 是滤波器的分水岭。低于这个频率的信号基本保留,高于的会被衰减。

计算公式:

f_c = 1 / (2 * π * R * C)

举个例子:R=1kΩ,C=1μF

f_c = 1 / (2 * 3.14 * 1000 * 0.000001) ≈ 159 Hz

这意味着159Hz以上的信号会被逐渐削弱。在电机控制里,我一般把电流采样滤波的截止频率设在开关频率的1/10到1/20之间。比如10kHz的PWM,f_c取500Hz到1kHz比较合适。

我的经验: 别把截止频率设得太低。我曾经为了滤除50Hz工频干扰,把f_c设到10Hz,结果电流环响应慢得像蜗牛,电机一加速就震荡。后来改用陷波器专门滤50Hz,低通只负责高频毛刺,问题才解决。

从模拟到数字:一阶后向差分法

模拟滤波器好用,但我们的控制器是数字的。怎么办?离散化

最常用的方法是一阶后向差分。说白了,就是用差分方程近似微分方程。

模拟域的传递函数:

Y(s) / X(s) = 1 / (1 + s * τ)

写成微分方程:

τ * dy/dt + y = x

用后向差分近似导数:

dy/dt ≈ (y[n] - y[n-1]) / T

其中T是采样周期。代入整理后得到:

y[n] = α * x[n] + (1 - α) * y[n-1]

这里的α是关键:

α = T / (τ + T) = T / (1/(2πf_c) + T)

你看,这个公式多简洁。当前输出 = 当前输入乘以α + 上次输出乘以(1-α)。

核心理解: α越小,滤波越强,但响应越慢。α=1时没有滤波,α=0时输出永远不变。

代码实现:C语言版

直接上代码。这是我常用的结构体实现:

typedef struct {
    float alpha;    // 滤波系数
    float y_prev;   // 上一次输出值
    float y;        // 当前输出值
} LPF_1stOrder;

// 初始化
void LPF_Init(LPF_1stOrder *lpf, float fc, float Ts) {
    float tau = 1.0f / (2.0f * 3.14159f * fc);
    lpf->alpha = Ts / (tau + Ts);
    lpf->y_prev = 0.0f;
    lpf->y = 0.0f;
}

// 滤波计算
float LPF_Process(LPF_1stOrder *lpf, float x) {
    lpf->y = lpf->alpha * x + (1.0f - lpf->alpha) * lpf->y_prev;
    lpf->y_prev = lpf->y;
    return lpf->y;
}

使用示例:

LPF_1stOrder current_filter;
LPF_Init(&current_filter, 500.0f, 0.0001f);  // fc=500Hz, Ts=100μs

while(1) {
    float raw_current = ADC_GetValue();
    float filtered_current = LPF_Process(&current_filter, raw_current);
    // 用filtered_current做控制
}

参数调优:实战经验

调参数这事,光看公式不行。我总结了几条实战经验:

场景 推荐截止频率 注意事项
电流采样滤波 开关频率/10 ~ 开关频率/20 太大会引入延迟,影响电流环带宽
速度计算滤波 速度环带宽的2~5倍 速度信号本身延迟大,滤波不要太强
位置信号滤波 1~10Hz 位置信号低频为主,滤波可以狠一点
编码器信号去毛刺 500Hz~2kHz 主要滤除机械振动引起的高频抖动
我曾经踩过的坑: 有一次做伺服驱动器,电流采样滤波用了α=0.01,结果电流环响应延迟了3个采样周期,电机在低速时抖得厉害。后来把α调到0.1,同时在前端加了一个硬件RC滤波,效果才正常。记住:数字滤波不是万能的,硬件滤波和软件滤波要配合使用

离散化公式的另一种形式

有时候你会看到这样的写法:

y[n] = y[n-1] + α * (x[n] - y[n-1])

这和前面的公式是等价的。我个人更喜欢这种写法,因为它更直观——每次只把输入和输出的差值的一部分加进去。α就是那个「一部分」的比例。

举个例子:如果α=0.1,每次只修正10%的误差。这样滤波就很平滑,但需要10个采样周期才能接近目标值的63%。

知识体系总览

下面这张图把一阶低通滤波器的核心逻辑串起来了:

一阶低通滤波器知识体系 模拟RC低通 H(s) = 1/(1+sRC) τ = RC f_c = 1/(2πRC) 后向差分 数字滤波器 y[n] = α·x[n] + (1-α)·y[n-1] α = T/(τ+T) α ∈ (0, 1] 参数调优 α↑ → 响应快,滤波弱 α↓ → 响应慢,滤波强 f_c = 开关频率/10~20 典型应用场景 电流采样滤波 速度计算滤波 位置信号滤波 f_c: 500Hz~2kHz f_c: 速度环带宽×2~5 f_c: 1~10Hz 硬件滤波 + 软件滤波 配合使用效果最佳

几个实用技巧

  • 初始化要小心: 第一次滤波时,y_prev最好赋值为当前输入值,而不是0。否则第一个输出会从0跳变到输入,产生冲击。
  • 定点数实现: 在低端MCU上,用定点数代替浮点数。α用Q15格式表示,乘法用移位实现,速度能快好几倍。
  • 变系数滤波: 启动时用大α快速跟踪,稳态后用小α平滑滤波。我做过一个自适应方案,效果比固定系数好很多。
  • 注意采样周期: α依赖于采样周期T。如果T变化(比如任务调度不稳定),滤波效果会飘。这时候要用定时器固定采样间隔。
调试小技巧: 用示波器同时看原始信号和滤波后的信号。如果滤波后的信号相位滞后太多,说明α太小了。我一般先设α=0.5,然后逐步减小,直到毛刺被滤除为止。

一阶低通滤波器看似简单,但用好了能解决很多实际问题。记住:滤波不是越强越好,关键是在噪声抑制和信号保真度之间找到平衡点。下次遇到传感器信号毛刺多的时候,先别急着加复杂的滤波器,试试一阶低通,说不定就搞定了。


公众号:蓝海资料掘金营,微信deep3321