学习该文章:
https://zhuanlan.zhihu.com/p/152662055

状态定义

struct State {double timestamp;Eigen::Vector3d lla;       // WGS84 position.Eigen::Vector3d G_p_I;     // The original point of the IMU frame in the Global frame.Eigen::Vector3d G_v_I;     // The velocity original point of the IMU frame in the Global frame.Eigen::Matrix3d G_R_I;     // The rotation from the IMU frame to the Global frame.Eigen::Vector3d acc_bias;  // The bias of the acceleration sensor.Eigen::Vector3d gyro_bias; // The bias of the gyroscope sensor.// Covariance.Eigen::Matrix<double, 15, 15> cov;// The imu data.ImuDataPtr imu_data_ptr;
};

包含:

Eigen::Vector3d G_p_I;     // The original point of the IMU frame in the Global frame.Eigen::Vector3d G_v_I;     // The velocity original point of the IMU frame in the Global frame.Eigen::Matrix3d G_R_I;     // The rotation from the IMU frame to the Global frame.Eigen::Vector3d acc_bias;  // The bias of the acceleration sensor.Eigen::Vector3d gyro_bias; // The bias of the gyroscope sensor.

5个状态量,在协方差表示时,旋转也用三维的旋转角表示,所以,其协方差矩阵为15。

ekf的公式

imu predict的细节

void ImuProcessor::Predict(const ImuDataPtr last_imu, const ImuDataPtr cur_imu, State* state) {// Time.const double delta_t = cur_imu->timestamp - last_imu->timestamp;const double delta_t2 = delta_t * delta_t;// Set last state.State last_state = *state;// Acc and gyro.const Eigen::Vector3d acc_unbias = 0.5 * (last_imu->acc + cur_imu->acc) - last_state.acc_bias;const Eigen::Vector3d gyro_unbias = 0.5 * (last_imu->gyro + cur_imu->gyro) - last_state.gyro_bias;// Normal state. // Using P58. of "Quaternion kinematics for the error-state Kalman Filter".state->G_p_I = last_state.G_p_I + last_state.G_v_I * delta_t + 0.5 * (last_state.G_R_I * acc_unbias + gravity_) * delta_t2;state->G_v_I = last_state.G_v_I + (last_state.G_R_I * acc_unbias + gravity_) * delta_t;const Eigen::Vector3d delta_angle_axis = gyro_unbias * delta_t;if (delta_angle_axis.norm() > 1e-12) {state->G_R_I = last_state.G_R_I * Eigen::AngleAxisd(delta_angle_axis.norm(), delta_angle_axis.normalized()).toRotationMatrix();}// Error-state. Not needed.// Covariance of the error-state.   Eigen::Matrix<double, 15, 15> Fx = Eigen::Matrix<double, 15, 15>::Identity();Fx.block<3, 3>(0, 3)   = Eigen::Matrix3d::Identity() * delta_t;Fx.block<3, 3>(3, 6)   = - state->G_R_I * GetSkewMatrix(acc_unbias) * delta_t;Fx.block<3, 3>(3, 9)   = - state->G_R_I * delta_t;if (delta_angle_axis.norm() > 1e-12) {Fx.block<3, 3>(6, 6) = Eigen::AngleAxisd(delta_angle_axis.norm(), delta_angle_axis.normalized()).toRotationMatrix().transpose();} else {Fx.block<3, 3>(6, 6).setIdentity();}Fx.block<3, 3>(6, 12)  = - Eigen::Matrix3d::Identity() * delta_t;Eigen::Matrix<double, 15, 12> Fi = Eigen::Matrix<double, 15, 12>::Zero();Fi.block<12, 12>(3, 0) = Eigen::Matrix<double, 12, 12>::Identity();Eigen::Matrix<double, 12, 12> Qi = Eigen::Matrix<double, 12, 12>::Zero();Qi.block<3, 3>(0, 0) = delta_t2 * acc_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(3, 3) = delta_t2 * gyro_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(6, 6) = delta_t * acc_bias_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(9, 9) = delta_t * gyro_bias_noise_ * Eigen::Matrix3d::Identity();state->cov = Fx * last_state.cov * Fx.transpose() + Fi * Qi * Fi.transpose();// Time and imu.state->timestamp = cur_imu->timestamp;state->imu_data_ptr = cur_imu;
}

predict主要是对nominal state的运动学估计,以及对协方差的递推(为了在观测值来的时候,结合两者的协方差算出K值)。
细节:
1、gravity_这里是加号,为什么不是减号?
2、协方差传递的Fx的计算,与《Quaternion Kinematics for the error-state KF》中的一致:
(误差的传递与nominal state的传递都是一样的,遵循的都是相同的运动学模型,只是误差会在之前的数值的基础上继续包括新增的各种运动误差,如imu的bias,随机游走等,而nominal则不管这些值,按照正常的运动学模型递推。)


就以上面的5.4.2来说,这个f()包括了几个函数:
关于δp\delta pδp的函数,关于 δv\delta vδv的函数,共有6个函数,分别叫做f1,f2,f3,f4,f5,f6吧。
则Fx的计算就是:
[∂f1∂δp∂f1∂δv∂f1∂δθ...∂f2∂δp∂f2∂δv∂f2∂δθ......∂f6∂δp∂f6∂δv∂f6∂δθ...]\begin{bmatrix} \frac{\partial f1}{\partial\delta p} \frac{\partial f1}{\partial\delta v} \frac{\partial f1}{\partial\delta \theta} ... \\ \frac{\partial f2}{\partial\delta p} \frac{\partial f2}{\partial\delta v} \frac{\partial f2}{\partial\delta \theta} ... \\ ... \\ \frac{\partial f6}{\partial\delta p} \frac{\partial f6}{\partial\delta v} \frac{\partial f6}{\partial\delta \theta} ... \\ \end{bmatrix} ⎣⎢⎢⎢⎡​∂δp∂f1​∂δv∂f1​∂δθ∂f1​...∂δp∂f2​∂δv∂f2​∂δθ∂f2​......∂δp∂f6​∂δv∂f6​∂δθ∂f6​...​⎦⎥⎥⎥⎤​

结果就是上面的公式269。

3、Fi的计算

Fi代表的是误差状态传递函数对干扰项的导数。
同样的,f1…f6对干扰项 viθiaiwiv_i \\ \theta_i \\ a_i \\ w_ivi​θi​ai​wi​进行求导:
对应代码中也是如此设置:

     Eigen::Matrix<double, 15, 12> Fi = Eigen::Matrix<double, 15, 12>::Zero();Fi.block<12, 12>(3, 0) = Eigen::Matrix<double, 12, 12>::Identity();

4、Qi的计算
Qi代表的是干扰项的协方差,这些干扰项各自独立,自然只有对角线上有值。干扰项的含义,以及协方差的计算介绍:

都是与imu相关的误差参数,这些值是可以标定出来的,例如:

accelerometer_noise_density: 0.012576 #continous
accelerometer_random_walk: 0.000232
gyroscope_noise_density: 0.0012615 #continous
gyroscope_random_walk: 0.0000075  

代码中是这样设置的:

    Eigen::Matrix<double, 12, 12> Qi = Eigen::Matrix<double, 12, 12>::Zero();Qi.block<3, 3>(0, 0) = delta_t2 * acc_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(3, 3) = delta_t2 * gyro_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(6, 6) = delta_t * acc_bias_noise_ * Eigen::Matrix3d::Identity();Qi.block<3, 3>(9, 9) = delta_t * gyro_bias_noise_ * Eigen::Matrix3d::Identity();

这些参数,代码中是在初始化时设置的:

LocalizationWrapper::LocalizationWrapper(ros::NodeHandle& nh) {// Load configs.double acc_noise, gyro_noise, acc_bias_noise, gyro_bias_noise;nh.param("acc_noise",       acc_noise, 1e-2);nh.param("gyro_noise",      gyro_noise, 1e-4);nh.param("acc_bias_noise",  acc_bias_noise, 1e-6);nh.param("gyro_bias_noise", gyro_bias_noise, 1e-8);
<launch><param name="acc_noise"       type="double" value="1e-2" /><param name="gyro_noise"      type="double" value="1e-4" /><param name="acc_bias_noise"  type="double" value="1e-6" /><param name="gyro_bias_noise" type="double" value="1e-8" /><param name="I_p_Gps_x"       type="double" value="0.0" /><param name="I_p_Gps_y"       type="double" value="0.0" /><param name="I_p_Gps_z"       type="double" value="0.0" /><param name="log_folder"      type="string" value="$(find imu_gps_localization)" /><node name="nmea_topic_driver" pkg="nmea_navsat_driver" type="nmea_topic_driver" output="screen" /><node name="imu_gps_localization_node" pkg="imu_gps_localization" type="imu_gps_localization_node" output="screen" /><node pkg="rviz" type="rviz" name="rviz" output="screen" args="-d $(find imu_gps_localization)/ros_wrapper/rviz/default.rviz" required="true"></node></launch>

Qi矩阵:

递推协方差的计算

协方差反应了递推的数据多不靠谱的程度。

经过上面计算的Fx,Fi,Qi,以及上一时刻的P,就可以计算了。
代码中:

state->cov = Fx * last_state.cov * Fx.transpose() + Fi * Qi * Fi.transpose();

观测函数的jacobian的计算

gps的观测,会得到一个位置的信息,其他都没有,就是说虽然imu估计了p、v、q、ba、bg,可以将这些估计状态转换为观测值的是:
G_p_I + G_R_I * I_p_Gps_
其中,I_p_Gps_是一开机时计算出来的gps原点与imu的转换关系,是固定的。
h函数对状态量求导:
对G_p_I,得到 I3∗3I_{3*3} I3∗3​
对G_v_I求导,0(33)
对G_R_I求导,得到- G_R_I * GetSkewMatrix(I_p_Gps_);参考:https://zhuanlan.zhihu.com/p/156895046
对acc_bias求导,得到0(3
3)
对gyro_bias求导,得到0(3*3)。

也就是以下代码实现:

void GpsProcessor::ComputeJacobianAndResidual(const Eigen::Vector3d& init_lla,  const GpsPositionDataPtr gps_data, const State& state,Eigen::Matrix<double, 3, 15>* jacobian,Eigen::Vector3d* residual) {const Eigen::Vector3d& G_p_I   = state.G_p_I;const Eigen::Matrix3d& G_R_I   = state.G_R_I;// Convert wgs84 to ENU frame.Eigen::Vector3d G_p_Gps;//测量值ConvertLLAToENU(init_lla, gps_data->lla, &G_p_Gps);// Compute residual.//I_p_Gps_在imu坐标系下的位移?是固定值?//G_p_I + G_R_I * I_p_Gps_预测的状态计算出来的坐标点*residual = G_p_Gps - (G_p_I + G_R_I * I_p_Gps_);// Compute jacobian.jacobian->setZero();jacobian->block<3, 3>(0, 0)  = Eigen::Matrix3d::Identity();jacobian->block<3, 3>(0, 6)  = - G_R_I * GetSkewMatrix(I_p_Gps_);
}

观测函数h在不同模型下是不同的,这里需要求出h对误差状态的jacobian,为啥是对误差状态量的jacobian呢?
算出这个jacobian H,就可以与递推的P,和观测噪声,共同计算K了。

上面imu predict的时候,是对什么进行递推?没有对具体的误差数值如 δp\delta pδp这些递推,只是递推了协方差的值,协方差本身就反应了误差数据,本身就代表了误差数据。

(啰嗦,还在深刻理解总结)
我们一直在计算误差的协方差,但是我们始终假设误差的mean是零,这个协方差反应了我们的nominal state的多不靠谱,也是error state的表示,两个是一个意思。当观测数据到来的时候,相当于就是用noimal state推理的状态,映射到观测空间,与观测值计算出残差,K乘于这个残差,就是对nomial state推理出来的数据的补偿。
error state推理的协方差,代表了nominal state的不可信度,观测数据的协方差,代表的是观测数据的不可信度。

nominal state在递推,误差的协方差也在递推,在递推的过程中,只是知道越来越不可信,但是究竟数值有多少,没有客观参考,无法得知。直到观测数据到来,才能让误差现身。

所谓的现身,是什么意思呢?就是将递推的数据,与观测的数据,一对比,就知道了。但是要让误差数值现身可不是随便来的,毕竟所得到的观测数据,自己也有误差啊。所以,需要kalman增益系数来综合两者,对观测数据和预测的状态的映射数据的差值乘上这个系数,才得到最可能的误差值。

不明白的是为何要计算观测函数对误差状态的jacobian。一会翻过来再理解一下ekf

总结一下:
我们要用综合计算出来的 δx^\delta \hat x δx^ 加到nominal state xx x上,得到最优的
x^t\hat x_t x^t​,即
x^t=x+δx^\hat x_t = x + \delta \hat x x^t​=x+δx^

而 :
δx^=K(y−h(x^t))\delta \hat x = K (y- h(\hat x_t))δx^=K(y−h(x^t​))
这里,y表示观测值,x^t\hat x_tx^t​表示真值,但是这时候哪有真值,唯有用当前推理得到的nominal state代替,这也是合理的,因为我们还有不确定度这个数据。

而:
K=PHt(HPHt+V)−1K= PH^t (HPH^t+V)^{-1}K=PHt(HPHt+V)−1

所以需要有H,
而H定义为观测函数对误差量的jacobian矩阵:
H≡∂h∂δxH \equiv \frac{\partial h}{\partial \delta x}H≡∂δx∂h​

h其实是状态变量的函数,对误差项怎么求导?
别忘了:
真值,其实是nominal state+误差项,
所以:


对于 HxH_x Hx​ 其实就是一般的对状态量的jacobian,跟这个观测方程有关。

第二项,其实就是真值对误差项目的求导,看下表:
composition 那一列:
所以,这个XδxX_{\delta x}Xδx​ 的表达式是固定的:

参考代码里的只是做了前面 HxH_x Hx​的计算,后面的XδxX_{\delta x}Xδx​没有计算。

   // Compute jacobian.jacobian->setZero();jacobian->block<3, 3>(0, 0)  = Eigen::Matrix3d::Identity();jacobian->block<3, 3>(0, 6)  = - G_R_I * GetSkewMatrix(I_p_Gps_);

结合论文理解gps与imu融合定位代码的细节相关推荐

  1. GPS定位平台软件,GPS/UWB/WIFI融合定位,提供开发接口

    GPS定位平台软件,GPS定位系统软件,GPS/UWB/WIFI融合定位,提供HTTP/MQTT开发接口. GPS与UWB融合定位 由于GPS只能在室外定位,通过引进UWB技术,可实现室内室外无死角高 ...

  2. vins中imu融合_VINS-Mono代码分析与总结(最终版)

    VINS-Mono代码分析总结 参考文献 前言 ??视觉与IMU融合的分类: 松耦合和紧耦合:按照是否把图像的Feature加入到状态向量区分,换句话说就是松耦合是在视觉和IMU各自求出的位姿的基础上 ...

  3. Quaternion kinematics for error state kalman filter实现GPS+IMU融合,(附源码)

    最近在学习kalman滤波相关的知识,恰好工作可能需要使用ESKF算法,因此将Joan Sola大神的书看了一遍,同时推导了相关的公式.俗话说得好:"Talk is cheap, show ...

  4. 基于UWB和IMU融合的MAV群精确三维定位

    基于UWB和IMU融合的MAV群精确三维定位 注:这篇翻译中的小牛全部替换为无人机 本地化全部替换为定位 百度翻译的问题 李嘉欣1,毕英才1,李坤2,王康丽2,冯琳3,陈本美2 摘要--由微型飞行器等 ...

  5. 多传感器融合定位(GPS+Wheel+camera)(1)-读取传感器数据

    多传感器融合定位(GPS+Wheel+camera)(1)-读取传感器数据 文章目录 1.读取Kaist数据集到融合系统中 1.读取Kaist数据集到融合系统中 int main(int argc, ...

  6. 【多传感器融合定位SLAM专栏】前端里程计、IMU预积分、滤波、图优化推导与应用(3)

    本专栏基于深蓝学院<多传感器融合定位>课程基础上进行拓展,对多传感器融合SLAM的学习过程进行记录 文章目录 第三章 惯性导航原理及误差分析 惯性器件 1. 机械陀螺 2. 激光陀螺 ME ...

  7. 论文笔记——基于多传感器融合的即时定位与地图构建方法研究

    1.多传感器融合SLAM不完全分类: 视觉和IMU融合VIO: 基于滤波的VIO:采用EKF进行视觉信息和IMU数据进行数据融合.预测和更新.经典算法:MSCKF.ROVIO 基于优化的VIO:对视觉 ...

  8. 论文笔记——基于因子图消元优化的多传感器融合定位算法

    创新点: 为了提高抗干扰性和容错能力,在链式因子图模型中加入滑动窗口用于保留窗口内历史状态信息:同时为了避免高维矩阵运算,引入消元算法将因子图转化为贝叶斯网络,依次边缘化历史状态,实现矩阵降维. 提出 ...

  9. 激光IMU融合——LIO-Mapping / LIOM / LINS / LIO-SAM算法解析

    激光IMU融合--LIO-Mapping / LIOM / LINS / LIO-SAM算法解析 激光IMU融合--LIO-Mapping / LIOM / LINS / LIO-SAM算法解析 1. ...

最新文章

  1. 3.Tomact部署webapp
  2. c++ fstream用得多不?_自动挡中的“手自一体”,其实很好用,但真正会用的不多...
  3. 对计算机领域中间层的理解
  4. 神舟六号的投入产出比:1比12
  5. 设置网页文字禁止复制
  6. [读书笔记] -《C++ API设计》第7章 性能
  7. 关于AI和区块链的技术落地,你不知道的是……
  8. java使用javax.mail包发送电子邮件:设置账号、密码、主题、文本、附件
  9. php goto 代码还原_【表哥有话说 第58期】代码审计思路小结
  10. mysql基础3-数据表的相关操作1
  11. 十八、JAVA基本数据类型的包装类
  12. Proteus的基本使用方法
  13. 冒死解密,微信逆向:破解聊天记录文件!
  14. 教你如何在vue-cli项目打包时避免踩雷(一)【早看早受益】
  15. 撰写合格的REST API
  16. oracle的userenv和nls_lang详解
  17. 游戏官网的HTML布局,游戏网站页面布局关键
  18. 智安网络丨居安思危·洞见未来 —— 智安网络安全周报
  19. 【autojs】Auto.js Pro陌陌点赞全脚本源代码
  20. macbook android 屏幕共享,苹果设备小技巧:iPhone,iPad,Mac进行屏幕共享和远程控制...

热门文章

  1. 计算机专业普渡大学全美排名,2019普渡大学USNews排名
  2. 基于N32G45智能家居平台
  3. 带有对话的整人html,整人语言陷阱
  4. 【数学】常用的 证明方法 / 思考方法
  5. 测试第三方接口所给的ip和端口通不通
  6. echarts初步使用
  7. 30 分钟看懂 CatBoost(Python代码)
  8. shell那点事儿——运维工程师必会shell知识
  9. 你掌握了数控开料机维护和安全操作的重要性了吗?
  10. Linux环境下 制作U盘启动盘