imuPreintegration

  • 总体叙述
  • 1. ParamServer
    • 1.1 参数定义
    • 1.2 ParamServer 构造
    • 1.3 other 函数
  • 2. TransformFusion
    • 2.1 成员变量
    • 2.2 TransformFusion 构造
    • 2.3 other函数
      • 1. 里程位姿的转换
      • 2. Imu里程计callback
      • 3. 激光里程计callback
  • 3. IMUPreintegration
    • 3.1 成员变量
    • 3.1 构造
    • 3.3 other
      • imuHandler callback
      • odometryHandler
      • failureDetection

总体叙述

位姿前端的预积分,数据间加了多帧帧间优化,优化变量pose,vel,bias

  1. 订阅激光里程计(来自MapOptimization)和IMU里程计,根据前一时刻激光里程计,
    和该时刻到当前时刻的IMU里程计变换增量,计算当前时刻IMU里程计;rviz展示IMU里程计轨迹(局部)
  2. 订阅激光里程计,来自mapOptimization
    1、每隔100帧激光里程计,重置ISAM2优化器,添加里程计、速度、偏置先验因子,执行优化
    2、计算前一帧激光里程计与当前帧激光里程计之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,
    添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态
    3、优化之后,执行重传播;优化更新了imu的偏置,用最新的偏置重新计算当前激光里程计时刻之后的imu预积分,这个预积分用于计算每时刻位姿
  3. 订阅imu原始数据
    1、用上一帧激光里程计时刻对应的状态、偏置,施加从该时刻开始到当前时刻的imu预计分量,得到当前时刻的状态,也就是imu里程计
    2、imu里程计位姿转到lidar系,发布里程计

1. ParamServer

1.1 参数定义

1.1 other

    ros::NodeHandle nh;std::string robot_id;

1.2 topic

    string pointCloudTopic;string imuTopic;string odomTopic;string gpsTopic;

1.3 Frame

    string lidarFrame;string baselinkFrame;string odometryFrame;string mapFrame;

1.4 GPS setting

    bool useImuHeadingInitialization;bool useGpsElevation;   //海拔float gpsCovThreshold;float poseCovThreshold;  位姿协方差阈值

1.5 save pcd

    bool savePCD;string savePCDDirectory;

1.6 Lidar Sensor Configuration

    SensorType sensor;  // velodyneint N_SCAN;  // 线数 16int Horizon_SCAN;  // 水平扫描点int downsampleRate;  float lidarMinRange;float lidarMaxRange;

1.7 IMU

    float imuAccNoise;float imuGyrNoise;float imuAccBiasN;float imuGyrBiasN;float imuGravity;float imuRPYWeight;vector<double> extRotV;vector<double> extRPYV;vector<double> extTransV;Eigen::Matrix3d extRot;Eigen::Matrix3d extRPY;Eigen::Vector3d extTrans;Eigen::Quaterniond extQRPY;

1.8 LOAM

    float edgeThreshold;  // 0.1float surfThreshold;  // 0.1int edgeFeatureMinValidNum;  //边特征的最小有效个数 10int surfFeatureMinValidNum;  // 100

1.9 voxel filter paprams

    float odometrySurfLeafSize;  // 0.2float mappingCornerLeafSize;  // 0.2float mappingSurfLeafSize ;  // 0.2float z_tollerance;  //FLT_MAXfloat rotation_tollerance;  //FLT_MAX

1.10 CPU Params

    int numberOfCores;  // 内核数  2double mappingProcessInterval;  // 建图间隔  0.15

1.11 Surrounding map

    float surroundingkeyframeAddingDistThreshold;  // 1.0float surroundingkeyframeAddingAngleThreshold;  // 0.float surroundingKeyframeDensity;  // 密度 1.0 float surroundingKeyframeSearchRadius;  // 50.

1.12 Loop closure

    bool  loopClosureEnableFlag;float loopClosureFrequency;  // 1.0int   surroundingKeyframeSize; // 50float historyKeyframeSearchRadius;  // 10.0float historyKeyframeSearchTimeDiff;  // 30.0int   historyKeyframeSearchNum;  //25float historyKeyframeFitnessScore;  //0.3

1.13 lobal map visualization radius

    float globalMapVisualizationSearchRadius;  // 1e3float globalMapVisualizationPoseDensity;  // 10.0float globalMapVisualizationLeafSize;  // 1.0

1.2 ParamServer 构造

param ros read

 nh.param<int>("lio_sam/N_SCAN", N_SCAN, 16);nh.param<float>("lio_sam/lidarMinRange", lidarMinRange, 1.0);nh.param<bool>("lio_sam/loopClosureEnableFlag", loopClosureEnableFlag, false);usleep(100);

1.3 other 函数

  • IMU数据转换
    imuConverter
  • 发布点云函数
    publishCloud
  • IMU ros 角度,加速度
    imuAngular2rosAngular
  • 点距离 一个点+二个点
    pointDistance

2. TransformFusion

优化变量

  • using gtsam::symbol_shorthand::X; // Pose3 (x,y,z,r,p,y)
  • using gtsam::symbol_shorthand::V; // Vel (xdot,ydot,zdot)
  • using gtsam::symbol_shorthand::B; // Bias (ax,ay,az,gx,gy,gz)

2.1 成员变量

互斥锁
std::mutex mtx;

订阅话题
subImuOdometry;
subLaserOdometry;

发布话题
发布Imu里程计 pubImuOdometry
发布Imu 路径 pubImuPath

lidarOdomAffine;
imuOdomAffineFront;
imuOdomAffineBack;

tf 接听
tf::TransformListener tfListener;
tf::StampedTransform lidar2Baselink;

里程计队列
double lidarOdomTime = -1;
deque<nav_msgs::Odometry> imuOdomQueue;

2.2 TransformFusion 构造

1、接听激光外参

// 如果lidar系与baselink系不同(激光系和载体系),需要外部提供二者之间的变换关系
if(lidarFrame != baselinkFrame)
{try{// 等待3s  + lidar系到baselink系的变换tfListener.waitForTransform(lidarFrame, baselinkFrame, ros::Time(0), ros::Duration(3.0));tfListener.lookupTransform(lidarFrame, baselinkFrame, ros::Time(0), lidar2Baselink);}catch (tf::TransformException ex){ROS_ERROR("%s",ex.what());}
}

2、订阅发布地图

 // 订阅激光里程计,来自mapOptimization
subLaserOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry", 5, &TransformFusion::lidarOdometryHandler, this, ros::TransportHints().tcpNoDelay());
// 订阅imu里程计,来自IMUPreintegration
subImuOdometry   = nh.subscribe<nav_msgs::Odometry>(odomTopic+"_incremental",   2000, &TransformFusion::imuOdometryHandler,   this, ros::TransportHints().tcpNoDelay());// 发布imu里程计,用于rviz展示
pubImuOdometry   = nh.advertise<nav_msgs::Odometry>(odomTopic, 2000);
// 发布imu里程计轨迹
pubImuPath       = nh.advertise<nav_msgs::Path>    ("lio_sam/imu/path", 1);

2.3 other函数

1. 里程位姿的转换

    Eigen::Affine3f odom2affine(nav_msgs::Odometry odom){double x, y, z, roll, pitch, yaw;x = odom.pose.pose.position.x;y = odom.pose.pose.position.y;z = odom.pose.pose.position.z;tf::Quaternion orientation;tf::quaternionMsgToTF(odom.pose.pose.orientation, orientation);tf::Matrix3x3(orientation).getRPY(roll, pitch, yaw);return pcl::getTransformation(x, y, z, roll, pitch, yaw);}

Affine3f中有rotation和translation分量,但Matrix4f中没有。

//1、Matrix4f到Affine3f:
Matrix4f m4f_transform;
Eigen::Transform<float, 3, Eigen::Affine> a3f_transform (m4f_transform);
//2、Affine3f到Matrix4f:
Eigen::Transform<float, 3, Eigen::Affine> a3f_transform ;
Matrix4f m4f_transform=a3f_transform.matrix();

2. Imu里程计callback

订阅imu里程计,来自IMUPreintegration

  • 1、以最近一帧激光里程计位姿为基础,计算该时刻与当前时刻间imu里程计增量位姿变换,相乘得到当前时刻imu里程计位姿
  • 2、发布当前时刻里程计位姿,用于rviz展示;发布imu里程计路径,注:只是最近一帧激光里程计时刻与当前时刻之间的一段
void imuOdometryHandler(const nav_msgs::Odometry::ConstPtr& odomMsg)
{// static tf // 发布tf,map与odom系设为同一个系static tf::TransformBroadcaster tfMap2Odom;static tf::Transform map_to_odom = tf::Transform(tf::createQuaternionFromRPY(0, 0, 0), tf::Vector3(0, 0, 0));tfMap2Odom.sendTransform(tf::StampedTransform(map_to_odom, odomMsg->header.stamp, mapFrame, odometryFrame));std::lock_guard<std::mutex> lock(mtx);// 添加imu里程计到队列imuOdomQueue.push_back(*odomMsg);// 从imu里程计队列中删除当前(最近的一帧)激光里程计时刻之前的数据// get latest odometry (at current IMU stamp)if (lidarOdomTime == -1)return;while (!imuOdomQueue.empty()){if (imuOdomQueue.front().header.stamp.toSec() <= lidarOdomTime)imuOdomQueue.pop_front();elsebreak;}// 最近的一帧激光里程计时刻对应imu里程计位姿Eigen::Affine3f imuOdomAffineFront = odom2affine(imuOdomQueue.front());// 当前时刻imu里程计位姿Eigen::Affine3f imuOdomAffineBack = odom2affine(imuOdomQueue.back());// imu里程计增量位姿变换Eigen::Affine3f imuOdomAffineIncre = imuOdomAffineFront.inverse() * imuOdomAffineBack;// 最近的一帧激光里程计位姿 * imu里程计增量位姿变换 = 当前时刻imu里程计位姿Eigen::Affine3f imuOdomAffineLast = lidarOdomAffine * imuOdomAffineIncre;float x, y, z, roll, pitch, yaw;pcl::getTranslationAndEulerAngles(imuOdomAffineLast, x, y, z, roll, pitch, yaw);// publish latest odometry // 发布当前时刻里程计位姿nav_msgs::Odometry laserOdometry = imuOdomQueue.back();laserOdometry.pose.pose.position.x = x;laserOdometry.pose.pose.position.y = y;laserOdometry.pose.pose.position.z = z;laserOdometry.pose.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);pubImuOdometry.publish(laserOdometry);// publish tf         // 发布tf,当前时刻odom与baselink系变换关系static tf::TransformBroadcaster tfOdom2BaseLink;tf::Transform tCur;tf::poseMsgToTF(laserOdometry.pose.pose, tCur);if(lidarFrame != baselinkFrame)tCur = tCur * lidar2Baselink;tf::StampedTransform odom_2_baselink = tf::StampedTransform(tCur, odomMsg->header.stamp, odometryFrame, baselinkFrame);tfOdom2BaseLink.sendTransform(odom_2_baselink);// publish IMU path // 发布imu里程计路径,注:只是最近一帧激光里程计时刻与当前时刻之间的一段static nav_msgs::Path imuPath;static double last_path_time = -1;double imuTime = imuOdomQueue.back().header.stamp.toSec();if (imuTime - last_path_time > 0.1){last_path_time = imuTime;geometry_msgs::PoseStamped pose_stamped;pose_stamped.header.stamp = imuOdomQueue.back().header.stamp;pose_stamped.header.frame_id = odometryFrame;pose_stamped.pose = laserOdometry.pose.pose;imuPath.poses.push_back(pose_stamped);while(!imuPath.poses.empty() && imuPath.poses.front().header.stamp.toSec() < lidarOdomTime - 1.0)imuPath.poses.erase(imuPath.poses.begin());if (pubImuPath.getNumSubscribers() != 0){imuPath.header.stamp = imuOdomQueue.back().header.stamp;imuPath.header.frame_id = odometryFrame;pubImuPath.publish(imuPath);}}
}

3. 激光里程计callback

将激光里程计数据赋值

3. IMUPreintegration

  • 用激光里程计,两帧激光里程计之间的IMU预计分量构建因子图,优化当前帧的状态(包括位姿、速度、偏置);
  • 以优化后的状态为基础,施加IMU预计分量,得到每一时刻的IMU里程计。

3.1 成员变量

// 公共锁
std::mutex mtx;# 订阅imu,odom,发布imu_odom
ros::Subscriber subImu;
ros::Subscriber subOdometry;
ros::Publisher pubImuOdometry;bool systemInitialized = false;// 噪声协方差,gtsam因子图中
gtsam::noiseModel::Diagonal::shared_ptr priorPoseNoise;
gtsam::noiseModel::Diagonal::shared_ptr priorVelNoise;
gtsam::noiseModel::Diagonal::shared_ptr priorBiasNoise;
gtsam::noiseModel::Diagonal::shared_ptr correctionNoise;
gtsam::noiseModel::Diagonal::shared_ptr correctionNoise2;
gtsam::Vector noiseModelBetweenBias;// imu预积分器
gtsam::PreintegratedImuMeasurements *imuIntegratorOpt_;
gtsam::PreintegratedImuMeasurements *imuIntegratorImu_;// imu数据队列
std::deque<sensor_msgs::Imu> imuQueOpt;
std::deque<sensor_msgs::Imu> imuQueImu;// imu因子图优化过程中的状态变量 pose\vel\status\bias
gtsam::Pose3 prevPose_;
gtsam::Vector3 prevVel_;
gtsam::NavState prevState_;
gtsam::imuBias::ConstantBias prevBias_;gtsam::NavState prevStateOdom;
gtsam::imuBias::ConstantBias prevBiasOdom;bool doneFirstOpt = false;
double lastImuT_imu = -1;
double lastImuT_opt = -1;// ISAM2优化器
gtsam::ISAM2 optimizer;
gtsam::NonlinearFactorGraph graphFactors;
gtsam::Values graphValues;const double delta_t = 0;int key = 1;//  imu2lidar  lidar2imu
gtsam::Pose3 imu2Lidar = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(-extTrans.x(), -extTrans.y(), -extTrans.z()));
gtsam::Pose3 lidar2Imu = gtsam::Pose3(gtsam::Rot3(1, 0, 0, 0), gtsam::Point3(extTrans.x(), extTrans.y(), extTrans.z()));

3.1 构造

初始化订阅发布,设定参数,构造imuIntegratorImu_+imuIntegratorOpt_

 // 订阅imu原始数据,用下面因子图优化的结果,施加两帧之间的imu预计分量,预测每一时刻(imu频率)的imu里程计
subImu      = nh.subscribe<sensor_msgs::Imu>  (imuTopic,                   2000, &IMUPreintegration::imuHandler,      this, ros::TransportHints().tcpNoDelay());
// 订阅激光里程计,来自mapOptimization,用两帧之间的imu预计分量构建因子图,优化当前帧位姿(这个位姿仅用于更新每时刻的imu里程计,以及下一次因子图优化)
subOdometry = nh.subscribe<nav_msgs::Odometry>("lio_sam/mapping/odometry_incremental", 5,    &IMUPreintegration::odometryHandler, this, ros::TransportHints().tcpNoDelay());
// 发布imu里程计
pubImuOdometry = nh.advertise<nav_msgs::Odometry> (odomTopic+"_incremental", 2000);// imu预积分的噪声协方差
boost::shared_ptr<gtsam::PreintegrationParams> p = gtsam::PreintegrationParams::MakeSharedU(imuGravity);
p->accelerometerCovariance  = gtsam::Matrix33::Identity(3,3) * pow(imuAccNoise, 2); // acc white noise in continuous
p->gyroscopeCovariance      = gtsam::Matrix33::Identity(3,3) * pow(imuGyrNoise, 2); // gyro white noise in continuous
p->integrationCovariance    = gtsam::Matrix33::Identity(3,3) * pow(1e-4, 2); // error committed in integrating position from velocities
gtsam::imuBias::ConstantBias prior_imu_bias((gtsam::Vector(6) << 0, 0, 0, 0, 0, 0).finished());; // assume zero initial bias// 噪声先验
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
priorVelNoise   = gtsam::noiseModel::Isotropic::Sigma(3, 1e4); // m/s
priorBiasNoise  = gtsam::noiseModel::Isotropic::Sigma(6, 1e-3); // 1e-2 ~ 1e-3 seems to be good
// 激光里程计scan-to-map优化过程中发生退化,则选择一个较大的协方差
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
correctionNoise2 = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) << 1, 1, 1, 1, 1, 1).finished()); // rad,rad,rad,m, m, m
noiseModelBetweenBias = (gtsam::Vector(6) << imuAccBiasN, imuAccBiasN, imuAccBiasN, imuGyrBiasN, imuGyrBiasN, imuGyrBiasN).finished();// imu预积分器,用于预测每一时刻(imu频率)的imu里程计(转到lidar系了,与激光里程计同一个系)
imuIntegratorImu_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for IMU message thread
// imu预积分器,用于因子图优化
imuIntegratorOpt_ = new gtsam::PreintegratedImuMeasurements(p, prior_imu_bias); // setting up the IMU integration for optimization

3.3 other

imuHandler callback

 * 订阅imu原始数据* 1、用上一帧激光里程计时刻对应的状态、偏置,施加从该时刻开始到当前时刻的imu预计分量,得到当前时刻的状态,也就是imu里程计* 2、imu里程计位姿转到lidar系,发布里程计

1、将imu数据放入两队列中 imuQueOpt 、imuQueImu

std::lock_guard<std::mutex> lock(mtx);
// imu原始测量数据转换到lidar系,加速度、角速度、RPY
sensor_msgs::Imu thisImu = imuConverter(*imu_raw);// 添加当前帧imu数据到队列
imuQueOpt.push_back(thisImu);
imuQueImu.push_back(thisImu);

2、计算dt,并赋值 lastImuT_imu,这儿时间回跳也没关系

double imuTime = ROS_TIME(&thisImu);
double dt = (lastImuT_imu < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_imu);
lastImuT_imu = imuTime;

3、imu预积分器添加一帧imu数据,注:这个预积分器的起始时刻是上一帧激光里程计时刻

// integrate this single imu message
imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu.linear_acceleration.x, thisImu.linear_acceleration.y, thisImu.linear_acceleration.z),gtsam::Vector3(thisImu.angular_velocity.x,    thisImu.angular_velocity.y,    thisImu.angular_velocity.z), dt);

4、预测odometry

// predict odometry
gtsam::NavState currentState = imuIntegratorImu_->predict(prevStateOdom, prevBiasOdom);// publish odometry
nav_msgs::Odometry odometry;
odometry.header.stamp = thisImu.header.stamp;
odometry.header.frame_id = odometryFrame;
odometry.child_frame_id = "odom_imu";

5、转换 imu_pose到激光frame

// transform imu pose to ldiar
gtsam::Pose3 imuPose = gtsam::Pose3(currentState.quaternion(), currentState.position());
gtsam::Pose3 lidarPose = imuPose.compose(imu2Lidar);odometry.pose.pose.position.x = lidarPose.translation().x();
odometry.pose.pose.position.y = lidarPose.translation().y();
odometry.pose.pose.position.z = lidarPose.translation().z();
odometry.pose.pose.orientation.x = lidarPose.rotation().toQuaternion().x();
odometry.pose.pose.orientation.y = lidarPose.rotation().toQuaternion().y();
odometry.pose.pose.orientation.z = lidarPose.rotation().toQuaternion().z();
odometry.pose.pose.orientation.w = lidarPose.rotation().toQuaternion().w();odometry.twist.twist.linear.x = currentState.velocity().x();
odometry.twist.twist.linear.y = currentState.velocity().y();
odometry.twist.twist.linear.z = currentState.velocity().z();
odometry.twist.twist.angular.x = thisImu.angular_velocity.x + prevBiasOdom.gyroscope().x();
odometry.twist.twist.angular.y = thisImu.angular_velocity.y + prevBiasOdom.gyroscope().y();
odometry.twist.twist.angular.z = thisImu.angular_velocity.z + prevBiasOdom.gyroscope().z();
pubImuOdometry.publish(odometry);

odometryHandler

/*** 订阅激光里程计,来自mapOptimization* 1、每隔100帧激光里程计,重置ISAM2优化器,添加里程计、速度、偏置先验因子,执行优化* 2、计算前一帧激光里程计与当前帧激光里程计之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,*     添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态* 3、优化之后,执行重传播;优化更新了imu的偏置,用最新的偏置重新计算当前激光里程计时刻之后的imu预积分,这个预积分用于计算每时刻位姿
*/

利用优化 估计了bias和角度
1、当前帧激光位姿,来自scan-to-map匹配、因子图优化后的位姿

double currentCorrectionTime = ROS_TIME(odomMsg);// make sure we have imu data to integrate
if (imuQueOpt.empty())return;float p_x = odomMsg->pose.pose.position.x;
float p_y = odomMsg->pose.pose.position.y;
float p_z = odomMsg->pose.pose.position.z;
float r_x = odomMsg->pose.pose.orientation.x;
float r_y = odomMsg->pose.pose.orientation.y;
float r_z = odomMsg->pose.pose.orientation.z;
float r_w = odomMsg->pose.pose.orientation.w;
bool degenerate = (int)odomMsg->pose.covariance[0] == 1 ? true : false;
gtsam::Pose3 lidarPose = gtsam::Pose3(gtsam::Rot3::Quaternion(r_w, r_x, r_y, r_z), gtsam::Point3(p_x, p_y, p_z));

2、初始化第一帧


// 0. initialize system
if (systemInitialized == false)
{// 重置ISAM2 优化器resetOptimization();// 从imu优化队列中删除当前帧激光里程计时刻之前的imu数据while (!imuQueOpt.empty()){if (ROS_TIME(&imuQueOpt.front()) < currentCorrectionTime - delta_t){lastImuT_opt = ROS_TIME(&imuQueOpt.front());imuQueOpt.pop_front();}elsebreak;}// initial pose 添加里程计位姿先验因子prevPose_ = lidarPose.compose(lidar2Imu);gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, priorPoseNoise);graphFactors.add(priorPose);// initial velocity  添加速度先验因子prevVel_ = gtsam::Vector3(0, 0, 0);gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, priorVelNoise);graphFactors.add(priorVel);// initial bias 添加imu偏置先验因子prevBias_ = gtsam::imuBias::ConstantBias();gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, priorBiasNoise);graphFactors.add(priorBias);// add values 变量节点赋初值graphValues.insert(X(0), prevPose_);graphValues.insert(V(0), prevVel_);graphValues.insert(B(0), prevBias_);// optimize once 优化一次optimizer.update(graphFactors, graphValues);graphFactors.resize(0);graphValues.clear();// 重置优化之后的偏置imuIntegratorImu_->resetIntegrationAndSetBias(prevBias_);imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);key = 1;systemInitialized = true;return;
}

3、每隔100帧激光里程计,重置ISAM2优化器,保证优化效率

// reset graph for speed
if (key == 100)
{// 前一帧的位姿、速度、偏置噪声模型gtsam::noiseModel::Gaussian::shared_ptr updatedPoseNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(X(key-1)));gtsam::noiseModel::Gaussian::shared_ptr updatedVelNoise  = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(V(key-1)));gtsam::noiseModel::Gaussian::shared_ptr updatedBiasNoise = gtsam::noiseModel::Gaussian::Covariance(optimizer.marginalCovariance(B(key-1)));// reset graph 重置ISAM2优化器resetOptimization();// add pose 添加位姿先验因子,用前一帧的值初始化gtsam::PriorFactor<gtsam::Pose3> priorPose(X(0), prevPose_, updatedPoseNoise);graphFactors.add(priorPose);// add velocity 添加速度先验因子,用前一帧的值初始化gtsam::PriorFactor<gtsam::Vector3> priorVel(V(0), prevVel_, updatedVelNoise);graphFactors.add(priorVel);// add bias 添加偏置先验因子,用前一帧的值初始化gtsam::PriorFactor<gtsam::imuBias::ConstantBias> priorBias(B(0), prevBias_, updatedBiasNoise);graphFactors.add(priorBias);// add values 变量节点赋初值,用前一帧的值初始化graphValues.insert(X(0), prevPose_);graphValues.insert(V(0), prevVel_);graphValues.insert(B(0), prevBias_);// optimize once 优化一次optimizer.update(graphFactors, graphValues);graphFactors.resize(0);graphValues.clear();key = 1;
}

4、 计算前一帧与当前帧之间的imu预积分量,用前一帧状态施加预积分量得到当前帧初始状态估计,添加来自mapOptimization的当前帧位姿,进行因子图优化,更新当前帧状态

while (!imuQueOpt.empty())
{// pop and integrate imu data that is between two optimizationssensor_msgs::Imu *thisImu = &imuQueOpt.front();double imuTime = ROS_TIME(thisImu);if (imuTime < currentCorrectionTime - delta_t){  // 两帧imu数据时间间隔double dt = (lastImuT_opt < 0) ? (1.0 / 500.0) : (imuTime - lastImuT_opt);imuIntegratorOpt_->integrateMeasurement(gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),gtsam::Vector3(thisImu->angular_velocity.x,    thisImu->angular_velocity.y,    thisImu->angular_velocity.z), dt);lastImuT_opt = imuTime;// 从队列中删除已经处理的imu数据imuQueOpt.pop_front();}elsebreak;
}

5、添加imu预积分到因子

const gtsam::PreintegratedImuMeasurements& preint_imu = dynamic_cast<const gtsam::PreintegratedImuMeasurements&>(*imuIntegratorOpt_);
// 参数:前一帧位姿,前一帧速度,当前帧位姿,当前帧速度,前一帧偏置,预计分量
gtsam::ImuFactor imu_factor(X(key - 1), V(key - 1), X(key), V(key), B(key - 1), preint_imu);
graphFactors.add(imu_factor);

6、添加imu偏置因子,前一帧偏置,当前帧偏置,观测值,噪声协方差;deltaTij()是积分段的时间

graphFactors.add(gtsam::BetweenFactor<gtsam::imuBias::ConstantBias>(B(key - 1), B(key), gtsam::imuBias::ConstantBias(),gtsam::noiseModel::Diagonal::Sigmas(sqrt(imuIntegratorOpt_->deltaTij()) * noiseModelBetweenBias)));

7、添加位姿因子

gtsam::Pose3 curPose = lidarPose.compose(lidar2Imu);
gtsam::PriorFactor<gtsam::Pose3> pose_factor(X(key), curPose, degenerate ? correctionNoise2 : correctionNoise);
graphFactors.add(pose_factor);

8、用前一帧的状态、偏置,施加imu预计分量,得到当前帧的状态

gtsam::NavState propState_ = imuIntegratorOpt_->predict(prevState_, prevBias_);
//变量节点赋初值
graphValues.insert(X(key), propState_.pose());
graphValues.insert(V(key), propState_.v());
graphValues.insert(B(key), prevBias_);

9、优化

optimizer.update(graphFactors, graphValues);
optimizer.update();
graphFactors.resize(0);
graphValues.clear();

10、为下次操作更新 数据

gtsam::Values result = optimizer.calculateEstimate();
prevPose_  = result.at<gtsam::Pose3>(X(key));
prevVel_   = result.at<gtsam::Vector3>(V(key));
prevState_ = gtsam::NavState(prevPose_, prevVel_);
prevBias_  = result.at<gtsam::imuBias::ConstantBias>(B(key));
// Reset the optimization preintegration object.
imuIntegratorOpt_->resetIntegrationAndSetBias(prevBias_);

11、检测优化,若失败则restParam
imu因子图优化结果,速度或者偏置过大,认为失败

// check optimization
if (failureDetection(prevVel_, prevBias_))
{resetParams();return;
}

12、优化之后,执行重传播;优化更新了imu的偏置,用最新的偏置重新计算当前激光里程计时刻之后的imu预积分,这个预积分用于计算每时刻位姿

prevStateOdom = prevState_;
prevBiasOdom  = prevBias_;
// first pop imu message older than current correction data
double lastImuQT = -1;
while (!imuQueImu.empty() && ROS_TIME(&imuQueImu.front()) < currentCorrectionTime - delta_t)
{lastImuQT = ROS_TIME(&imuQueImu.front());imuQueImu.pop_front();
}
// repropogate  对剩余的imu数据计算预积分
if (!imuQueImu.empty())
{// reset bias use the newly optimized biasimuIntegratorImu_->resetIntegrationAndSetBias(prevBiasOdom);// integrate imu message from the beginning of this optimizationfor (int i = 0; i < (int)imuQueImu.size(); ++i){sensor_msgs::Imu *thisImu = &imuQueImu[i];double imuTime = ROS_TIME(thisImu);double dt = (lastImuQT < 0) ? (1.0 / 500.0) :(imuTime - lastImuQT);imuIntegratorImu_->integrateMeasurement(gtsam::Vector3(thisImu->linear_acceleration.x, thisImu->linear_acceleration.y, thisImu->linear_acceleration.z),gtsam::Vector3(thisImu->angular_velocity.x,    thisImu->angular_velocity.y,    thisImu->angular_velocity.z), dt);lastImuQT = imuTime;}
}++key;
doneFirstOpt = true;

failureDetection

imu因子图优化结果,速度或者偏置过大,认为失败
速度 模大于30
bias 加速度和角速度 模 大于1

Eigen::Vector3f vel(velCur.x(), velCur.y(), velCur.z());
if (vel.norm() > 30)
{ROS_WARN("Large velocity, reset IMU-preintegration!");return true;
}Eigen::Vector3f ba(biasCur.accelerometer().x(), biasCur.accelerometer().y(), biasCur.accelerometer().z());
Eigen::Vector3f bg(biasCur.gyroscope().x(), biasCur.gyroscope().y(), biasCur.gyroscope().z());
if (ba.norm() > 1.0 || bg.norm() > 1.0)
{ROS_WARN("Large bias, reset IMU-preintegration!");return true;
}return false;

LIO-SAM imuPreintegration相关推荐

  1. SLAM相关学习资料:综述/激光/视觉/数据集/常用库

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨菠萝包包包@知乎 来源丨https://zhuanlan.zhihu.com/p/4348743 ...

  2. 八种常用激光雷达和视觉SLAM算法的评估与比较

    文章:Evaluation and comparison of eight popular Lidar and Visual SLAM algorithms 作者:Bharath Garigipati ...

  3. 国内外LiDAR SLAM实验室总结

    知乎上有一个关于视觉的SLAM实验室以及20年前的视觉SLAM开源算法的汇总SLAM 领域国内外优秀实验室汇总,非常详细,但是偏视觉一点,本文主要关注LiDAR SLAM. 这篇文章一开始是非常简短的 ...

  4. 激光SLAM论文简单导读--LOAM、VLOAM、LeGO-LOAM、LIO-SAM、LVI-SAM、LIMO、LIC-FUSION、TVL-SLAM、R2LIVE、R3LIVE

    激光SLAM论文简单导读--LOAM.VLOAM.LeGO-LOAM.LIO-SAM.LVI-SAM.LIMO.LIC-FUSION.TVL-SLAM.R2LIVE.R3LIVE 时间线 开篇巨作LO ...

  5. iSCSI target介绍及LIO实操入门

    前文介绍了iSCSI的基本架构及启动器的基本操作,也就是在客户端的操作.今天我们介绍一下目标器的相关概念.开源实现和基本操作.Linux操作系统下面有很多目标器的开源实现,比如LIO.SCST和TGT ...

  6. lio linux工具,Linux中三种SCSI target的介绍之LIO

    1. 简介 Linux-IO Target在Linux内核中(linux 2.6.38后),用软件实现各种SCSI Target,其支持的SAN技术中所有流行的存储协议包括Fibre Channel( ...

  7. 2019.03.01 bzoj2555: SubString(sam+lct)

    传送门 题意简述: 要求在线支持两个操作 (1):在当前字符串的后面插入一个字符串 (2):询问字符串s在当前字符串中出现了几次?(作为连续子串) 思路: 考虑用lctlctlct来动态维护samsa ...

  8. 4566: [Haoi2016]找相同字符 SAM

    折腾了好久.不过收获还是很多的.第一次自己去画SAM所建出来fail树.深入体会了这棵树的神奇性质. 当然,我最终靠着自己A掉了.(这是我第一次推SAM的性质(以前都是抄别人的,感觉自己好可耻),不过 ...

  9. BZOJ1396:识别子串(SAM)

    Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample I ...

  10. 【MIT Sam Hopkins教授】如何读论文?How to Read a Paper

    来源:专知 本文多图,建议阅读5分钟MIT Sam Hopkins助理教授为您讲解提供有效阅读论文的工具.

最新文章

  1. 跟益达学Solr5之Schema.xml详解
  2. python和access哪个实用_access和python学哪个
  3. python引用传递的区别_python的值类型和引用类型及值传递和引用传递的区别
  4. 举例介绍活动目录的优势
  5. 计算机网络(二十五)-IP数据报格式
  6. 网鼎杯2020php反序列化,2020-网鼎杯(青龙组)_Web题目 AreUserialz Writeup
  7. VMware Horizon7安装-分步指南
  8. 新年快乐!这是份值得收藏的2017年AI与深度学习要点大全
  9. Laravel 5.x 启动过程分析 [转]
  10. 《xxx重大需求征集系统的》可用性和可修改性战术分析
  11. vb 链接 oracle数据库,vb 连接oracle数据库
  12. class文件的反编译过程
  13. 【C语言经典100题】乒乓球队的比赛
  14. 数据全生命周期管理,华为FusionData一个方案搞定
  15. 淘宝之初:湖畔花园小区里诞生的巨人
  16. Elastic Stack之Beats(Filebeat、Metricbeat)、Kibana、Logstash教程
  17. 基于STC89C52单片机实现简易计算器
  18. 原生JS利用XMLHttpRequest实现Get和Post请求
  19. win10 任务栏图标左击无效,右击有效的处理办法
  20. 微信“公众平台测试账号”接口接入指南

热门文章

  1. 【软考题目】假设某磁盘的每个磁道划分成11个物理块,每块存放1个逻辑记录。
  2. 新应用——信息化财务管理,一站式满足多个需求
  3. 新疆库尔勒市杜鹃河上演人禽共泳和谐相处画卷
  4. 洛克菲勒家族是如何发家的,我们都看看
  5. Tromino(更准确地说是“右Trominio”)是一个由棋盘上的三个1x1方块组成的L型骨牌。
  6. 剪刀石头布(自留底)01
  7. navicat ssh postgresql 报错:no password supplied
  8. 小米5查看设备号信息及验证type-c数据线
  9. 中国电视艺术家协会名誉主席赵化勇一行莅临蓝海彤翔参观指导
  10. 关于快捷键 Ctrl+z 无效命令的问题