卡尔曼滤波概要

做啥用的

我们想知道一个系统某个时刻的真实状态,手段往往有多种,比如最常见的两种:

  1. 用合适的传感器测量出来;
  2. 根据上一时刻的状态用数学模型推算出来;

那么,应该相信哪一种办法得到的值呢?

怎么做的

先把上述概念实例化一下,对于一个小车,我们想知道它在当前时刻t距离障碍物的真实距离xt。我们用两种办法:

  1. 测距雷达测得的测量值mt
  2. 根据上一时刻的车速vt1上一时刻的最终估计值pt1得到的预测值nt,即有nt=pt1vt1

然后,想办法平衡mtnt,得到本时刻的最终估计值pt。卡尔曼的做法是,将所有变量都视作一个正态的概率分布,而不是一个简单的标量值。即有

  1. 自变量vt1 -> vt1(μv,σv),数据来自于传感器,可以认为均值和方差已知。
  2. 自变量mt -> mt(μm,σm),数据来自于传感器,可以认为均值和方差已知。
  3. 中间变量nt -> nt(μn,σn)
  4. 结果变量p -> p(μp,σp)

然后,按照经典方式,分两步计算来得到pt

Step1 预测

首先因为

nt=pt1vt1

所以,可以得到nt的均值和方差分别为

μtn=μt1pμt1vσtn=σt1p+σt1v

Step2 融合/更新/校正

平衡mtnt这两个概率分布的方式是概率的乘法,即有

μtp=μtnσtm+μtmσtnσtm+σtnσtp=σtmσtnσtm+σtn

由于正态分布在变量取均值是概率最大,所以μtp即为t时刻的最终估计值pt。而σtp则用于计算下时刻的最终估计值。

代码实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(1, 100, 100) # 在1~100s内采样100次
a = 0.5 # 加速度值
position = (a * t ** 2) / 2

position_noise = position + np.random.normal(0, 120, size=(t.shape[0])) # 模拟生成GPS位置测量数据(带噪声)

plt.plot(t, position, label='truth position')
plt.plot(t, position_noise, label='only use measured position')

# ---------------卡尔曼滤波----------------
# 初始的估计导弹的位置就直接用GPS测量的位置
predicts = [position_noise[0]]
position_predict = predicts[0]

predict_var = 0
odo_var = 120 ** 2 # 这是我们自己设定的位置测量仪器的方差,越大则测量值占比越低
v_std = 50 # 测量仪器的方差(这个方差在现实生活中是需要我们进行传感器标定才能算出来的,可搜Allan方差标定)
for i in range(1, t.shape[0]):
dv = (position[i] - position[i - 1]) + np.random.normal(0, 50) # 模拟从IMU读取出的速度
position_predict = position_predict + dv # 利用上个时刻的位置和速度预测当前位置
predict_var += v_std ** 2 # 更新预测数据的方差
# 下面是Kalman滤波
position_predict = position_predict * odo_var / (predict_var + odo_var) + position_noise[i] * predict_var / (
predict_var + odo_var)
predict_var = (predict_var * odo_var) / (predict_var + odo_var) ** 2
predicts.append(position_predict)

plt.plot(t, predicts, label='kalman filtered position')

plt.legend()
plt.show()

参考链接

  1. https://zhuanlan.zhihu.com/p/77327349