4. 软件滤波算法:均值滤波、中值滤波、卡尔曼滤波在编码器信号处理中的应用对比
编码器信号这东西,看着是方波,实际上进了单片机引脚之后,啥妖蛾子都能碰上。毛刺、抖动、偶尔丢一个脉冲——你要是直接拿这个信号去换相,电机能抖得像筛子。所以,软件滤波是必须的。
我个人习惯把滤波分成三类:简单粗暴型、稳重型、还有那种“带脑子”的。今天咱们就把均值、中值、卡尔曼这三兄弟拉出来遛遛。
核心观点:没有最好的滤波,只有最合适的场景。换相时刻的实时性要求,决定了你选哪种。
4.1 均值滤波:简单,但有代价
均值滤波的原理,说白了就是“取平均”。你连续采 N 个位置值,加起来除以 N,输出一个平滑后的结果。
// 均值滤波示例(N=8)
#define FILTER_N 8
uint16_t buffer[FILTER_N] = {0};
uint8_t index = 0;
uint32_t sum = 0;
uint16_t mean_filter(uint16_t new_sample) {
sum -= buffer[index];
buffer[index] = new_sample;
sum += new_sample;
index = (index + 1) % FILTER_N;
return sum / FILTER_N;
}
这个代码我用了好多年,确实能压住高频噪声。但有个坑——它会把真实的换相边沿也给抹平了。你想想看,电机刚转过一个霍尔边沿,位置本该跳变,均值滤波一平均,跳变变成了斜坡。换相点就滞后了。
我曾经在一个高速无刷电机项目里用均值滤波,转速一上 8000 RPM,换相滞后直接导致相电流畸变,电机啸叫。后来换成中值滤波才解决。
所以,均值滤波适合什么场景?编码器信号只有轻微白噪声,且你对实时性要求不高的时候。比如位置环的慢速反馈,或者转速计算前的预处理。
4.2 中值滤波:抗脉冲干扰的利器
中值滤波就聪明一点。它不取平均,而是把 N 个采样值排序,取中间那个。这样一来,哪怕有一个采样点因为干扰跳到了离谱的值,只要 N 足够大,中间值基本不受影响。
// 中值滤波示例(N=5)
#define MEDIAN_N 5
uint16_t median_filter(uint16_t new_sample) {
static uint16_t ring[MEDIAN_N] = {0};
static uint8_t idx = 0;
uint16_t temp[MEDIAN_N];
uint8_t i, j;
uint16_t tmp;
ring[idx] = new_sample;
idx = (idx + 1) % MEDIAN_N;
// 拷贝到临时数组并排序
for(i = 0; i < MEDIAN_N; i++) temp[i] = ring[i];
for(i = 0; i < MEDIAN_N - 1; i++) {
for(j = i + 1; j < MEDIAN_N; j++) {
if(temp[i] > temp[j]) {
tmp = temp[i];
temp[i] = temp[j];
temp[j] = tmp;
}
}
}
return temp[MEDIAN_N / 2];
}
嗯,这里要注意——排序会消耗 CPU 时间。N 越大,排序越慢。我一般 N 取 3 或 5,再大就不划算了。
中值滤波最大的优点是保留边沿。一个真实的跳变,在排序后仍然会出现在中间位置附近,不会像均值那样被拉成斜坡。所以,换相信号处理,我首选中值滤波。
我的经验:在霍尔传感器换相应用中,N=3 的中值滤波就能干掉 90% 的随机毛刺,而且延迟只有 1-2 个采样周期。性价比极高。
4.3 卡尔曼滤波:带“脑子”的滤波器
卡尔曼滤波听起来高大上,其实它的核心就一句话:用模型预测 + 测量修正。它不只看当前采样值,还根据上一时刻的状态和系统模型,猜一下当前应该是什么值,然后再用测量值去修正这个猜测。
对于编码器信号,我们可以把电机位置建模成一个匀速运动模型。卡尔曼滤波就能在噪声中估计出真实位置,甚至还能预测下一时刻的位置。
// 一维卡尔曼滤波(位置估计)
typedef struct {
float x; // 估计位置
float P; // 估计误差协方差
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
} Kalman1D;
void kalman_init(Kalman1D *k, float init_x, float init_P, float Q, float R) {
k->x = init_x;
k->P = init_P;
k->Q = Q;
k->R = R;
}
float kalman_update(Kalman1D *k, float z) {
// 预测
float P_pred = k->P + k->Q;
// 卡尔曼增益
float K = P_pred / (P_pred + k->R);
// 更新
k->x = k->x + K * (z - k->x);
k->P = (1 - K) * P_pred;
return k->x;
}
你看,代码量其实不大。但调参是个技术活——Q 和 R 怎么设?
- Q(过程噪声):设大了,滤波器响应快但噪声多;设小了,响应慢但平滑。
- R(测量噪声):设大了,滤波器更相信模型;设小了,更相信传感器。
我记得第一次调卡尔曼滤波时,Q 和 R 设反了,结果滤波后的位置比原始信号还抖。后来才明白,R 应该根据编码器数据手册的精度来设,Q 则根据你对电机模型的信任程度来调。
避坑指南:卡尔曼滤波在换相时刻有一个致命问题——如果模型预测的位置和实际换相位置偏差太大,滤波器会“犹豫”,导致换相延迟。我曾经在低速大负载场景下吃过这个亏,后来加了换相时刻的强制修正才解决。
4.4 三种滤波的对比与选型
说了这么多,到底怎么选?我整理了一个表格,你一看就明白。
| 特性 | 均值滤波 | 中值滤波 | 卡尔曼滤波 |
|---|---|---|---|
| 计算开销 | 低(仅加法与除法) | 中(需排序) | 中高(浮点运算) |
| 延迟 | 高(N/2 采样周期) | 低(约 1-2 周期) | 低(可调) |
| 抗脉冲干扰 | 差 | 优秀 | 良好 |
| 边沿保持 | 差(抹平跳变) | 好 | 好(需调参) |
| 适用场景 | 慢速位置环、转速预处理 | 霍尔换相、脉冲计数 | 高精度位置估计、速度预测 |
| 调参难度 | 低(只需选 N) | 低(只需选 N) | 高(Q、R 需调试) |
说白了,如果你只想快速解决问题,选 N=3 的中值滤波。如果你对位置精度有极致要求,而且 CPU 算力够,卡尔曼滤波值得一试。均值滤波嘛——我建议只在非实时路径上用。
4.5 本章知识体系
下面这张图把三种滤波的核心逻辑和适用边界画清楚了,你可以存下来当参考。
最后说一句实在的——别在滤波上过度设计。我见过有人把卡尔曼滤波调了两周,最后发现换相抖动是因为霍尔安装间隙太大。先确认硬件没问题,再谈软件滤波。这是我一贯的原则。