前言
在上一篇文章中,我们已经对卡尔曼滤波的Filter问题进行了数学推导求解【机器学习】卡尔曼滤波原理推导-优快云博客,得到了卡尔曼黄金五式。接下来,我们就案例而言,来看看卡尔曼滤波如何应用到生活当中。
案例引入
小佳从拼夕夕砍来了一辆小汽车,这车除了能开啥子功能都没有。每次小佳都是以一定的速度去开车,并且无时无刻都在想着当前的位置信息是多少。作为一个大聪明文科生,小佳根本不知道运动学公式。于是他买了一个GPS定位器,实时返回位置信息。但没想到这个拼夕夕9.9包邮的定位器不准。人都被创飞好几米远了还说离前方人员还有几百米。为了解决这个问题,小佳学习物理,打算自己计算位置信息。可是,他家住在山旮旯附近,路面凹凸不平;狭管效应严重,天天七级台风能把人吹成大聪明,车辆的运动状态根本就不满足严格的运动学公式。智商堪比爱因斯坦的小佳决定综合GPS,利用卡尔曼滤波,对小车的运动轨迹实现较为精准的预测。
不难看到,预测的难度实际上就是在于,即便小佳每次都以一定的油门行驶,但是路面的凹凸不平,和能把人吹成大聪明的7级太分会对小车的实际速度受到随机扰动,所以即便是我们知道在 t − 1 t-1 t−1时刻的位置信息,也很难准确估计出 t t t时刻的位置信息。而对于9.9包邮的GPS又存在一定的误差。
小佳要计算的位置信息包括当前的所在的位置和当前的速度是多少。我们假设小佳做的是匀速直线运动,那么依据运动学公式
s
=
s
0
+
v
0
t
s=s_0+v_{0}t
s=s0+v0t(假设每1秒计算一次,那么t=1,下文就不不是出来了),可以得到下一个时刻位置
s
t
=
s
t
−
1
+
v
t
−
1
s_t=s_{t-1}+v_{t-1}
st=st−1+vt−1
我们用t表示当前时刻,t-1表示上一个时刻,也就是说当前的位置
s
t
s_t
st就等于上一个时刻的位置
s
t
−
1
s_{t-1}
st−1,加上上一个时刻的速度
v
t
=
v
t
−
1
v_t=v_{t-1}
vt=vt−1。
而对于速度信息,很显然因为是匀速直线运动,所以直接就等于
v
t
−
1
v_{t-1}
vt−1。我们前面说过,这只是一个理想状态下的情况,我们是存在一定的随机扰动项的。也就是说,最终实际上我们可以写成
s
t
=
s
t
−
1
+
v
t
−
1
+
u
1
;
v
t
=
v
t
−
1
+
u
2
;
(1)
s_t=s_{t-1}+v_{t-1}+u_1; \\v_t=v_{t-1}+u_2;\tag{1}
st=st−1+vt−1+u1;vt=vt−1+u2;(1)
其中
u
1
∼
N
(
0
,
Q
1
)
,
u
2
∼
N
(
0
,
Q
2
)
u_1 \sim N(0,Q_1),u_2 \sim N(0,Q_2)
u1∼N(0,Q1),u2∼N(0,Q2),也就是在自然界中,我们认为这些随机误差是服从正态分布的。所以每一项都加上一个正态分布。
同样的,对于GPS而言,在没有误差的情况下,设GPS定位出来的信息位置用
x
x
x表示,速度用
y
y
y表示。理论上t时刻的位置信息就直接等于
x
t
=
s
t
y
t
=
v
t
x_t=s_t \\y_t=v_t
xt=styt=vt
但我们说过,GPS是存在误差的,所以
x
t
=
s
t
+
ω
1
y
t
=
v
t
+
ω
2
(2)
x_t=s_t+\omega_1 \\y_t=v_t+\omega_2\tag{2}
xt=st+ω1yt=vt+ω2(2)
其中
ω
1
∼
N
(
0
,
R
1
)
,
ω
2
∼
N
(
0
,
R
2
)
\omega_1 \sim N(0,R_1),\omega_2 \sim N(0,R_2)
ω1∼N(0,R1),ω2∼N(0,R2)。我们依然认为误差服从正态分布。
那么,依据卡尔曼滤波,我们需要将其转化成矩阵相乘的形式。对于公式1
(
s
t
v
t
)
=
(
1
1
0
1
)
(
s
t
−
1
v
t
−
1
)
+
(
u
1
u
2
)
\begin{pmatrix} s_t \\ v_t \end{pmatrix}= \begin{pmatrix} 1 & 1 \\ 0 & 1 \end{pmatrix} \begin{pmatrix} s_{t-1} \\ v_{t-1} \end{pmatrix} +\begin{pmatrix} u_1 \\ u_2 \end{pmatrix}
(stvt)=(1011)(st−1vt−1)+(u1u2)
为了与我们前面推导的符号一致,我们用其他符号代换,在上面这个公式中,第一个括号用
z
t
z_t
zt代替,第二个括号用
A
A
A代替,第三个括号用
z
t
−
1
z_{t-1}
zt−1代替(要与z时刻一致符号),第四个括号直接用
u
u
u代替,所以得到
z
t
=
A
z
t
−
1
+
u
z_t=Az_{t-1}+u
zt=Azt−1+u
其中
u
∼
N
(
0
,
Q
)
u \sim N(0,Q)
u∼N(0,Q)
对于公式2我们同理
(
x
t
y
t
)
=
(
1
0
0
1
)
(
s
t
v
t
)
+
(
ω
1
ω
2
)
\begin{pmatrix} x_t \\ y_t \end{pmatrix}= \begin{pmatrix} 1 & 0\\ 0 & 1 \end{pmatrix} \begin{pmatrix} s_{t} \\ v_{t} \end{pmatrix} +\begin{pmatrix} \omega_1 \\ \omega_2 \end{pmatrix}
(xtyt)=(1001)(stvt)+(ω1ω2)
用其他符号代换,第一个括号用
x
t
x_t
xt代替,第二个括号用
C
C
C代替,第三个括号用
z
t
z_t
zt代替(要与前面一致符号),第四个用
v
v
v代替,得
x
t
=
C
z
t
+
v
x_t=Cz_t+v
xt=Czt+v
其中
v
∼
N
(
0
,
R
)
v \sim N(0,R)
v∼N(0,R)
所以我们就得到线性关系式
z
t
=
A
z
t
−
1
+
u
x
t
=
C
z
t
+
v
(3)
z_t=Az_{t-1}+u \\x_t=Cz_t+v\tag{3}
zt=Azt−1+uxt=Czt+v(3)
绘制成卡尔曼滤波的模型图,如图所示
即在第一个时刻,我们能从z1的值得到x1和z2。然后不断地迭代下去,最终得到zt。我们的目的就是计算出前面t个时刻的小佳汽车的实际位置信息。
我们不妨想想,如何通过GPS和运动学公式去获取更加精准的位置?从频率派的角度而言。很显然,我们可以用加权平均的思想,也就是实际的位置信息P
P
=
K
∗
x
t
+
(
1
−
K
)
∗
z
t
P=K*x_t+(1-K)*z_t
P=K∗xt+(1−K)∗zt
K是一个系数,里面的
K
∈
[
0
,
1
]
K \in [0,1]
K∈[0,1],如果我们认为GPS里面的误差更小,更加可信,那么K的值就大一些。那如果我们认为运动学的误差更小,更可信,自然令K小一些。我们再将其转化一些
P
=
K
∗
x
t
+
z
t
−
K
z
t
=
z
t
+
K
(
x
t
−
z
t
)
P=K*x_t+z_t-Kz_t=z_t+K(x_t-z_t)
P=K∗xt+zt−Kzt=zt+K(xt−zt)
实际上,这里的K,被称为卡尔曼增益系数。
问题解决
如果看这一段看得云里雾里,看下一段的案例流程也许就懂了
本篇文章将从贝叶斯派的角度出发去解决这个问题,因为我们前面的推导就是从贝叶斯派的角度去推导。
对于贝叶斯派而言,上面的问题实际上就是卡尔曼滤波的Filter问题。也就是求出 P ( z t ∣ x 1 , ⋯ , x t ) P(z_t|x_1,\cdots,x_t) P(zt∣x1,⋯,xt)。
就是在给定了公式3的关系式和GPS前t个时刻的观测值 x x x,去求出 z t z_t zt。如果你看过上一篇的推导,你会发现 P ( z t ∣ x 1 , ⋯ , x t ) P(z_t|x_1,\cdots,x_t) P(zt∣x1,⋯,xt)是可以迭代求解的,比如只要我们求出 P ( z t − 1 ∣ x 1 , ⋯ , x t − 1 ) P(z_{t-1}|x_1,\cdots,x_{t-1}) P(zt−1∣x1,⋯,xt−1),就可以求出 P ( z t ∣ x 1 , ⋯ , x t ) P(z_t|x_1,\cdots,x_t) P(zt∣x1,⋯,xt)。所以最终不断递推到 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1∣x1)。
所以我们的只需要求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1∣x1),就可以求出 P ( z 2 ∣ x 1 , x 2 ) , . . . . . . , P ( z t ∣ x 1 , ⋯ , x t ) P(z_2|x_1,x_2),......,P(z_t|x_1,\cdots,x_t) P(z2∣x1,x2),......,P(zt∣x1,⋯,xt)。保留我们每一个时刻的值,就是我们最终的小车运动的位置信息轨迹。
那么如何求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1∣x1),如果你看过上一篇的推导,你会发现要求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1∣x1),就要先求出 P ( z 1 ∣ x 0 ) P(z_1|x_0) P(z1∣x0);要求出 P ( z 2 ∣ x 1 , x 2 ) P(z_2|x_1,x_2) P(z2∣x1,x2),就要求出 P ( z t ∣ x 1 ) P(z_t|x_1) P(zt∣x1);实际上,要求出 P ( z t ∣ x 1 , ⋯ , x t ) P(z_t|x_1,\cdots,x_t) P(zt∣x1,⋯,xt),就要求出优先求出 P ( z t ∣ x 1 , ⋯ , x t − 1 ) P(z_t|x_1,\cdots,x_{t-1}) P(zt∣x1,⋯,xt−1)。
它们都是符合正态分布的。对于正态分布,我们只需要知道它们的期望和协方差矩阵即可,因为期望是概率最大的位置,我们可以将其当作就是卡尔曼滤波之后的值。对于 P ( z t ∣ x 1 , ⋯ , x t − 1 ) P(z_t|x_1,\cdots,x_{t-1}) P(zt∣x1,⋯,xt−1),我们就设定它的期望和协方差为 μ ˉ t , Σ ˉ t \bar\mu_t,\bar\Sigma_t μˉt,Σˉt。对于 P ( z t ∣ x 1 , ⋯ , x t ) P(z_t|x_1,\cdots,x_{t}) P(zt∣x1,⋯,xt),我们设定它的期望和协方差矩阵为 μ ^ t , Σ ^ t \hat \mu_t,\hat\Sigma_{t} μ^t,Σ^t
那么按照流程,我们先求解
P
(
z
t
∣
x
1
,
⋯
,
x
t
−
1
)
P(z_t|x_1,\cdots,x_{t-1})
P(zt∣x1,⋯,xt−1),在上一篇文章我们推导出来过,在这里直接给出它的参数公式
μ
ˉ
t
=
A
μ
^
t
−
1
Σ
ˉ
t
=
A
Σ
^
t
−
1
A
T
+
Q
\begin{equation} \begin{aligned} \bar\mu_t=&A\hat\mu_{t-1} \\\bar\Sigma_t=&A\hat\Sigma_{t-1}A^T+Q\nonumber \nonumber \end{aligned} \end{equation}
μˉt=Σˉt=Aμ^t−1AΣ^t−1AT+Q
得到
P
(
z
t
∣
x
1
,
⋯
,
x
t
−
1
)
P(z_t|x_1,\cdots,x_{t-1})
P(zt∣x1,⋯,xt−1),再计算
P
(
z
t
∣
x
1
,
⋯
,
x
t
)
P(z_t|x_1,\cdots,x_{t})
P(zt∣x1,⋯,xt)
K
=
Σ
ˉ
t
C
T
(
C
Σ
ˉ
t
C
T
+
R
)
−
1
μ
^
t
=
μ
ˉ
t
+
K
(
x
t
−
C
μ
ˉ
t
)
Σ
^
t
=
(
I
−
K
C
)
Σ
ˉ
t
K=\bar\Sigma_tC^T\left(C\bar\Sigma_{t}C^T+R\right)^{-1} \\\hat\mu_{t}=\bar \mu_t+K(x_t-C\bar\mu_t)\nonumber \\\hat\Sigma_t=(I-KC)\bar\Sigma_{t}
K=ΣˉtCT(CΣˉtCT+R)−1μ^t=μˉt+K(xt−Cμˉt)Σ^t=(I−KC)Σˉt
我们前面说过,我们是要递归求解的,也就是不断递归到第一个时刻
P
(
z
1
∣
x
1
)
P(z_1|x_1)
P(z1∣x1),我们来看看对于第一个时刻
P
(
z
1
∣
x
0
)
P(z_1|x_0)
P(z1∣x0)的参数求解
μ
ˉ
1
=
A
μ
^
0
Σ
ˉ
1
=
A
Σ
^
0
A
T
+
Q
\begin{equation} \begin{aligned} \bar\mu_1=&A\hat\mu_{0} \\\bar\Sigma_1=&A\hat\Sigma_{0}A^T+Q\nonumber \nonumber \end{aligned} \end{equation}
μˉ1=Σˉ1=Aμ^0AΣ^0AT+Q
这里的
μ
^
0
\hat \mu_0
μ^0和
Σ
^
0
\hat\Sigma_0
Σ^0是0时刻的值,我们是没有的,因此,我们需要给定
μ
ˉ
1
\bar\mu_1
μˉ1和
Σ
ˉ
1
\bar\Sigma_1
Σˉ1的值
案例流程
流程:
①预测,计算
P
(
z
t
∣
x
1
,
⋯
,
x
t
−
1
)
P(z_t|x_1,\cdots,x_{t-1})
P(zt∣x1,⋯,xt−1)对应的参数
μ
ˉ
t
=
A
μ
^
t
−
1
Σ
ˉ
t
=
A
Σ
^
t
−
1
A
T
+
Q
\begin{equation} \begin{aligned} \bar\mu_t=&A\hat\mu_{t-1} \\\bar\Sigma_t=&A\hat\Sigma_{t-1}A^T+Q\nonumber \nonumber \end{aligned} \end{equation}
μˉt=Σˉt=Aμ^t−1AΣ^t−1AT+Q
②更新,计算
P
(
z
t
∣
x
1
,
⋯
,
x
t
)
P(z_t|x_1,\cdots,x_{t})
P(zt∣x1,⋯,xt)对应的参数
K
=
Σ
ˉ
t
C
T
(
C
Σ
ˉ
t
C
T
+
R
)
−
1
μ
^
t
=
μ
ˉ
t
+
K
(
x
t
−
C
μ
ˉ
t
)
Σ
^
t
=
(
I
−
K
C
)
Σ
ˉ
t
K=\bar\Sigma_tC^T\left(C\bar\Sigma_{t}C^T+R\right)^{-1} \\\hat\mu_{t}=\bar \mu_t+K(x_t-C\bar\mu_t)\nonumber \\\hat\Sigma_t=(I-KC)\bar\Sigma_{t}
K=ΣˉtCT(CΣˉtCT+R)−1μ^t=μˉt+K(xt−Cμˉt)Σ^t=(I−KC)Σˉt
不断迭代①、②,直到算到t个时刻。
下面我们来举个例子
时刻1:
假定我们在时刻1,我们需要计算 P ( z 1 ∣ x 0 ) P(z_1|x_0) P(z1∣x0),因为 x 0 x_0 x0是不存在的,实际上就是计算 P ( z 1 ) P(z_1) P(z1),这个初始的值,需要我们自己给(这一步被称为预测)
假设
μ
ˉ
1
=
1
,
Σ
ˉ
1
=
2
(4)
\bar \mu_1=1,\bar\Sigma_1=2\tag{4}
μˉ1=1,Σˉ1=2(4)
得到了期望和方差。接下来计算
P
(
z
1
∣
x
1
)
P(z_1|x_1)
P(z1∣x1),来看公式
K
=
Σ
ˉ
1
C
T
(
C
Σ
ˉ
1
C
T
+
R
)
−
1
μ
^
1
=
μ
ˉ
1
+
K
(
x
1
−
C
μ
ˉ
1
)
Σ
^
1
=
(
I
−
K
C
)
Σ
ˉ
1
K=\bar\Sigma_1C^T\left(C\bar\Sigma_{1}C^T+R\right)^{-1} \\\hat\mu_{1}=\bar \mu_1+K(x_1-C\bar\mu_1)\nonumber \\\hat\Sigma_1=(I-KC)\bar\Sigma_{1}
K=Σˉ1CT(CΣˉ1CT+R)−1μ^1=μˉ1+K(x1−Cμˉ1)Σ^1=(I−KC)Σˉ1
我们将公式4得到的值代进里面(这一步被称为更新)
K
=
2
∗
C
T
(
C
∗
2
∗
C
T
+
R
)
−
1
=
3
μ
^
1
=
1.5
+
3
∗
(
x
1
−
C
∗
1.5
)
=
4
Σ
^
1
=
(
I
−
3
∗
C
)
∗
2
=
4
(5)
K=2*C^T\left(C*2*C^T+R\right)^{-1}=3 \\\hat\mu_{1}=1.5+3*(x_1-C*1.5)=4 \\\hat\Sigma_1=(I-3*C)*2=4\tag{5}
K=2∗CT(C∗2∗CT+R)−1=3μ^1=1.5+3∗(x1−C∗1.5)=4Σ^1=(I−3∗C)∗2=4(5)
好现在得到了
P
(
z
1
∣
x
1
)
P(z_1|x_1)
P(z1∣x1),里面对应期望和协方差矩阵就是我们需要的东西。里面的期望我们认为是概率最大的地方,所以我们认为这个期望就是我们优化后的值
时刻2:
现在计算第2个时刻.先计算
P
(
z
2
∣
x
1
)
P(z_2|x_1)
P(z2∣x1)
μ
ˉ
2
=
A
μ
^
1
Σ
ˉ
2
=
A
Σ
^
1
A
T
+
Q
\begin{equation} \begin{aligned} \bar\mu_2=&A\hat\mu_{1} \\\bar\Sigma_2=&A\hat\Sigma_{1}A^T+Q\nonumber \nonumber \end{aligned} \end{equation}
μˉ2=Σˉ2=Aμ^1AΣ^1AT+Q
里面的
μ
^
1
,
Σ
^
1
\hat\mu_{1},\hat\Sigma_{1}
μ^1,Σ^1就是我们公式5中计算出来的,将其带进去,得到
μ
ˉ
2
=
A
μ
^
1
=
A
∗
4
=
10
Σ
ˉ
2
=
A
Σ
^
1
A
T
+
Q
=
A
∗
4
∗
A
T
+
Q
=
9
(6)
\begin{equation} \begin{aligned} \bar\mu_2=&A\hat\mu_{1}=A*4=10 \\\bar\Sigma_2=&A\hat\Sigma_{1}A^T+Q\nonumber=A*4*A^T+Q\nonumber=9 \nonumber \end{aligned} \end{equation}\tag{6}
μˉ2=Σˉ2=Aμ^1=A∗4=10AΣ^1AT+Q=A∗4∗AT+Q=9(6)
再计算
P
(
z
t
∣
x
1
,
x
2
)
P(z_t|x_1,x_2)
P(zt∣x1,x2)
K
=
Σ
ˉ
2
C
T
(
C
Σ
ˉ
2
C
T
+
R
)
−
1
μ
^
2
=
μ
ˉ
2
+
K
(
x
2
−
C
μ
ˉ
2
)
Σ
^
2
=
(
I
−
K
C
)
Σ
ˉ
2
K=\bar\Sigma_2C^T\left(C\bar\Sigma_{2}C^T+R\right)^{-1} \\\hat\mu_{2}=\bar \mu_2+K(x_2-C\bar\mu_2)\nonumber \\\hat\Sigma_2=(I-KC)\bar\Sigma_{2}
K=Σˉ2CT(CΣˉ2CT+R)−1μ^2=μˉ2+K(x2−Cμˉ2)Σ^2=(I−KC)Σˉ2
我们将公式6中的值带进去即可得到对应的期望和协方差矩阵。
然后以此反复,得出t个时刻的所有期望,就是我们综合GPS和运动学公式得到的位置信息。
代码实现
结果如图,可以看到,滤波之后的值相对平滑,并且每一个时刻的值都是更加靠近直线匀速运动的,因为我们在代码中设定的匀速运动的方差更小,而GPS的可信度相对差一些。
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
class Kalman_Filter():
def __init__(self):
self.A = np.array([[1, 1],
[0, 1]]) #A矩阵
self.C = np.array([[1, 0],
[0, 1]]) #C矩阵
self.mean = np.array([0, 0]) #随机变量u和v的期望
self.Q = np.array([[0.1, 0],
[0, 0.1]]) #随机变量u的协方差矩阵,设定的值小一些,说明匀速直线运动更可信,并且假设相互独立
self.R = np.array([[1, 0],
[0, 1]]) #随机变量v的协方差矩阵,说明GPS的可信度小,也假设相互独立
z_0 = np.array([[0],
[1]]) #给定初始位置为0,速度为1 的 z1
self.t=20 #假设要计算20个时刻对应的值
self.z=np.zeros(shape=(self.t,2)) #初始化一个矩阵z,用于储存直线运动的所得的数据
self.x=np.zeros(shape=(self.t,2)) #初始化一个矩阵x,用于储存GPS定位所得的数据
for i in range(self.t): #迭代十次
#计算新的值,并且加上随机变量,随机数从多维正态分布中抽样出一个数据加上去。reshape(-1,1)作用是将其变成二维数据
z_0=self.A @ z_0 + stats.multivariate_normal.rvs(mean=self.mean,cov=self.Q).reshape(-1,1)
x_0=self.C @ z_0 + stats.multivariate_normal.rvs(mean=self.mean,cov=self.R).reshape(-1,1)
self.z[i, :] = z_0.squeeze() # 将计算出来的直线运动的值储存,squeeze()作用是将z_0从二维数据变成一位
self.x[i, :] = x_0.squeeze() # 将计算出来的GPS的值储存
def train(self):
predict_μ = np.array([0, 0]) #初始化P(x1)的参数,设置期望为0
predict_Σ = np.array([[1, 0],
[0, 1]])#协方差矩阵对角线初始化为1
result=np.zeros(shape=(self.t,2)) #用于储存计算出来的结果
for i in range(self.t):
K=predict_Σ @ self.C.T @ np.linalg.inv(self.C @ predict_Σ @ self.C.T + self.R) #卡尔曼增益系数
update_μ=predict_μ + K @ (self.x[i,:] - self.C @ predict_μ) #更新的期望值
update_Σ=predict_Σ - K @ self.C @ predict_Σ #更新的协方差矩阵
predict_μ=self.A @ update_μ #计算下一个时刻的预测期望值
predict_Σ=self.A @ update_Σ @ self.A.T + self.Q #计算下一个时刻的预测协方差矩阵
result[i,:]=update_μ #保存更新后的期望值
plot_figure(self.x, self.z,result) #绘图
def plot_figure(x,z,new_x):
#仅绘制速度的图片
plt.plot(x[:,1],label="GPS",linewidth=3.0,marker="o")
plt.plot(z[:,1],label="直线匀速",linewidth=3.0,marker="o")
plt.plot(new_x[:,1],label="卡尔曼滤波轨迹",linewidth=3.0,marker="o")
plt.legend()
plt.show()
if __name__ == '__main__':
kalman_filter=Kalman_Filter() #初始化
kalman_filter.train() #训练
结束
以上,就是卡尔曼滤波在运动轨迹优化上的应用了。如有问题,还望指出,阿里嘎多