当前位置:   article > 正文

LIO-SAM紧耦合imu阅读笔记_liosam imu因子作用

liosam imu因子作用

1.1 概述

        本文的书写参考了许多zzk大佬文章SLAM学习笔记(二十)LIO-SAM流程及代码详解(最全)以及知乎卢涛大佬的文章LIO-SAM源码解析(三):IMUPreintegration,在二者的基础上对源代码进行了梳理,关于GTSAM图优化部分的理解基本都是在学习两位大佬的解释,有读到这篇文章并在imu紧耦合方面有志于学的读者可以多多参考两位的文章。

1.2 整体梳理

        整体梳理了一遍发现还是对图优化的部分云里雾里看不太明白,目前仅仅是对整体流程有了一点理解,这种main函数的书写方式也确实是令我耳目一新,主要起作用的部分是两个类TransformFusion类和ImuPreintegration类,前者的功能是利用高帧率的imu数据实现了对低帧率的雷达点云数据的修正,替代了一般意义上松耦合imu对雷达点云的线性外推与旋转补正功能,节约内存和计算时间,写的很好;后者的功能是整个LIO-SAM代码的精髓内容,这里引用卢涛大佬的说法,用激光里程计,两帧激光里程计之间的imu预计分量构建因子图,优化当前帧的状态(包括位姿、速度、偏置),以优化后的状态为基础,施加imu预计分量,得到每一时刻的imu里程计。这部分是mapOptmization的一个前馈修正,里面关于imu预积分和构建因子图的方式十分的精炼高效,对初学者的学习有很大的帮助。

1.3 代码阅读笔记

        

  1. #include "utility.h"
  2. #include <gtsam/geometry/Rot3.h>
  3. #include <gtsam/geometry/Pose3.h>
  4. #include <gtsam/slam/PriorFactor.h>
  5. #include <gtsam/slam/BetweenFactor.h>
  6. #include <gtsam/navigation/GPSFactor.h>
  7. #include <gtsam/navigation/ImuFactor.h>
  8. #include <gtsam/navigation/CombinedImuFactor.h>
  9. #include <gtsam/nonlinear/NonlinearFactorGraph.h>
  10. #include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>
  11. #include <gtsam/nonlinear/Marginals.h>
  12. #include <gtsam/nonlinear/Values.h>
  13. #include <gtsam/inference/Symbol.h>
  14. #include <gtsam/nonlinear/ISAM2.h>
  15. #include <gtsam_unstable/nonlinear/IncrementalFixedLagSmoother.h>
  16. using gtsam::symbol_shorthand::X; // Pose3 (x,y,z,r,p,y)
  17. using gtsam::symbol_shorthand::V; // Vel (xdot,ydot,zdot)
  18. using gtsam::symbol_shorthand::B; // Bias (ax,ay,az,gx,gy,gz)
  19. //这个类主要是实现利用高帧率的imu数据对低帧率的雷达里程计进行精度修正
  20. class TransformFusion : public ParamServer
  21. {
  22. public:
  23. std::mutex mtx;
  24. ros::Subscriber subImuOdometry;
  25. ros::Subscriber subLaserOdometry;
  26. ros::Publisher pubImuOdometry;
  27. ros::Publisher pubImuPath;
  28. //激光里程计对应旋转变换矩阵
  29. Eigen::Affine3f lidarOdomAffine;
  30. //imu里程计队列中最早的一帧对应旋转变换矩阵
  31. Eigen::Affine3f imuOdomAffineFront;
  32. //imu里程计队列中最新的一帧对应旋转变换矩阵
  33. Eigen::Affine3f imuOdomAffineBack;
  34. tf::TransformListener tfListener;
  35. tf::StampedTransform lidar2Baselink;
  36. double lidarOdomTime = -1;
  37. deque<nav_msgs::Odometry> imuOdomQueue;
  38. TransformFusion()
  39. {
  40. if(lidarFrame != baselinkFrame)
  41. {
  42. try
  43. {
  44. tfListener.waitForTransform(lidarFrame, baselinkFrame, ros::Time(0), ros::Duration(3.0));
  45. tfListener.lookupTransform(lidarFrame, baselinkFrame, ros::Time(0), lidar2Baselink);
  46. }
  47. catch (tf::TransformException ex)
  48. {
  49. ROS_ERROR("%s",ex.what());
  50. }
  51. }
  52. subLaserOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry", 5, &TransformFusion::lidarOdometryHandler, this, ros::TransportHints().tcpNoDelay());
  53. subImuOdometry = nh.subscribe<nav_msgs::Odometry>(odomTopic+"_incremental", 2000, &TransformFusion::imuOdometryHandler, this, ros::TransportHints().tcpNoDelay());
  54. pubImuOdometry = nh.advertise<nav_msgs::Odometry>(odomTopic, 2000);
  55. pubImuPath = nh.advertise<nav_msgs::Path> ("lio_sam/imu/path", 1);
  56. }
  57. //将里程计里面的位姿信息转化为旋转变换矩阵
  58. Eigen::Affine3f odom2affine(nav_msgs::Odometry odom)
  59. {
  60. //xyz信息直接从里程计中提取
  61. double x, y, z, roll, pitch, yaw;
  62. x = odom.pose.pose.position.x;
  63. y = odom.pose.pose.position.y;
  64. z = odom.pose.pose.position.z;
  65. //rpy角信息先从里程计中获取四元数,利用tf库转换为3X3的矩阵,然后再转化为rpy三个角
  66. tf::Quaternion orientation;
  67. tf::quaternionMsgToTF(odom.pose.pose.orientation, orientation);
  68. tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);
  69. //将给定的xyzrpy角创建平移旋转变换矩阵
  70. return pcl::getTransformation(x, y, z, roll, pitch, yaw);
  71. }
  72. //雷达里程计回调函数,将位姿信息转化为旋转变换矩阵
  73. void lidarOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  74. {
  75. std::lock_guard<std::mutex> lock(mtx);
  76. lidarOdomAffine = odom2affine(*odomMsg);
  77. lidarOdomTime = odomMsg->header.stamp.toSec();
  78. }
  79. //imu里程计回调函数,输入里程计信息 核心功能是保存imu队列,并计算imu里程计增量变换,用得到的增量旋转变换矩阵来修正雷达里程计,提高精度
  80. void imuOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  81. {
  82. //创建一个tf发布对象tfMap2Odom,创建一个tf对象map_to_odom,初始值全部是0 这块我没理清楚,感觉里面并没有赋值的过程,一直在发0,也就是两个坐标系重合在一块?
  83. static tf::TransformBroadcaster tfMap2Odom;
  84. static tf::Transform map_to_odom = tf::Transform(tf::createQuaternionFromRPY(0, 0, 0), tf::Vector3(0, 0, 0));
  85. //通过sendTransform发布tf变换
  86. //tf::StampedTransform是tf的一种特殊情况:它需要frame_id和stamp以及child_frame_id
  87. tfMap2Odom.sendTransform(tf::StampedTransform(map_to_odom, odomMsg->header.stamp, mapFrame, odometryFrame));
  88. std::lock_guard<std::mutex> lock(mtx);
  89. //imu里程计装入最新一帧
  90. imuOdomQueue.push_back(*odomMsg);
  91. //保证雷达里程计里面有数,没有的话直接返回了,只有初始化的时候触发一次
  92. if (lidarOdomTime == -1)
  93. return;
  94. while (!imuOdomQueue.empty())
  95. {
  96. //把imu里程计中,时间戳小于当前雷达里程计时间戳的数据全都pop出去
  97. if (imuOdomQueue.front().header.stamp.toSec() <= lidarOdomTime)
  98. imuOdomQueue.pop_front();
  99. else
  100. break;
  101. }
  102. //imu里程计队列中最早的一帧,也就是和当前雷达里程计时间最接近的那一帧,转化为旋转矩阵
  103. Eigen::Affine3f imuOdomAffineFront = odom2affine(imuOdomQueue.front());
  104. //imu里程计队列中最新的一帧,转化为旋转矩阵
  105. Eigen::Affine3f imuOdomAffineBack = odom2affine(imuOdomQueue.back());
  106. //矩阵运算front到back的旋转变换量,T_back = T_front * T_inc,那么T_inc = T_front逆 * T_back
  107. Eigen::Affine3f imuOdomAffineIncre = imuOdomAffineFront.inverse() * imuOdomAffineBack;
  108. //矩阵运算,将雷达里程计根据imu里程计的增量作修正 T_lidar_now = T_lidar * T_inc
  109. Eigen::Affine3f imuOdomAffineLast = lidarOdomAffine * imuOdomAffineIncre;
  110. float x, y, z, roll, pitch, yaw;
  111. //把上面修正过的雷达里程计中的xyzrpy取出来,进行后面的运算
  112. pcl::getTranslationAndEulerAngles(imuOdomAffineLast, x, y, z, roll, pitch, yaw);
  113. //把修正过的雷达里程计发布出来,初值用的是imuOdomQueue,这个我个人感觉无所谓,用空的nav_msgs::Odometry应该也可以
  114. nav_msgs::Odometry laserOdometry = imuOdomQueue.back();
  115. laserOdometry.pose.pose.position.x = x;
  116. laserOdometry.pose.pose.position.y = y;
  117. laserOdometry.pose.pose.position.z = z;
  118. laserOdometry.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
  119. pubImuOdometry.publish(laserOdometry);
  120. //发布修正过后的雷达里程计到baselink的tf变换
  121. static tf::TransformBroadcaster tfOdom2BaseLink;
  122. //修正过后的雷达里程计装入tCur
  123. tf::Transform tCur;
  124. tf::poseMsgToTF(laserOdometry.pose.pose, tCur);
  125. if(lidarFrame != baselinkFrame)
  126. tCur = tCur * lidar2Baselink;
  127. tf::StampedTransform odom_2_baselink = tf::StampedTransform(tCur, odomMsg->header.stamp, odometryFrame, baselinkFrame);
  128. tfOdom2BaseLink.sendTransform(odom_2_baselink);
  129. //发布imu里程计path,用来在rviz实时显示轨迹,不过根据while中的删选条件能够看到显示的是一秒内的imu轨迹
  130. static nav_msgs::Path imuPath;
  131. static double last_path_time = -1;
  132. double imuTime = imuOdomQueue.back().header.stamp.toSec();
  133. if (imuTime - last_path_time > 0.1)
  134. {
  135. last_path_time = imuTime;
  136. geometry_msgs::PoseStamped pose_stamped;
  137. pose_stamped.header.stamp = imuOdomQueue.back().header.stamp;
  138. pose_stamped.header.frame_id = odometryFrame;
  139. pose_stamped.pose = laserOdometry.pose.pose;
  140. imuPath.poses.push_back(pose_stamped);
  141. while(!imuPath.poses.empty() && imuPath.poses.front().header.stamp.toSec() < lidarOdomTime - 1.0)
  142. imuPath.poses.erase(imuPath.poses.begin());
  143. if (pubImuPath.getNumSubscribers() != 0)
  144. {
  145. imuPath.header.stamp = imuOdomQueue.back().header.stamp;
  146. imuPath.header.frame_id = odometryFrame;
  147. pubImuPath.publish(imuPath);
  148. }
  149. }
  150. }
  151. };
  152. //这个类的主要功能是构建因子图,把imu加入其中,实现紧耦合
  153. class IMUPreintegration : public ParamServer
  154. {
  155. public:
  156. std::mutex mtx;
  157. ros::Subscriber subImu;
  158. ros::Subscriber subOdometry;
  159. ros::Publisher pubImuOdometry;
  160. bool systemInitialized = false;
  161. //gtsam构建的白噪声
  162. gtsam::noiseModel::Diagonal::shared_ptr priorPoseNoise;
  163. gtsam::noiseModel::Diagonal::shared_ptr priorVelNoise;
  164. gtsam::noiseModel::Diagonal::shared_ptr priorBiasNoise;
  165. gtsam::noiseModel::Diagonal::shared_ptr correctionNoise;
  166. gtsam::noiseModel::Diagonal::shared_ptr correctionNoise2;
  167. gtsam::Vector noiseModelBetweenBias;
  168. //imu预积分器,用来记录两帧激光里程计之间的imu数据预积分
  169. gtsam::PreintegratedImuMeasurements *imuIntegratorOpt_;
  170. //imu预积分器,根据最新接收到的激光雷达里程计,预测下一帧激光雷达里程计到达时的imu积分增量,用作因子图优化
  171. gtsam::PreintegratedImuMeasurements *imuIntegratorImu_;
  172. std::deque<sensor_msgs::Imu> imuQueOpt;
  173. std::deque<sensor_msgs::Imu> imuQueImu;
  174. gtsam::Pose3 prevPose_;
  175. gtsam::Vector3 prevVel_;
  176. gtsam::NavState prevState_;
  177. gtsam::imuBias::ConstantBias prevBias_;
  178. gtsam::NavState prevStateOdom;
  179. gtsam::imuBias::ConstantBias prevBiasOdom;
  180. bool doneFirstOpt = false;
  181. double lastImuT_imu = -1;
  182. double lastImuT_opt = -1;
  183. gtsam::ISAM2 optimizer;
  184. gtsam::NonlinearFactorGraph graphFactors;
  185. gtsam::Values graphValues;
  186. const double delta_t = 0;
  187. int key = 1;
  188. // T_bl:雷达坐标系转化到imu坐标系
  189. gtsam::Pose3 imu2Lidar = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(-extTrans.x(), -extTrans.y(), -extTrans.z()));
  190. // T_lb:imu坐标系转化到雷达坐标系
  191. gtsam::Pose3 lidar2Imu = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(extTrans.x(), extTrans.y(), extTrans.z()));
  192. IMUPreintegration()
  193. {
  194. subImu = nh.subscribe<sensor_msgs::Imu> (imuTopic, 2000, &IMUPreintegration::imuHandler, this, ros::TransportHints().tcpNoDelay());
  195. subOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry_incremental", 5, &IMUPreintegration::odometryHandler, this, ros::TransportHints().tcpNoDelay());
  196. pubImuOdometry = nh.advertise<nav_msgs::Odometry> (odomTopic+"_incremental", 2000);
  197. boost::shared_ptr<gtsam::PreintegrationParams> p = gtsam::PreintegrationParams::MakeSharedU(imuGravity);
  198. p->accelerometerCovariance = gtsam::Matrix33::Identity(3,3) * pow(imuAccNoise, 2); // acc white noise in continuous
  199. p->gyroscopeCovariance = gtsam::Matrix33::Identity(3,3) * pow(imuGyrNoise, 2); // gyro white noise in continuous
  200. p->integrationCovariance = gtsam::Matrix33::Identity(3,3) * pow(1e-4, 2); // error committed in integrating position from velocities
  201. gtsam::imuBias::ConstantBias prior_imu_bias((gtsam::Vector(6) << 0, 0, 0, 0, 0, 0).finished());; // assume zero initial bias
  202. priorPoseNoise = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << 1e-2, 1e-2, 1e-2, 1e-2, 1e-2, 1e-2).finished()); // rad,rad,rad,m, m, m
  203. priorVelNoise = gtsam::noiseModel::Isotropic::Sigma(3, 1e4); // m/s
  204. priorBiasNoise = gtsam::noiseModel::Isotropic::Sigma(6, 1e-3); // 1e-2 ~ 1e-3 seems to be good
  205. correctionNoise = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << 0.05, 0.05, 0.05, 0.1, 0.1, 0.1).finished()); // rad,rad,rad,m, m, m
  206. correctionNoise2 = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << 1, 1, 1, 1, 1, 1).finished()); // rad,rad,rad,m, m, m
  207. noiseModelBetweenBias = (gtsam::Vector(6) << imuAccBiasN, imuAccBiasN, imuAccBiasN, imuGyrBiasN, imuGyrBiasN, imuGyrBiasN).finished();
  208. imuIntegratorImu_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for IMU message thread
  209. imuIntegratorOpt_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for optimization
  210. }
  211. //重置ISAM优化器
  212. void resetOptimization()
  213. {
  214. gtsam::ISAM2Params optParameters;
  215. optParameters.relinearizeThreshold = 0.1;
  216. optParameters.relinearizeSkip = 1;
  217. optimizer = gtsam::ISAM2(optParameters);
  218. gtsam::NonlinearFactorGraph newGraphFactors;
  219. graphFactors = newGraphFactors;
  220. gtsam::Values NewGraphValues;
  221. graphValues = NewGraphValues;
  222. }
  223. //重置参数,主要是几个回掉函数的标志位
  224. void resetParams()
  225. {
  226. lastImuT_imu = -1;
  227. doneFirstOpt = false;
  228. systemInitialized = false;
  229. }
  230. //激光里程计回调函数
  231. void odometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  232. {
  233. std::lock_guard<std::mutex> lock(mtx);
  234. double currentCorrectionTime = ROS_TIME(odomMsg);
  235. // make sure we have imu data to integrate
  236. if (imuQueOpt.empty())
  237. return;
  238. float p_x = odomMsg->pose.pose.position.x;
  239. float p_y = odomMsg->pose.pose.position.y;
  240. float p_z = odomMsg->pose.pose.position.z;
  241. float r_x = odomMsg->pose.pose.orientation.x;
  242. float r_y = odomMsg->pose.pose.orientation.y;
  243. float r_z = odomMsg->pose.pose.orientation.z;
  244. float r_w = odomMsg->pose.pose.orientation.w;
  245. bool degenerate = (int)odomMsg->pose.covariance[0] == 1 ? true : false;
  246. gtsam::Pose3 lidarPose = gtsam::Pose3(gtsam::Rot3::Quaternion(r_w, r_x, r_y, r_z), gtsam::Point3(p_x, p_y, p_z));
  247. //初始化
  248. if (systemInitialized == false)
  249. {
  250. resetOptimization();
  251. //delta_t在类中声明为0,所以实质上是删除imu队列中早于激光雷达里程计时间的帧
  252. while (!imuQueOpt.empty())
  253. {
  254. if (ROS_TIME(&imuQueOpt.front()) < currentCorrectionTime - delta_t)
  255. {
  256. lastImuT_opt = ROS_TIME(&imuQueOpt.front());
  257. imuQueOpt.pop_front();
  258. }
  259. else
  260. break;
  261. }
  262. //雷达坐标系转化到imu坐标系
  263. prevPose_ = lidarPose.compose(lidar2Imu);
  264. gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, priorPoseNoise);
  265. graphFactors.add(priorPose);
  266. //速度因子
  267. prevVel_ = gtsam::Vector3(0, 0, 0);
  268. gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, priorVelNoise);
  269. graphFactors.add(priorVel);
  270. //噪声因子
  271. prevBias_ = gtsam::imuBias::ConstantBias();
  272. gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, priorBiasNoise);
  273. graphFactors.add(priorBias);
  274. //graphValues赋值,把构建好的graphFactors赋进去
  275. graphValues.insert(X(0), prevPose_);
  276. graphValues.insert(V(0), prevVel_);
  277. graphValues.insert(B(0), prevBias_);
  278. //进行一次图优化
  279. optimizer.update(graphFactors, graphValues);
  280. graphFactors.resize(0);
  281. graphValues.clear();
  282. //reset
  283. imuIntegratorImu_->resetIntegrationAndSetBias(prevBias_);
  284. imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);
  285. key = 1;
  286. systemInitialized = true;
  287. return;
  288. }
  289. //每当前面的优化进行一百次,将imu积分器内的数全部清0,减少累计误差
  290. if (key == 100)
  291. {
  292. // get updated noise before reset
  293. gtsam::noiseModel::Gaussian::shared_ptr updatedPoseNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(X(key-1)));
  294. gtsam::noiseModel::Gaussian::shared_ptr updatedVelNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(V(key-1)));
  295. gtsam::noiseModel::Gaussian::shared_ptr updatedBiasNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(B(key-1)));
  296. // reset graph
  297. resetOptimization();
  298. // add pose
  299. gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, updatedPoseNoise);
  300. graphFactors.add(priorPose);
  301. // add velocity
  302. gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, updatedVelNoise);
  303. graphFactors.add(priorVel);
  304. // add bias
  305. gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, updatedBiasNoise);
  306. graphFactors.add(priorBias);
  307. // add values
  308. graphValues.insert(X(0), prevPose_);
  309. graphValues.insert(V(0), prevVel_);
  310. graphValues.insert(B(0), prevBias_);
  311. // optimize once
  312. optimizer.update(graphFactors, graphValues);
  313. graphFactors.resize(0);
  314. graphValues.clear();
  315. key = 1;
  316. }
  317. //引用知乎卢涛大佬的解释,计算前一帧与当前帧之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态
  318. while (!imuQueOpt.empty())
  319. {
  320. //delta_t在类中声明为0,所以实质上是删除imu队列中早于激光雷达里程计时间的帧
  321. sensor_msgs::Imu *thisImu = &imuQueOpt.front();
  322. double imuTime = ROS_TIME(thisImu);
  323. if (imuTime < currentCorrectionTime - delta_t)
  324. {
  325. //将imu信息的线加速度与角速度加入预积分器中
  326. double dt = (lastImuT_opt < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_opt);
  327. imuIntegratorOpt_->integrateMeasurement(
  328. gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),
  329. gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt);
  330. lastImuT_opt = imuTime;
  331. imuQueOpt.pop_front();
  332. }
  333. else
  334. break;
  335. }
  336. //imufactor中存入前一帧位姿与速度,当前帧位姿与速度,以及之前形成的imu预计分量
  337. const gtsam::PreintegratedImuMeasurements& preint_imu = dynamic_cast<const gtsam::PreintegratedImuMeasurements&>(*imuIntegratorOpt_);
  338. gtsam::ImuFactor imu_factor(X(key - 1), V(key - 1), X(key), V(key), B(key - 1), preint_imu);
  339. graphFactors.add(imu_factor);
  340. //add imu bias between factor
  341. graphFactors.add(gtsam::BetweenFactor<gtsam::imuBias::ConstantBias>(B(key - 1), B(key), gtsam::imuBias::ConstantBias(),
  342. gtsam::noiseModel::Diagonal::Sigmas(sqrt(imuIntegratorOpt_->deltaTij()) * noiseModelBetweenBias)));
  343. // add pose factor
  344. gtsam::Pose3 curPose = lidarPose.compose(lidar2Imu);
  345. gtsam::PriorFactor<gtsam::Pose3> pose_factor(X(key), curPose, degenerate ? correctionNoise2 : correctionNoise);
  346. graphFactors.add(pose_factor);
  347. // insert predicted values
  348. gtsam::NavState propState_ = imuIntegratorOpt_->predict(prevState_, prevBias_);
  349. graphValues.insert(X(key), propState_.pose());
  350. graphValues.insert(V(key), propState_.v());
  351. graphValues.insert(B(key), prevBias_);
  352. // optimize
  353. optimizer.update(graphFactors, graphValues);
  354. optimizer.update();
  355. graphFactors.resize(0);
  356. graphValues.clear();
  357. // Overwrite the beginning of the preintegration for the next step.
  358. //更新位姿、速度、偏置等优化结果
  359. gtsam::Values result = optimizer.calculateEstimate();
  360. prevPose_ = result.at<gtsam::Pose3>(X(key));
  361. prevVel_ = result.at<gtsam::Vector3>(V(key));
  362. prevState_ = gtsam::NavState(prevPose_, prevVel_);
  363. prevBias_ = result.at<gtsam::imuBias::ConstantBias>(B(key));
  364. // Reset the optimization preintegration object.
  365. imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);
  366. // check optimization
  367. if (failureDetection(prevVel_, prevBias_))
  368. {
  369. resetParams();
  370. return;
  371. }
  372. // 2. after optiization, re-propagate imu odometry preintegration
  373. //这两个变量在imu回调函数里用到了,用来预测当前帧的位姿
  374. prevStateOdom = prevState_;
  375. prevBiasOdom = prevBias_;
  376. // first pop imu message older than current correction data
  377. double lastImuQT = -1;
  378. //引用zzk大佬的解释
  379. //注意,这里是要“删除”当前帧“之前”的imu数据,是想根据当前帧“之后”的累积递推。
  380. //而前面imuIntegratorOpt_做的事情是,“提取”当前帧“之前”的imu数据,用两帧之间的imu数据进行积分。处理过的就弹出来。
  381. //因此,新到一帧激光帧里程计数据时,imuQueOpt队列变化如下:
  382. //当前帧之前的数据被提出来做积分,用一个删一个(这样下一帧到达后,队列中就不会有现在这帧之前的数据了)
  383. //那么在更新完以后,imuQueOpt队列不再变化,剩下的原始imu数据用作下一次优化时的数据。
  384. //而imuQueImu队列则是把当前帧之前的imu数据都给直接剔除掉,仅保留当前帧之后的imu数据,
  385. //用作两帧lidar里程计到达时刻之间发布的imu增量式里程计的预测。
  386. //imuQueImu和imuQueOpt的区别要明确,imuIntegratorImu_和imuIntegratorOpt_的区别也要明确
  387. while (!imuQueImu.empty() && ROS_TIME(&imuQueImu.front()) < currentCorrectionTime - delta_t)
  388. {
  389. lastImuQT = ROS_TIME(&imuQueImu.front());
  390. imuQueImu.pop_front();
  391. }
  392. // repropogate
  393. if (!imuQueImu.empty())
  394. {
  395. // reset bias use the newly optimized bias
  396. imuIntegratorImu_->resetIntegrationAndSetBias(prevBiasOdom);
  397. // integrate imu message from the beginning of this optimization
  398. for (int i = 0; i < (int)imuQueImu.size(); ++i)
  399. {
  400. sensor_msgs::Imu *thisImu = &imuQueImu[i];
  401. double imuTime = ROS_TIME(thisImu);
  402. double dt = (lastImuQT < 0) ? (1.0 / 500.0) :(imuTime - lastImuQT);
  403. imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),
  404. gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt);
  405. lastImuQT = imuTime;
  406. }
  407. }
  408. ++key;
  409. doneFirstOpt = true;
  410. }
  411. bool failureDetection(const gtsam::Vector3& velCur, const gtsam::imuBias::ConstantBias& biasCur)
  412. {
  413. Eigen::Vector3f vel(velCur.x(), velCur.y(), velCur.z());
  414. if (vel.norm() > 30)
  415. {
  416. ROS_WARN("Large velocity, reset IMU-preintegration!");
  417. return true;
  418. }
  419. Eigen::Vector3f ba(biasCur.accelerometer().x(), biasCur.accelerometer().y(), biasCur.accelerometer().z());
  420. Eigen::Vector3f bg(biasCur.gyroscope().x(), biasCur.gyroscope().y(), biasCur.gyroscope().z());
  421. if (ba.norm() > 1.0 || bg.norm() > 1.0)
  422. {
  423. ROS_WARN("Large bias, reset IMU-preintegration!");
  424. return true;
  425. }
  426. return false;
  427. }
  428. //imu回调函数
  429. void imuHandler(const sensor_msgs::Imu::ConstPtr& imu_raw)
  430. {
  431. std::lock_guard<std::mutex> lock(mtx);
  432. //imuConverter函数定义在utility.h文件里,把imu的数据旋转到前左上坐标系下
  433. sensor_msgs::Imu thisImu = imuConverter(*imu_raw);
  434. imuQueOpt.push_back(thisImu);
  435. imuQueImu.push_back(thisImu);
  436. //odometryHandler结束标志位
  437. if (doneFirstOpt == false)
  438. return;
  439. double imuTime = ROS_TIME(&thisImu);
  440. double dt = (lastImuT_imu < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_imu);
  441. lastImuT_imu = imuTime;
  442. // gtsam中的imu预积分器,将thisImu里的数据和dt存进去
  443. imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu.linear_acceleration.x, thisImu.linear_acceleration.y, thisImu.linear_acceleration.z),
  444. gtsam::Vector3(thisImu.angular_velocity.x, thisImu.angular_velocity.y, thisImu.angular_velocity.z), dt);
  445. // predict odometry
  446. gtsam::NavState currentState = imuIntegratorImu_->predict(prevStateOdom, prevBiasOdom);
  447. //发布imu里程计
  448. nav_msgs::Odometry odometry;
  449. odometry.header.stamp = thisImu.header.stamp;
  450. odometry.header.frame_id = odometryFrame;
  451. odometry.child_frame_id = "odom_imu";
  452. // transform imu pose to ldiar
  453. gtsam::Pose3 imuPose = gtsam::Pose3(currentState.quaternion(), currentState.position());
  454. gtsam::Pose3 lidarPose = imuPose.compose(imu2Lidar);
  455. odometry.pose.pose.position.x = lidarPose.translation().x();
  456. odometry.pose.pose.position.y = lidarPose.translation().y();
  457. odometry.pose.pose.position.z = lidarPose.translation().z();
  458. odometry.pose.pose.orientation.x = lidarPose.rotation().toQuaternion().x();
  459. odometry.pose.pose.orientation.y = lidarPose.rotation().toQuaternion().y();
  460. odometry.pose.pose.orientation.z = lidarPose.rotation().toQuaternion().z();
  461. odometry.pose.pose.orientation.w = lidarPose.rotation().toQuaternion().w();
  462. odometry.twist.twist.linear.x = currentState.velocity().x();
  463. odometry.twist.twist.linear.y = currentState.velocity().y();
  464. odometry.twist.twist.linear.z = currentState.velocity().z();
  465. odometry.twist.twist.angular.x = thisImu.angular_velocity.x + prevBiasOdom.gyroscope().x();
  466. odometry.twist.twist.angular.y = thisImu.angular_velocity.y + prevBiasOdom.gyroscope().y();
  467. odometry.twist.twist.angular.z = thisImu.angular_velocity.z + prevBiasOdom.gyroscope().z();
  468. pubImuOdometry.publish(odometry);
  469. }
  470. };
  471. int main(int argc, char** argv)
  472. {
  473. ros::init(argc, argv, "roboat_loam");
  474. IMUPreintegration ImuP;
  475. TransformFusion TF;
  476. ROS_INFO("\033[1;32m----> IMU Preintegration Started.\033[0m");
  477. ros::MultiThreadedSpinner spinner(4);
  478. spinner.spin();
  479. return 0;
  480. }

1.4后记

        LIO-SAM中的imu紧耦合激光雷达SLAM的方式是作者Tixiao Shan做这一套系统的核心,其他部分与LeGO-LOAM并无太大区别,作者后续又在这套系统的基础上加入了视觉系统即LVI-SAM,也做的十分优秀,值得学习。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/116688
推荐阅读
相关标签
  

闽ICP备14008679号