此片blog的目的是理解卡尔曼算法的思想和实际应用的物理含义,想法很好,却只能理解冰山一角,先记下这一角

另本blog参考卡尔曼滤波 -- 从推导到应用和徐亦达卡尔曼推导视频

首先认识卡尔曼算法在数学领域是什么位置:处理线性高斯模型的算法

然后认识卡尔曼算法的思想:

一片绿油油的草地上有一条曲折的小径,通向一棵大树.一个要求被提出:从起点沿着小径走到树下.

“很简单.” A说,于是他丝毫不差地沿着小径走到了树下.

现在,难度被增加了:蒙上眼。

“也不难,我当过特种兵。” B说,于是他歪歪扭扭地走到了树旁。“唉,好久不练,生疏了。”(只凭自己的预测能力)

“看我的,我有 DIY的 GPS!” C说,于是他像个醉汉似地歪歪扭扭的走到了树旁。“唉,这个 GPS没做好,漂移太大。”(只依靠外界有很大噪声的测量)

“我来试试。”旁边一也当过特种兵的拿过 GPS,蒙上眼,居然沿着小径很顺滑的走到了树下。(自己能预测+测量结果的反馈)

“这么厉害!你是什么人?”
“卡尔曼 ! ”
“卡尔曼?!你就是卡尔曼?”众人大吃一惊。
“我是说这个 GPS卡而慢。”

也就是说,预测值有高斯噪声,测量值也有高斯噪声,这2个噪声相互独立,单独的利用任何一个都不能很好的得到真实值,所以在2者之间有个信赖度的问题,应该相信谁更多些,这也就是卡尔曼算法的核心,这个信赖度就是卡尔曼增益,卡尔曼增益通过测量值和真实值之间的协方差最小时确定的,由此求这个协方差偏导为0时的系数,这个系数就是卡尔曼增益,这样就能很好的融合预测值和测量值。并且推导卡尔曼增益的时候,发现协方差是可以递归的,由此只要刚开始指定初始协方差就可以源源不断的求出卡尔曼增益和新的协方差,从而不断的跟新真实值。

接着是普及数学卡尔曼知识(推导卡尔曼运算过程)

动态模型:

最后是实际应用:

1.为了方便表达,把几个公式用矩阵表达式表达

2.估计过程:

(1)上一时刻状态推导这一时刻状态线性系统的状态。差分方程为

,其中x是系统的状态向量,大小为n*1列。A为转换矩阵,大小为n*n。u为系统输入,大小为k*1。B是将输入转换为状态的矩阵,大小为n*k。随机变量w为系统噪声。注意这些矩阵的大小,它们与你实际编程密切相关。

(2)测量值当然是由系统状态变量映射出来的,方程形式如下:

注意Z是测量值,大小为m*1(不是n*1,也不是1*1,后面将说明),H也是状态变量到测量的转换矩阵。大小为m*n。随机变量v是测量噪声。

同时对于匀加速模型,假设下车是匀加速远离我们,我们站在原点用超声波仪器测量小车离我们的距离。

也就是测量值直接等于位移。可能又会问,为什么不直接用测量值呢?测量值噪声太大了,根本不能直接用它来进行计算。试想一个本来是朝着一个方向做匀加速运动的小车,你测出来的位移确是前后移动(噪声影响),只根据测量的结果,你就以为车子一会往前开一会往后开。

对于状态方程中的系统噪声w和测量噪声v,假设服从如下多元高斯分布,并且w,v是相互独立的。其中Q,R为噪声变量的协方差矩阵。

看到这里自然要提个问题,为什么噪声模型就得服从高斯分布呢?个人觉得卡尔曼是在线性高斯的环境下建立的模型,也就是说这个推导有些公式或者定理只能适用于高斯分布这个条件,具体的话,个人只记得如果刚开始协方差满足高斯,递推下去,之后的协方差都满足高斯。

对于小车匀加速运动的的模型,假设系统的噪声向量只存在速度分量上,且速度噪声的方差是一个常量0.01,位移分量上的系统噪声为0。测量值只有位移,它的协方差矩阵大小是1*1,就是测量噪声的方差本身。

那么:

Q中,叠加在速度上系统噪声方差为0.01,位移上的为0,它们间协方差为0,即噪声间没有关联。

(3)噪声(偏差)是如何处理的。貌似有了这个模型就能完全估计系统状态了,速度能计算出,位移也能计算出(这个模型下面的图片有介绍)。那还要卡尔曼干嘛,问题是很多实际系统复杂到根本就建不了模。并且,即使你建立了较为准确的模型,只要你在某一步有误差,由递推公式,很可能不断将你的误差放大A倍(A就是那个状态转换矩阵),以至于最后得到的估计结果完全不能用了。回到最开始的那个笑话,如果那个完全凭预测的特种兵在某一步偏离了正确的路径,当他站在错误的路径上(而他自己以为是正确的)做下一步预测时,肯定走的路径也会错了,到最后越走越偏。

既然如此,我们就引进反馈。从概率论贝叶斯模型的观点来看前面预测的结果就是先验,测量出的结果就是后验。

(4)如何引入反馈。理论预测(先验)有了,测量值(后验)也有了,那怎么根据这两者得到最优的估计值呢?首先想到的就是加权,或者称之为反馈。 我们认定是预测(先验)值,是估计值,为测量值的预测,在下面的推导中,请注意估计和预测两者的区别,不混为一谈。由一般的反馈思想我们得到估计值:

其中,称之为残差,也就是预测的和你实际测量值之间的差距。如果这项等于0,说明预测和测量出的完全吻合。

(5)卡尔曼的核心:求取卡尔曼增益(也就是加权)。这个里面涉及方差或者协方差,方差好比误差的平方和,目的是求和真实值最小的方差的加权(个人理解)。

友情提示:以下看不懂可以结合上面数学推导理解

先看估计值和真实值间误差的协方差矩阵,提醒一下协方差矩阵的对角线元素就是方差,求这个协方差矩阵,就是为了利用他的对角线元素的和计算得到均方差.

这里请注意ek是向量,它由各个系统状态变量的误差组成。如匀加速运动模型里,ek便是由位移误差和速度误差,他们组成的协方差矩阵。表示如下:

其中,Serr代表位移误差,Verr代表速度误差,对角线上就是各自的方差。

把前面得到的估计值代入这里能够化简得:

(这里中间是乘)                  (1)式

同理,能够得到预测值和真实值之间误差的协方差矩阵:

注意到系统状态x变量和测量噪声之间是相互独立的。于是展开(1)式可得:

最后得到:

(Pk用Pk’简化)

继续展开:

接下来最小均方差开始正式登场了,回忆之前提到的,协方差矩阵的对角线元素就是方差。这样一来,把矩阵P的对角线元素求和,用字母T来表示这种算子,他的学名叫矩阵的迹。

最小均方差就是使得上式最小,对未知量K求导,令导函数等于0,就能找到K的值。

注意这个计算式K,转换矩阵H式常数,测量噪声协方差R也是常数。因此K的大小将与预测值的误差协方差有关。不妨进一步假设,上面式子中的矩阵维数都是1*1大小的,并假设H=1,不等于0。那么K可以写成如下:

所以越大,那么K就越大,权重将更加重视反馈,如果等于0,也就是预测值和真实值相等,那么K=0,估计值就等于预测值(先验)。(完全满足之前说的:方差好比误差的平方和,目的是求和真实值最小的方差的加权。也就是说预测值和真实值的方差越大,卡尔曼增益越大,越相信测量值;预测值和真实值的方差越小,卡尔曼增益越小,越相信预测值)
将计算出的这个K反代入Pk中,就能简化Pk,估计协方差矩阵Pk的:

因此递推公式中每一步的K就计算出来了,同时每一步的估计协方差也能计算出来。但K的公式中好像又多了一个我们还未曾计算出来的东西,他称之为预测值和真实值之间误差的协方差矩阵。它的递推计算如下:
 请先注意到预测值的递推形式是:

由于系统状态变量和噪声之间是独立,故可以写成:

由此也得到了的递推公式。因此我们只需设定最初的,就能不断递推下去。
这个递推的过程是:我们需要卡尔曼增益k,作为最优加权值,用于估计真实值

;于是要求出的最初值我们可以设定;还差的非初始值,

“死”步骤

再来点lpe的程序分析:那就以气压和光流为例

程序就是按上面介绍的思路来写的:先用物理模型推测下一刻的状态,再用传感器得到带有噪声的位置速度等信息,最后通过算最小协方差时的卡尔曼增益,并加权得到最优解。

  1. void BlockLocalPositionEstimator::predict()

  2. {

  3. // if can't update anything, don't propagate

  4. // state or covariance

  5. if (!_canEstimateXY && !_canEstimateZ) { return; }

  6. if (_integrate.get() && _sub_att.get().R_valid) {

  7. Matrix3f R_att(_sub_att.get().R);

  8. Vector3f a(_sub_sensor.get().accelerometer_m_s2);

  9. _u = R_att * a;

  10. _u(U_az) += 9.81f; // add g

  11. } else {

  12. _u = Vector3f(0, 0, 0);

  13. }

  14. // dynamics matrix

  15. Matrix<float, n_x, n_x> A; // state dynamics matrix

  16. A.setZero();

  17. // derivative of position is velocity

  18. A(X_x, X_vx) = 1;

  19. A(X_y, X_vy) = 1;

  20. A(X_z, X_vz) = 1;

  21. // derivative of velocity is accelerometer acceleration

  22. // (in input matrix) - bias (in body frame)

  23. Matrix3f R_att(_sub_att.get().R);

  24. A(X_vx, X_bx) = -R_att(0, 0);

  25. A(X_vx, X_by) = -R_att(0, 1);

  26. A(X_vx, X_bz) = -R_att(0, 2);

  27. A(X_vy, X_bx) = -R_att(1, 0);

  28. A(X_vy, X_by) = -R_att(1, 1);

  29. A(X_vy, X_bz) = -R_att(1, 2);

  30. A(X_vz, X_bx) = -R_att(2, 0);

  31. A(X_vz, X_by) = -R_att(2, 1);

  32. A(X_vz, X_bz) = -R_att(2, 2);

  33. // input matrix

  34. Matrix<float, n_x, n_u> B; // input matrix

  35. B.setZero();

  36. B(X_vx, U_ax) = 1;

  37. B(X_vy, U_ay) = 1;

  38. B(X_vz, U_az) = 1;

  39. // input noise covariance matrix

  40. Matrix<float, n_u, n_u> R;

  41. R.setZero();

  42. R(U_ax, U_ax) = _accel_xy_stddev.get() * _accel_xy_stddev.get();

  43. R(U_ay, U_ay) = _accel_xy_stddev.get() * _accel_xy_stddev.get();

  44. R(U_az, U_az) = _accel_z_stddev.get() * _accel_z_stddev.get();

  45. // process noise power matrix

  46. Matrix<float, n_x, n_x> Q;

  47. Q.setZero();

  48. float pn_p_sq = _pn_p_noise_density.get() * _pn_p_noise_density.get();

  49. float pn_v_sq = _pn_v_noise_density.get() * _pn_v_noise_density.get();

  50. Q(X_x, X_x) = pn_p_sq;

  51. Q(X_y, X_y) = pn_p_sq;

  52. Q(X_z, X_z) = pn_p_sq;

  53. Q(X_vx, X_vx) = pn_v_sq;

  54. Q(X_vy, X_vy) = pn_v_sq;

  55. Q(X_vz, X_vz) = pn_v_sq;

  56. // technically, the noise is in the body frame,

  57. // but the components are all the same, so

  58. // ignoring for now

  59. float pn_b_sq = _pn_b_noise_density.get() * _pn_b_noise_density.get();

  60. Q(X_bx, X_bx) = pn_b_sq;

  61. Q(X_by, X_by) = pn_b_sq;

  62. Q(X_bz, X_bz) = pn_b_sq;

  63. // terrain random walk noise

  64. float pn_t_sq = _pn_t_noise_density.get() * _pn_t_noise_density.get();

  65. Q(X_tz, X_tz) = pn_t_sq;

  66. // continuous time kalman filter prediction

  67. Vector<float, n_x> dx = (A * _x + B * _u) * getDt();

  68. // only predict for components we have

  69. // valid measurements for

  70. if (!_canEstimateXY) {

  71. dx(X_x) = 0;

  72. dx(X_y) = 0;

  73. dx(X_vx) = 0;

  74. dx(X_vy) = 0;

  75. }

  76. if (!_canEstimateZ) {

  77. dx(X_z) = 0;

  78. dx(X_vz) = 0;

  79. }

  80. // propagate

  81. _x += dx;

  82. _P += (A * _P + _P * A.transpose() +

  83. B * R * B.transpose() + Q) * getDt();

  84. }

这部分具体分析可见pixhawk Lacal_position_estimator数据流,完成的任务是用物理模型推测下一刻的状态,并算出协方差,最后2行是处理结果。

  1. void BlockLocalPositionEstimator::baroCorrect()

  2. {

  3. // measure

  4. Vector<float, n_y_baro> y;

  5. if (baroMeasure(y) != OK) { return; }

  6. // subtract baro home alt

  7. y -= _baroAltHome;

  8. // baro measurement matrix

  9. Matrix<float, n_y_baro, n_x> C;

  10. C.setZero();

  11. C(Y_baro_z, X_z) = -1; // measured altitude, negative down dir.

  12. Matrix<float, n_y_baro, n_y_baro> R;

  13. R.setZero();

  14. R(0, 0) = _baro_stddev.get() * _baro_stddev.get();

  15. // residual

  16. Matrix<float, n_y_baro, n_y_baro> S_I =

  17. inv<float, n_y_baro>((C * _P * C.transpose()) + R);

  18. Vector<float, n_y_baro> r = y - (C * _x);

  19. // fault detection

  20. float beta = (r.transpose() * (S_I * r))(0, 0);

  21. if (beta > BETA_TABLE[n_y_baro]) {

  22. if (_baroFault < FAULT_MINOR) {

  23. //mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] baro fault, r %5.2f m, beta %5.2f",

  24. //double(r(0)), double(beta));

  25. _baroFault = FAULT_MINOR;

  26. }

  27. } else if (_baroFault) {

  28. _baroFault = FAULT_NONE;

  29. //mavlink_and_console_log_info(&mavlink_log_pub, "[lpe] baro OK");

  30. }

  31. // kalman filter correction if no fault

  32. if (_baroFault < fault_lvl_disable) {

  33. Matrix<float, n_x, n_y_baro> K = _P * C.transpose() * S_I;

  34. Vector<float, n_x> dx = K * r;

  35. if (!_canEstimateXY) {

  36. dx(X_x) = 0;

  37. dx(X_y) = 0;

  38. dx(X_vx) = 0;

  39. dx(X_vy) = 0;

  40. }

  41. _x += dx;

  42. _P -= K * C * _P;

  43. }

  44. }

这段的代码也很好理解,倒着看。

代码的目的是矫正高度,怎么矫正?predict()函数最后面_x += dx;这个dx就是通过模型推导的,而baroCorrect()函数_x += dx;这里的dx = K * r;其中K是卡尔曼增益K = _P * C.transpose() * S_I;r是测量值和预测值的误差,r = y - (C * _x);这里y是测量值,C是转换矩阵将预测矩阵转换成测量矩阵,_x就是预测值。这里的核心就是K的求法,这里涉及了协方差和一些运算,这个是可以从程序一眼看出来的,具体公式和求法没研究的很清楚。

相同的,看光流,求法是一样的,只是测量矩阵不一样了,噪声等“个性化”东西不一样,处理过程是一样的。

--------------------- 本文来自 虾米一代 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/czyv587/article/details/52061523?utm_source=copy

卡尔曼算法笔记---思想和实际应用物理含义的理解相关推荐

  1. 《算法笔记》学习笔记(1)

    <算法笔记>学习笔记(1) 2021/4/7号 晚上21:36开始学习 第二章 c++/c快速入门 有的时候不要在一个程序中同时使用cout 和 printf 有的时候会出现问题. 头文件 ...

  2. 数据结构与算法笔记(青岛大学王卓老师视频)

    写在前面的话: 因为在学习数据结构之前,学习过一年的算法,所以有一些基础,一些我觉得 没必要的代码或知识就没写上,记得多是一些知识点,写的可能对于别人来说 很难接受,望谅解.我学习算法是在Acwing ...

  3. 第2章KNN算法笔记_函数classify0

    <机器学习实战>知识点笔记目录 K-近邻算法(KNN)思想: 1,计算未知样本与所有已知样本的距离 2,按照距离递增排序,选前K个样本(K<20) 3,针对K个样本统计各个分类的出现 ...

  4. 算法笔记知识点整理大全

    每次刷题都觉得自己吃了知识点不全,基础不牢固的亏,刷题的时候目标也不明确,于是看完了算法笔记并把知识点归纳了一下,当然直接看书会更加详细,这个归纳只是学习时加深印象以及方便自己之后回顾而已:之后刷题大 ...

  5. 强化学习经典算法笔记(十九):无监督策略学习算法Diversity Is All You Need

    强化学习经典算法笔记19:无监督策略学习算法Diversity Is All You Need DIAYN核心要点 模型定义 目标函数的构造 DIAYN算法细节 目标函数的优化 SAC的训练 判别器的 ...

  6. 《算法笔记》——笔记

    算法笔记 胡凡 曾磊 主编 机械工业出版社 文章目录 算法笔记 C/C++快速入门 提醒 memset sscanf与sprintf 引用 浮点数的比较 圆周率 复杂度 黑盒测试 入门篇(1)--入门 ...

  7. pix2pix算法笔记(精)

    目录 一.工作流程: 二.优化目标 3.关于生成器和判别器的网络结构设计 pix2pix是基于cGAN实现图像翻译,因为cGAN可以通过添加条件信息来指导图像生成,因此在图像翻译中就可以将输入图像作为 ...

  8. 《算法笔记》之链表二叉树

    链表 之前找工作的时候,受益于算法笔记,这次开始复习,也是从这本书入手,打算重新学习算法,因此从最基础的开始,只要我们去一点一点付出,最后一定结果不会差.加油,道虽远,行将就至. 链表有若干个节点组成 ...

  9. 数据结构与算法笔记 - 绪论

    数据结构与算法笔记 - 绪论 1. 什么是计算 2. 评判DSA优劣的参照(直尺) 3. 度量DSA性能的尺度(刻度) 4. DSA的性能度量的方法 5. DSA性能的设计及其优化 x1. 理论模型与 ...

最新文章

  1. 再次推荐数据安全清除软件----Eraser
  2. 马拦过河卒(NOIP2002)
  3. C/C++与Matlab混合编程初探
  4. ubuntu 编译QT Basic XLib functionality test failed!问题解决
  5. MySQL--安装linux(5.6版本)
  6. 鸿蒙电视是无线么,鸿蒙系统首秀,在自家设备上和普通电视大不相同赵崇带你走世界...
  7. 用C/C++实现SMC动态代码加密技术
  8. c语言环比,同比、环比的区别及计算公式
  9. ArcGIS实验教程——实验二:ArcGIS地理配准完整操作步骤
  10. linux中文件权限为d-rwxr-xr,Linux基础知识之文件权限详解
  11. SpringBoot2基础篇
  12. iOS——directory not found for option 链接错误
  13. python 哪些比赛-国内数据挖掘比赛有哪些?
  14. 启动电脑时出现0xc000000f错误的解决办法
  15. 前端ps切图,图文教程,详细。
  16. OpenCV4.5.2(+opencv_contrib)+CMake3.20.1+vs2017重新编译
  17. 安全狗漏洞通告|Linux kernel越界写入漏洞解决方案
  18. 任务6 -传统机器学习--SVM
  19. 计算机小学培训班老师,小学教师信息技术培训心得体会(通用5篇)
  20. 个人博客搭建流程(域名、主机、解析、源码、搭建)全程详解

热门文章

  1. 最小生成树(Kruskal )
  2. date对象加十分钟_一张图讲解对象锁和关键字synchronized修饰方法(代码块)
  3. php ajax弹出框传值,PHP_Yii2.0 模态弹出框+ajax提交表单,如题 我们使用模态弹出框+ajax - phpStudy...
  4. 那些方式可以合并php数组,合并数组(PHP)
  5. 怎么安装linux系统 硬盘,如何实现硬盘安装linux系统
  6. 对delegate进行扩展 打造通用的计时完成方法
  7. OpenResty+Keepalived组建高可用集群+双主模式
  8. 【早知云世】云计算在商务智能中的应用及竞争力
  9. 算法帖——用舞蹈链算法(Dancing Links)求解俄罗斯方块覆盖问题
  10. 华为路由器静态路由配置