AddPose 对整个位姿估计器进行校准,本身估计器就是IMU加上里程计进行预测,预测肯定就有不准的情况,因此需要校准。就需要整个匹配后的Pose进行校准
首先是IMU_tracker 没有初始化则初始化
把Pose加入到
1 2 timed_pose_queue_.push_back (TimedPose{time, pose}); 
 
把这一帧Pose加入到timed_pose_queue,同时也要裁剪
1 2 3 AdvanceImuTracker (time, imu_tracker_.get ());
 
AdvanceImuTracker 把imu_tracker 更新到time时刻
整个函数完成了一个,根据IMU数据队列,利用imu_tracker对时间点time的imu姿态进行预测,然后校准,再预测,再校准,
此函数将imutracker 更新到time时刻
还有一个情况,如果不适用imu数据,或者需要预测的时刻都还没有imu的数据
那么就角速度就取里程计的,如果不适用里程计,那么就使用angularvelocity_from_poses  ,校准的位姿
因此即便是没有使用IMU,计算的方式其实也是一样的,只不过线性加速度永远不会变,角速度来自于odometry
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 void  PoseExtrapolator::AdvanceImuTracker (const  common::Time time,                                          ImuTracker* const  imu_tracker)  const   {     CHECK_GE (time, imu_tracker->time ());      if  (imu_data_.empty () || time < imu_data_.front ().time) {                              imu_tracker->Advance (time);          imu_tracker->AddImuLinearAccelerationObservation (Eigen::Vector3d::UnitZ ());          imu_tracker->AddImuAngularVelocityObservation (         odometry_data_.size () < 2  ? angular_velocity_from_poses_                                   : angular_velocity_from_odometry_);     return ;   }         if  (imu_tracker->time () < imu_data_.front ().time) {          imu_tracker->Advance (imu_data_.front ().time);   }            auto  it = std::lower_bound (       imu_data_.begin (), imu_data_.end (), imu_tracker->time (),       [](const  sensor::ImuData& imu_data, const  common::Time& time) {         return  imu_data.time < time;       });         while  (it != imu_data_.end () && it->time < time) {          imu_tracker->Advance (it->time);          imu_tracker->AddImuLinearAccelerationObservation (it->linear_acceleration);          imu_tracker->AddImuAngularVelocityObservation (it->angular_velocity);     ++it;   }      imu_tracker->Advance (time); } 
 
注意,这个advance,就是去变更imu_tracker的在某一个时刻time的四元数位姿,变更的依据为用于校准的角速度。即旋转情况。
EstimateGravityOrientation 预测得到time时刻 tracking frame 在 local 坐标系下的姿态
这个数据传过来的时候就已经被传到local坐标系下了
1 2 3 4 5 6 7 8 9 Eigen::Quaterniond PoseExtrapolator::EstimateGravityOrientation (      const  common::Time time)   {  ImuTracker imu_tracker = *imu_tracker_;      AdvanceImuTracker(time, &imu_tracker);      return  imu_tracker.orientation(); } 
 
有一个缓存变量,如果cachedextrapolated_pose  时间已经算过了就不需要再一次计算了
这里extrapolationimu_tracker  是用于旋转的预测,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 transform::Rigid3d PoseExtrapolator::ExtrapolatePose (const  common::Time time)   {  const  TimedPose& newest_timed_pose = timed_pose_queue_.back ();   CHECK_GE (time, newest_timed_pose.time);      if  (cached_extrapolated_pose_.time != time) {          const  Eigen::Vector3d translation =         ExtrapolateTranslation (time) + newest_timed_pose.pose.translation ();          const  Eigen::Quaterniond rotation =         newest_timed_pose.pose.rotation () *         ExtrapolateRotation (time, extrapolation_imu_tracker_.get ());     cached_extrapolated_pose_ =         TimedPose{time, transform::Rigid3d{translation, rotation}};   }   return  cached_extrapolated_pose_.pose; } 
 
预测一下time时刻的位移,通过  线性加速度乘以时间差,就可以得到位移差
1 2 3 4 5 6 7 8 9 10 11 12 13 Eigen::Vector3d PoseExtrapolator::ExtrapolateTranslation (common::Time time)   {  const  TimedPose& newest_timed_pose = timed_pose_queue_.back ();   const  double  extrapolation_delta =       common::ToSeconds (time - newest_timed_pose.time);             if  (odometry_data_.size () < 2 ) {     return  extrapolation_delta * linear_velocity_from_poses_;   }      return  extrapolation_delta * linear_velocity_from_odometry_; } 
 
先通过imu_tracker 获取上一次位姿校准的姿态,然后乘上现在的orientation,求出姿态变化量。
注意:
姿态变化量=上一次位姿的逆 * 这一次的位姿
1 2 3 4 5 6 7 8 9 10 11 12 13 // 计算从imu_tracker到time时刻的姿态变化量 Eigen::Quaterniond PoseExtrapolator::ExtrapolateRotation(     const common::Time time, ImuTracker* const imu_tracker) const {   CHECK_GE(time, imu_tracker->time());   // 更新imu_tracker的状态到time时刻   AdvanceImuTracker(time, imu_tracker);   // 通过imu_tracker_获取上一次位姿校准时的姿态   const Eigen::Quaterniond last_orientation = imu_tracker_->orientation();   // 求取上一帧到当前时刻预测出的姿态变化量:上一帧姿态四元数的逆 乘以 当前时刻预测出来的姿态四元数   return last_orientation.inverse() * imu_tracker->orientation(); } 
 
其返回结果为N个pose , 当前的速度和一个位姿
1 2 3 4 5 6 7 8 9 10 11 12 13   struct  ExtrapolationResult  {          std::vector<transform::Rigid3f> previous_poses;          transform::Rigid3d current_pose;     Eigen::Vector3d current_velocity;     Eigen::Quaterniond gravity_from_tracking;   }; 
 
按照时间逐个预测位姿,存入vector
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 PoseExtrapolator::ExtrapolationResult PoseExtrapolator::ExtrapolatePosesWithGravity (     const  std::vector<common::Time>& times) {   std::vector<transform::Rigid3f> poses;               for  (auto  it = times.begin (); it != std::prev (times.end ()); ++it) {     poses.push_back (ExtrapolatePose (*it).cast<float >());   }      const  Eigen::Vector3d current_velocity = odometry_data_.size () < 2                                                 ? linear_velocity_from_poses_                                                : linear_velocity_from_odometry_;   return  ExtrapolationResult{poses, ExtrapolatePose (times.back ()),                              current_velocity,                              EstimateGravityOrientation (times.back ())}; } 
 
总结 PoseExtrapolator 这个位姿推测器,他就是用来预测位姿,位姿又分为平移和旋转(姿态),姿态通过IMUtraker预测,平移通过线速度进行预测,使用里程计的话就用里程计的线速度进行预测,没有的画 就用Pose计算出来的线速度进行预测
旋转:首选考虑的IMU,先预测,然后进行通过线性加速度进行校准,imu_tracker一个是确定俯仰角和翻滚角,还有一个就是对yaw角进行推测,偏航角,这个角就是用角速度进行推测,首先考虑imu的角速度,次之使用里程计的角速度,再次之使用pose的角速度
cartographer总是认为他的位姿推测器是准确的,因此如果IMU不准,有加速度却没有动的话,他会以为你的机器人在移动
使用里程计的时候,最好还是要加上imu,因为 里程计的角度是通过其他方式进行预测的,不如就自己搞雷达