位姿推测器的实现位于cartographer/mapping/pose_extrapolator.h
中
他继承于类PoseExtrapolatorInterface
,在这个类的上方,有一个函数,创建位姿推测器的参数配置
1 2 3 4 5 6 7 8 9 10 11 12 proto::PoseExtrapolatorOptions CreatePoseExtrapolatorOptions ( common::LuaParameterDictionary* const parameter_dictionary) { proto::PoseExtrapolatorOptions options; options.set_use_imu_based (parameter_dictionary->GetBool ("use_imu_based" )); *options.mutable_constant_velocity () = CreateConstantVelocityPoseExtrapolatorOptions ( parameter_dictionary->GetDictionary ("constant_velocity" ).get ()); *options.mutable_imu_based () = CreateImuBasedPoseExtrapolatorOptions ( parameter_dictionary->GetDictionary ("imu_based" ).get ()); return options; }
从这里加载三个位姿推测器的参数,返回configuration_files/trajectory_builder_2d.lua
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 pose_extrapolator = { use_imu_based = false , constant_velocity = { imu_gravity_time_constant = 10. , pose_queue_duration = 0.001 , }, imu_based = { pose_queue_duration = 5. , gravity_constant = 9.806 , pose_translation_weight = 1. , pose_rotation_weight = 1. , imu_acceleration_weight = 1. , imu_rotation_weight = 1. , odometry_translation_weight = 1. , odometry_rotation_weight = 1. , solver_options = { use_nonmonotonic_steps = false ; max_num_iterations = 10 ; num_threads = 1 ; }, }, },
可以看见,pose_extrapolator 里面就是这 三项
最后他会把option返回给CreateLocalTrajectoryBuilderOption2D
,返回CreateTrajectoryOptions
,最后返回的就是LoadOption
位姿推测器 思维导图
构造函数 1 2 3 4 5 6 7 8 9 10 11 12 PoseExtrapolator::PoseExtrapolator (const common::Duration pose_queue_duration, double imu_gravity_time_constant) : pose_queue_duration_ (pose_queue_duration), gravity_time_constant_ (imu_gravity_time_constant), cached_extrapolated_pose_{common::Time::min (), transform::Rigid3d::Identity ()} {}
GetLastPoseTime 通过一个队列把数据进行返回
1 2 3 4 5 6 7 8 common::Time PoseExtrapolator::GetLastPoseTime () const { if (timed_pose_queue_.empty ()) { return common::Time::min (); } return timed_pose_queue_.back ().time; }
获取上一次预测位姿的时间
1 2 3 4 5 6 7 common::Time PoseExtrapolator::GetLastExtrapolatedTime () const { if (!extrapolation_imu_tracker_) { return common::Time::min (); } return extrapolation_imu_tracker_->time (); }
AddPose 将扫描匹配后的pose加入到pose队列中,计算线速度与角速度,并将imutracker 的状态更新到time时刻
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 void PoseExtrapolator::AddPose (const common::Time time, const transform::Rigid3d& pose) { if (imu_tracker_ == nullptr ) { common::Time tracker_start = time; if (!imu_data_.empty ()) { tracker_start = std::min (tracker_start, imu_data_.front ().time); } imu_tracker_ = absl::make_unique<ImuTracker>(gravity_time_constant_, tracker_start); } timed_pose_queue_.push_back (TimedPose{time, pose}); while (timed_pose_queue_.size () > 2 && timed_pose_queue_[1 ].time <= time - pose_queue_duration_) { timed_pose_queue_.pop_front (); } UpdateVelocitiesFromPoses (); AdvanceImuTracker (time, imu_tracker_.get ()); TrimImuData (); TrimOdometryData (); odometry_imu_tracker_ = absl::make_unique<ImuTracker>(*imu_tracker_); extrapolation_imu_tracker_ = absl::make_unique<ImuTracker>(*imu_tracker_); }
AddImuData 被调用处 于LocalTrajectoryBuilder2D::AddImuData中调用,在这里初始化了位姿估计器,并且添加了IMU数据、
上面的函数被调用于 GlobalTrajectoryBuilder文件的AddSensorData函数中,此函数分别把imu数据传入前端和后端
定义 1 2 3 4 5 6 7 void PoseExtrapolator::AddImuData (const sensor::ImuData& imu_data) { CHECK (timed_pose_queue_.empty () || imu_data.time >= timed_pose_queue_.back ().time); imu_data_.push_back (imu_data); TrimImuData (); }
直接把数据添加到imu数据队列,并且对他进行一个裁剪TrimImuData
1 2 3 4 5 6 7 8 void PoseExtrapolator::TrimImuData () { while (imu_data_.size () > 1 && !timed_pose_queue_.empty () && imu_data_[1 ].time <= timed_pose_queue_.back ().time) { imu_data_.pop_front (); } }
AddOdometryData 向odom数据队列中添加odom数据,并进行数据队列的修剪,并计算角速度与线速度
注意,这里的sensor::OdometryData
中的Pose已经是转化为tracking_frame的pose了。
计算角速度的方式为,记录odom数据队列的队首和队尾的两个Pose的时间差$\Delta t$,两个Pose的旋转角度差$\Delta\theta$
计算线速度的方式和上面一样,只是从计算角度差变成计算为平移量的差
最后把线速度变换到local 坐标系下
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 void PoseExtrapolator::AddOdometryData ( const sensor::OdometryData& odometry_data) { CHECK (timed_pose_queue_.empty () || odometry_data.time >= timed_pose_queue_.back ().time); odometry_data_.push_back (odometry_data); TrimOdometryData (); if (odometry_data_.size () < 2 ) { return ; } const sensor::OdometryData& odometry_data_oldest = odometry_data_.front (); const sensor::OdometryData& odometry_data_newest = odometry_data_.back (); const double odometry_time_delta = common::ToSeconds (odometry_data_oldest.time - odometry_data_newest.time); const transform::Rigid3d odometry_pose_delta = odometry_data_newest.pose.inverse () * odometry_data_oldest.pose; angular_velocity_from_odometry_ = transform::RotationQuaternionToAngleAxisVector ( odometry_pose_delta.rotation ()) / odometry_time_delta; if (timed_pose_queue_.empty ()) { return ; } const Eigen::Vector3d linear_velocity_in_tracking_frame_at_newest_odometry_time = odometry_pose_delta.translation () / odometry_time_delta; const Eigen::Quaterniond orientation_at_newest_odometry_time = timed_pose_queue_.back ().pose.rotation () * ExtrapolateRotation (odometry_data_newest.time, odometry_imu_tracker_.get ()); linear_velocity_from_odometry_ = orientation_at_newest_odometry_time * linear_velocity_in_tracking_frame_at_newest_odometry_time; }
TrimOdometryData 最后pop的情况为,假设有两个数据了,那么pose为在中间,两个odom为在一个左边一个右边
1 2 3 4 5 6 7 8 void PoseExtrapolator::TrimOdometryData () { while (odometry_data_.size () > 2 && !timed_pose_queue_.empty () && odometry_data_[1 ].time <= timed_pose_queue_.back ().time) { odometry_data_.pop_front (); } }