• 正文
  • 相关推荐
申请入驻 产业图谱

一维卡尔曼替代一阶低通滤波更好吗?要理解关键,不是用个高级的名字

20小时前
258
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

做硬件的人,大概都见过这种场面:传感器明明没动,串口里的数却一直跳。拿一阶低通压一压,数字安静了;真让它动起来,显示值又慢半拍。

系数调小,稳是稳了,跟不上。系数调大,跟得上了,又开始抖。一个参数来回拧,像拧水龙头,总找不到那个既舒服又合适的位置。

最近读到一篇讲一阶低通和一维卡尔曼的文章,里面有个切口值得展开:滤波的权重,能不能不再固定,而是跟着工况变化?

能。但必须把这句话讲完整。否则,把低通换成卡尔曼,只是换了一个听起来更高级的名字。

这里说的低通,是单片机里常用的那种一阶递推滤波,不是所有低通滤波器。

它的核心就一句:

估计值 = 上次估计值 + α ×(这次测量值 − 上次估计值)

α 越小,越相信过去;α 越大,越相信这一次测量。问题是,固定 α 不知道眼前的变化到底是什么。

温度真的升高了,它按原来的力度往下压;传感器受干扰了,它也按原来的力度往下压。真实变化和测量噪声,进来时都只是一个新数字。

滤波想解决的,不是把曲线画得更漂亮,而是在噪声和真实变化之间,尽量作出正确的取舍。

如果信号、采样周期和噪声都比较稳定,固定系数完全可以很好用。不要因为算法名字简单,就先判它落后。

一维卡尔曼多算了一件事:我现在有多没把握

先限定模型:我们只估计一个量,假设下一时刻的真实值大体延续上一时刻,但允许它随机变化。这叫随机游走模型。

它更新信号的写法,其实和刚才很像:

估计值 = 上次估计值 + K ×(这次测量值 − 上次估计值)

不同之处在 K 怎么来。它不只保存一个估计值,还保存这份估计的不确定性 P。

Q 表示一个采样间隔内,真实状态可能变化多少,用方差表达;R 表示测量噪声有多大,同样是方差。P、Q、R 的单位都是被测量单位的平方,不是随手填进去的三个神秘旋钮。

每来一次数据,先把“不确定性账本”往上加一点:P预测 = P + Q。再计算这次测量应该占多大权重:K = P预测 /(P预测 + R)。

预测越没把握,K 越大,多听新测量的;测量越不可靠,K 越小,少被它带着跑。更新完估计值,再把 P 收小:P =(1 − K)× P预测。

它比固定系数多出来的,是一套关于“不确定性”的计算,而不是凭空知道真值的本领。

最容易讲错的地方:固定 Q、R,并不会自动识别突变

很多介绍写到这里,就开始说“信号稳定时自动平滑,信号变化时自动跟随”。但如果程序里的 Q、R 一直不变,这个结论并不成立。

在上述随机游走模型下,采样周期固定、每次都有有效测量、Q 和 R 都是固定正数时,K 会经过启动阶段,逐渐收敛到一个常数。

更关键的是:这几行计算 K 的公式,根本没有用到测量值变化了多少。 因此,一个真实阶跃不会仅凭自身就让 K 突然变大。

比如 Q=0.01、R=1,稳态 K 约为 0.0951。进入稳态后,它对信号的更新就相当于 α≈0.0951 的一阶递推低通。数学上,对这个模型有 Q/R = K²/(1−K)。

所以,普通一维卡尔曼确实会递推增益,于自动分清真实变化和噪声。

真正的动态系数,要有动态的信息来源

想让滤波器静止时稳、变化时快,首先得告诉它:现在为什么应该相信另一套规则。

一种可靠做法,是利用系统本来就知道的工况。电机刚收到运动指令、阀门刚被打开、称重平台进入加料阶段,真实状态更可能变化,就适当增大 Q。设备进入稳定阶段,再把 Q 降下来。

另一种做法,是利用测量质量。距离传感器回波质量变差、某个测量量程的噪声变大,就增大 R,降低这次读数的权重。质量恢复,再恢复 R。完全无效的读数,应当跳过更新,而不是当成一个正常数字硬塞进去。

这时,K 才会随着工况和测量可靠性变化。注意,Q 增大意味着“状态可能真的在变”;R 增大意味着“这次测量可能不可信”。这两个判断不能混为一谈。

下面这组曲线是仿真,不是实测。蓝色区域对应系统已知的动作指令,短时增大 Q;橙色区域对应已知的测量质量下降,增大 R。它不是偷看真值,而是使用了滤波器之外的工况信息。

还可以根据“测量值减预测值”这个差值,也就是创新,来辅助调参。但这里有个硬把真实变化拒之门外。仅凭一次差值,通常分不清这两件事。

需要结合连续多次观测、物理变化速度上限、传感器质量标志和其他工况信息,并限制参数的范围。自适应不是取消判断,而是把判断从一个固定系数,搬到了工况识别和噪声估计里。

单片机上怎么落地?核心依然只有几行

下面是随机游走模型的基本内核。真正的动态部分,是上层根据工况传入不同的 Q、R,而不是在代码里写死两个常数,再期待它自动理解现场。

#include <math.h>
#include <stdbool.h>

typedef struct {
    float x;       // 当前估计值
    float p;       // 估计误差方差
    bool ready;
} KF1;

// q:本次采样间隔的过程噪声方差
// r:当前测量噪声方差,必须大于 0
// valid:测量有效标志
bool kf1_update(KF1 *s, float z, bool valid,
                float q, float r, float *out)
{
    if (!s || !out || !isfinite(q) || q < 0 ||
        !isfinite(r) || r <= 0) return false;

    valid = valid && isfinite(z);
    if (!s->ready) {
        if (!valid) return false;
        s->x = z;
        s->p = r;
        s->ready = true;
    } else {
        float pp = s->p + q;
        if (!isfinite(pp) || !isfinite(pp + r))
            return false;
        if (valid) {
            float k = pp / (pp + r);
            s->x += k * (z - s->x);
            s->p = (1.0f - k) * pp;
        } else {
            s->p = pp; // 缺测:只预测,不校正
        }
    }
    *out = s->x;
    return true;
}

每个通道单独维护一个状态,使用前把结构体清零。缺测时返回的是预测值,不代表传感器恢复有效;缺测状态和超时报警还要由上层保留。代码用于展示算法结构,量产前还应按量程、异常数据和数值范围做测试。

调参先量噪声:在输入真实稳定的条件下,采一段数据,估算测量噪声方差作为 R 的起点。不要把明显的温漂和真实变化都算进“白噪声”里。

Q 则应反映相邻采样间隔内的状态变化。不等间隔采样时,不能不加分析地沿用同一个 Q;对按时间累积的随机游走模型,可以采用 Q = q_rate × dt,具体数值仍要用数据验证。

参数的目标不是“最平”,而是在允许的响应延迟下,把估计误差压下来。仿真有真值可以对照;实际设备则要靠参考仪器、已知输入或受控试验,不能拿滤波后的曲线自己证明自己。

哪些信号值得用,哪些不该硬套

温度、压力、液位等缓变量。 当测量噪声可估计,且随机游走足以近似目标变化时,一维卡尔曼适合做趋势估计。加热、开阀等工况还能帮助调度 Q。若噪声始终稳定,固定低通也可能已经够用。

称重、电池电压、直流电流的稳定值或慢变化。 空载、加料、负载切换等状态可以提供调参依据。但滤波后的慢信号不能拿来替代过流保护或捕捉瞬态峰值,安全保护路径应独立设计。

ToF、超声等距离信号。 测量质量可以辅助设置 R,但失效回波和异常值要先处理。目标持续快速运动时,仅估计位置的一维模型容易滞后,加入速度状态的模型通常更值得考虑。

IMU 单轴的慢变化或偏置趋势。 可以在明确目标的前提下做平滑估计,但给每个轴套一个一维滤波器,不等于完成姿态融合,更不能直接承诺把真实振动和运动信息保住。

声音、振动、冲击、脉冲,或者必须的抗混叠措施;已经混叠进来的信号,换个算法名字不会自动还原。

最后,把“更真实”三个字落到工程上

一维卡尔曼的价值,不是让每条曲线都比低通更平滑,而是给动态权重一个可以解释、可以验证的依据。在模型和噪声假设合理时,工况调度可以改善某些阶段的跟踪和抗噪;假设错了,它一样会滞后,一样会相信错误的读数。

静态工况用简单方法不丢人,变化工况用动态权重有价值。真正决定信号能不能更接近实际的,不是“卡尔曼”这三个字,而是你有没有把真实变化和测量噪声这两笔账分开。

相关推荐

多年硬件从业经验,专注分享从研发到供应链,再到精益制造过程中的经验和感悟!