3、软件滤波算法:均值滤波、中值滤波、滑动平均滤波、卡尔曼滤波、IIR/FIR滤波器

说到电流采样噪声,硬件滤波能解决一部分问题,但真正让波形变得「干净」的,还得靠软件。我做了这么多年电机控制,可以负责任地告诉你:软件滤波才是 FOC 电流环的灵魂

为什么?因为硬件滤波器会引入相位延迟,而电流环对实时性要求极高。你想想看,一个延迟了 50μs 的电流信号,到了 PI 调节器那里,相位裕度可能就没了,系统直接振荡。所以,软件滤波算法就成了我们的必修课。

这一章,我把常用的几种滤波算法掰开揉碎了讲。每种算法我都会给出适用场景、代码实现,以及我踩过的坑。

核心观点:没有最好的滤波器,只有最合适的滤波器。选型要看你的采样频率、噪声特性、以及 CPU 算力。
软件滤波 算法选型 均值滤波 简单平均 中值滤波 抗脉冲噪声 滑动平均 实时更新 卡尔曼滤波 最优估计 IIR/FIR 数字滤波器 关键对比维度 • 计算量:均值 < 滑动平均 < 中值 < FIR < IIR < 卡尔曼 • 延迟:均值 ≈ 滑动平均 < FIR < IIR < 中值 < 卡尔曼 • 平滑效果:卡尔曼 > IIR > FIR > 滑动平均 > 均值 > 中值 • 抗脉冲:中值 > 卡尔曼 > 其他 选型原则:根据采样频率、噪声类型、实时性要求综合判断

3.1 均值滤波:最简单,但别小看它

均值滤波的原理,说白了就是「取平均」。连续采 N 个点,加起来除以 N。就这么简单。

// 均值滤波示例
#define FILTER_N 8

uint16_t mean_filter(uint16_t new_sample) {
    static uint16_t buf[FILTER_N];
    static uint8_t index = 0;
    static uint32_t sum = 0;
    
    // 减去旧值,加上新值
    sum -= buf[index];
    sum += new_sample;
    buf[index] = new_sample;
    
    index = (index + 1) % FILTER_N;
    
    return (uint16_t)(sum / FILTER_N);
}

我个人习惯把 N 设为 2 的幂次,比如 4、8、16。这样除法可以用移位操作,快很多。在 Cortex-M4 上,一次均值滤波只需要几个时钟周期。

小技巧:如果采样率是 10kHz,N 取 8,等效截止频率大约是 10kHz / 8 = 1.25kHz。这个值对电流环来说刚刚好。

但均值滤波有个致命弱点——它对脉冲噪声毫无抵抗力。如果某个采样点被干扰了,整个平均值都会被拉偏。我在做伺服驱动器时遇到过这种情况:IGBT 开关瞬间产生的尖峰,让均值滤波后的电流值直接跳变,导致电流环误动作。

3.2 中值滤波:专治「毛刺」

中值滤波的思路很直接:把 N 个采样值排序,取中间那个。它能完美干掉脉冲噪声,因为不管尖峰多大,只要它出现在少数几个点上,排序后就会被挤到两端。

// 中值滤波示例(N=5,冒泡排序)
uint16_t median_filter_5(uint16_t *buf) {
    uint16_t temp[5];
    uint16_t tmp;
    
    // 复制数据
    for(int i = 0; i < 5; i++) temp[i] = buf[i];
    
    // 冒泡排序
    for(int i = 0; i < 4; i++) {
        for(int j = 0; j < 4 - i; j++) {
            if(temp[j] > temp[j+1]) {
                tmp = temp[j];
                temp[j] = temp[j+1];
                temp[j+1] = tmp;
            }
        }
    }
    
    return temp[2]; // 返回中间值
}
注意:中值滤波的延迟比较大。N=5 时,输出滞后了 2 个采样周期。如果电流环带宽要求很高,要谨慎使用。

我曾经在一个项目里,电流采样总是被 PWM 开关噪声干扰。硬件加了 RC 滤波,效果还是不理想。后来我在软件里加了一级中值滤波,N=3,毛刺全没了。嗯,有时候最简单的方案反而最有效。

3.3 滑动平均滤波:实时性最好的「平均」

滑动平均滤波和均值滤波很像,但它不需要每次重新计算总和。它维护一个环形缓冲区,每次新数据进来,就减去最旧的数据,加上最新的数据。这样每次计算只需要一次加法和一次减法。

// 滑动平均滤波
typedef struct {
    uint16_t *buffer;
    uint16_t size;
    uint16_t index;
    uint32_t sum;
} sliding_avg_t;

uint16_t sliding_avg_update(sliding_avg_t *filter, uint16_t sample) {
    filter->sum -= filter->buffer[filter->index];
    filter->sum += sample;
    filter->buffer[filter->index] = sample;
    filter->index = (filter->index + 1) % filter->size;
    
    return (uint16_t)(filter->sum / filter->size);
}

我个人特别喜欢滑动平均滤波。它在实时性和平滑效果之间取得了很好的平衡。在 FOC 电流环中,我通常用 8 点的滑动平均,延迟只有 4 个采样周期,对 10kHz 的电流环来说完全可以接受。

3.4 卡尔曼滤波:理论最优,但别滥用

卡尔曼滤波是状态估计的「天花板」。它通过预测-更新两个步骤,结合系统模型和测量值,给出最优估计。在电机控制中,它常用于转速估计和位置跟踪。

但说实话,在电流采样滤波这个场景,我不太推荐用卡尔曼。为什么?因为电流采样的噪声特性相对简单,用卡尔曼有点「杀鸡用牛刀」。而且卡尔曼滤波的计算量比较大,需要矩阵运算,对低端 MCU 不太友好。

// 一维卡尔曼滤波简化版
typedef struct {
    float Q;  // 过程噪声协方差
    float R;  // 测量噪声协方差
    float P;  // 估计误差协方差
    float K;  // 卡尔曼增益
    float X;  // 状态估计值
} kalman_1d_t;

float kalman_update(kalman_1d_t *kf, float measurement) {
    // 预测
    kf->P = kf->P + kf->Q;
    
    // 更新
    kf->K = kf->P / (kf->P + kf->R);
    kf->X = kf->X + kf->K * (measurement - kf->X);
    kf->P = (1 - kf->K) * kf->P;
    
    return kf->X;
}
我的建议:除非你的系统噪声特性非常复杂(比如传感器本身有漂移),否则别用卡尔曼。在电流采样这个场景,滑动平均滤波 + 中值滤波的组合,效果已经足够好。

3.5 IIR/FIR 数字滤波器:专业玩家的选择

如果你对滤波效果有极致要求,那就得上 IIR 或 FIR 了。这两种滤波器可以精确控制截止频率、通带纹波、阻带衰减等指标。

IIR 滤波器(无限脉冲响应)的优点是阶数低,计算量小。但它的相位特性是非线性的,可能会引起信号失真。在电流环中,IIR 滤波器容易引起相位滞后,需要小心调整。

FIR 滤波器(有限脉冲响应)的优点是线性相位,不会引起相位失真。但它的阶数通常比较高,计算量大。在低采样率下,FIR 是个不错的选择。

// 一阶 IIR 低通滤波器
typedef struct {
    float alpha;  // 滤波系数,0~1
    float output; // 上一次输出
} iir_lpf_t;

float iir_lpf_update(iir_lpf_t *filter, float input) {
    filter->output = filter->alpha * input + (1 - filter->alpha) * filter->output;
    return filter->output;
}

// 等效截止频率计算:fc = alpha * fs / (2 * pi)
// 例如 alpha=0.1, fs=10kHz, fc ≈ 159Hz
经验值:在 FOC 电流环中,IIR 滤波器的 alpha 值通常取 0.05~0.2。alpha 越小,滤波越平滑,但延迟也越大。我一般从 0.1 开始调,根据实际波形微调。

3.6 实战选型指南

说了这么多,到底该用哪个?我根据多年经验,整理了一个选型表:

场景 推荐算法 理由
采样率低(<5kHz) FIR 滤波器 计算量可接受,线性相位
采样率高(>10kHz) 滑动平均 + 中值 计算量小,实时性好
脉冲噪声严重 中值滤波 专治毛刺,效果立竿见影
需要精确截止频率 IIR 或 FIR 可精确设计滤波器特性
CPU 资源紧张 均值滤波 最简单,最省资源
噪声特性复杂 卡尔曼滤波 理论最优,但实现复杂

最后说一句:滤波算法不是越复杂越好。我见过很多工程师,一上来就上卡尔曼,结果调了两个月都没调好。其实用滑动平均 + 中值,半小时就搞定了。你想想看,是不是这个道理?

避坑指南:我曾经在一个项目里,把 IIR 滤波器的 alpha 设成了 0.01,结果电流环响应慢得像蜗牛。后来才发现,alpha 太小导致相位滞后太大,电流环带宽被压到了 50Hz 以下。记住:滤波越狠,延迟越大。

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