IMU和GPS融合定位(ESKF)

news/2024/11/7 20:51:36/

说明

1.本文理论部分参考文章https://zhuanlan.zhihu.com/p/152662055和https://blog.csdn.net/brightming/article/details/118057262
ROS下的实践参考https://blog.csdn.net/qinqinxiansheng/article/details/107108475和https://zhuanlan.zhihu.com/p/163038275

理论

坐标系
在这里插入图片描述
error-state
在这里插入图片描述
ESKF GPS更新
在这里插入图片描述
初始化
初始位置为0,方向的roll和pitch可由加速度的重力方向确定,bias设置为0.

代码

代码请见github:https://github.com/ydsf16/imu_gps_localization
在这里插入图片描述
总体流程如下:接收IMU数据,进行积分,更新系统方程,接收GPS数据,计算K,更新状态量

代码整体框架说明
代码具体结构框架如下图所示:
在这里插入图片描述
在这里插入图片描述
主要函数介绍
1)状态定义:

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。

在这里插入图片描述

2) LocalizationWrapper构造函数
LocalizationWrapper构造函数主要实现加载参数、添加数据保存位置、订阅话题和发布话题

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);double x, y, z;nh.param("I_p_Gps_x", x, 0.);nh.param("I_p_Gps_y", y, 0.);nh.param("I_p_Gps_z", z, 0.);const Eigen::Vector3d I_p_Gps(x, y, z);std::string log_folder= "/home/sunshine/catkin_imu_gps_localization/src/imu_gps_localization";ros::param::get("log_folder", log_folder);// Log.file_state_.open(log_folder + "/state.csv");file_gps_.open(log_folder +"/gps.csv");// Initialization imu gps localizer.imu_gps_localizer_ptr_ = std::make_unique<ImuGpsLocalization::ImuGpsLocalizer>(acc_noise, gyro_noise,acc_bias_noise, gyro_bias_noise,I_p_Gps);// Subscribe topics.imu_sub_ = nh.subscribe("/imu/data", 10,  &LocalizationWrapper::ImuCallback, this);//TODO 运行数据集需要修改的地方: gps的话题为/fixgps_position_sub_ = nh.subscribe("/fix", 10,  &LocalizationWrapper::GpsPositionCallback, this);//发布融合后的轨迹state_pub_ = nh.advertise<nav_msgs::Path>("fused_path", 10);
}

3)滤波算法进行预测

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;// mid_point integration methods// 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) {// std::cout << "norm" << delta_angle_axis.norm() << "normlized"//           << delta_angle_axis.normalized() << std::endl;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值)。

协方差传递的Fx的计算,与《Quaternion Kinematics for the error-state KF》中的一致:
(误差的传递与nominal state的传递都是一样的,遵循的都是相同的运动学模型,只是误差会在之前的数值的基础上继续包括新增的各种运动误差,如imu的bias,随机游走等,而nominal则不管这些值,按照正常的运动学模型递推。)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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>

在这里插入图片描述
代码中:

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

在这里插入图片描述
也就是以下代码实现:

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_);
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第二项,其实就是真值对误差项目的求导,看下表:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

   // 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_);

http://www.ppmy.cn/news/74417.html

相关文章

springboot+jsp+javaweb学生信息管理系统 05hs

springboot是基于spring的快速开发框架, 相比于原生的spring而言, 它通过大量的java config来避免了大量的xml文件, 只需要简单的生成器便能生成一个可以运行的javaweb项目, 是目前最火热的java开发框架 &#xff08;1&#xff09;管理员模块&#xff1a;系统中的核心用户是管…

20.04Ubuntu换源:提升软件下载速度和更新效率

在使用Ubuntu操作系统时&#xff0c;一个常见的优化措施是更改软件源&#xff0c;以提高软件下载速度和更新效率。软件源是指存储软件包的服务器&#xff0c;通过更换软件源&#xff0c;你可以选择更靠近你所在地区的服务器&#xff0c;从而加快软件下载速度&#xff0c;并减少…

活字印刷。

题目描述 你有一套活字字模 tiles&#xff0c;其中每个字模上都刻有一个字母 tiles[i]。返回你可以印出的非空字母序列的数目。 注意&#xff1a;本题中&#xff0c;每个活字字模只能使用一次。 示例 1&#xff1a; 输入&#xff1a;“AAB” 输出&#xff1a;8 解释&#x…

2023-5-22第二十二天

回家一天&#xff0c;单词学习了但是没有记载 intergrate合并&#xff0c;整合 applied应用的&#xff0c;实用的 explicit明确的 modifier修饰语 customize定制 partition隔板&#xff0c;分隔&#xff0c;分裂 potentially潜在的 subset子集&#xff0c;分组&#xf…

ThreadLocal为什么容易内存泄露?

文章目录 一、Java的四种引用二、ThreadLocal为什么容易内存泄露&#xff1f;三、源码 一、Java的四种引用 1、强引用&#xff1a;强引用在程序内存不足&#xff08;OOM&#xff09;的时候也不会被回收 2、软引用&#xff1a;软引用在程序内存不足时&#xff0c;会被回收 3、弱…

日用行业外贸ERP软件系统,提高工作效率降低成本

日用行业是一个广泛的行业&#xff0c;包括了许多不同的产品&#xff0c;如家居用品、化妆品、个人护理用品、厨房用具等等。日用行业产品出口&#xff0c;也是我国传统外贸产业之一&#xff0c;在外贸市场来说相对有竞争力优势&#xff0c;在国际贸易中具有很大的需求和市场潜…

Android中的蓝牙技术

随着智能化生活的发展&#xff0c;手机成为人们生活的必需品&#xff0c;而蓝牙技术也随之应运而生。蓝牙技术作为现代移动设备与设备之间传输数据的一种主流方式&#xff0c;已经广泛应用于手表、耳机、车载系统等多种设备。在Android设备中&#xff0c;蓝牙技术也被大量使用&…

MATLAB画图相关操作

axis([x_min,x_max,y_min,y_max]) %设置坐标轴范围 set(gca,‘XTick’,[-1:0.2:1]) % 设置坐标刻度 xlabel(‘x轴数据’); ylabel(‘y轴数据’); title(‘标题’); legend(‘图例1’,‘图例2’) % 去掉图例边框 legend boxoff; % 法2 % 设置绘图外围颜色 set(gcf, ‘Colo…