4、数字滤波算法:滑动平均滤波、中值滤波、卡尔曼滤波在称重信号中的应用
说到称重信号处理,我第一个想聊的就是滤波。为什么?因为电梯里的称重传感器,说白了就是个“受气包”。电梯启动的震动、乘客走动的冲击、甚至风吹轿厢的晃动,都会叠加到信号上。你想想看,如果直接把原始信号拿去判断超载,那电梯门一关一开,报警器能响个不停。
所以,数字滤波就是给信号“洗个澡”,把那些乱七八糟的噪声去掉,留下真实的重量信息。今天咱们就聊聊三种最常用的滤波算法:滑动平均、中值滤波、卡尔曼滤波。这三种我都用过,各有各的脾气。
4.1 滑动平均滤波:简单粗暴,但管用
滑动平均滤波,说白了就是“取个平均值”。把最近N次采样值存起来,每次新数据进来,就扔掉最老的那个,然后算个平均数。这招对付高频噪声特别有效。
我在项目中遇到过这样的情况:电梯启动瞬间,传感器输出会有一个尖峰。如果用滑动平均,这个尖峰就会被“平均”掉,不会误触发超载报警。
核心公式:
y[n] = (x[n] + x[n-1] + ... + x[n-N+1]) / N
代码实现也很简单:
#define FILTER_LEN 8
static uint16_t buffer[FILTER_LEN];
static uint8_t index = 0;
static uint32_t sum = 0;
uint16_t moving_average(uint16_t new_sample) {
sum -= buffer[index];
buffer[index] = new_sample;
sum += new_sample;
index = (index + 1) % FILTER_LEN;
return sum / FILTER_LEN;
}
我的经验:N值选多大?我一般取4到16之间。N太小,滤波效果差;N太大,响应变慢。电梯称重我习惯用8,平衡了平滑度和实时性。
4.2 中值滤波:专治“野点”
中值滤波的思路更直接:把N个采样值排序,取中间那个。这招对付脉冲噪声(比如突然的电磁干扰)特别灵。
我曾经遇到过一个问题:电梯运行时,变频器产生的电磁干扰会偶尔让传感器输出一个离谱的值。滑动平均虽然能压一压,但那个“野点”还是会拉偏平均值。换成中值滤波后,这种干扰直接被“踢出局”了。
注意:中值滤波的N值必须是奇数,不然中间两个数取哪个?我一般用3或5。N越大,对脉冲噪声的抑制越强,但计算量也越大。
代码示例:
#define MEDIAN_LEN 5
uint16_t median_filter(uint16_t new_sample) {
static uint16_t buf[MEDIAN_LEN];
static uint8_t idx = 0;
uint16_t temp[MEDIAN_LEN];
uint8_t i, j;
buf[idx] = new_sample;
idx = (idx + 1) % MEDIAN_LEN;
// 复制并排序
for(i = 0; i < MEDIAN_LEN; i++) temp[i] = buf[i];
for(i = 0; i < MEDIAN_LEN - 1; i++) {
for(j = i + 1; j < MEDIAN_LEN; j++) {
if(temp[i] > temp[j]) {
uint16_t t = temp[i];
temp[i] = temp[j];
temp[j] = t;
}
}
}
return temp[MEDIAN_LEN / 2];
}
4.3 卡尔曼滤波:有点“智能”的滤波
卡尔曼滤波就高级一些了。它不光看当前测量值,还根据上一时刻的估计值和系统模型,预测当前应该是什么值。然后,它用测量值来“修正”这个预测。说白了,它是在“猜”和“测”之间找个平衡。
我刚开始学卡尔曼滤波时,觉得公式太复杂。后来发现,在嵌入式里用一维卡尔曼就够了,没那么吓人。
一维卡尔曼滤波核心步骤:
- 预测:根据上一时刻估计值,预测当前值
- 计算卡尔曼增益:根据预测误差和测量误差,算出一个权重
- 更新:用测量值修正预测值
- 更新误差:为下一轮做准备
代码实现:
typedef struct {
float Q; // 过程噪声协方差
float R; // 测量噪声协方差
float P; // 估计误差协方差
float K; // 卡尔曼增益
float X; // 估计值
} Kalman_1D;
void Kalman_Init(Kalman_1D *kf, float init_val) {
kf->Q = 0.01f; // 我一般设0.01,根据实际情况调
kf->R = 0.1f; // 测量噪声,传感器手册上能找到
kf->P = 1.0f;
kf->X = init_val;
}
float Kalman_Update(Kalman_1D *kf, float measurement) {
// 预测
float P_pred = kf->P + kf->Q;
// 计算卡尔曼增益
kf->K = P_pred / (P_pred + kf->R);
// 更新估计值
kf->X = kf->X + kf->K * (measurement - kf->X);
// 更新误差
kf->P = (1.0f - kf->K) * P_pred;
return kf->X;
}
调参心得:Q和R怎么设?Q越大,系统越相信测量值;R越大,系统越相信预测值。我一般先让R大一点(比如0.5),让滤波平滑些,再慢慢减小R,直到响应速度满足要求。
4.4 三种滤波的对比与选择
这三种滤波各有千秋,我整理了个表格,方便你对比:
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 滑动平均 | 简单、计算量小、平滑高频噪声 | 响应慢、对脉冲噪声效果一般 | 电梯平稳运行时的称重 |
| 中值滤波 | 有效抑制脉冲噪声、实现简单 | 需要排序、N大时计算量大 | 电磁干扰严重的环境 |
| 卡尔曼滤波 | 自适应、平滑且响应快、精度高 | 需要调参、计算量稍大 | 动态称重、需要高精度场景 |
避坑指南:我曾经在一个项目里只用滑动平均,结果电梯启动时信号延迟太大,导致超载判断滞后。后来我改成“滑动平均+中值滤波”组合:先用中值滤波去掉野点,再用滑动平均平滑。效果好了很多。
4.5 实战建议:怎么选?
如果你刚入门,我建议先从滑动平均开始。它简单、可靠,能解决80%的问题。如果发现偶尔有异常跳变,就加上中值滤波。如果对精度和响应速度都有要求,再考虑卡尔曼滤波。
嗯,这里要注意一点:滤波不是越复杂越好。我见过有人用卡尔曼滤波处理静态称重,结果调参调了三天,最后发现滑动平均就够用了。所以,先搞清楚你的噪声是什么类型,再选合适的算法。
最后说一句:滤波算法选好了,别忘了在硬件上也下点功夫。比如传感器供电加个LC滤波、信号线用屏蔽线,这些都能让数字滤波事半功倍。