在上一篇卡尔曼滤波详解已经给出详细的推导过程。这里给出几个示例加深理解,这个是几年前学习卡尔曼滤波时候整理的,比较啰嗦,初学者可以看看,了解过的就不用浪费时间了,主要是最近在整理资料差点弄丢,还是放到网上保存吧。

简单示例1

这个示例中假设状态是稳定的,也就是真实的状态是不随着时间变化的,那么利用卡尔曼滤波算法最后会收敛到最优的状态。

参数设置

对于所有的变量,本场景下都是一个具体的数值而不是矩阵:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_02

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_03

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_04

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_05

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_06扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_07

基本模型
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_08

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_09

基本公式

Time Update

Measurement Update

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_10

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_11

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_12

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_13

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_14

仿真实验

给定测量值:

TIME(ms)

1

2

3

4

5

6

7

8

9

10

VALUE (V)

0.39

0.50

0.48

0.29

0.25

0.32

0.34

0.48

0.41

0.45

基本的卡尔曼滤波算法的代码:

z = [0, 0.39, 0.50, 0.48, 0.29, 0.25, 0.32, 0.34, 0.48, 0.41, 0.45];
s = size(z);
xp = [0]; Pp = [1]; xpp = [0]; Ppp = [1]; K = [0];
A = 1; H = 1; Q = 0; R = 0.1;
for k=2:s(2)
    xp(k) = A*xpp(k-1);
    Pp(k) = A*Ppp(k-1)*A' + Q;
    K(k) = Pp(k)*H/(H*Pp(k)*H + R);
    xpp(k) = xp(k) + K(k)*(z(k) - H*xp(k));
    Ppp(k) = (1 - K(k)*H)*Pp(k);
end

plot(1:s(2)-1, xpp(2:s(2)), 'r', 'LineWidth', 2);
axis([1 s(2)-1 0 0.5]);

下面是卡尔曼滤波算法运行过程中基本公式变量的变化:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_15

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_16

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_17

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_18

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_19

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_20

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_21

0

0

1

1

0.39

0

1

0.9091

0.3545

0.0909

2

0.50

0.3545

0.0909

0.4762

0.4238

0.0476

3

0.48

0.4238

0.0476

0.3226

0.4419

0.0323

4

0.29

0.4419

0.0323

0.2439

0.4049

0.0244

5

0.25

0.4049

0.0244

0.1961

0.3745

0.0196

6

0.32

0.3745

0.0196

0.1639

0.3656

0.0164

7

0.34

0.3656

0.0164

0.1408

0.3620

0.0141

8

0.48

0.3620

0.0141

0.1235

0.3765

0.0123

9

0.41

0.3765

0.0123

0.1099

0.3802

0.0110

10

0.45

0.3802

0.0110

0.0990

0.3871

0.0099

估计状态的效果图,可以看出来已经收敛了:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_22

简单示例2

在本例子中,估计一个随机的标量常量,例如电压,真实的状态是不随着时间变化的,那么利用卡尔曼滤波算法最后会收敛到最优的状态。

参数设置

对于所有的变量,本场景下都是一个具体的数值而不是矩阵:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_02

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_03

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_04

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_05

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_28

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_29

基本模型
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_30

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_09

基本公式

Time Update

Measurement Update

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32

仿真实验

在这里,设置要获得的标量常量 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_32 。然后去模拟生成50个单独的测量数据 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_38

生成数据代码如下,把生成的数据保存下来,后面运行卡尔曼滤波算法时不再重新生成,直接加载:

x = -0.37727;
num = 50;
mnoise = normrnd(0, 0.1, num, 1);
z = mnoise + x;
save('z.mat', 'z');

plot(1:num, repmat(x, [1, num]), 'k', 'LineWidth', 2);
hold on;
plot(1:num, z, 'b*');
maxz = max(z);
minz = min(z);
axis([0 num+1 minz-0.01 maxz+0.01]);
xlabel('Iteration');  
ylabel('Voltage');  
set(gca,'XTick',10:10:50)  
set(gca,'YTick',-0.6:0.1:-0.1)

生成图如下,其中黑色线表示真实的状态值,而蓝色*表示添加白色高斯噪声之后的生成的测量值:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_39

基本的卡尔曼滤波算法代码:

x = -0.37727;
num = 50;
mat = load('z.mat');
z = mat.z;

xp = [0]; Pp = [1]; xpp = [0]; Ppp = [1]; K = [0];
A = 1; H = 1; Q = 0.000001; R = 0.01;
for k=2:num+1
    xp(k) = A*xpp(k-1);
    Pp(k) = A*Ppp(k-1)*A' + Q;
    K(k) = Pp(k)*H/(H*Pp(k)*H + R);
    xpp(k) = xp(k) + K(k)*(z(k-1) - H*xp(k));
    Ppp(k) = (1 - K(k)*H)*Pp(k);
end

plot(1:num, repmat(x, [1, num]), 'k', 'LineWidth', 2);
hold on;
plot(1:num, z, 'b*');
plot(1:num, xpp(2:num+1), 'r', 'LineWidth', 2);

maxz = max(z);
minz = min(z);
axis([0 num+1 minz-0.01 maxz+0.01]);
xlabel('Iteration');  
ylabel('Voltage');  
set(gca,'XTick',10:10:50)  
set(gca,'YTick',-0.6:0.1:-0.1)  

% plot P versus iteration
figure;
plot(1:num, Ppp(2:num+1), 'k', 'LineWidth', 2);
xlabel('Iteration');  
ylabel('Pk');  
set(gca,'XTick',10:10:50)  
set(gca,'YTick',0.002:0.002:0.1)

结果如下,可以看出来虽然测量值的噪声比较大,但是利用卡尔曼滤波算法还是很快收敛到正确的值,红色线是卡尔曼滤波算法估计的值:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_40

然后上面考虑到对于 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_41 的设置问题,提到只要设置为不为0的数值即可,最后结果也会正确的收敛。在下图中是 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_42


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_43

前面也提到过对于 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_44扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45

下面是把上面的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_47

下面是把上面的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_49

把这三个画在一张图上近距离观察一下,可以更好感受这个变化:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_50

简单示例3

在本例子中,估计一个随机的标量变量,也就是说每个时间的状态是不一样的,例如飞机着陆时的海拔(当然也可以估计速度和燃料),那么利用卡尔曼滤波算法最后会估计每个时刻最优的状态。

参数设置

对于所有的变量,本场景下都是一个具体的数值而不是矩阵:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_52

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_03

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_54

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_05

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_06

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_57

基本模型
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_58

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_09

基本公式

Time Update

Measurement Update

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_60

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_61

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_62

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_13

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_14

仿真实验

在这里,设置最初的飞机海拔为1000 ,其他时刻的真实海拔为上一时刻海拔的0.98。然后去模拟生成100个(再多的话没意义,都是0了)单独的测量数据 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_38

生成数据代码如下,把生成的数据保存下来,生成具有随机性,这里结果的数据见附件altitude_z1.mat,后面运行卡尔曼滤波算法时不再重新生成,直接加载:

x0 = 1000;
num = 100;
x = [];
for i=1:num
    x0 =  x0 * 0.98;
    x(i) = x0;
end
mnoise = normrnd(0, 30, num, 1);
z = mnoise + x';
save('z.mat', 'z');

plot(1:num, z, 'g', 'LineWidth', 2);
hold on;
plot(1:num, x, 'k', 'LineWidth', 2);

数据分布图如下,其中黑色线表示真实的状态值,而绿色添加白色高斯噪声之后的生成的测量值:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_66

基本的卡尔曼滤波算法代码:

x0 = 1000;
num = 100;
x = [];
for i=1:num
    x0 =  x0 * 0.98;
    x(i) = x0;
end
mat = load('z.mat');
z = mat.z;

xp = [1000]; Pp = [1]; xpp = [1000]; Ppp = [1]; K = [0];
A = 0.98; H = 1; Q = 0; R = 900;
for k=2:num+1
    xp(k) = A*xpp(k-1);
    Pp(k) = A*Ppp(k-1)*A' + Q;
    K(k) = Pp(k)*H/(H*Pp(k)*H + R);
    xpp(k) = xp(k) + K(k)*(z(k-1) - H*xp(k));
    Ppp(k) = (1 - K(k)*H)*Pp(k);
end

plot(1:num, z, 'g', 'LineWidth', 2);
hold on;
plot(1:num, x, 'k', 'LineWidth', 2);
plot(1:num, xpp(2:num+1), 'r', 'LineWidth', 2);
xlabel('Iteration');  
ylabel('Altitude');

结果如下,可以看出来虽然测量值的噪声比较大,但是利用卡尔曼滤波算法还是很快收敛到正确的值,黑色线是真实的飞机海拔,红色线是卡尔曼滤波算法估计的海拔,可以看到黑色线几乎被全部遮挡住了,说明完美拟合了:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_67

但是为什么效果会这么好呢,好的有点不符合预期,对上面的实验修改一下:

生成成新的数据但是添加测量噪声时是在当前时刻的真实飞机海拔上添加[-100, 100]内的一个随机数,不是高斯噪声了,增加噪声来看拟合效果,其他变量条件不变:

生成数据代码如下,生成具有随机性,这里结果的数据见附件altitude_z2.mat:

x0 = 1000;
num = 100;
x = [];
z= [];
for i=1:num
    x0 =  x0 * 0.98;
    x(i) = x0;
    z(i) = x(i) + randi([-100, 100], 1, 1);
end
save('z.mat', 'z');

plot(1:num, z, 'g', 'LineWidth', 2);
hold on;
plot(1:num, x, 'k', 'LineWidth', 2);

数据分布图如下,其中黑色线表示真实的状态值,而绿色添加随机数噪声之后的生成的测量值,这时候的噪声已经很大了:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_68

那么在执行上面的卡尔曼滤波算法,里面参数不变:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_69

看上面的结果还能拟合的这么好,在这么大的噪声前提下,这说明啥问题,现在解释一下,主要有下面两方面原因:

第一是设置了一个正确的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_70

第二是设置了一个比较大的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_71

这会导致什么情况呢?卡尔曼滤波算法基本上会忽略掉测量值的作用,因为 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45 太大表示测量误差太大,系统不相信该值,在示例2中也讨论过,系统会尽可能相信过程中计算的飞机海拔,同时还提供了一个完美的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_73

那么好在进行卡尔曼滤波算法时设置 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_74,终于不是完美的拟合了吧,明显红色是另一条函数曲线,所以目前仅仅是利用了过程计算的信息,测量的值几乎忽略掉了,当然本来也是当前的测量值误差也太大:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_75

在该基础上调整 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_76 , 可以看到下面效果要好很多了,主要是设置初始的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_73


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_78

为什么说初始的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_73 会有这么大影响了,回到刚才的数据(添加高斯白噪声的),设置精确的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_80 ,然后设置 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_74 ,得到下面的结果,虽然说 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_83

然后和上面一样调整 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_76 , 可以看到下面效果要好很多了,主要是设置初始的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_73


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_86

根据上面的测试以及示例2中的结果,如果设置的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_73 的值和真实相差太大,其实一般情况下的话,该值是根据场景自己来定义的,不会出现大的问题。但是在特殊情况下,如果该值的设置偏移比较大,那么就不能在进行卡尔曼滤波算法时设置太大的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45 值,因为这种情况下,会导致测量噪声太大,系统不会相信提供的测量值,而仅仅是相信过程值,但是又因为过程值的初始值都是错误的,有很大的偏移,所以需要设置一个小的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45 值,这样可以得到一个正确的估计。其实从常理上也可以推断,既然有测量值,就要尽可能的相信它,这其实是唯一的输入来源,所以 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_45 不能设置特别大。在该示例中越小越好,可以把上面两组数据的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_91


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_92

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_93

但还有种情况就是误差特别特别大,此时也不能设置的太小,不然的话会过于相信测量数据,在示例2中已经证明了。

但是看到上面的结果,误差其实也不小了,而且提供的初值偏移很大,还能这么好的被拟合,说明卡尔曼滤波算法太强大了。

然后对这两组数据调整一下 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_94 (设置为0效果一样),其他参数 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_74扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_76 ,结果如下,上面也提到该值如果太小,也会导致 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_97


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_98

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_99

上面也提到修改 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_100


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_101

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_102

所以根据上面对各个参数的设置调整实验,在实际使用中要尽可能设置一个合理的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_103 ,然后为了更好的利用测量值,扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_71 的值不能设置太大也不能太小,尽可能合理。当然如果系统的初始量可以尽可能的设置正确是最好了,这样才能加快收敛过程。对于 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_105 一般来说是设置为0或者是很小的数,因为表明过程变化就是符合这个线性方程的,只是有很小的噪声扰动,设置太大,过程方程就利用不上了。而关于其他的参数 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_106

简单示例4

对示例3进行扩展,上面仅仅是估计飞机海拔,在这里估计飞机海拔(altitude),飞机航向(heading),飞机速度(airspeed)三个变量,然后测量仪器分别是用于测量海拔的气压计(barometer)和GPS(注意有两个测量仪器,需要融合),用于测量航向的罗盘(compass),用于测量速度的皮托流速测定管(pitot tube)。

这种情况下会变得很复杂,基本模型中的测量方程如下:
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_107
本示例主要聚焦在传感器数据融合计算上,所以简化上面的模型,还是只顾及飞机海拔,但是利用气压计以及gps两个传感器的测量值来进行估计。

参数设置

对于所有的变量,本场景下都是都是以向量和矩阵的形式:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_52

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_110

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_54

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_05

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_06

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_114

基本模型
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_115

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_116

基本公式

Time Update

Measurement Update

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_115

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_115

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_115

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_120

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_120

注意其中 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_120扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_123 都是标量,扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_124扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_125

仿真实验

在这里,设置最初的飞机海拔为1000 ,其他时刻的真实海拔为上一时刻海拔的0.98。然后去模拟生成100个(再多的话没意义,都是0了)单独的测量数据 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_38

生成数据代码如下,把生成的数据保存下来,生成具有随机性,这里结果的数据见附件altitude_z.mat ,后面运行卡尔曼滤波算法时不再重新生成,直接加载:

x0 = 1000;
num = 100;
x = [];
for i=1:num
    x0 =  x0 * 0.98;
    x(i) = x0;
end
bnoise = normrnd(0, 30, num, 1);
gnoise = normrnd(0, 30, num, 1);
z = [bnoise + x',gnoise + x'];
save('z.mat', 'z');

plot(1:num, z(:, 1), 'g', 'LineWidth', 2);
hold on;
plot(1:num, z(:, 2), 'm', 'LineWidth', 2);
legend('barometer', 'gps')
plot(1:num, x, 'k', 'LineWidth', 2);

数据分布图如下,其中黑色线表示真实的状态值,而绿色添加白色高斯噪声之后的生成的气压计测量值,洋红色是添加白色高斯噪声之后的生成的GPS测量值:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_127

基本的卡尔曼滤波算法代码:

x0 = 1000;
num = 100;
x = [];
for i=1:num
    x0 =  x0 * 0.98;
    x(i) = x0;
end
mat = load('z.mat');
z = mat.z;

xp = [1000]; Pp = [1]; xpp = [1000]; Ppp = [1]; K = [0 0];
A = 0.98; H = [1 1]'; Q = 0; R = [900 0;0 900];
for k=2:num+1
    xp(k) = A*xpp(k-1);
    Pp(k) = A*Ppp(k-1)*A' + Q;
    K = [K; Pp(k)*H'/(H*Pp(k)*H' + R)];
    xpp(k) = xp(k) + K(k, :)*(z(k-1, :)' - H*xp(k));
    Ppp(k) = (1 - K(k, :)*H)*Pp(k);
end

plot(1:num, z(:, 1), 'g', 'LineWidth', 2);
hold on;
plot(1:num, z(:, 2), 'm', 'LineWidth', 2);
legend('barometer', 'gps')
plot(1:num, x, 'k', 'LineWidth', 2);
plot(1:num, xpp(2:num+1), 'r', 'LineWidth', 2);
xlabel('Iteration');  
ylabel('Altitude');

结果如下,可以看出来虽然测量值的噪声比较大,但是利用卡尔曼滤波算法还是很快收敛到正确的值,黑色线是真实的飞机海拔,红色线是卡尔曼滤波算法估计的海拔,可以看到黑色线几乎被全部遮挡住了,说明完美拟合了:


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_128

和示例3一样设置 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_74 ,然后随后设置 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_130


扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_131

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_132

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_133

简单示例5

再给出一个简单的小栗子,大家很熟悉的自由落体运动,在这里测量值只是针对高度。

参数设置

对于所有的变量,本场景下都是都是以向量和矩阵的形式:

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_135

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_136扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_137

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_138

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_139

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_140

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_06

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_142

基本模型

过程模型,包括高度(这里表示的是下降的高度,所以当前高度等于上一高度加上该时间段内上升的高度,在这里是下降,所以速度和加速度都是负值)以及速度:
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_143
所以把这两个状态值统一起来,(这里的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_144 表示速度不是测量误差):
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_145
而且在这里 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_146扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_147 ,所以上面的 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_148扩招卡尔曼滤波模型java 卡尔曼滤波 实例_扩招卡尔曼滤波模型java_149

所以得到最终的模型:
扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_150

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_151

基本公式

Time Update

Measurement Update

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_152

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波示例_153

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_154

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_155

扩招卡尔曼滤波模型java 卡尔曼滤波 实例_卡尔曼滤波_156

注意其中 扩招卡尔曼滤波模型java 卡尔曼滤波 实例_参数设置_157扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_158扩招卡尔曼滤波模型java 卡尔曼滤波 实例_kalman filter_159

仿真实验

在这里,给出6组测量数据,如下:

TIME(ms)

1

2

3

4

5

6

HEIGHT(V)

127.0

115.3

110.9

72.4

50.7

0.3

基本的卡尔曼滤波算法的代码:

num = 6;
z = [ 127.0, 115.3, 110.9, 72.4, 50.7, 0.3];
x = [125];
xp = [100 0]'; Pp{1} = [1 1;1 1]; xpp = [100 0]'; Ppp{1} = [1 1;1 1]; K = [0 0]';
A = [1 1;0 1]; B = [1/2, 1]'; u = -9.81; H = [1 0]; Q = 0; R = 10;
for k=2:num
    x = [x x(1)-1/2*9.81*(k-1)^2];
    xp = [xp A*xpp(:, k-1) + B*u];
    Pp{k} = A*Ppp{k-1}*A' + Q;
    K = [K Pp{k}*H'/(H*Pp{k}*H' + R)];
    xpp = [xpp xp(:, k) + K(:, k)*(z(k) - H*xp(:, k))];
    Ppp{k} = (1 - K(:, k)*H)*Pp{k};
end

plot(0:num-1, z, 'g', 'LineWidth', 2);
hold on;
plot(0:num-1, x(1:num), 'k', 'LineWidth', 2);
plot(0:num-1, xpp(1,1:num), 'r', 'LineWidth', 2);
xlabel('height');  
ylabel('time');

这个例子和之前的比较类似,就不详细介绍了。