第4章:卡尔曼滤波入门:状态空间模型、预测与更新方程、一维卡尔曼滤波手推与代码实现
各位同学,欢迎来到传感器融合的核心章节。今天我们要啃的这块硬骨头,叫卡尔曼滤波。
说实话,我当年刚接触这玩意儿的时候,也是一头雾水。什么状态空间、协方差矩阵,看着就头大。但后来在项目中真正用起来才发现,它其实就是个「猜得准」的数学工具。你想想看,传感器数据有噪声,物理模型有误差,卡尔曼滤波就是那个能把两者揉在一起,给出最优估计的家伙。
4.1 状态空间模型:把问题装进数学框子里
要理解卡尔曼滤波,先得搞懂状态空间模型。说白了,就是把一个动态系统用数学语言描述出来。
一个典型的状态空间模型包含两个核心方程:
- 状态方程:描述系统状态如何随时间演变
- 观测方程:描述传感器如何观测到系统状态
拿一个最简单的例子来说——测量温度。假设你有一个恒温箱,温度会缓慢变化,但变化过程有随机扰动。那么状态方程可以写成:
x(k) = x(k-1) + w(k-1)
其中 x(k) 是当前时刻的真实温度,w(k-1) 是过程噪声(比如开门带来的温度波动)。
观测方程呢?你放了个温度传感器在里面:
z(k) = x(k) + v(k)
z(k) 是传感器读数,v(k) 是测量噪声(传感器本身的误差)。
嗯,这里要注意:过程噪声和测量噪声都是随机变量,我们假设它们服从高斯分布。这也是卡尔曼滤波能工作的前提条件之一。
核心思想:状态空间模型把「真实状态」和「观测数据」分开了。我们永远无法直接知道真实状态,只能通过观测去估计它。
4.2 预测与更新方程:卡尔曼滤波的两步走
卡尔曼滤波的精髓,就是两个步骤交替进行:预测(Predict) 和 更新(Update)。
我习惯用一个比喻来理解:你闭着眼睛往前走(预测),然后睁开眼睛看一眼(更新),再闭眼走一步,再睁眼看一眼……每一步都比单靠感觉或单靠眼睛更准。
4.2.1 预测步骤
预测步骤利用状态方程,从上一时刻的最优估计出发,推算出当前时刻的先验估计:
x̂(k|k-1) = x̂(k-1|k-1) + u(k-1) // 状态预测
P(k|k-1) = P(k-1|k-1) + Q // 误差协方差预测
这里:
- x̂(k|k-1) 表示用 k-1 时刻的信息预测 k 时刻的状态
- P 是误差协方差,衡量估计的不确定性
- Q 是过程噪声协方差
我在项目中遇到过一个问题:Q 值设得太小,滤波器反应迟钝;设得太大,又容易受噪声干扰。这个参数调起来,确实需要一些经验。
4.2.2 更新步骤
更新步骤利用观测数据,修正预测结果:
K(k) = P(k|k-1) / (P(k|k-1) + R) // 卡尔曼增益计算
x̂(k|k) = x̂(k|k-1) + K(k) * (z(k) - x̂(k|k-1)) // 状态更新
P(k|k) = (1 - K(k)) * P(k|k-1) // 误差协方差更新
这里 R 是测量噪声协方差。卡尔曼增益 K 是个关键角色——它决定了你更相信预测还是更相信测量。
个人经验:当 K 接近 1 时,说明测量噪声小,滤波器更信任传感器数据;当 K 接近 0 时,说明过程噪声小,滤波器更信任模型预测。我曾经在一个无人机项目中,因为忘记调整 R 值,导致滤波器完全无视 GPS 数据,差点炸机……
4.3 一维卡尔曼滤波手推:从公式到数字
光看公式不过瘾,我们来手推一个实际例子。假设我们要估计一个恒温箱的温度:
- 真实温度:25°C(但我们不知道)
- 初始估计:20°C,误差协方差 P = 5
- 过程噪声 Q = 0.1
- 测量噪声 R = 1
- 第一次测量值:z(1) = 24°C
第一步:预测
x̂(1|0) = x̂(0|0) = 20
P(1|0) = P(0|0) + Q = 5 + 0.1 = 5.1
第二步:更新
K(1) = 5.1 / (5.1 + 1) = 0.836
x̂(1|1) = 20 + 0.836 * (24 - 20) = 23.344
P(1|1) = (1 - 0.836) * 5.1 = 0.836
你看,经过一次更新,估计值从 20°C 跳到了 23.344°C,更接近真实值 25°C 了。误差协方差也从 5.1 降到了 0.836,说明不确定性大大减小。
避坑指南:我曾经在调试时发现,如果初始估计偏差太大,卡尔曼滤波需要好几个周期才能收敛。所以初始值尽量给得靠谱一些,别太离谱。
4.4 代码实现:把理论跑起来
理论说完了,咱们上代码。下面是一个完整的一维卡尔曼滤波实现,用 Python 写的:
import numpy as np
import matplotlib.pyplot as plt
class KalmanFilter1D:
def __init__(self, x0, P0, Q, R):
self.x = x0 # 状态估计
self.P = P0 # 误差协方差
self.Q = Q # 过程噪声
self.R = R # 测量噪声
def predict(self):
# 预测步骤
self.P = self.P + self.Q
# 状态预测(这里假设系统是恒定的,所以 x 不变)
return self.x
def update(self, z):
# 更新步骤
K = self.P / (self.P + self.R)
self.x = self.x + K * (z - self.x)
self.P = (1 - K) * self.P
return self.x, K
# 模拟数据
true_temp = 25.0
measurements = true_temp + np.random.randn(50) * 2 # 测量噪声标准差2
# 初始化滤波器
kf = KalmanFilter1D(x0=20.0, P0=5.0, Q=0.1, R=4.0)
# 运行滤波
estimates = []
for z in measurements:
kf.predict()
est, _ = kf.update(z)
estimates.append(est)
# 绘制结果
plt.figure(figsize=(10, 6))
plt.plot(measurements, 'r.', label='测量值', alpha=0.5)
plt.plot(estimates, 'b-', label='滤波估计', linewidth=2)
plt.axhline(y=true_temp, color='g', linestyle='--', label='真实值')
plt.legend()
plt.xlabel('时间步')
plt.ylabel('温度 (°C)')
plt.title('一维卡尔曼滤波效果')
plt.grid(True)
plt.show()
这段代码我用了很多次,每次给学生讲的时候都会强调:Q 和 R 的比值决定了滤波器的行为。你可以试着把 Q 改成 10,看看滤波器会变成什么样子——估计值会剧烈抖动,因为你觉得过程噪声很大,模型不可靠。
核心要点总结:
- 卡尔曼滤波 = 预测 + 更新,两步循环
- 卡尔曼增益 K 是核心,平衡模型与测量
- Q 和 R 需要根据实际系统调参
- 一维情况是理解多维卡尔曼滤波的基础
说实话,卡尔曼滤波入门不难,难的是在实际系统中调好参数。我建议你先从一维情况开始,把每个公式手推一遍,再用代码跑一遍。等彻底理解了,再去看多维的情况,会轻松很多。
最后说一句:别怕公式。你把它当成一个「猜数字」的游戏,每次猜完用测量值修正一下,慢慢就上手了。我在实际项目中,从温度估计到姿态解算,从 GPS 定位到视觉跟踪,卡尔曼滤波无处不在。掌握了它,你的传感器融合之路就迈出了最关键的一步。
可可精品资料库