1、简介
\qquad 卡尔曼滤波(Kalman filtering)是一种线性系统状态最优估计算法。
2、离散卡尔曼滤波器
状态方程:
x
k
=
A
x
k
−
1
+
B
u
k
−
1
+
w
k
−
1
x_k = Ax_{k-1}+Bu_{k-1}+w_{k-1}
xk=Axk−1+Buk−1+wk−1
观测方程:
z
k
=
H
x
k
+
v
k
z_k = Hx_k+v_k
zk=Hxk+vk
过程激励噪声和观测噪声为相互独立,正态分布的白噪声:
p
(
w
)
∼
N
(
0
,
Q
)
p(w) \sim N(0,Q)
p(w)∼N(0,Q)
p
(
v
)
∼
N
(
0
,
R
)
p(v) \sim N(0,R)
p(v)∼N(0,R)
卡尔曼滤波器用反馈控制的方法估计过程状态:滤波器估计过程某一时刻的状态,然后以(含噪声的)测量变量的方式获得反馈。因此卡尔曼滤波器可分为两个部分:时间更新方程和测量更新方程。
预测方程:
x
^
k
−
=
A
x
^
k
−
1
+
B
u
k
−
1
\hat{x}_k^{-} = A\hat{x}_{k-1}+Bu_{k-1}
x^k−=Ax^k−1+Buk−1
P
^
k
−
=
A
P
^
k
−
1
A
T
+
Q
\hat{P}_k^{-} = A\hat{P}_{k-1}A^T+Q
P^k−=AP^k−1AT+Q
更新方程:
K
k
=
P
^
k
−
H
T
(
H
P
^
k
−
H
T
+
R
)
−
1
K_k = \hat{P}_k^-H^T(H\hat{P}_k^-H^T+R)^{-1}
Kk=P^k−HT(HP^k−HT+R)−1
x
^
k
=
x
^
k
−
+
K
k
(
z
k
−
H
x
^
k
−
)
\hat{x}_k = \hat{x}_k^-+K_k(z_k-H\hat{x}_k^-)
x^k=x^k−+Kk(zk−Hx^k−)
P
^
k
=
(
I
−
K
k
H
)
P
^
k
−
\hat{P}_k = (I-K_kH)\hat{P}_k^-
P^k=(I−KkH)P^k−
3、推导
这里设定的最优准则是使误差方差最小,即
P
k
P_k
Pk的迹最小。依据这一准则,下面来推导卡尔曼增益
K
K
K的表达式。
首先
P
k
=
E
(
(
e
k
)
(
e
k
)
T
)
P_k = E((e_k)(e_k)^T)
Pk=E((ek)(ek)T)
其中,
e
k
=
x
k
−
x
^
k
=
x
k
−
x
^
k
−
−
K
k
(
z
k
−
H
x
^
k
−
)
e_k=x_k-\hat{x}_k= x_k- \hat{x}_k^--K_k(z_k-H\hat{x}_k^-)
ek=xk−x^k=xk−x^k−−Kk(zk−Hx^k−),将
z
k
=
H
x
k
+
v
k
z_k=Hx_k+v_k
zk=Hxk+vk代入上式,
P
k
=
E
(
(
I
−
K
k
H
)
e
^
k
−
−
K
k
v
k
)
(
I
−
K
k
H
)
e
^
k
−
−
K
k
v
k
)
T
)
P_k = E((I-K_kH)\hat{e}^-_k-K_kv_k)(I-K_kH)\hat{e}^-_k-K_kv_k)^T)
Pk=E((I−KkH)e^k−−Kkvk)(I−KkH)e^k−−Kkvk)T)
其中,
e
^
k
−
=
x
k
−
x
^
k
−
\hat{e}^-_k=x_k-\hat{x}_k^-
e^k−=xk−x^k−
由于
v
k
v_k
vk和
e
^
k
−
\hat{e}_k^-
e^k−相互独立,上式可以展开为:
P
k
=
P
^
k
−
−
2
K
k
H
P
^
k
−
+
K
k
H
P
^
k
−
H
T
K
k
T
+
K
k
R
K
k
T
P_k =\hat{P}_k^- -2K_kH\hat{P}^-_k+K_kH\hat{P}_k^-H^TK_k^T+K_kRK_k^T
Pk=P^k−−2KkHP^k−+KkHP^k−HTKkT+KkRKkT
其中,
P
^
k
−
=
E
(
e
^
k
−
(
e
^
k
−
)
T
)
\hat{P}_k^-= E(\hat{e}^-_k(\hat{e}^-_k)^T)
P^k−=E(e^k−(e^k−)T)
所以
t
r
(
P
k
)
=
t
r
(
P
^
k
−
)
−
2
t
r
(
K
k
H
P
^
k
−
)
+
t
r
(
K
k
H
P
^
k
−
H
T
K
k
T
)
+
t
r
(
K
k
R
K
k
T
)
tr(P_k )=tr(\hat{P}_k^-) -2tr(K_kH\hat{P}^-_k)+tr(K_kH\hat{P}_k^-H^TK_k^T)+tr(K_kRK_k^T)
tr(Pk)=tr(P^k−)−2tr(KkHP^k−)+tr(KkHP^k−HTKkT)+tr(KkRKkT)
上式对
K
k
K_k
Kk求导,可得:
(
K
k
)
o
p
t
=
P
^
k
−
H
T
(
H
P
^
k
−
H
T
+
R
)
−
1
(K_k)_{opt}=\hat{P}_k^-H^T(H\hat{P}_k^-H^T+R)^{-1}
(Kk)opt=P^k−HT(HP^k−HT+R)−1
其中,使用到的两个迹的求导公式:
d
(
t
r
(
A
B
)
)
/
d
(
A
)
=
B
T
d(tr(AB))/d(A)=B^T
d(tr(AB))/d(A)=BT,
d
(
t
r
(
A
B
A
T
)
)
/
d
(
A
)
=
2
A
B
d(tr(ABA^T))/d(A)=2AB
d(tr(ABAT))/d(A)=2AB。
参考:https://www.bilibili.com/video/BV1hC4y1b7K7
一个简单的ekf例子:
#include<vector>
#include<Eigen/Core>
#include<stdlib.h>
#include<cmath>
#include<iostream>
using namespace std;
using namespace Eigen;
template<class T>
auto f = [](T s, T e, T dt)
{
vector<T> r;
T b = s;
for(;b<=e;b+=dt)
{
r.push_back(b);
}
return r;
};
auto hx = [](Vector3d x)
{
return sqrt(x(0)*x(0)+x(2)*x(2));
};
auto Hjacob(Vector3d xp){
Vector3d H = Vector3d::Zero();
H(0) = xp(0)/sqrt(xp(0)*xp(0)+xp(2)*xp(2));
H(1) = 0;
H(2) = xp(2)/sqrt(xp(0)*xp(0)+xp(2)*xp(2));
return H;
};
class EKF
{
public:
EKF(){}
~EKF(){}
public:
int posp = 0;
Matrix3d A,Q,P;
Vector3d x;
double R;
bool initialized = false;
double GetRadar(double dt)
{
double vel = 100 + 5*rand()/double(RAND_MAX);
double alt = 1000 + 10*rand()/double(RAND_MAX);
double pos = posp + vel*dt;
posp = pos;
double v = 0 + pos*0.05*rand()/double(RAND_MAX);
return sqrt(pos*pos + alt*alt)+v;
}
Vector3d RadarEKF(double z, double dt)
{
if(!initialized)
{
Matrix3d m3d;
m3d<<0,1,0,0,0,0,0,0,0;
A = Matrix3d::Identity() + dt*m3d;
Q << 0,0,0,0,0.001,0,0,0,0.001;
R = 10;
x<< 0,90,1100;
P = 10*Matrix3d::Identity();
initialized = true;
}
Vector3d H = Hjacob(x);
Vector3d xp = A*x;
Matrix3d Pp = A*P*A.transpose()+Q;
Vector3d K = Pp*H/(H.transpose()*Pp*H+R);
x = xp + K*(z-hx(xp));
P = Pp - K*H.transpose()*Pp;
return x;
}
};
int main()
{
double dt = 0.05;
double s = 0.0, e = 20.0;
vector<double> t = f<double>(s,e,dt);
int Nsamples = t.size();
vector<Vector3d> Xsaved(Nsamples,Vector3d({0,0,0}));
vector<double> Zsaved(Nsamples,0);
EKF ekf;
for(auto k=0;k<Nsamples;k++)
{
double r = ekf.GetRadar(dt);
Vector3d pva = ekf.RadarEKF(r,dt);
std::cout<<pva<<std::endl;
Xsaved[k] = pva;
Zsaved[k] = r;
}
return 0;
}