其实也就是包括两个方面的内容:类似于运动模型的位姿估计和扫描匹配,因为需要计算速度,所以时间就有必要了!

1. PoseExtrapolator解决了IMU数据、里程计和位姿信息进行融合的问题。

该类定义了三个队列。

 std::deque<TimedPose> timed_pose_queue_;
std::deque<sensor::ImuData> imu_data_;
std::deque<sensor::OdometryData> odometry_data_;

定义了(a)通过位姿计算线速度和角速度对象

  Eigen::Vector3d linear_velocity_from_poses_ = Eigen::Vector3d::Zero();
Eigen::Vector3d angular_velocity_from_poses_ = Eigen::Vector3d::Zero();

和(b)通过里程计计算线速度和角速度对象

 Eigen::Vector3d linear_velocity_from_odometry_ = Eigen::Vector3d::Zero();
Eigen::Vector3d angular_velocity_from_odometry_ = Eigen::Vector3d::Zero();

轮询处理三类消息 IMU消息、里程计消息,激光测距消息。有如下情况:

1)不使用IMU和里程计

  只执行AddPose,注意12-15行的代码,$ time{\rm{ }} - timed\_pose\_queue\_\left[ 1 \right].time \ge pose\_queue\_duration\_ $,队列中最前面的数据的时间,当距离当前时间超过一定间隔时执行,作用是将较早时间的数据剔除。接着,根据位姿计算运动速度。对齐IMU数据,里程计数据。

 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_ =
common::make_unique<ImuTracker>(gravity_time_constant_, tracker_start);
}
timed_pose_queue_.push_back(TimedPose{time, pose});
while (timed_pose_queue_.size() > &&
timed_pose_queue_[].time <= time - pose_queue_duration_) {
timed_pose_queue_.pop_front();
}
UpdateVelocitiesFromPoses();
AdvanceImuTracker(time, imu_tracker_.get());
TrimImuData();
TrimOdometryData();
odometry_imu_tracker_ = common::make_unique<ImuTracker>(*imu_tracker_);
extrapolation_imu_tracker_ = common::make_unique<ImuTracker>(*imu_tracker_);
}

第16行,更新了根据Pose计算的线速度和角速度。

 void PoseExtrapolator::UpdateVelocitiesFromPoses()
{
if (timed_pose_queue_.size() < )
{
// We need two poses to estimate velocities.
return;
}
CHECK(!timed_pose_queue_.empty());
const TimedPose& newest_timed_pose = timed_pose_queue_.back();
const auto newest_time = newest_timed_pose.time;
const TimedPose& oldest_timed_pose = timed_pose_queue_.front();
const auto oldest_time = oldest_timed_pose.time;
const double queue_delta = common::ToSeconds(newest_time - oldest_time);
if (queue_delta < 0.001) { // 1 ms
LOG(WARNING) << "Queue too short for velocity estimation. Queue duration: "
<< queue_delta << " ms";
return;
}
const transform::Rigid3d& newest_pose = newest_timed_pose.pose;
const transform::Rigid3d& oldest_pose = oldest_timed_pose.pose;
linear_velocity_from_poses_ =
(newest_pose.translation() - oldest_pose.translation()) / queue_delta;
angular_velocity_from_poses_ =
transform::RotationQuaternionToAngleAxisVector(
oldest_pose.rotation().inverse() * newest_pose.rotation()) /
queue_delta;
}

PoseExtrapolator::UpdateVelocitiesFromPoses()

17行执行了PoseExtrapolator::AdvanceImuTracker方法,当不使用IMU数据时,将 angular_velocity_from_poses_ 或者 angular_velocity_from_odometry_ 数据传入了imu_tracker.

 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)
{
// There is no IMU data until 'time', so we advance the ImuTracker and use
// the angular velocities from poses and fake gravity to help 2D stability.
imu_tracker->Advance(time);
imu_tracker->AddImuLinearAccelerationObservation(Eigen::Vector3d::UnitZ());
imu_tracker->AddImuAngularVelocityObservation(
odometry_data_.size() < ? angular_velocity_from_poses_
: angular_velocity_from_odometry_);
return;
}
if (imu_tracker->time() < imu_data_.front().time) {
// Advance to the beginning of 'imu_data_'.
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);
}

在执行ExtrapolatePose(),推测某一时刻的位姿的时候,调用了 ExtrapolateTranslation 和 ExtrapolateRotation 方法。

 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;
}

可以看到使用的是:(1)旋转,imu_tracker的方位角角的变化量;(2)平移,里程计或者位姿线速度计算的移动量。

 Eigen::Quaterniond PoseExtrapolator::ExtrapolateRotation(
const common::Time time, ImuTracker* const imu_tracker) const {
CHECK_GE(time, imu_tracker->time());
AdvanceImuTracker(time, imu_tracker);
const Eigen::Quaterniond last_orientation = imu_tracker_->orientation();
return last_orientation.inverse() * imu_tracker->orientation();
} 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() < ) {
return extrapolation_delta * linear_velocity_from_poses_;
}
return extrapolation_delta * linear_velocity_from_odometry_;
}

2)使用IMU和里程计

  IMU频率最高,假设消息进入的先后顺序是IMU、里程计,最后是激光消息。

2. RealTimeCorrelativeScanMatcher解决了Scan和子图的扫描匹配的问题。

通过 real_time_correlative_scan_matcher_ 和 ceres_scan_matcher_ 实现的

 std::unique_ptr<transform::Rigid2d> LocalTrajectoryBuilder::ScanMatch(
const common::Time time, const transform::Rigid2d& pose_prediction,
const sensor::RangeData& gravity_aligned_range_data)
{
std::shared_ptr<const Submap> matching_submap =
active_submaps_.submaps().front();
// The online correlative scan matcher will refine the initial estimate for
// the Ceres scan matcher.
transform::Rigid2d initial_ceres_pose = pose_prediction;
sensor::AdaptiveVoxelFilter adaptive_voxel_filter(
options_.adaptive_voxel_filter_options());
const sensor::PointCloud filtered_gravity_aligned_point_cloud =
adaptive_voxel_filter.Filter(gravity_aligned_range_data.returns);
if (filtered_gravity_aligned_point_cloud.empty())
{
return nullptr;
}
if (options_.use_online_correlative_scan_matching())
{
real_time_correlative_scan_matcher_.Match(
pose_prediction, filtered_gravity_aligned_point_cloud,
matching_submap->probability_grid(), &initial_ceres_pose);
} auto pose_observation = common::make_unique<transform::Rigid2d>();
ceres::Solver::Summary summary;
ceres_scan_matcher_.Match(pose_prediction.translation(), initial_ceres_pose,
filtered_gravity_aligned_point_cloud,
matching_submap->probability_grid(),
pose_observation.get(), &summary);
return pose_observation;
}

Cartographer源码阅读(7):轨迹推算和位姿推算的原理的更多相关文章

  1. Cartographer源码阅读(6):LocalTrajectoryBuilder和PoseExtrapolator

    LocalTrajectoryBuilder意思是局部轨迹的构建,下面的类图中方法的参数没有画进去. 注意其中的三个类:PoseExtrapolator类,RealTimeCorrelativeSca ...

  2. Cartographer源码阅读(2):Node和MapBuilder对象

    上文提到特别注意map_builder_bridge_.AddTrajectory(x,x),查看其中的代码.两点: 首先是map_builder_.AddTrajectoryBuilder(...) ...

  3. angular源码阅读3:真的,依赖注入的原理

    前面已经提到了: 如何注册一个module. 如何获取一个module. injector与module以及provider的关系. 那么已经剩下最后一部分了,就是关于依赖是如何被注入的. 且看下面这 ...

  4. Vue2.0源码阅读笔记(二):响应式原理

      Vue是数据驱动的框架,在修改数据时,视图会进行更新.数据响应式系统使得状态管理变的简单直接,在开发过程中减少与DOM元素的接触.而深入学习其中的原理十分有必要,能够回避一些常见的问题,使开发变的 ...

  5. Cartographer源码阅读(4):Node和MapBuilder对象2

    MapBuilder的成员变量sensor::Collator sensor_collator_; 再次阅读MapBuilder::AddTrajectoryBuilder方法.首先构造了mappin ...

  6. Cartographer源码阅读(1):程序入口

    带着几个思考问题: (1)IMU数据的使用,如何融合,Kalman滤波? (2)图优化的具体实现,闭环检测的策略? (3)3D激光的接入和闭环策略? 1. 安装Kdevelop工具: http://b ...

  7. Cartographer源码阅读(8):imu_tracker

    IMU的输入为imu_linear_acceleration 和  imu_angular_velocity 线加速和角速度.最终作为属性输出的是方位四元数.  Eigen::Quaterniond ...

  8. Cartographer源码阅读(5):PoseGraph位姿图

    PoseGraph位姿图 mapping2D::PoseGraph类的注释: // Implements the loop closure method called Sparse Pose Adju ...

  9. Cartographer源码阅读(3):程序逻辑结构

    Cartographer早期的代码在进行3d制图的时候使用了UKF方法,查看现有的tag版本,可以转到0.1.0和0.2.0查看,包含kalman_filter文件夹. 文件夹中的pose_track ...

随机推荐

  1. .NET Core+NLog+存储配置 日志存入到数据库

    nlog-config.xml 配置文件: <?xml version="1.0" encoding="utf-8" ?> <nlog xml ...

  2. 解決中文地址Uri.IsWellFormedUriString返回false

    數字和大小寫字母都ok,但是中文地址就會有問題 public bool IslocalURL(string url) { if (string.IsNullOrEmpty(url)) { return ...

  3. 【转】Android系统开篇

    版权声明:本站所有博文内容均为原创,转载请务必注明作者与原文链接,且不得篡改原文内容.另外,未经授权文章不得用于任何商业目的. 一.引言 Android系统非常庞大.错综复杂,其底层是采用Linux作 ...

  4. vue及ElementUI环境搭建

    1. nodejs安装及npm安装 下载地址:https://nodejs.org/en/download/ 选择windows Installer 下载完成后 运行node-v8.11.1-x64. ...

  5. C#中准确跟踪错误异常所在的文件位置方法

    准确跟踪错误异常所在的文件位置方法是在发布改文件所在的DLL时候,把对应的pdb文件也一同发布. pdb文件是:PDB全称Program Database,不知道中文翻译叫什么.相信使用过VS的人对于 ...

  6. 以太坊客户端Geth命令用法-参数详解【转载】

    原文链接:http://www.cnblogs.com/tinyxiong/p/7918706.html Geth在以太坊智能合约开发中最常用的工具(必备开发工具),一个多用途的命令行工具.熟悉Get ...

  7. 3.贝叶斯网络表示(The Bayesian Network Representation)

    对于一个n随机变量的联合分布,一般需要2**n-1个参数来表示这个分布.但是,我们可以通过随机变量之间的独立性,减少参数的个数. naive Beyes model: Bayesian Network ...

  8. (原)关于ffmpeg使用custom io-context遇到的一些坑

    今天在使用android-as_video_player这个开源的框架来实现自己项目中的一个播放器,中间关于ndk编译ffmpeg遇到的坑,现在想起来,对ndk的心态,现在心里都有一万个cnm在奔腾, ...

  9. C# 在while循环中new的对象

    一: 问:那每次循环都会new一个A?那内存不是会满吗?还是说要把这个初始化对象的动作放到循环外面的写法会比较好? while(true) { A a = new A(); ... sleep(100 ...

  10. VSCode之快捷键和常用插件

    前言 介绍一下我在VSCode中常用的一些快捷方式: ctrl+上下箭头 上下滚动页面 Ctrl+Shift+K 删除某一行 Alt+ ↑ / ↓ 移动某一行 Shift+Alt + ↓ / ↑ 复制 ...