先记住一句话:卡尔曼滤波就是通过“预测+测量反馈”来动态修正估计值的算法,像“不断纠错的GPS”。
假设你在用GPS测一辆行驶中小车的位置,但GPS有噪声(误差)。同时,小车的速度传感器也有误差。如何融合这两种不完美的数据,得到更精准的位置估计?
这就是卡尔曼滤波要解决的问题。
卡尔曼滤波分两步循环执行:
关键:动态调整对“预测”和“测量”的信任权重(即卡尔曼增益)
假设系统状态是位置和速度(二维向量),定义以下变量:
• 状态向量(x):[位置, 速度]
• 预测矩阵(F):描述状态如何变化(如匀速运动)
• 控制矩阵(B):外部控制输入的影响(如油门)
• 测量矩阵(H):如何从状态得到测量值(如GPS只测位置)
• 过程噪声协方差(Q):预测的不确定性
• 测量噪声协方差(R):测量的不确定性
• 卡尔曼增益(K):决定信任预测还是测量
• 预测状态:x' = F·x + B·u
(用运动模型预测下一时刻的位置和速度)
• 预测协方差:P' = F·P·F^T + Q
(预测的不确定性增加,Q越大表示模型越不可靠)
• 计算卡尔曼增益:
K = P'·H^T / (H·P'·H^T + R)
(R越小,K越大,越信任测量值)
• 修正状态:
x = x' + K·(z - H·x')
(用测量值z调整预测值x’)
• 更新协方差:
P = (I - K·H)·P'
(更新后的不确定性降低)
• 卡尔曼增益K:像一个“滑动条”
• 如果测量噪声R很小(测量准),K趋近1 → 更相信测量
• 如果预测噪声Q很大(模型不准),K趋近0 → 更相信预测
• 协方差P:表示估计的不确定性,随着预测和更新动态变化
卡尔曼滤波通过动态平衡模型预测和实际测量,在噪声中提炼真实状态。
(就像用天气预报和实际温度计读数,不断调整对明天温度的估计)
我们用一个具体案例结合数学推导,彻底讲透卡尔曼滤波的底层逻辑。
• 真实状态:小车位置 p p p,速度 v v v
• 传感器:
• 速度计:测量速度 v v v,但有噪声(误差方差 σ v 2 = 0.1 \sigma_v^2 = 0.1 σv2=0.1)
• GPS:测量位置 p p p,噪声更大(误差方差 σ p 2 = 1 \sigma_p^2 = 1 σp2=1)
• 目标:融合速度和位置测量,估计更准确的位置和速度。
• 状态向量:
x k = [ p k v k ] \mathbf{x}_k = \begin{bmatrix} p_k \\ v_k \end{bmatrix} xk=[pkvk]
包含位置 p k p_k pk和速度 v k v_k vk。
• 状态方程(预测模型):
假设匀速运动(无外力),时间步长 Δ t = 1 \Delta t = 1 Δt=1:
x k = F x k − 1 + w k \mathbf{x}_k = \mathbf{F} \mathbf{x}_{k-1} + \mathbf{w}_k xk=Fxk−1+wk
其中:
F = [ 1 Δ t 0 1 ] = [ 1 1 0 1 ] \mathbf{F} = \begin{bmatrix} 1 & \Delta t \\ 0 & 1 \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix} F=[10Δt1]=[1011]
w k \mathbf{w}_k wk是过程噪声(协方差矩阵 Q \mathbf{Q} Q),表示模型不确定性(如风速影响)。
• 观测方程:
假设同时测量位置和速度:
z k = H x k + v k \mathbf{z}_k = \mathbf{H} \mathbf{x}_k + \mathbf{v}_k zk=Hxk+vk
其中:
H = [ 1 0 0 1 ] , v k ∼ N ( 0 , R ) \mathbf{H} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}, \quad \mathbf{v}_k \sim \mathcal{N}(0, \mathbf{R}) H=[1001],vk∼N(0,R)
R \mathbf{R} R是测量噪声协方差矩阵,对角线元素为各传感器的误差方差。
• 预测状态:
x ^ k − = F x ^ k − 1 \mathbf{\hat{x}}^-_k = \mathbf{F} \mathbf{\hat{x}}_{k-1} x^k−=Fx^k−1
用上一时刻的最优估计 x ^ k − 1 \mathbf{\hat{x}}_{k-1} x^k−1预测当前状态。
• 预测协方差:
P k − = F P k − 1 F ⊤ + Q \mathbf{P}^-_k = \mathbf{F} \mathbf{P}_{k-1} \mathbf{F}^\top + \mathbf{Q} Pk−=FPk−1F⊤+Q
表示预测的不确定性(误差扩散)。
其中 Q = [ σ p 2 0 0 σ v 2 ] \mathbf{Q} = \begin{bmatrix} \sigma_p^2 & 0 \\ 0 & \sigma_v^2 \end{bmatrix} Q=[σp200σv2],这里假设 σ p 2 = 0.1 \sigma_p^2 = 0.1 σp2=0.1, σ v 2 = 0.1 \sigma_v^2 = 0.1 σv2=0.1。
• 卡尔曼增益:
K k = P k − H ⊤ ( H P k − H ⊤ + R ) − 1 \mathbf{K}_k = \mathbf{P}^-_k \mathbf{H}^\top \left( \mathbf{H} \mathbf{P}^-_k \mathbf{H}^\top + \mathbf{R} \right)^{-1} Kk=Pk−H⊤(HPk−H⊤+R)−1
核心公式!调整对预测和测量的信任权重。
其中 R = [ 1 0 0 0.1 ] \mathbf{R} = \begin{bmatrix} 1 & 0 \\ 0 & 0.1 \end{bmatrix} R=[1000.1],GPS噪声大,速度计噪声小。
• 状态修正:
x ^ k = x ^ k − + K k ( z k − H x ^ k − ) \mathbf{\hat{x}}_k = \mathbf{\hat{x}}^-_k + \mathbf{K}_k \left( \mathbf{z}_k - \mathbf{H} \mathbf{\hat{x}}^-_k \right) x^k=x^k−+Kk(zk−Hx^k−)
用测量残差 z k − H x ^ k − \mathbf{z}_k - \mathbf{H} \mathbf{\hat{x}}^-_k zk−Hx^k−修正预测值。
• 协方差更新:
P k = ( I − K k H ) P k − \mathbf{P}_k = (\mathbf{I} - \mathbf{K}_k \mathbf{H}) \mathbf{P}^-_k Pk=(I−KkH)Pk−
更新后的不确定性降低。
假设初始状态和协方差:
• 初始位置 p 0 = 0 p_0 = 0 p0=0,速度 v 0 = 1 m/s v_0 = 1 \, \text{m/s} v0=1m/s,即
x ^ 0 = [ 0 1 ] , P 0 = [ 0 0 0 0 ] \mathbf{\hat{x}}_0 = \begin{bmatrix} 0 \\ 1 \end{bmatrix}, \quad \mathbf{P}_0 = \begin{bmatrix} 0 & 0 \\ 0 & 0 \end{bmatrix} x^0=[01],P0=[0000]
初始完全信任初始值。
预测阶段:
x ^ 1 − = F x ^ 0 = [ 1 × 0 + 1 × 1 0 × 0 + 1 × 1 ] = [ 1 1 ] \mathbf{\hat{x}}^-_1 = \mathbf{F} \mathbf{\hat{x}}_0 = \begin{bmatrix} 1 \times 0 + 1 \times 1 \\ 0 \times 0 + 1 \times 1 \end{bmatrix} = \begin{bmatrix} 1 \\ 1 \end{bmatrix} x^1−=Fx^0=[1×0+1×10×0+1×1]=[11]
P 1 − = F P 0 F ⊤ + Q = Q = [ 0.1 0 0 0.1 ] \mathbf{P}^-_1 = \mathbf{F} \mathbf{P}_0 \mathbf{F}^\top + \mathbf{Q} = \mathbf{Q} = \begin{bmatrix} 0.1 & 0 \\ 0 & 0.1 \end{bmatrix} P1−=FP0F⊤+Q=Q=[0.1000.1]
测量值:
GPS测得位置 z p = 1.2 z_p = 1.2 zp=1.2,速度计测得速度 z v = 0.9 z_v = 0.9 zv=0.9,即
z 1 = [ 1.2 0.9 ] \mathbf{z}_1 = \begin{bmatrix} 1.2 \\ 0.9 \end{bmatrix} z1=[1.20.9]
卡尔曼增益:
K 1 = P 1 − H ⊤ ( H P 1 − H ⊤ + R ) − 1 \mathbf{K}_1 = \mathbf{P}^-_1 \mathbf{H}^\top \left( \mathbf{H} \mathbf{P}^-_1 \mathbf{H}^\top + \mathbf{R} \right)^{-1} K1=P1−H⊤(HP1−H⊤+R)−1
代入数值:
K 1 = [ 0.1 0 0 0.1 ] [ 1 0 0 1 ] ( [ 1 0 0 1 ] [ 0.1 0 0 0.1 ] [ 1 0 0 1 ] + [ 1 0 0 0.1 ] ) − 1 \mathbf{K}_1 = \begin{bmatrix} 0.1 & 0 \\ 0 & 0.1 \end{bmatrix} \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \left( \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \begin{bmatrix} 0.1 & 0 \\ 0 & 0.1 \end{bmatrix} \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} + \begin{bmatrix} 1 & 0 \\ 0 & 0.1 \end{bmatrix} \right)^{-1} K1=[0.1000.1][1001]([1001][0.1000.1][1001]+[1000.1])−1
计算得:
K 1 = [ 0.0909 0 0 0.5 ] \mathbf{K}_1 = \begin{bmatrix} 0.0909 & 0 \\ 0 & 0.5 \end{bmatrix} K1=[0.0909000.5]
关键观察:速度的卡尔曼增益更大(0.5),因为速度计噪声更小(更可信)。
状态修正:
x ^ 1 = [ 1 1 ] + [ 0.0909 0 0 0.5 ] ( [ 1.2 0.9 ] − [ 1 1 ] ) = [ 1 + 0.0909 × 0.2 1 + 0.5 × ( − 0.1 ) ] = [ 1.018 0.95 ] \mathbf{\hat{x}}_1 = \begin{bmatrix} 1 \\ 1 \end{bmatrix} + \begin{bmatrix} 0.0909 & 0 \\ 0 & 0.5 \end{bmatrix} \left( \begin{bmatrix} 1.2 \\ 0.9 \end{bmatrix} - \begin{bmatrix} 1 \\ 1 \end{bmatrix} \right) = \begin{bmatrix} 1 + 0.0909 \times 0.2 \\ 1 + 0.5 \times (-0.1) \end{bmatrix} = \begin{bmatrix} 1.018 \\ 0.95 \end{bmatrix} x^1=[11]+[0.0909000.5]([1.20.9]−[11])=[1+0.0909×0.21+0.5×(−0.1)]=[1.0180.95]
协方差更新:
P 1 = ( I − K 1 H ) P 1 − = [ 0.909 0 0 0.5 ] [ 0.1 0 0 0.1 ] = [ 0.0909 0 0 0.05 ] \mathbf{P}_1 = (\mathbf{I} - \mathbf{K}_1 \mathbf{H}) \mathbf{P}^-_1 = \begin{bmatrix} 0.909 & 0 \\ 0 & 0.5 \end{bmatrix} \begin{bmatrix} 0.1 & 0 \\ 0 & 0.1 \end{bmatrix} = \begin{bmatrix} 0.0909 & 0 \\ 0 & 0.05 \end{bmatrix} P1=(I−K1H)P1−=[0.909000.5][0.1000.1]=[0.0909000.05]
结果:协方差减小,估计更准确。
• 卡尔曼增益动态分配权重:
对速度的修正权重更大(速度计更准),对位置的修正权重较小(GPS噪声大)。
• 协方差迭代收敛:
随着时间推移,协方差矩阵 P \mathbf{P} P会逐渐减小,系统趋于稳定。
import numpy as np
# 初始化
x = np.array([[0], [1]]) # 初始状态 [位置, 速度]
P = np.diag([0, 0]) # 初始协方差
F = np.array([[1, 1], [0, 1]])# 状态转移矩阵
Q = np.diag([0.1, 0.1]) # 过程噪声
H = np.eye(2) # 观测矩阵
R = np.diag([1, 0.1]) # 测量噪声
# 迭代过程
for z in measurements:
# 预测
x = F @ x
P = F @ P @ F.T + Q
# 更新
K = P @ H.T @ np.linalg.inv(H @ P @ H.T + R)
x = x + K @ (z - H @ x)
P = (np.eye(2) - K @ H) @ P