告别玄学调参:用Python手把手实现一个卡尔曼滤波器(附AR序列预测完整代码)
告别玄学调参:用Python手把手实现一个卡尔曼滤波器(附AR序列预测完整代码)
卡尔曼滤波在工程实践中常被戏称为"玄学调参"——明明公式清晰,但面对状态转移矩阵、噪声协方差等参数时,工程师们往往陷入"调参看运气"的困境。本文将以AR(2)时间序列预测为例,用不到100行Python代码,带您拆解参数设置的底层逻辑,实现一个可复用的卡尔曼滤波器。
1. 从AR模型到状态空间:数学语言的工程转换
AR(2)模型定义为 $x_t = \phi_1 x_{t-1} + \phi_2 x_{t-2} + w_t$,其中$w_t$是过程噪声。要将其转换为卡尔曼滤波框架,关键在于构建状态向量。这里我们定义状态向量为:
state_vector = np.array([[x_t], [x_{t-1}]]) # 形状(2,1)
对应的状态转移矩阵F和过程噪声协方差Q为:
F = np.array([[phi1, phi2],
[1, 0]]) # 形状(2,2)
Q = np.array([[sigma_w**2, 0],
[0, 0]]) # 形状(2,2)
观测矩阵H设计为提取当前状态:
H = np.array([[1, 0]]) # 形状(1,2)
R = np.array([[sigma_v**2]]) # 观测噪声方差
工程经验 :当状态变量物理意义不同时(如同时包含位置和速度),Q的非对角线元素可能不为零。但在AR模型中,我们通常假设过程噪声只影响当前时刻状态。
2. 卡尔曼滤波器的Python实现
完整的滤波器类实现如下,关键步骤已添加注释:
class KalmanFilter:
def __init__(self, F, H, Q, R, P0, x0):
self.F = F # 状态转移矩阵
self.H = H # 观测矩阵
self.Q = Q # 过程噪声协方差
self.R = R # 观测噪声协方差
self.P = P0 # 初始估计协方差
self.x = x0 # 初始状态估计
def predict(self):
# 预测状态
self.x = self.F @ self.x
# 预测协方差
self.P = self.F @ self.P @ self.F.T + self.Q
return self.x
def update(self, z):
# 计算卡尔曼增益
S = self.H @ self.P @ self.H.T + self.R
K = self.P @ self.H.T @ np.linalg.inv(S)
# 更新状态估计
self.x = self.x + K @ (z - self.H @ self.x)
# 更新协方差估计
self.P = self.P - K @ self.H @ self.P
return self.x
常见坑点 :
- 矩阵维度不匹配:确保所有矩阵乘法维度相容,特别是当状态维度>1时
- 协方差矩阵非正定:理论上P、Q、R都应是半正定矩阵,实践中可添加小量单位矩阵保证数值稳定
3. 参数调优实战:噪声方差的影响
通过生成AR(2)序列并添加不同噪声,我们可以直观观察Q和R的影响:
| 场景 | Q设置 | R设置 | 滤波效果特征 |
|---|---|---|---|
| 低过程噪声 | [[0.01, 0],[0,0]] | [[1,0],[0,1]] | 跟踪滞后,对观测变化不敏感 |
| 高观测噪声 | [[1, 0],[0,0]] | [[10,0],[0,10]] | 预测平滑但偏差较大 |
| 理想匹配 | [[0.1, 0],[0,0]] | [[0.5,0],[0,0.5]] | 跟踪及时且平滑 |
调整策略:
- 先固定R,从小到大调整Q直到跟踪响应速度合适
- 固定Q,调整R直到滤波结果不过度震荡
- 使用创新序列(实际观测-预测观测)检验:理想情况应呈白噪声
4. 诊断与调试技巧
当滤波器表现异常时,可按以下流程排查:
-
检查协方差矩阵演化 :
print("预测协方差:", kf.P) print("后验协方差:", kf.P_post)正常情况下应收敛到稳态值
-
验证卡尔曼增益 :
K = kf.P @ kf.H.T @ np.linalg.inv(kf.H @ kf.P @ kf.H.T + kf.R) print("计算增益:", K)增益值应在[0,1]区间,过大或过小都表明参数设置不当
-
监测新息序列 :
innovation = z - kf.H @ kf.x_pred plt.plot(innovation) # 应近似白噪声
典型故障模式 :
- 发散:通常因Q设置过小或P0设置过小
- 过度平滑:R相对于Q设置过大
- 震荡:Q过大或R过小
5. 扩展应用:多维状态与非线性系统
对于更复杂的系统(如机器人定位),状态向量可能包含位置、速度等多维信息。此时状态转移矩阵变为:
F = np.array([[1, dt, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, dt],
[0, 0, 0, 1]]) # 匀速模型
对于非线性系统,可采用扩展卡尔曼滤波(EKF):
- 在预测步骤使用非线性函数f(x)
- 通过雅可比矩阵线性化计算协方差传播
def f(x):
return np.array([x[0] + x[1]*dt,
x[1],
x[2] + x[3]*dt,
x[3]])
# 计算雅可比矩阵
J = numerical_derivative(f, x_current)
P_pred = J @ P_current @ J.T + Q
实际项目中,建议先用本文的线性滤波器验证基础流程,再逐步过渡到复杂模型。完整的AR(2)预测代码已上传GitHub仓库(见文末链接),包含数据生成、滤波实现和可视化对比功能。
更多推荐
所有评论(0)