MapBuilder的成员变量sensor::Collator sensor_collator_;

  再次阅读MapBuilder::AddTrajectoryBuilder方法。首先构造了mapping::GlobalTrajectoryBuilder实例,接着作为参数构造了CollatedTrajectoryBuilder实例。

trajectory_builders_.push_back(
common::make_unique<CollatedTrajectoryBuilder>(
&sensor_collator_, trajectory_id, expected_sensor_ids,
common::make_unique<mapping::GlobalTrajectoryBuilder<mapping_2d::LocalTrajectoryBuilder,mapping_2d::proto::LocalTrajectoryBuilderOptions,mapping_2d::PoseGraph>>
(trajectory_options.trajectory_builder_2d_options(),trajectory_id, pose_graph_2d_.get(),local_slam_result_callback)
)
);

  这里sensor_collator_作为参数传入,参与CollatedTrajectoryBuilder构造。查看构造函数:

CollatedTrajectoryBuilder::CollatedTrajectoryBuilder(sensor::Collator* const sensor_collator, const int trajectory_id, const std::unordered_set<std::string>& expected_sensor_ids,   std::unique_ptr<TrajectoryBuilderInterface> wrapped_trajectory_builder)
: sensor_collator_(sensor_collator)
, trajectory_id_(trajectory_id)
, wrapped_trajectory_builder_(std::move(wrapped_trajectory_builder))
, last_logging_time_(std::chrono::steady_clock::now())
{
sensor_collator_->AddTrajectory(trajectory_id, expected_sensor_ids,
[this](const std::string& sensor_id, std::unique_ptr<sensor::Data> data)
{
HandleCollatedSensorData(sensor_id, std::move(data));
}
);
}

  这里是回调函数,std::unique_ptr是表示参数为智能指针。

 [this](const std::string& sensor_id, std::unique_ptr<sensor::Data> data)
{
HandleCollatedSensorData(sensor_id, std::move(data));
}

  (1)查看sensor::Collator的AddTrajectory方法:

void Collator::AddTrajectory( const int trajectory_id, const std::unordered_set<std::string>& expected_sensor_ids, const Callback& callback)
{
for (const auto& sensor_id : expected_sensor_ids)
{
const auto queue_key = QueueKey{trajectory_id, sensor_id};
queue_.AddQueue(queue_key, [callback, sensor_id](std::unique_ptr<Data> data)
{
callback(sensor_id, std::move(data));
});
queue_keys_[trajectory_id].push_back(queue_key);
}
}

  for (const auto& sensor_id : expected_sensor_ids)用到了C++11的auto新特性。

  (2)查看HandleCollatedSensorData方法。调用了data->AddToTrajectoryBuilder(wrapped_trajectory_builder_.get());这里wrapped_trajectory_builder_是在CollatedTrajectoryBuilder构造函数中赋值的。为GlobalTrajectoryBuilder对象。因而查看sensor::Data的AddToTrajectoryBuilder() 方法。

  virtual void AddToTrajectoryBuilder(mapping::TrajectoryBuilderInterface *trajectory_builder) = 0;是sensor::Data类的一个虚方法。内部执行了trajectory_builder->AddSensorData(sensor_id_, data_);

最后调用的是GlobalTrajectoryBuilder对象的AddSensorData(xx)方法。

 void CollatedTrajectoryBuilder::HandleCollatedSensorData( const std::string& sensor_id, std::unique_ptr<sensor::Data> data)
{
auto it = rate_timers_.find(sensor_id);
if (it == rate_timers_.end())
{
it = rate_timers_ .emplace(
std::piecewise_construct, std::forward_as_tuple(sensor_id),
std::forward_as_tuple(common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds))) .first;
}
it->second.Pulse(data->GetTime()); if (std::chrono::steady_clock::now() - last_logging_time_ >
common::FromSeconds(kSensorDataRatesLoggingPeriodSeconds))
{
for (const auto& pair : rate_timers_)
{
LOG(INFO) << pair.first << " rate: " << pair.second.DebugString();
}
last_logging_time_ = std::chrono::steady_clock::now();
} data->AddToTrajectoryBuilder(wrapped_trajectory_builder_.get());
} }

CollatedTrajectoryBuilder::HandleCollatedSensorData

template <typename DataType>
class Dispatchable : public Data
{
public:
Dispatchable(const std::string &sensor_id, const DataType &data): Data(sensor_id), data_(data) {} common::Time GetTime() const override { return data_.time; } void AddToTrajectoryBuilder( mapping::TrajectoryBuilderInterface *const trajectory_builder) override
{
trajectory_builder->AddSensorData(sensor_id_, data_);
} private:
const DataType data_;
};

  再以IMU数据为例,GlobalTrajectoryBuilder类的AddSensorData(xx):

void AddSensorData(const std::string& sensor_id,  const sensor::ImuData& imu_data) override
{
local_trajectory_builder_.AddImuData(imu_data);
pose_graph_->AddImuData(trajectory_id_, imu_data);
}

  再看一下激光点云的数据

 void AddSensorData( const std::string& sensor_id, const sensor::TimedPointCloudData& timed_point_cloud_data) override
{
std::unique_ptr<typename LocalTrajectoryBuilder::MatchingResult> matching_result =
local_trajectory_builder_.AddRangeData( timed_point_cloud_data.time,
sensor::TimedRangeData {timed_point_cloud_data.origin,
timed_point_cloud_data.ranges, {}}
);
if (matching_result == nullptr)
{
// The range data has not been fully accumulated yet.
return;
}
std::unique_ptr<mapping::NodeId> node_id;
if (matching_result->insertion_result != nullptr)
{
node_id = ::cartographer::common::make_unique<mapping::NodeId>(
pose_graph_->AddNode(matching_result->insertion_result->constant_data,
trajectory_id_, matching_result->insertion_result->insertion_submaps));
CHECK_EQ(node_id->trajectory_id, trajectory_id_);
}
if (local_slam_result_callback_)
{
local_slam_result_callback_( trajectory_id_, matching_result->time,
matching_result->local_pose,
std::move(matching_result->range_data_in_local), std::move(node_id));
}
}

  这里有两个重要的步骤一个是local_trajectory_builder_.AddRangeData(xxx),一个是 pose_graph_->AddNode(xxx)方法。同时std::unique_ptr<typename LocalTrajectoryBuilder::MatchingResult> matching_result作为AddNode方法的参数。

 mapping::NodeId PoseGraph::AddNode(
std::shared_ptr<const mapping::TrajectoryNode::Data> constant_data,
const int trajectory_id,
const std::vector<std::shared_ptr<const Submap>>& insertion_submaps) {
const transform::Rigid3d optimized_pose(
GetLocalToGlobalTransform(trajectory_id) * constant_data->local_pose); common::MutexLocker locker(&mutex_);
AddTrajectoryIfNeeded(trajectory_id);
const mapping::NodeId node_id = trajectory_nodes_.Append(
trajectory_id, mapping::TrajectoryNode{constant_data, optimized_pose});
++num_trajectory_nodes_; // Test if the 'insertion_submap.back()' is one we never saw before.
if (submap_data_.SizeOfTrajectoryOrZero(trajectory_id) == ||
std::prev(submap_data_.EndOfTrajectory(trajectory_id))->data.submap !=
insertion_submaps.back()) {
// We grow 'submap_data_' as needed. This code assumes that the first
// time we see a new submap is as 'insertion_submaps.back()'.
const mapping::SubmapId submap_id =
submap_data_.Append(trajectory_id, SubmapData());
submap_data_.at(submap_id).submap = insertion_submaps.back();
} // We have to check this here, because it might have changed by the time we
// execute the lambda.
const bool newly_finished_submap = insertion_submaps.front()->finished();
AddWorkItem([=]() REQUIRES(mutex_) {
ComputeConstraintsForNode(node_id, insertion_submaps,
newly_finished_submap);
});
return node_id;
}

PoseGraph::AddNode

  PoseGraph::AddNode方法很重要,分析节点和子图的关系。

  此处强调一下GlobalTrajectoryBuilder的两个关键对象local_trajectory_builder_和pose_graph_。

  PoseGraph* const pose_graph_;
LocalTrajectoryBuilder local_trajectory_builder_;

  接下来按照准备安装ROS消息发布和处理的流程进行分析,即数据流。


参考资料:

http://blog.csdn.net/datase/article/details/78665862

http://blog.csdn.net/learnmoreonce/article/category/6989560

Cartographer源码阅读(4):Node和MapBuilder对象2的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. Cartographer源码阅读(9):图优化的前端——闭环检测

    约束计算 闭环检测的策略:搜索闭环,通过匹配检测是否是闭环,采用了分支定界法. 前已经述及PoseGraph的内容,此处继续.位姿图类定义了pose_graph::ConstraintBuilder ...

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

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

  8. Cartographer源码阅读(7):轨迹推算和位姿推算的原理

    其实也就是包括两个方面的内容:类似于运动模型的位姿估计和扫描匹配,因为需要计算速度,所以时间就有必要了! 1. PoseExtrapolator解决了IMU数据.里程计和位姿信息进行融合的问题. 该类 ...

  9. koa源码阅读[0]

    koa源码阅读[0] Node.js也是写了两三年的时间了,刚开始学习Node的时候,hello world就是创建一个HttpServer,后来在工作中也是经历过Express.Koa1.x.Koa ...

随机推荐

  1. Atitti 创业团队vs打工的团队 attilax总结

    Atitti 创业团队vs打工的团队 attilax总结 创业公司的性质与特点  热情,创新,效率 ,使命 为什么阿里员工很热情?因为他们大概都知道公司要做什么事情,也知道公司的使命.他们经常会跳出来 ...

  2. Asp.Net WebApi接口返回值IHttpActionResult

    WebApi是微软在VS2012 MVC4版本中绑定发行的,webapi2.0同mvc5发行的 webapi一共有以下接口返回值 1.void无返回值2.IHttpActionResult Json( ...

  3. Openfire 单人聊天和多人聊天(发送消息、接收消息)

    Openfire 单人聊天和多人聊天(发送消息.接收消息) 一.单人聊天 1)发送消息: 首先要获取一个聊天窗口,getConnection()为获取连接connection的方法,调用getFrie ...

  4. 产品设计利器--axure

    1.axute的使用方法: 2.普通线框图的使用: 3.高保真原型图: 4.交互思维. Axure RP8 是美国Axure Software Solution公司的旗舰产品,是一个快速的原型工具,主 ...

  5. 开始学习Functional Programming

    打算先学F#, 再学Scala. 第一个F#程序 open System [<EntryPoint>] let main argv = let a = "Hello, World ...

  6. mysql密码的坑

    一段时间没用本机的mysql,忘了root密码,从网上找的修改方法用起来大多都有问题.mysql版本8.0.12. 网上大多数思路:修改msql启动方式为带--skip-grant-tables参数: ...

  7. HTTP 03 HTTP 报文

    客户端的HTTP报文, 叫做请求报文 服务器端的叫做 响应报文. HTTP 报文本身是由多行 (用 CR+LF 作换行符) 数据构成的字符串文本. HTTP 报文大致分为报文首部 和 报文主体 两部分 ...

  8. [微信小程序] 微信小程序开发初步探索

    1.开发文档 https://developers.weixin.qq.com/miniprogram/dev/ app.json配置:https://developers.weixin.qq.com ...

  9. 用WordPress建立专业网站教程 (一步步建站, 一步也不少)

    最新美国域名中心US Domain Center: http://www.usdomaincenter.com/ 建站教程 (10分钟上线, 无需备案): https://www.qiyewp.com ...

  10. C# 3个延时函数

    ) { int time = Environment.TickCount; while (true) { if (Environment.TickCount - time >= DelayTim ...