从理论到实战-如何理解那个把嫦娥送上天的卡尔曼滤波算法Kalman filter?
原文链接:https://zhuanlan.zhihu.com/p/77327349
直观理解
**首先卡尔曼滤波要解决的问题是什么?**我以我军发射一枚导弹攻击敌方某固定位置目标为例(搞科技的总要点情怀,老是讲啥小车运动,温度计这些就太low)。导弹需要每隔一秒开雷达测量下离目标的距离。然后由于雷达有误差,所以需要融合自己上个时刻的位置、速度等信息来更准确的确定当前时刻离目标的距离。这就是卡尔曼滤波需要解决的事
从直观理解卡尔曼滤波是怎么解决这个问题的呢?
首先导弹已知“当前这秒雷达测量的导弹离目标的距离(我们称它为观测值,比如雷达直接测量导弹离目标距离7m)”,“上个时刻导弹离目标距离”和“导弹自己当前时刻的速度”这三个数据。而根据“上个时刻导弹离目标距离”和“导弹自己当前时刻的速度”可以估算出当前导弹离目标的距离(我们称它为估计值)。比如:上一秒离目标10m,速度是4m/s,那么现在这秒估计就离目标距离是6m。这个速度数据可以从传感器里面读取也可以从发动机那获得。(根据速度估计导弹距离这个计算叫做移动模型建模,想了解移动模型与观测值和滤波算法之间的联系可以看这篇文章《机器人移动模型:根据控制命令预测机器人位置》)
那么问题来了,导弹离目标的距离现在既有个观测值7m,又有个估计值6m。到底相信哪个?单纯相信观测值万一雷达被敌方干扰了呢?单纯相信估计值那么万一上个时刻的距离估计值或者速度不准呢?所以,我们要根据观测值和估计值的准确度来得到最终导弹离目标的距离估计值。准确度高的就最终结果比重高,准确度低就占比低。如果雷达测量的那个7m准确度是49%,根据速度估计出的那个6m准确度是1%,那么最终的距离估计结果就是
(这些数据大家有直观理解就好,文末会给出计算方法)
卡尔曼滤波怎么做的?
我们先回顾总结下直观理解中是怎么做的。
根据上一秒导弹的位置 和 导弹的的速度估计出当前时刻导弹的位置粗略估计值。
将雷达测得导弹位置测量值和我们计算出的导弹位置粗略估计值根据这两种数据可信度来进行线性加权和得到准确的导弹位置估计值。
在前面我们也提到了导弹的位置和雷达测量值都是有误差的。所以卡尔曼想用概率来衡量数据的可信度。
比如:雷达测量的数据它就不只是一个数字了。而是说测量发现导弹有0.8的概率在7m那个位置,有0.1的概率在7.2m那个位置。有0.1的概率在6.9m那个位置。这些数据就叫做概率分布。概率分布的意思就是很多个值还有它们各自出现的概率多大所组成的数据就叫做概率分布。
卡尔曼认为导弹速度、导弹位置、雷达测距的测量值这些都服从正态分布(对就是高中学的那个正态分布)。这是啥意思呢?比如下面这个图是讲上个时刻导弹的位置的概率分布,从图中看出它在10m(横轴)那个位置的纵坐标最高所以概率最大。它在其他地方的概率相对小一点。
卡尔曼滤波Python代码实践
这个实践的已知量是导弹做水平运动,已知导弹每个时刻的速度dv,和速度测量仪器的标准差是v_std,还已知每个时刻用GPS测量出导弹位置position_noise以及GPS的方差是predict_var。(注意标准差的平方是方差,不用觉得奇怪)
我们需要用卡尔曼滤波根据这些信息获得融合两种传感器后的位置信息position_predict
#coding=utf-8
import numpy as np
#-----------------------------生成无误差的GPS坐标点------------------------------------------
# 模拟数据
t = np.linspace(1,100,100)#在1-100之间,按照同样的间隔生成100个数据
a = 0.5 #加速度
position = (a * t**2)/2 #根据加速度获取位置,模拟GPS获取的位置
#给位置添加一个具有正态分布的误差
# random.normal 正态分布 均值,标准差。样本数(t.shape[0]获取和第一维度数一样的样本数)
#---------------------------生成带高斯分布误差的GPS测量值误差--------------------------
position_noise = position+np.random.normal(0,120,size=(t.shape[0]))
import matplotlib.pyplot as plt
plt.plot(t,position,label='truth position')
plt.plot(t,position_noise,label='only use measured position')
#----------------------------------------设定初始值-----------------------------------------------------------------
# 初始的估计导弹的位置就直接用GPS测量的位置
predicts = [position_noise[0]]
position_predict = predicts[0]
#------------------------------------------设定方差-------------------------------------------------------------------
predict_var = 0 #预测方差
odo_var = 120**2 #这是我们自己设定的位置测量仪器的方差,越大则测量值占比越低,位置误差方差
v_std = 50 # 速度标准差
for i in range(1,t.shape[0]):#循环从1开始到样本数前一个结束
dv = (position[i]-position[i-1]) + np.random.normal(0,50) # 模拟从IMU读取出的速度
#------------------预测--------------------
position_predict = position_predict + dv # 利用上个时刻的位置和速度预测当前位置
predict_var += v_std**2 # 预测速度方差
# ---------------更新------------------------
#根据权重更新位置预测
position_predict = position_predict*odo_var/(predict_var + odo_var)+position_noise[i]*predict_var/(predict_var + odo_var)
predict_var = (predict_var * odo_var)/(predict_var + odo_var)**2#更新速度方差
predicts.append(position_predict)
plt.plot(t,predicts,label='kalman filtered position')
plt.legend()
plt.show()
上一篇: 如何获取PHP字符串的最后n个字符
下一篇: php实现的简单日志写入函数_php技巧