3. 线性卡尔曼滤波(KF)原理:状态空间模型、预测与更新方程、卡尔曼增益推导
各位同学,今天我们来啃一块硬骨头——线性卡尔曼滤波的核心原理。说实话,我刚入行那会儿,看到一堆矩阵公式就头大。但后来在项目中调了无数次滤波器之后,才真正理解这些公式背后的物理意义。说白了,卡尔曼滤波就是一套“预测-修正”的循环,跟咱们日常做决策的逻辑一模一样。
3.1 状态空间模型——你得先知道系统长啥样
要玩卡尔曼滤波,第一步就是把实际问题抽象成数学模型。我个人习惯用“状态空间”来描述系统。什么是状态?就是能完全描述系统当前状况的一组最小变量。
举个例子,我在做无人机导航时,状态向量通常包含:
- 位置(x, y, z)
- 速度(vx, vy, vz)
- 姿态角(roll, pitch, yaw)
状态空间模型由两个方程组成:
状态方程(描述系统怎么演化):
x(k) = A * x(k-1) + B * u(k-1) + w(k-1)
观测方程(描述传感器怎么测量):
z(k) = H * x(k) + v(k)
这里:
- x(k) 是k时刻的状态向量
- A 是状态转移矩阵——说白了就是“上一时刻的状态怎么变成这一时刻的”
- B 是控制输入矩阵,u是控制量(比如你给电机的油门)
- w 是过程噪声,代表模型的不确定性
- z(k) 是观测值,H是观测矩阵,v是观测噪声
重要提醒:过程噪声w和观测噪声v,我们都假设它们是高斯白噪声,均值是0,协方差分别是Q和R。这个假设是卡尔曼滤波能工作的前提。
3.2 预测与更新——两步走,稳得很
卡尔曼滤波的核心就两个步骤:预测(Predict)和更新(Update)。我当年在调试车载组合导航时,就是靠这两步反复迭代,才把定位误差从米级压到了厘米级。
3.2.1 预测步骤(时间更新)
预测就是根据上一时刻的状态,猜一下当前时刻的状态。公式如下:
// 状态预测
x_pred = A * x_est + B * u
// 协方差预测
P_pred = A * P_est * A^T + Q
这里:
- x_pred 是预测的状态
- P_pred 是预测的误差协方差——它代表你对预测结果的信心
- Q 是过程噪声协方差,越大说明模型越不可靠
嗯,这里要注意:P_pred 在每次预测后都会变大,因为模型的不确定性在累积。你想想看,如果你只靠模型猜,时间越长,误差肯定越大,对吧?
3.2.2 更新步骤(测量更新)
更新就是拿传感器的测量值来修正预测值。公式如下:
// 卡尔曼增益
K = P_pred * H^T * (H * P_pred * H^T + R)^(-1)
// 状态更新
x_est = x_pred + K * (z - H * x_pred)
// 协方差更新
P_est = (I - K * H) * P_pred
这里:
- K 是卡尔曼增益——它是整个滤波器的灵魂
- z - H*x_pred 叫“新息”(innovation),就是实际测量和预测测量之间的差异
- R 是观测噪声协方差,越大说明传感器越不可靠
我的经验:调Q和R矩阵是门手艺活。Q设太大,滤波器会过度相信测量值,噪声大;Q设太小,滤波器反应迟钝。我曾经在一个惯性导航项目里,光调Q就花了两周时间。
3.3 卡尔曼增益推导——为什么它这么重要?
卡尔曼增益K,说白了就是一个“权重分配器”。它决定了你是更相信模型预测,还是更相信传感器测量。
我们来看K的表达式:
K = P_pred * H^T * (H * P_pred * H^T + R)^(-1)
这个公式怎么来的?我简单说下思路:
- 我们希望估计误差的方差最小——这是最优估计的准则
- 把x_est = x_pred + K*(z - H*x_pred)代入误差协方差公式
- 对K求导,令导数为0,解出K
推导结果就是上面那个公式。你仔细看:
- 如果R很大(传感器噪声大),分母变大,K变小——我更相信模型
- 如果P_pred很大(模型误差大),分子变大,K变大——我更相信测量
为什么会这样?因为K本质上是在做“加权平均”。我当年在调试GPS/INS组合导航时,就靠这个特性,在GPS信号差的时候自动降低测量权重,系统照样跑得稳。
避坑指南:我曾经犯过一个错误——把R设得太小,结果滤波器完全跟着噪声跑,输出全是毛刺。后来才明白,R不是越小越好,它应该反映传感器的真实噪声水平。建议先用静态数据算一下传感器的标准差,再设R的初值。
3.4 完整算法流程——一张图看懂
下面我用一张流程图把整个KF的迭代过程展示出来。这张图是我做项目时一直贴在工位上的,每次调试卡壳了就看一眼。
3.5 代码实现——动手写一个最简单的KF
光说不练假把式。下面我给出一个一维卡尔曼滤波的C语言实现。这个代码我在STM32上跑过,用来滤波一个温度传感器的读数。
#include <math.h>
typedef struct {
float x; // 状态估计值
float P; // 估计误差协方差
float Q; // 过程噪声协方差
float R; // 观测噪声协方差
float K; // 卡尔曼增益
} KalmanFilter;
// 初始化滤波器
void KF_Init(KalmanFilter *kf, float init_x, float init_P, float Q, float R) {
kf->x = init_x;
kf->P = init_P;
kf->Q = Q;
kf->R = R;
kf->K = 0.0f;
}
// 预测步骤(一维情况,A=1, B=0)
void KF_Predict(KalmanFilter *kf) {
kf->P = kf->P + kf->Q; // P_pred = P_est + Q
}
// 更新步骤(一维情况,H=1)
void KF_Update(KalmanFilter *kf, float z) {
// 计算卡尔曼增益
kf->K = kf->P / (kf->P + kf->R);
// 更新状态估计
kf->x = kf->x + kf->K * (z - kf->x);
// 更新误差协方差
kf->P = (1.0f - kf->K) * kf->P;
}
// 使用示例
int main() {
KalmanFilter kf;
KF_Init(&kf, 25.0f, 1.0f, 0.01f, 0.1f);
float measurements[] = {24.5, 25.1, 24.8, 25.3, 25.0};
for (int i = 0; i < 5; i++) {
KF_Predict(&kf);
KF_Update(&kf, measurements[i]);
printf("测量值: %.2f, 滤波后: %.2f\n", measurements[i], kf.x);
}
return 0;
}
调试技巧:我建议你在调参时,先把Q和R设成1.0,然后观察滤波效果。如果响应太慢,减小Q;如果噪声太大,增大R。记住,Q和R的比值比绝对值更重要。
3.6 总结——KF的核心思想
线性卡尔曼滤波,说白了就是一套“预测-测量-修正”的闭环。它用状态空间模型描述系统,用预测方程外推状态,用更新方程融合测量,用卡尔曼增益自动调节权重。
我个人觉得,理解KF的关键不在于背公式,而在于理解“不确定性传递”这个概念。每次预测,不确定性增加;每次测量,不确定性减少。卡尔曼增益就是在这两者之间找平衡。
好了,这一章的内容就到这里。记住,纸上得来终觉浅,绝知此事要躬行。找个实际数据跑一遍,比看十遍公式都管用。
公众号:蓝海资料掘金营,微信deep3321