当前位置:   article > 正文

LIO-SAM源码解析(四):imuPreintegration.cpp

imupreintegration.cpp

1. 代码流程

2. 功能说明

这个cpp文件主要有两个类,一个叫IMUPreintegration类,一个叫TransformFusion类。

现在我们分开讲,先说IMUPreintegration类。

关于IMU原始数据,送入imuhandle中:

2.1. imuhandle

imu原始数据,会先被坐标系转换,通过调用头文件里的imuConverter函数,转换到一个“雷达中间系”中,其实和真正的雷达系差了一个平移。

转换后,会存入两个队列,一个imuQueOpt队列,一个imuQueImu队列。这两队列有什么区别和联系呢?这个主要在另一个回调函数odometryHandler会被处理,在那个地方我会讲。这里我们可以先理解为,imuQueImu是真正我们要用的数据,imuQueOpt是一个中间缓存的数据结构,用来优化imu的bias之类的东西。

标志位doneFirstOpt为True的时候(注意这个标志位,这是一个很重要的变量,之后会再提到),每到一个imu数据,就用imuIntegratorImu_这个预积分器,把这一帧imu数据累积进去,然后预测当前时刻的状态:currentState = imuIntegratorImu_->predict(prevStateOdom, prevBiasOdom); 其中prevStateOdom就是之前系统所保持的状态。

把currentState,通过imu2Lidar,从“中间系”给平移到真正的雷达系,然后发布出去。发布的话题就叫odometry/imu_incremental,这也就是imageProjection.cpp的总结部分的第2点部分提到的“imu”里程计

2.2. odomHandle

这部分订阅的是/mapping/odometry_incremental,这个话题是由mapOptmization.cpp发布的,可以把它理解为激光里程计。同理,也不要被incremental误导,觉得好像是两帧激光之间的变换,可不是这样的啊。它和imu里程计性质类似,就是相对世界坐标系的位姿。

2.2.1. 初始化系统

从imuQueOpt队列里,删掉当前这帧激光里程计时间上之前的数据,然后把雷达的pose变换到“中间系”,保存为prevPose。图优化部分,加入乱七八糟各种东西,priorVel,priorBias,把两个预积分器imuIntegratorImu_,imuIntegratorOpt_给reset一下。(之后简称imu积分器和opt积分器)

这两个预积分器opt积分器和imu积分器有什么区别呢?马上就要讲,在1.2.3部分。

2.2.2. 清理缓存

100帧激光里程计数据了,就把优化器给reset一下(用前面的先验协方差给reset一下)。注意,1.2.1和1.2.2的主要区别在于,1.2.1中的乱七八糟协方差数据,是用构造函数中写入的一大堆(我认为是经验值),而1.2.2这里的协方差,用的是optimizer.marginalCovariance这个轮子算出来的先验协方差。

2.2.3. 正式处理

假设数据如下分布:

之前imu数据 ——————第一帧开始——————第二帧开始————之后imu数据

把“第一帧开始”——“第二帧开始”这个之间的imu数据拿出来,送入opt积分器。这样得到二者之间的预积分,构建imu因子。

然后把Xkey-1 到Xkey之间,加入这个imu因子以及 激光里程计提供的pose因子,整体做一个优化。优化的结果就是bias,以及“第二帧开始”这个时刻的系统位姿。

把优化的结果(主要是bias),重置opt积分器和imu积分器。 然后把当前帧(上图的“第二帧开始”)之前的数据给删掉,用imu积分器,从“第二帧开始”这里开始往后积分。(我们需要明确一点,在这个处理过程中,imu队列也在持续的进数据,(即1.1的imuhandle中)),这里处理完,那么就置donefirst=True,这样1.1.3部分,就可以无缝衔接接着在这里的基础上对imu积分器继续积分。(可以看出,这点处理,Tixiaoshan还是做的比较牛的)

 回顾:在1.1.3部分,发布imu里程计,在这里我们可以的出结果,它并非是纯粹的imu里程计,因为时不时是要加入激光里程计的信息做因子来优化得到imu的bias等信息的。

2.3. TransformFusion类。

2.3.1 lidarOdometryHandler

这部分监听的是/mapping/odometry,(也就是激光雷达里程计)这个回调函数比较特殊,它并没有把雷达里程计的东西再像别的回调函数一样,时刻存到什么队列里去。而是就保存当前的雷达里程计的数据到lidarOdomAffine里面,把时间戳存到lidarOdomTime里面去。

    注意,这里/mapping/odometry和/mapping/odometry_incremental有什么区别呢?我认为本质上区别不大,前者是激光里程计部分直接优化得到的激光位姿,后者相当于激光的位姿经过旋转约束和z轴约束的限制以后,又和原始imu信息里面的角度做了一个加权以后的位姿。

2.3.2 imuOdometryHandler

这部分监听的是/imu/odometry_incremental(也就是上面我一直在说的imu里程计),把imu里程计放到imuodomQueue里面保存,然后把lidarOdomTime之前的数据扔掉,用imu里程计的两时刻差异,再加上lidarOdomAffine的基础进行发布。

实际上,/imu/odometry_incremental本身就是雷达里程计基础上imu数据上的发布,而在现在这里,也是雷达里程计在“imu里程计”上的一个“再次精修”。最后会发布两个内容,一个是odometry/imu,但是这个实际上是无人监听的,我觉得作者主要是发布tf变换,顺手给它发布了。当然我觉得用户可以监听这个数据,我觉得这个应该是频率上最高的一个里程计数据了。

另外还会发布一个path,用来rviz显示,名字叫lio-sam/imu/path。

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. class TransformFusion : public ParamServer
  20. {
  21. public:
  22. std::mutex mtx;
  23. ros::Subscriber subImuOdometry;
  24. ros::Subscriber subLaserOdometry;
  25. ros::Publisher pubImuOdometry;
  26. ros::Publisher pubImuPath;
  27. Eigen::Affine3f lidarOdomAffine;
  28. Eigen::Affine3f imuOdomAffineFront;
  29. Eigen::Affine3f imuOdomAffineBack;
  30. tf::TransformListener tfListener;
  31. tf::StampedTransform lidar2Baselink;
  32. double lidarOdomTime = -1;
  33. deque<nav_msgs::Odometry> imuOdomQueue;
  34. /**
  35. * 构造函数
  36. */
  37. TransformFusion()
  38. {
  39. // 如果lidar系与baselink系不同(激光系和载体系),需要外部提供二者之间的变换关系
  40. if(lidarFrame != baselinkFrame)
  41. {
  42. try
  43. {
  44. // 等待3s
  45. tfListener.waitForTransform(lidarFrame, baselinkFrame, ros::Time(0), ros::Duration(3.0));
  46. // lidar系到baselink系的变换
  47. tfListener.lookupTransform(lidarFrame, baselinkFrame, ros::Time(0), lidar2Baselink);
  48. }
  49. catch (tf::TransformException ex)
  50. {
  51. ROS_ERROR("%s",ex.what());
  52. }
  53. }
  54. // 订阅激光里程计,来自mapOptimization
  55. subLaserOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry", 5, &TransformFusion::lidarOdometryHandler, this, ros::TransportHints().tcpNoDelay());
  56. // 订阅imu里程计,来自IMUPreintegration
  57. subImuOdometry = nh.subscribe<nav_msgs::Odometry>(odomTopic+"_incremental", 2000, &TransformFusion::imuOdometryHandler, this, ros::TransportHints().tcpNoDelay());
  58. // 发布imu里程计,用于rviz展示
  59. pubImuOdometry = nh.advertise<nav_msgs::Odometry>(odomTopic, 2000);
  60. // 发布imu里程计轨迹
  61. pubImuPath = nh.advertise<nav_msgs::Path> ("lio_sam/imu/path", 1);
  62. }
  63. /**
  64. * 里程计对应变换矩阵
  65. */
  66. Eigen::Affine3f odom2affine(nav_msgs::Odometry odom)
  67. {
  68. double x, y, z, roll, pitch, yaw;
  69. x = odom.pose.pose.position.x;
  70. y = odom.pose.pose.position.y;
  71. z = odom.pose.pose.position.z;
  72. tf::Quaternion orientation;
  73. tf::quaternionMsgToTF(odom.pose.pose.orientation, orientation);
  74. tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);
  75. return pcl::getTransformation(x, y, z, roll, pitch, yaw);
  76. }
  77. /**
  78. * 订阅激光里程计,来自mapOptimization
  79. */
  80. void lidarOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  81. {
  82. std::lock_guard<std::mutex> lock(mtx);
  83. // 激光里程计对应变换矩阵
  84. lidarOdomAffine = odom2affine(*odomMsg);
  85. // 激光里程计时间戳
  86. lidarOdomTime = odomMsg->header.stamp.toSec();
  87. }
  88. /**
  89. * 订阅imu里程计,来自IMUPreintegration
  90. * 1、以最近一帧激光里程计位姿为基础,计算该时刻与当前时刻间imu里程计增量位姿变换,相乘得到当前时刻imu里程计位姿
  91. * 2、发布当前时刻里程计位姿,用于rviz展示;发布imu里程计路径,注:只是最近一帧激光里程计时刻与当前时刻之间的一段
  92. */
  93. void imuOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  94. {
  95. // 发布tf,map与odom系设为同一个系
  96. static tf::TransformBroadcaster tfMap2Odom;
  97. static tf::Transform map_to_odom = tf::Transform(tf::createQuaternionFromRPY(0, 0, 0), tf::Vector3(0, 0, 0));
  98. tfMap2Odom.sendTransform(tf::StampedTransform(map_to_odom, odomMsg->header.stamp, mapFrame, odometryFrame));
  99. std::lock_guard<std::mutex> lock(mtx);
  100. // 添加imu里程计到队列
  101. imuOdomQueue.push_back(*odomMsg);
  102. // 从imu里程计队列中删除当前(最近的一帧)激光里程计时刻之前的数据
  103. if (lidarOdomTime == -1)
  104. return;
  105. while (!imuOdomQueue.empty())
  106. {
  107. if (imuOdomQueue.front().header.stamp.toSec() <= lidarOdomTime)
  108. imuOdomQueue.pop_front();
  109. else
  110. break;
  111. }
  112. // 最近的一帧激光里程计时刻对应imu里程计位姿
  113. Eigen::Affine3f imuOdomAffineFront = odom2affine(imuOdomQueue.front());
  114. // 当前时刻imu里程计位姿
  115. Eigen::Affine3f imuOdomAffineBack = odom2affine(imuOdomQueue.back());
  116. // imu里程计增量位姿变换
  117. Eigen::Affine3f imuOdomAffineIncre = imuOdomAffineFront.inverse() * imuOdomAffineBack;
  118. // 最近的一帧激光里程计位姿 * imu里程计增量位姿变换 = 当前时刻imu里程计位姿
  119. Eigen::Affine3f imuOdomAffineLast = lidarOdomAffine * imuOdomAffineIncre;
  120. float x, y, z, roll, pitch, yaw;
  121. pcl::getTranslationAndEulerAngles(imuOdomAffineLast, x, y, z, roll, pitch, yaw);
  122. // 发布当前时刻里程计位姿
  123. nav_msgs::Odometry laserOdometry = imuOdomQueue.back();
  124. laserOdometry.pose.pose.position.x = x;
  125. laserOdometry.pose.pose.position.y = y;
  126. laserOdometry.pose.pose.position.z = z;
  127. laserOdometry.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
  128. pubImuOdometry.publish(laserOdometry);
  129. // 发布tf,当前时刻odom与baselink系变换关系
  130. static tf::TransformBroadcaster tfOdom2BaseLink;
  131. tf::Transform tCur;
  132. tf::poseMsgToTF(laserOdometry.pose.pose, tCur);
  133. if(lidarFrame != baselinkFrame)
  134. tCur = tCur * lidar2Baselink;
  135. tf::StampedTransform odom_2_baselink = tf::StampedTransform(tCur, odomMsg->header.stamp, odometryFrame, baselinkFrame);
  136. tfOdom2BaseLink.sendTransform(odom_2_baselink);
  137. // 发布imu里程计路径,注:只是最近一帧激光里程计时刻与当前时刻之间的一段
  138. static nav_msgs::Path imuPath;
  139. static double last_path_time = -1;
  140. double imuTime = imuOdomQueue.back().header.stamp.toSec();
  141. // 每隔0.1s添加一个
  142. if (imuTime - last_path_time > 0.1)
  143. {
  144. last_path_time = imuTime;
  145. geometry_msgs::PoseStamped pose_stamped;
  146. pose_stamped.header.stamp = imuOdomQueue.back().header.stamp;
  147. pose_stamped.header.frame_id = odometryFrame;
  148. pose_stamped.pose = laserOdometry.pose.pose;
  149. imuPath.poses.push_back(pose_stamped);
  150. // 删除最近一帧激光里程计时刻之前的imu里程计
  151. while(!imuPath.poses.empty() && imuPath.poses.front().header.stamp.toSec() < lidarOdomTime - 1.0)
  152. imuPath.poses.erase(imuPath.poses.begin());
  153. if (pubImuPath.getNumSubscribers() != 0)
  154. {
  155. imuPath.header.stamp = imuOdomQueue.back().header.stamp;
  156. imuPath.header.frame_id = odometryFrame;
  157. pubImuPath.publish(imuPath);
  158. }
  159. }
  160. }
  161. };
  162. class IMUPreintegration : public ParamServer
  163. {
  164. public:
  165. std::mutex mtx;
  166. // 订阅与发布
  167. ros::Subscriber subImu;
  168. ros::Subscriber subOdometry;
  169. ros::Publisher pubImuOdometry;
  170. bool systemInitialized = false;
  171. // 噪声协方差
  172. gtsam::noiseModel::Diagonal::shared_ptr priorPoseNoise;
  173. gtsam::noiseModel::Diagonal::shared_ptr priorVelNoise;
  174. gtsam::noiseModel::Diagonal::shared_ptr priorBiasNoise;
  175. gtsam::noiseModel::Diagonal::shared_ptr correctionNoise;
  176. gtsam::noiseModel::Diagonal::shared_ptr correctionNoise2;
  177. gtsam::Vector noiseModelBetweenBias;
  178. // imu预积分器
  179. gtsam::PreintegratedImuMeasurements *imuIntegratorOpt_;
  180. gtsam::PreintegratedImuMeasurements *imuIntegratorImu_;
  181. // imu数据队列
  182. std::deque<sensor_msgs::Imu> imuQueOpt;
  183. std::deque<sensor_msgs::Imu> imuQueImu;
  184. // imu因子图优化过程中的状态变量
  185. gtsam::Pose3 prevPose_;
  186. gtsam::Vector3 prevVel_;
  187. gtsam::NavState prevState_;
  188. gtsam::imuBias::ConstantBias prevBias_;
  189. // imu状态
  190. gtsam::NavState prevStateOdom;
  191. gtsam::imuBias::ConstantBias prevBiasOdom;
  192. bool doneFirstOpt = false;
  193. double lastImuT_imu = -1;
  194. double lastImuT_opt = -1;
  195. // ISAM2优化器
  196. gtsam::ISAM2 optimizer;
  197. gtsam::NonlinearFactorGraph graphFactors;
  198. gtsam::Values graphValues;
  199. const double delta_t = 0;
  200. int key = 1;
  201. // imu-lidar位姿变换
  202. gtsam::Pose3 imu2Lidar = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(-extTrans.x(), -extTrans.y(), -extTrans.z()));
  203. gtsam::Pose3 lidar2Imu = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(extTrans.x(), extTrans.y(), extTrans.z()));
  204. /**
  205. * 构造函数
  206. */
  207. IMUPreintegration()
  208. {
  209. // 订阅imu原始数据,用下面因子图优化的结果,施加两帧之间的imu预计分量,预测每一时刻(imu频率)的imu里程计
  210. subImu = nh.subscribe<sensor_msgs::Imu> (imuTopic, 2000, &IMUPreintegration::imuHandler, this, ros::TransportHints().tcpNoDelay());
  211. // 订阅激光里程计,来自mapOptimization,用两帧之间的imu预计分量构建因子图,优化当前帧位姿(这个位姿仅用于更新每时刻的imu里程计,以及下一次因子图优化)
  212. subOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry_incremental", 5, &IMUPreintegration::odometryHandler, this, ros::TransportHints().tcpNoDelay());
  213. // 发布imu里程计
  214. pubImuOdometry = nh.advertise<nav_msgs::Odometry> (odomTopic+"_incremental", 2000);
  215. // imu预积分的噪声协方差
  216. boost::shared_ptr<gtsam::PreintegrationParams> p = gtsam::PreintegrationParams::MakeSharedU(imuGravity);
  217. p->accelerometerCovariance = gtsam::Matrix33::Identity(3,3) * pow(imuAccNoise, 2); // acc white noise in continuous
  218. p->gyroscopeCovariance = gtsam::Matrix33::Identity(3,3) * pow(imuGyrNoise, 2); // gyro white noise in continuous
  219. p->integrationCovariance = gtsam::Matrix33::Identity(3,3) * pow(1e-4, 2); // error committed in integrating position from velocities
  220. gtsam::imuBias::ConstantBias prior_imu_bias((gtsam::Vector(6) << 0, 0, 0, 0, 0, 0).finished());; // assume zero initial bias
  221. // 噪声先验
  222. 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
  223. priorVelNoise = gtsam::noiseModel::Isotropic::Sigma(3, 1e4); // m/s
  224. priorBiasNoise = gtsam::noiseModel::Isotropic::Sigma(6, 1e-3); // 1e-2 ~ 1e-3 seems to be good
  225. // 激光里程计scan-to-map优化过程中发生退化,则选择一个较大的协方差
  226. 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
  227. correctionNoise2 = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << 1, 1, 1, 1, 1, 1).finished()); // rad,rad,rad,m, m, m
  228. noiseModelBetweenBias = (gtsam::Vector(6) << imuAccBiasN, imuAccBiasN, imuAccBiasN, imuGyrBiasN, imuGyrBiasN, imuGyrBiasN).finished();
  229. // imu预积分器,用于预测每一时刻(imu频率)的imu里程计(转到lidar系了,与激光里程计同一个系)
  230. imuIntegratorImu_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for IMU message thread
  231. // imu预积分器,用于因子图优化
  232. imuIntegratorOpt_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for optimization
  233. }
  234. /**
  235. * 重置ISAM2优化器
  236. */
  237. void resetOptimization()
  238. {
  239. gtsam::ISAM2Params optParameters;
  240. optParameters.relinearizeThreshold = 0.1;
  241. optParameters.relinearizeSkip = 1;
  242. optimizer = gtsam::ISAM2(optParameters);
  243. gtsam::NonlinearFactorGraph newGraphFactors;
  244. graphFactors = newGraphFactors;
  245. gtsam::Values NewGraphValues;
  246. graphValues = NewGraphValues;
  247. }
  248. /**
  249. * 重置参数
  250. */
  251. void resetParams()
  252. {
  253. lastImuT_imu = -1;
  254. doneFirstOpt = false;
  255. systemInitialized = false;
  256. }
  257. /**
  258. * 订阅激光里程计,来自mapOptimization
  259. * 1、每隔100帧激光里程计,重置ISAM2优化器,添加里程计、速度、偏置先验因子,执行优化
  260. * 2、计算前一帧激光里程计与当前帧激光里程计之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态
  261. * 3、优化之后,执行重传播;优化更新了imu的偏置,用最新的偏置重新计算当前激光里程计时刻之后的imu预积分,这个预积分用于计算每时刻位姿
  262. */
  263. void odometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
  264. {
  265. std::lock_guard<std::mutex> lock(mtx);
  266. // 当前帧激光里程计时间戳
  267. double currentCorrectionTime = ROS_TIME(odomMsg);
  268. // 确保imu优化队列中有imu数据进行预积分
  269. if (imuQueOpt.empty())
  270. return;
  271. // 当前帧激光位姿,来自scan-to-map匹配、因子图优化后的位姿
  272. float p_x = odomMsg->pose.pose.position.x;
  273. float p_y = odomMsg->pose.pose.position.y;
  274. float p_z = odomMsg->pose.pose.position.z;
  275. float r_x = odomMsg->pose.pose.orientation.x;
  276. float r_y = odomMsg->pose.pose.orientation.y;
  277. float r_z = odomMsg->pose.pose.orientation.z;
  278. float r_w = odomMsg->pose.pose.orientation.w;
  279. bool degenerate = (int)odomMsg->pose.covariance[0] == 1 ? true : false;
  280. gtsam::Pose3 lidarPose = gtsam::Pose3(gtsam::Rot3::Quaternion(r_w, r_x, r_y, r_z), gtsam::Point3(p_x, p_y, p_z));
  281. // 0. 系统初始化,第一帧
  282. if (systemInitialized == false)
  283. {
  284. // 重置ISAM2优化器
  285. resetOptimization();
  286. // 从imu优化队列中删除当前帧激光里程计时刻之前的imu数据
  287. while (!imuQueOpt.empty())
  288. {
  289. if (ROS_TIME(&imuQueOpt.front()) < currentCorrectionTime - delta_t)
  290. {
  291. lastImuT_opt = ROS_TIME(&imuQueOpt.front());
  292. imuQueOpt.pop_front();
  293. }
  294. else
  295. break;
  296. }
  297. // 添加里程计位姿先验因子
  298. prevPose_ = lidarPose.compose(lidar2Imu);
  299. gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, priorPoseNoise);
  300. graphFactors.add(priorPose);
  301. // 添加速度先验因子
  302. prevVel_ = gtsam::Vector3(0, 0, 0);
  303. gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, priorVelNoise);
  304. graphFactors.add(priorVel);
  305. // 添加imu偏置先验因子
  306. prevBias_ = gtsam::imuBias::ConstantBias();
  307. gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, priorBiasNoise);
  308. graphFactors.add(priorBias);
  309. // 变量节点赋初值
  310. graphValues.insert(X(0), prevPose_);
  311. graphValues.insert(V(0), prevVel_);
  312. graphValues.insert(B(0), prevBias_);
  313. // 优化一次
  314. optimizer.update(graphFactors, graphValues);
  315. graphFactors.resize(0);
  316. graphValues.clear();
  317. // 重置优化之后的偏置
  318. imuIntegratorImu_->resetIntegrationAndSetBias(prevBias_);
  319. imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);
  320. key = 1;
  321. systemInitialized = true;
  322. return;
  323. }
  324. // 每隔100帧激光里程计,重置ISAM2优化器,保证优化效率
  325. if (key == 100)
  326. {
  327. // 前一帧的位姿、速度、偏置噪声模型
  328. gtsam::noiseModel::Gaussian::shared_ptr updatedPoseNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(X(key-1)));
  329. gtsam::noiseModel::Gaussian::shared_ptr updatedVelNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(V(key-1)));
  330. gtsam::noiseModel::Gaussian::shared_ptr updatedBiasNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(B(key-1)));
  331. // 重置ISAM2优化器
  332. resetOptimization();
  333. // 添加位姿先验因子,用前一帧的值初始化
  334. gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, updatedPoseNoise);
  335. graphFactors.add(priorPose);
  336. // 添加速度先验因子,用前一帧的值初始化
  337. gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, updatedVelNoise);
  338. graphFactors.add(priorVel);
  339. // 添加偏置先验因子,用前一帧的值初始化
  340. gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, updatedBiasNoise);
  341. graphFactors.add(priorBias);
  342. // 变量节点赋初值,用前一帧的值初始化
  343. graphValues.insert(X(0), prevPose_);
  344. graphValues.insert(V(0), prevVel_);
  345. graphValues.insert(B(0), prevBias_);
  346. // 优化一次
  347. optimizer.update(graphFactors, graphValues);
  348. graphFactors.resize(0);
  349. graphValues.clear();
  350. key = 1;
  351. }
  352. // 1. 计算前一帧与当前帧之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态
  353. while (!imuQueOpt.empty())
  354. {
  355. // 提取前一帧与当前帧之间的imu数据,计算预积分
  356. sensor_msgs::Imu *thisImu = &imuQueOpt.front();
  357. double imuTime = ROS_TIME(thisImu);
  358. if (imuTime < currentCorrectionTime - delta_t)
  359. {
  360. // 两帧imu数据时间间隔
  361. double dt = (lastImuT_opt < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_opt);
  362. // imu预积分数据输入:加速度、角速度、dt
  363. imuIntegratorOpt_->integrateMeasurement(
  364. gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),
  365. gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt);
  366. lastImuT_opt = imuTime;
  367. // 从队列中删除已经处理的imu数据
  368. imuQueOpt.pop_front();
  369. }
  370. else
  371. break;
  372. }
  373. // 添加imu预积分因子
  374. const gtsam::PreintegratedImuMeasurements& preint_imu = dynamic_cast<const gtsam::PreintegratedImuMeasurements&>(*imuIntegratorOpt_);
  375. // 参数:前一帧位姿,前一帧速度,当前帧位姿,当前帧速度,前一帧偏置,预计分量
  376. gtsam::ImuFactor imu_factor(X(key - 1), V(key - 1), X(key), V(key), B(key - 1), preint_imu);
  377. graphFactors.add(imu_factor);
  378. // 添加imu偏置因子,前一帧偏置,当前帧偏置,观测值,噪声协方差;deltaTij()是积分段的时间
  379. graphFactors.add(gtsam::BetweenFactor<gtsam::imuBias::ConstantBias>(B(key - 1), B(key), gtsam::imuBias::ConstantBias(),
  380. gtsam::noiseModel::Diagonal::Sigmas(sqrt(imuIntegratorOpt_->deltaTij()) * noiseModelBetweenBias)));
  381. // 添加位姿因子
  382. gtsam::Pose3 curPose = lidarPose.compose(lidar2Imu);
  383. gtsam::PriorFactor<gtsam::Pose3> pose_factor(X(key), curPose, degenerate ? correctionNoise2 : correctionNoise);
  384. graphFactors.add(pose_factor);
  385. // 用前一帧的状态、偏置,施加imu预计分量,得到当前帧的状态
  386. gtsam::NavState propState_ = imuIntegratorOpt_->predict(prevState_, prevBias_);
  387. // 变量节点赋初值
  388. graphValues.insert(X(key), propState_.pose());
  389. graphValues.insert(V(key), propState_.v());
  390. graphValues.insert(B(key), prevBias_);
  391. // 优化
  392. optimizer.update(graphFactors, graphValues);
  393. optimizer.update();
  394. graphFactors.resize(0);
  395. graphValues.clear();
  396. // 优化结果
  397. gtsam::Values result = optimizer.calculateEstimate();
  398. // 更新当前帧位姿、速度
  399. prevPose_ = result.at<gtsam::Pose3>(X(key));
  400. prevVel_ = result.at<gtsam::Vector3>(V(key));
  401. // 更新当前帧状态
  402. prevState_ = gtsam::NavState(prevPose_, prevVel_);
  403. // 更新当前帧imu偏置
  404. prevBias_ = result.at<gtsam::imuBias::ConstantBias>(B(key));
  405. // 重置预积分器,设置新的偏置,这样下一帧激光里程计进来的时候,预积分量就是两帧之间的增量
  406. imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);
  407. // imu因子图优化结果,速度或者偏置过大,认为失败
  408. if (failureDetection(prevVel_, prevBias_))
  409. {
  410. // 重置参数
  411. resetParams();
  412. return;
  413. }
  414. // 2. 优化之后,执行重传播;优化更新了imu的偏置,用最新的偏置重新计算当前激光里程计时刻之后的imu预积分,这个预积分用于计算每时刻位姿
  415. prevStateOdom = prevState_;
  416. prevBiasOdom = prevBias_;
  417. // 从imu队列中删除当前激光里程计时刻之前的imu数据
  418. double lastImuQT = -1;
  419. while (!imuQueImu.empty() && ROS_TIME(&imuQueImu.front()) < currentCorrectionTime - delta_t)
  420. {
  421. lastImuQT = ROS_TIME(&imuQueImu.front());
  422. imuQueImu.pop_front();
  423. }
  424. // 对剩余的imu数据计算预积分
  425. if (!imuQueImu.empty())
  426. {
  427. // 重置预积分器和最新的偏置
  428. imuIntegratorImu_->resetIntegrationAndSetBias(prevBiasOdom);
  429. // 计算预积分
  430. for (int i = 0; i < (int)imuQueImu.size(); ++i)
  431. {
  432. sensor_msgs::Imu *thisImu = &imuQueImu[i];
  433. double imuTime = ROS_TIME(thisImu);
  434. double dt = (lastImuQT < 0) ? (1.0 / 500.0) :(imuTime - lastImuQT);
  435. imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),
  436. gtsam::Vector3(thisImu->angular_velocity.x, thisImu->angular_velocity.y, thisImu->angular_velocity.z), dt);
  437. lastImuQT = imuTime;
  438. }
  439. }
  440. ++key;
  441. doneFirstOpt = true;
  442. }
  443. /**
  444. * imu因子图优化结果,速度或者偏置过大,认为失败
  445. */
  446. bool failureDetection(const gtsam::Vector3& velCur, const gtsam::imuBias::ConstantBias& biasCur)
  447. {
  448. Eigen::Vector3f vel(velCur.x(), velCur.y(), velCur.z());
  449. if (vel.norm() > 30)
  450. {
  451. ROS_WARN("Large velocity, reset IMU-preintegration!");
  452. return true;
  453. }
  454. Eigen::Vector3f ba(biasCur.accelerometer().x(), biasCur.accelerometer().y(), biasCur.accelerometer().z());
  455. Eigen::Vector3f bg(biasCur.gyroscope().x(), biasCur.gyroscope().y(), biasCur.gyroscope().z());
  456. if (ba.norm() > 1.0 || bg.norm() > 1.0)
  457. {
  458. ROS_WARN("Large bias, reset IMU-preintegration!");
  459. return true;
  460. }
  461. return false;
  462. }
  463. /**
  464. * 订阅imu原始数据
  465. * 1、用上一帧激光里程计时刻对应的状态、偏置,施加从该时刻开始到当前时刻的imu预计分量,得到当前时刻的状态,也就是imu里程计
  466. * 2、imu里程计位姿转到lidar系,发布里程计
  467. */
  468. void imuHandler(const sensor_msgs::Imu::ConstPtr& imu_raw)
  469. {
  470. std::lock_guard<std::mutex> lock(mtx);
  471. // imu原始测量数据转换到lidar系,加速度、角速度、RPY
  472. sensor_msgs::Imu thisImu = imuConverter(*imu_raw);
  473. // 添加当前帧imu数据到队列
  474. imuQueOpt.push_back(thisImu);
  475. imuQueImu.push_back(thisImu);
  476. // 要求上一次imu因子图优化执行成功,确保更新了上一帧(激光里程计帧)的状态、偏置,预积分重新计算了
  477. if (doneFirstOpt == false)
  478. return;
  479. double imuTime = ROS_TIME(&thisImu);
  480. double dt = (lastImuT_imu < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_imu);
  481. lastImuT_imu = imuTime;
  482. // imu预积分器添加一帧imu数据,注:这个预积分器的起始时刻是上一帧激光里程计时刻
  483. imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu.linear_acceleration.x, thisImu.linear_acceleration.y, thisImu.linear_acceleration.z),
  484. gtsam::Vector3(thisImu.angular_velocity.x, thisImu.angular_velocity.y, thisImu.angular_velocity.z), dt);
  485. // 用上一帧激光里程计时刻对应的状态、偏置,施加从该时刻开始到当前时刻的imu预计分量,得到当前时刻的状态
  486. gtsam::NavState currentState = imuIntegratorImu_->predict(prevStateOdom, prevBiasOdom);
  487. // 发布imu里程计(转到lidar系,与激光里程计同一个系)
  488. nav_msgs::Odometry odometry;
  489. odometry.header.stamp = thisImu.header.stamp;
  490. odometry.header.frame_id = odometryFrame;
  491. odometry.child_frame_id = "odom_imu";
  492. // 变换到lidar系
  493. gtsam::Pose3 imuPose = gtsam::Pose3(currentState.quaternion(), currentState.position());
  494. gtsam::Pose3 lidarPose = imuPose.compose(imu2Lidar);
  495. odometry.pose.pose.position.x = lidarPose.translation().x();
  496. odometry.pose.pose.position.y = lidarPose.translation().y();
  497. odometry.pose.pose.position.z = lidarPose.translation().z();
  498. odometry.pose.pose.orientation.x = lidarPose.rotation().toQuaternion().x();
  499. odometry.pose.pose.orientation.y = lidarPose.rotation().toQuaternion().y();
  500. odometry.pose.pose.orientation.z = lidarPose.rotation().toQuaternion().z();
  501. odometry.pose.pose.orientation.w = lidarPose.rotation().toQuaternion().w();
  502. odometry.twist.twist.linear.x = currentState.velocity().x();
  503. odometry.twist.twist.linear.y = currentState.velocity().y();
  504. odometry.twist.twist.linear.z = currentState.velocity().z();
  505. odometry.twist.twist.angular.x = thisImu.angular_velocity.x + prevBiasOdom.gyroscope().x();
  506. odometry.twist.twist.angular.y = thisImu.angular_velocity.y + prevBiasOdom.gyroscope().y();
  507. odometry.twist.twist.angular.z = thisImu.angular_velocity.z + prevBiasOdom.gyroscope().z();
  508. pubImuOdometry.publish(odometry);
  509. }
  510. };
  511. int main(int argc, char** argv)
  512. {
  513. ros::init(argc, argv, "roboat_loam");
  514. IMUPreintegration ImuP;
  515. TransformFusion TF;
  516. ROS_INFO("\033[1;32m----> IMU Preintegration Started.\033[0m");
  517. ros::MultiThreadedSpinner spinner(4);
  518. spinner.spin();
  519. return 0;
  520. }

参考文献

LIOSAM代码架构 - 知乎

SLAM学习笔记(二十)LIO-SAM流程及代码详解(最全)_zkk9527的博客-CSDN博客_lio-sam 

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

闽ICP备14008679号