本文主要是介绍利用Python创建Kalman滤波器用于多普勒测速目标跟踪,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
利用Python构造Kalman滤波器和拓展卡尔曼滤波器Class用于目标跟踪
文章目录
- 利用Python构造Kalman滤波器和拓展卡尔曼滤波器Class用于目标跟踪
- Kalman滤波概述
- Kalman滤波器Python类
- 线性动力学、线性观测
- 所有代码
Kalman滤波概述
其基本思路是:
- 新的最佳估计基于原最佳估计和已知外部影响校正后得到的预测。
- 新的不确定性基于原不确定性和外部环境的不确定性得到的预测。
对于系统已知以下离散系统方程、线性观测方程:
x k + 1 = Φ k + 1 , k x k + Ψ k + 1 , k u k + Γ k w k z k + 1 = H k + 1 x k + 1 + v k + 1 \begin{aligned} &x_{k+1}=\Phi_{k+1, k} x_{k}+\Psi_{k+1, k} u_{k}+\Gamma_{k} w_{k} \\ &z_{k+1}=H_{k+1} x_{k+1}+v_{k+1} \end{aligned} xk+1=Φk+1,kxk+Ψk+1,kuk+Γkwkzk+1=Hk+1xk+1+vk+1Kalman滤波的基本公式如下:
一步预测状态 ⋯ x ^ k + 1 ∣ k = Φ k + 1 , k x ^ k ∣ k + Ψ k + 1 , k u k 预测系统协方差矩阵 ⋯ P k + 1 ∣ k = Φ k + 1 , k P k ∣ k Φ k + 1 , k T + Γ k Q k Γ k T Kalman增益计算 ⋯ K k + 1 = P k + 1 ∣ k H k + 1 T [ H k + 1 P k + 1 ∣ k H k + 1 T + R k + 1 ] − 1 修正后的估计状态 ⋯ x ^ k + 1 ∣ k + 1 = x ^ k + 1 ∣ k + K k + 1 [ z k + 1 − H k + 1 x ^ k + 1 ∣ k ] 状态误差最优估计 ⋯ P k + 1 ∣ k + 1 = [ I − K k + 1 H k + 1 ] P k + 1 ∣ k \begin{aligned} \texttt{一步预测状态}\cdots \hat{x}_{k+1 \mid k}=&\Phi_{k+1, k} \hat{x}_{k \mid k}+\Psi_{k+1, k} u_{k} \\ \texttt{预测系统协方差矩阵}\cdots P_{k+1 \mid k}=&\Phi_{k+1, k} P_{k \mid k} \Phi_{k+1, k}^{\mathrm{T}}+\Gamma_{k} Q_{k} \Gamma_{k}^{\mathrm{T}} \\ \texttt{Kalman增益计算}\cdots K_{k+1}=&P_{k+1 \mid k} H_{k+1}^{\mathrm{T}}\left[H_{k+1} P_{k+1 \mid k} H_{k+1}^{T}+R_{k+1}\right]^{-1} \\ \texttt{修正后的估计状态}\cdots \hat{x}_{k+1 \mid k+1}=&\hat{x}_{k+1 \mid k}+K_{k+1}\left[z_{k+1}-H_{k+1} \hat{x}_{k+1 \mid k}\right] \\ \texttt{状态误差最优估计}\cdots P_{k+1 \mid k+1}=&\left[I-K_{k+1} H_{k+1}\right] P_{k+1 \mid k} \end{aligned} 一步预测状态⋯x^k+1∣k=预测系统协方差矩阵⋯Pk+1∣k=Kalman增益计算⋯Kk+1=修正后的估计状态⋯x^k+1∣k+1=状态误差最优估计⋯Pk+1∣k+1=Φk+1,kx^k∣k+Ψk+1,kukΦk+1,kPk∣kΦk+1,kT+ΓkQkΓkTPk+1∣kHk+1T[Hk+1Pk+1∣kHk+1T+Rk+1]−1x^k+1∣k+Kk+1[zk+1−Hk+1x^k+1∣k][I−Kk+1Hk+1]Pk+1∣k式中:
符号 | 解释 |
---|---|
x ^ k + 1 / k \hat x_{k+1/k} x^k+1/k | 根据 k k k时刻状态和系统方程预测的(先验)值 |
x ^ k + 1 / k + 1 \hat x_{k+1/k+1} x^k+1/k+1 | Kalman滤波得到的 k + 1 k+1 k+1时刻,也就是综合了以前结果和当前这一步观测值,所得到的状态估计值 |
x k + 1 x_{k+1} xk+1或者 x k + 1 / k + 1 x_{k+1/k+1} xk+1/k+1 | k + 1 k+1 k+1时刻真实的状态 |
z ^ k + 1 / k \hat z_{k+1/k} z^k+1/k | 根据预测状态 x k + 1 / k x_{k+1/k} xk+1/k,对观测结果的预测 |
z k + 1 z_{k+1} zk+1 | k + 1 k+1 k+1时刻传感器的测量值 |
z k + 1 − H x ^ k + 1 / k z_{k+1}-H\hat x_{k+1/k} zk+1−Hx^k+1/k | 预测的和实际测量值之间的差距 |
P k + 1 / k P_{k+1/k} Pk+1/k | 预测值和真实值之间误差的协方差矩阵 |
P k + 1 / k + 1 P_{k+1/k+1} Pk+1/k+1 | 下一步 k + 1 k+1 k+1估计值和真实值之间的误差协方差矩阵 |
K k K_k Kk | kalman增益 |
状态协方差矩阵定义为 P k = E [ e k e k T ] = E [ ( x k − x ^ k ) ( x k − x ^ k ) T ] P_{k}=E\left[e_{k} e_{k}^{T}\right]=E\left[\left(x_{k}-\hat{x}_{k}\right)\left(x_{k}-\hat{x}_{k}\right)^{T}\right] Pk=E[ekekT]=E[(xk−x^k)(xk−x^k)T],代表估计值和真实值之间的误差。滤波时,可以估计出状态协方差的初值 P 0 P_0 P0(迭代更新),测量噪声协方差 R = Cov ( w 1 ) R = \operatorname{Cov}(w_1) R=Cov(w1)、系统噪声协方差矩阵 Q = Cov ( [ v 1 , v 2 ] ) Q =\operatorname{Cov}([v_1,v_2]) Q=Cov([v1,v2]),以及初值 x ^ 0 ∣ 0 = E x 0 = m 0 \hat{x}_{0 \mid 0}=E x_{0}=m_{0} x^0∣0=Ex0=m0
关于这几个矩阵的设定,可见过客冲冲 - 理解Kalman滤波的使用 cnblogs。
P是误差矩阵,初始化可以是一个随机的矩阵或者0,只要经过几次的处理基本上就能调整到正常的水平,因此也就只会影响前面几次的滤波结果。
Q和R分别是预测和观测状态协方差矩阵,一般可以简单认为系统状态各维之间(即上面的a和b)相互独立,那么Q和R就可以设置为对角阵。而这两个对角线元素的大小将直接影响着滤波结果,若Q的元素远大于R的元素,则预测噪声大,从而更相信观测值,这样可能使得kalman滤波结果与观测值基本一致;反之,则更相信预测,kalman滤波结果会表现得比较规整和平滑;若二者接近,则滤波结果介于前面两者之间,根据实验效果看也缺乏实际使用价值。
Kalman滤波器Python类
下面这个是根据上图中的公式写的,简单改一下就是EKF,参考360doc 时钟转动 / 卡尔曼滤波 / Kalman滤波器的C++实现
import numpy as np
import math
# import scipy.stats as st
# import scipy
# import matplotlib as mpl
import matplotlib.pyplot as pltclass KalmanFilter:stateSize = 1measureSize = 1controlSize = 0IsNonLinearObserve , funHz = (0, '')x,z,A,B,G,u,Px,K,Hz,Qv,Rm = [0,0,0,0,0,0, 0,0,0,0,0]def __init__(self,n,m,nc):self.stateSize = nself.measureSize = mself.controlSize = ncself.x = np.zeros([n,1])self.z = np.zeros([m,1])self.In = np.eye(n)self.A = np.eye(n) # state transition matrix, discreteif nc>0:self.u = np.zeros([nc,1])self.B = np.zeros(n,nc)self.G = np.eye(n)self.Px = np.eye(n)*1e-6self.Qv = 1e-6*np.eye(n) # process varianceself.K = np.zeros([n,m]) # kalman gainself.Hz = np.zeros([m,n])self.Rm = 1e-6*np.eye(m) # estimate of measurement variance,def init(self,x_,P_,Q_,R_):for i in range(0,self.stateSize):self.x[i,:] = x_[i,:]self.Px = P_self.Qv = Q_self.Rm = R_return selfdef predict(self,A_):self.A = A_self.x = self.A@self.x
# print(self.x)self.P = self.A@self.Px@self.A.T + self.G@self.Qv@self.G.T return selfdef observe(self,x):if self.IsNonLinearObserve:# 这是非线性观测方程z = self.funHz(x)z_predict = np.array( [ [z[0]], [z[1]] ] )else:z_predict = self.Hz@xreturn z_predictdef update(self,H_,z_):self.Hz = H_Ht = self.Hz.Ttemp1 = self.Hz@self.P@Ht + self.Rm
# temp2 = temp1.inverse() #(H*P*H'+R)^(-1)self.K = self.P@Ht@np.linalg.inv(temp1)self.z = self.observe(self.x)# 根据方程类型调用不同的观测函数self.x = self.x + self.K@(z_ - self.z)
# print(self.x)self.P = (self.In - self.K@self.Hz)@self.Pxreturn self
线性动力学、线性观测
以下是一个一维Doppler测速估计目标状态的模型:
x ˙ = A x + ν = [ r ˙ v ˙ ] = [ 0 1 0 0 ] [ r v ] + [ v 1 v 2 ] z = H x + w = [ 0 1 ] [ r v ] + w 1 \begin{aligned} &\dot{x}=Ax+\nu =\left[\begin{array}{l} \dot{r} \\ \dot{v} \end{array}\right]=\left[\begin{array}{ll} 0 & 1 \\ 0 & 0 \end{array}\right]\left[\begin{array}{l} r \\ v \end{array}\right]+\left[\begin{array}{ll} v_{1}\\ v_{2} \end{array}\right] \\ &z = Hx +w=\left[\begin{array}{ll} 0 & 1 \end{array}\right]\left[\begin{array}{r} r \\ v \end{array}\right]+w_1 \end{aligned} x˙=Ax+ν=[r˙v˙]=[0010][rv]+[v1v2]z=Hx+w=[01][rv]+w1然后协方差矩阵定义为: Cov ( x ^ − x ) = P , Cov ( [ v 1 , v 2 ] ) = Q , Cov ( w 1 ) = R \operatorname{Cov}(\hat{x}-x)=P \ ,\ \operatorname{Cov}([v_1,v_2])=Q \ ,\ \operatorname{Cov}(w_1)=R Cov(x^−x)=P , Cov([v1,v2])=Q , Cov(w1)=R目标初始时刻 x ( 0 ) ≈ 0 x(0) \approx 0 x(0)≈0, 已知目标是匀速直线运动,要求用KF估计其位置和速度。
初始化动力学系统:
# #
# SEC 2, SYSTEM#
# #
def calExpAt(A,dt):n = len(A)I = np.eye(n)return I+A*dt+A@A/2*dt*dt#########################################################################
# 系统设置,初值估计
#########################################################################
measureStep = 0.1;
tf = 30;
lth = 1 + int(tf/measureStep);
ts = np.linspace(0,tf,lth)A = np.array([[0,1.0],[0,0]]) # system matrix
H = np.array([[0,1.0]]) # 注意sizex0 = np.array([0,0.8]) # 我这里随意设置了一个初值,与真实值相差较大
x0 = x0[:,np.newaxis]
# x0 = np.array([[x1],[x2]])
#########################################################################
# 先验统计误差,
#########################################################################
sigmaQv = np.array([0.1,0.1]) # estimated pertubation of dynamical system per step# 如果第一步的初始状态估计误差就很大,这里就需要设置稍大;否则,收敛将很慢
Q = np.eye(2)
Q[0,0] = Q[0,0]*sigmaQv[0]**2 # pertubation from white noise, may be from
Q[1,1] = Q[1,1]*sigmaQv[1]**2
sigmaRm = 5 # a priori precision of measurement
R = np.array([[sigmaRm**2]]) # covariance of measurement precisionP0 = np.eye(2) # error of state, estimated at start
P0[0,0] = 1*P0[0,0]
P0[1,1] = 3*P0[1,1] #########################################################################
# 采用先验误差设定,模拟真实的、带有噪声的测量数据
#########################################################################
# z_ts = np.linspace(0,18,lth)+np.random.normal(0,sigmaRm,lth) # 匀加速
v = 1.2
z_real = v*np.ones((lth)) # 一维数组,尚未扩充维度到2
z_ts = z_real+np.random.normal(0,sigmaQv[1]*sigmaQv[1],lth)
z_ts = z_ts[np.newaxis, :] # 作为行向量输入
创建Kalman滤波器并滤波,获得目标的状态:
# #
# SEC 3, ESTIMATE/#
# #
# 创建并初始化
dopp = KalmanFilter(2,1,0)
# 设置先验误差矩阵
dopp.init(x0,P0,Q,R)# 保存数据
# P_ts = P[:,:,np.newaxis]
x_ts = np.zeros([lth,2])for it in range(0,lth):dopp.predict(calExpAt(A,measureStep))dopp.update(H,z_ts[:,it,np.newaxis])x_ts[it,:] = dopp.x.T[0]# x_ts[0,it] = dopp.x[0],x_ts[1,it] = dopp.x[1]# 绘图
plt.subplot(2,1,1)
plt.plot(ts,x_ts[:,1])
plt.plot(ts,z_ts[0])
plt.legend(['filtered data', 'measure data'])
plt.title('Doppler velocity estimate result')plt.subplot(2,1,2)
plt.plot(ts,x_ts[:,1]-z_real)
plt.xlabel('t/[s]')
plt.title('estimatation error')
plt.show()
以下是两个设定的例子, 估计速度初值为 0.8 m / s 0.8m/s 0.8m/s,实际速度 1.2 m / s 1.2m/s 1.2m/s;初始位置为 0 0 0,协方差矩阵设定不同:
Q = diag ( 0.1 , 0.1 ) 2 , R = diag ( 5.0 ) 2 , P 0 = diag ( 3 , 3 ) Q=\operatorname{diag}(0.1,0.1)^2,R=\operatorname{diag}(5.0)^2,P_0=\operatorname{diag}(3,3) Q=diag(0.1,0.1)2,R=diag(5.0)2,P0=diag(3,3)
Q = diag ( 1.0 , 0.1 ) 2 , R = diag ( 0.2 ) 2 , P 0 = diag ( 3 , 3 ) Q=\operatorname{diag}(1.0,0.1)^2,R=\operatorname{diag}(0.2)^2,P_0=\operatorname{diag}(3,3) Q=diag(1.0,0.1)2,R=diag(0.2)2,P0=diag(3,3)
按照误差标准差公式 S = ∑ i = 1 n ( x i − x ˉ ) 2 n − 1 S=\sqrt{\frac{\sum_{i=1}^{n}\left(x_{i}-\bar{x}\right)^{2}}{n-1}} S=n−1∑i=1n(xi−xˉ)2统计误差
# 统计一下估计误差
v_hat = x_ts[-100:,1]
err = v_hat-z_real[-100:]
std = np.sum((err- np.mean(err))**2)/99
print('estimated covariance of navigation: ',math.sqrt(std))
所有代码
限于篇幅,这里给出剩下的内容,包括一个非线性观测、非线性系统情况下的拓展卡尔曼滤波案例。
Simple Extended Kalman Filter for target tracking
这篇关于利用Python创建Kalman滤波器用于多普勒测速目标跟踪的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!