【机器学习】卡尔曼滤波对运动轨迹优化(Python)

本文介绍卡尔曼滤波在小车运动轨迹优化上的应用。以小佳开车为例,因路面状况和GPS误差难以准确估计位置。通过将问题转化为矩阵形式,从贝叶斯派角度求解,给出预测和更新流程,并举例说明迭代计算过程,代码实现结果显示滤波后的值更优。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在上一篇文章中,我们已经对卡尔曼滤波的Filter问题进行了数学推导求解【机器学习】卡尔曼滤波原理推导-优快云博客,得到了卡尔曼黄金五式。接下来,我们就案例而言,来看看卡尔曼滤波如何应用到生活当中。

案例引入

在这里插入图片描述

小佳从拼夕夕砍来了一辆小汽车,这车除了能开啥子功能都没有。每次小佳都是以一定的速度去开车,并且无时无刻都在想着当前的位置信息是多少。作为一个大聪明文科生,小佳根本不知道运动学公式。于是他买了一个GPS定位器,实时返回位置信息。但没想到这个拼夕夕9.9包邮的定位器不准。人都被创飞好几米远了还说离前方人员还有几百米。为了解决这个问题,小佳学习物理,打算自己计算位置信息。可是,他家住在山旮旯附近,路面凹凸不平;狭管效应严重,天天七级台风能把人吹成大聪明,车辆的运动状态根本就不满足严格的运动学公式。智商堪比爱因斯坦的小佳决定综合GPS,利用卡尔曼滤波,对小车的运动轨迹实现较为精准的预测。

不难看到,预测的难度实际上就是在于,即便小佳每次都以一定的油门行驶,但是路面的凹凸不平,和能把人吹成大聪明的7级太分会对小车的实际速度受到随机扰动,所以即便是我们知道在 t − 1 t-1 t1时刻的位置信息,也很难准确估计出 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=st1+vt1
我们用t表示当前时刻,t-1表示上一个时刻,也就是说当前的位置 s t s_t st就等于上一个时刻的位置 s t − 1 s_{t-1} st1,加上上一个时刻的速度 v t = v t − 1 v_t=v_{t-1} vt=vt1

而对于速度信息,很显然因为是匀速直线运动,所以直接就等于 v t − 1 v_{t-1} vt1。我们前面说过,这只是一个理想状态下的情况,我们是存在一定的随机扰动项的。也就是说,最终实际上我们可以写成
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=st1+vt1+u1;vt=vt1+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) u1N(0,Q1),u2N(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) ω1N(0,R1),ω2N(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)(st1vt1)+(u1u2)
为了与我们前面推导的符号一致,我们用其他符号代换,在上面这个公式中,第一个括号用 z t z_t zt代替,第二个括号用 A A A代替,第三个括号用 z t − 1 z_{t-1} zt1代替(要与z时刻一致符号),第四个括号直接用 u u u代替,所以得到
z t = A z t − 1 + u z_t=Az_{t-1}+u zt=Azt1+u
其中 u ∼ N ( 0 , Q ) u \sim N(0,Q) uN(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) vN(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=Azt1+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=Kxt+(1K)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=Kxt+ztKzt=zt+K(xtzt)
实际上,这里的K,被称为卡尔曼增益系数。

问题解决

如果看这一段看得云里雾里,看下一段的案例流程也许就懂了

本篇文章将从贝叶斯派的角度出发去解决这个问题,因为我们前面的推导就是从贝叶斯派的角度去推导。

对于贝叶斯派而言,上面的问题实际上就是卡尔曼滤波的Filter问题。也就是求出 P ( z t ∣ x 1 , ⋯   , x t ) P(z_t|x_1,\cdots,x_t) P(ztx1,,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(ztx1,,xt)是可以迭代求解的,比如只要我们求出 P ( z t − 1 ∣ x 1 , ⋯   , x t − 1 ) P(z_{t-1}|x_1,\cdots,x_{t-1}) P(zt1x1,,xt1),就可以求出 P ( z t ∣ x 1 , ⋯   , x t ) P(z_t|x_1,\cdots,x_t) P(ztx1,,xt)。所以最终不断递推到 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1)

所以我们的只需要求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1),就可以求出 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(z2x1,x2),......,P(ztx1,,xt)。保留我们每一个时刻的值,就是我们最终的小车运动的位置信息轨迹。

那么如何求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1),如果你看过上一篇的推导,你会发现要求出 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1),就要先求出 P ( z 1 ∣ x 0 ) P(z_1|x_0) P(z1x0);要求出 P ( z 2 ∣ x 1 , x 2 ) P(z_2|x_1,x_2) P(z2x1,x2),就要求出 P ( z t ∣ x 1 ) P(z_t|x_1) P(ztx1);实际上,要求出 P ( z t ∣ x 1 , ⋯   , x t ) P(z_t|x_1,\cdots,x_t) P(ztx1,,xt),就要求出优先求出 P ( z t ∣ x 1 , ⋯   , x t − 1 ) P(z_t|x_1,\cdots,x_{t-1}) P(ztx1,,xt1)

它们都是符合正态分布的。对于正态分布,我们只需要知道它们的期望和协方差矩阵即可,因为期望是概率最大的位置,我们可以将其当作就是卡尔曼滤波之后的值。对于 P ( z t ∣ x 1 , ⋯   , x t − 1 ) P(z_t|x_1,\cdots,x_{t-1}) P(ztx1,,xt1),我们就设定它的期望和协方差为 μ ˉ 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(ztx1,,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(ztx1,,xt1),在上一篇文章我们推导出来过,在这里直接给出它的参数公式
μ ˉ 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μ^t1AΣ^t1AT+Q
得到 P ( z t ∣ x 1 , ⋯   , x t − 1 ) P(z_t|x_1,\cdots,x_{t-1}) P(ztx1,,xt1),再计算 P ( z t ∣ x 1 , ⋯   , x t ) P(z_t|x_1,\cdots,x_{t}) P(ztx1,,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(xtCμˉt)Σ^t=(IKC)Σˉt
我们前面说过,我们是要递归求解的,也就是不断递归到第一个时刻 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1),我们来看看对于第一个时刻 P ( z 1 ∣ x 0 ) P(z_1|x_0) P(z1x0)的参数求解
μ ˉ 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(ztx1,,xt1)对应的参数
μ ˉ 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μ^t1AΣ^t1AT+Q
​ ②更新,计算 P ( z t ∣ x 1 , ⋯   , x t ) P(z_t|x_1,\cdots,x_{t}) P(ztx1,,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(xtCμˉt)Σ^t=(IKC)Σˉt
不断迭代①、②,直到算到t个时刻。

下面我们来举个例子

时刻1:

假定我们在时刻1,我们需要计算 P ( z 1 ∣ x 0 ) P(z_1|x_0) P(z1x0),因为 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(z1x1),来看公式
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(x1Cμˉ1)Σ^1=(IKC)Σˉ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=2CT(C2CT+R)1=3μ^1=1.5+3(x1C1.5)=4Σ^1=(I3C)2=4(5)
好现在得到了 P ( z 1 ∣ x 1 ) P(z_1|x_1) P(z1x1),里面对应期望和协方差矩阵就是我们需要的东西。里面的期望我们认为是概率最大的地方,所以我们认为这个期望就是我们优化后的值

时刻2:

现在计算第2个时刻.先计算 P ( z 2 ∣ x 1 ) P(z_2|x_1) P(z2x1)
μ ˉ 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=A4=10AΣ^1AT+Q=A4AT+Q=9(6)
再计算 P ( z t ∣ x 1 , x 2 ) P(z_t|x_1,x_2) P(ztx1,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(x2Cμˉ2)Σ^2=(IKC)Σˉ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() #训练

结束

以上,就是卡尔曼滤波在运动轨迹优化上的应用了。如有问题,还望指出,阿里嘎多

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值