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

  首先是map_builder_.AddTrajectoryBuilder(...),调用了map_builder_对象的方法。其次是sensor_bridges_键值对的赋值。

int MapBuilderBridge::AddTrajectory(const std::unordered_set<std::string>& expected_sensor_ids,  const TrajectoryOptions& trajectory_options)
{
const int trajectory_id = map_builder_.AddTrajectoryBuilder(expected_sensor_ids, trajectory_options.trajectory_builder_options,
::std::bind(&MapBuilderBridge::OnLocalSlamResult, this,
::std::placeholders::_1, ::std::placeholders::_2,
::std::placeholders::_3, ::std::placeholders::_4,
::std::placeholders::_5));
LOG(INFO) << "Added trajectory with ID '" << trajectory_id << "'."; // Make sure there is no trajectory with 'trajectory_id' yet.
CHECK_EQ(sensor_bridges_.count(trajectory_id), 0);
sensor_bridges_[trajectory_id] = cartographer::common::make_unique<SensorBridge>(
trajectory_options.num_subdivisions_per_laser_scan,
trajectory_options.tracking_frame,
node_options_.lookup_transform_timeout_sec, tf_buffer_,
map_builder_.GetTrajectoryBuilder(trajectory_id));
auto emplace_result = trajectory_options_.emplace(trajectory_id, trajectory_options);
CHECK(emplace_result.second == true);
return trajectory_id;
}

  其中map_builder_.AddTrajectoryBuilder(...)是Cartographer项目中的代码了。

int MapBuilder::AddTrajectoryBuilder( const std::unordered_set<std::string>& expected_sensor_ids, const proto::TrajectoryBuilderOptions& trajectory_options,
LocalSlamResultCallback local_slam_result_callback)
{
const int trajectory_id = trajectory_builders_.size();//生成trajectory_id
if (options_.use_trajectory_builder_3d())
{
CHECK(trajectory_options.has_trajectory_builder_3d_options());
trajectory_builders_.push_back(common::make_unique<CollatedTrajectoryBuilder>(
&sensor_collator_, trajectory_id, expected_sensor_ids,
common::make_unique<mapping::GlobalTrajectoryBuilder<
mapping_3d::LocalTrajectoryBuilder,
mapping_3d::proto::LocalTrajectoryBuilderOptions,
mapping_3d::PoseGraph>>(
trajectory_options.trajectory_builder_3d_options(),
trajectory_id, pose_graph_3d_.get(),
local_slam_result_callback)));//注意此处的push_back()方法
}
else
{
CHECK(trajectory_options.has_trajectory_builder_2d_options());
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)));//注意此处的push_back()方法
}
if (trajectory_options.pure_localization())
{
constexpr int kSubmapsToKeep = 3;
pose_graph_->AddTrimmer(common::make_unique<PureLocalizationTrimmer>(trajectory_id, kSubmapsToKeep));
}
if (trajectory_options.has_initial_trajectory_pose())
{
const auto& initial_trajectory_pose = trajectory_options.initial_trajectory_pose();
pose_graph_->SetInitialTrajectoryPose(trajectory_id, initial_trajectory_pose.to_trajectory_id(),
transform::ToRigid3(initial_trajectory_pose.relative_pose()), common::FromUniversal(initial_trajectory_pose.timestamp()));
}
return trajectory_id;
}

  注意,trajectory_builders_是根据trajectory_id添加的。以后调用的时候根据trajectory_id调用。

  2D/3D区分:同时可以看到,这里对2D和3D情况作了区分,根据options_.use_trajectory_builder_3d()确定使用的类型。

  在ROS的主循环运行过程中,会不断处理传感器传入的数据。

  以IMU数据为例,auto sensor_bridge_ptr = map_builder_bridge_.sensor_bridge(trajectory_id),根据trajectory_id获取sensor_bridge_ptr。注意这里因为是订阅的其它ROS主题(Topic),所以sensor_id参数是从其他主题传入的。(即当前程序内部有一套主题名称的字符串,订阅了外部主题也有一套名称字符串表示。这样两者通过同样的名称字符串建立了关系)

void Node::HandleImuMessage(const int trajectory_id, const std::string& sensor_id, const sensor_msgs::Imu::ConstPtr& msg)
{
carto::common::MutexLocker lock(&mutex_);
if (!sensor_samplers_.at(trajectory_id).imu_sampler.Pulse())
{
return;
}
auto sensor_bridge_ptr = map_builder_bridge_.sensor_bridge(trajectory_id);
auto imu_data_ptr = sensor_bridge_ptr->ToImuData(msg);
if (imu_data_ptr != nullptr)
{
extrapolators_.at(trajectory_id).AddImuData(*imu_data_ptr);
}
sensor_bridge_ptr->HandleImuMessage(sensor_id, msg);
}

  最后调用了sensor_bridge_ptr->HandleImuMessage(sensor_id, msg);的代码。这里又通过trajectory_builder_调用了AddSensorData方法,由于之前做为参数传入的是CollatedTrajectoryBuilder,所以实际调用的是CollatedTrajectoryBuilder的AddSensorData方法。

void SensorBridge::HandleImuMessage(const std::string& sensor_id, const sensor_msgs::Imu::ConstPtr& msg)
{
std::unique_ptr<::cartographer::sensor::ImuData> imu_data = ToImuData(msg);
if (imu_data != nullptr)
{
trajectory_builder_->AddSensorData( sensor_id, cartographer::sensor::ImuData{imu_data->time, imu_data->linear_acceleration, imu_data->angular_velocity});
}
}

  SensorBridge类实现代码,消息转换函数查看msg_conversion.cc文件:

 SensorBridge::SensorBridge(
const int num_subdivisions_per_laser_scan,
const std::string& tracking_frame,
const double lookup_transform_timeout_sec, tf2_ros::Buffer* const tf_buffer,
carto::mapping::TrajectoryBuilderInterface* const trajectory_builder)
: num_subdivisions_per_laser_scan_(num_subdivisions_per_laser_scan),
tf_bridge_(tracking_frame, lookup_transform_timeout_sec, tf_buffer),
trajectory_builder_(trajectory_builder) {} std::unique_ptr<::cartographer::sensor::OdometryData>
SensorBridge::ToOdometryData(const nav_msgs::Odometry::ConstPtr& msg) {
const carto::common::Time time = FromRos(msg->header.stamp);
const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
time, CheckNoLeadingSlash(msg->child_frame_id));
if (sensor_to_tracking == nullptr) {
return nullptr;
}
return ::cartographer::common::make_unique<
::cartographer::sensor::OdometryData>(
::cartographer::sensor::OdometryData{
time, ToRigid3d(msg->pose.pose) * sensor_to_tracking->inverse()});
} void SensorBridge::HandleOdometryMessage(
const std::string& sensor_id, const nav_msgs::Odometry::ConstPtr& msg) {
std::unique_ptr<::cartographer::sensor::OdometryData> odometry_data =
ToOdometryData(msg);
if (odometry_data != nullptr) {
trajectory_builder_->AddSensorData(
sensor_id, cartographer::sensor::OdometryData{odometry_data->time,
odometry_data->pose});
}
} std::unique_ptr<::cartographer::sensor::ImuData> SensorBridge::ToImuData(
const sensor_msgs::Imu::ConstPtr& msg) {
CHECK_NE(msg->linear_acceleration_covariance[], -)
<< "Your IMU data claims to not contain linear acceleration measurements "
"by setting linear_acceleration_covariance[0] to -1. Cartographer "
"requires this data to work. See "
"http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html.";
CHECK_NE(msg->angular_velocity_covariance[], -)
<< "Your IMU data claims to not contain angular velocity measurements "
"by setting angular_velocity_covariance[0] to -1. Cartographer "
"requires this data to work. See "
"http://docs.ros.org/api/sensor_msgs/html/msg/Imu.html."; const carto::common::Time time = FromRos(msg->header.stamp);
const auto sensor_to_tracking = tf_bridge_.LookupToTracking(
time, CheckNoLeadingSlash(msg->header.frame_id));
if (sensor_to_tracking == nullptr) {
return nullptr;
}
CHECK(sensor_to_tracking->translation().norm() < 1e-)
<< "The IMU frame must be colocated with the tracking frame. "
"Transforming linear acceleration into the tracking frame will "
"otherwise be imprecise.";
return ::cartographer::common::make_unique<::cartographer::sensor::ImuData>(
::cartographer::sensor::ImuData{
time,
sensor_to_tracking->rotation() * ToEigen(msg->linear_acceleration),
sensor_to_tracking->rotation() * ToEigen(msg->angular_velocity)});
} void SensorBridge::HandleImuMessage(const std::string& sensor_id,
const sensor_msgs::Imu::ConstPtr& msg) {
std::unique_ptr<::cartographer::sensor::ImuData> imu_data = ToImuData(msg);
if (imu_data != nullptr) {
trajectory_builder_->AddSensorData(
sensor_id, cartographer::sensor::ImuData{imu_data->time,
imu_data->linear_acceleration,
imu_data->angular_velocity});
}
} void SensorBridge::HandleLaserScanMessage(
const std::string& sensor_id, const sensor_msgs::LaserScan::ConstPtr& msg) {
::cartographer::sensor::PointCloudWithIntensities point_cloud;
::cartographer::common::Time time;
std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);
HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
} void SensorBridge::HandleMultiEchoLaserScanMessage(
const std::string& sensor_id,
const sensor_msgs::MultiEchoLaserScan::ConstPtr& msg) {
::cartographer::sensor::PointCloudWithIntensities point_cloud;
::cartographer::common::Time time;
std::tie(point_cloud, time) = ToPointCloudWithIntensities(*msg);
HandleLaserScan(sensor_id, time, msg->header.frame_id, point_cloud);
} void SensorBridge::HandlePointCloud2Message(
const std::string& sensor_id,
const sensor_msgs::PointCloud2::ConstPtr& msg) {
pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
pcl::fromROSMsg(*msg, pcl_point_cloud);
carto::sensor::TimedPointCloud point_cloud;
for (const auto& point : pcl_point_cloud) {
point_cloud.emplace_back(point.x, point.y, point.z, .f);
}
HandleRangefinder(sensor_id, FromRos(msg->header.stamp), msg->header.frame_id,
point_cloud);
} const TfBridge& SensorBridge::tf_bridge() const { return tf_bridge_; } void SensorBridge::HandleLaserScan(
const std::string& sensor_id, const carto::common::Time time,
const std::string& frame_id,
const carto::sensor::PointCloudWithIntensities& points) {
CHECK_LE(points.points.back()[], );
// TODO(gaschler): Use per-point time instead of subdivisions.
for (int i = ; i != num_subdivisions_per_laser_scan_; ++i) {
const size_t start_index =
points.points.size() * i / num_subdivisions_per_laser_scan_;
const size_t end_index =
points.points.size() * (i + ) / num_subdivisions_per_laser_scan_;
carto::sensor::TimedPointCloud subdivision(
points.points.begin() + start_index, points.points.begin() + end_index);
if (start_index == end_index) {
continue;
}
const double time_to_subdivision_end = subdivision.back()[];
// `subdivision_time` is the end of the measurement so sensor::Collator will
// send all other sensor data first.
const carto::common::Time subdivision_time =
time + carto::common::FromSeconds(time_to_subdivision_end);
for (auto& point : subdivision) {
point[] -= time_to_subdivision_end;
}
CHECK_EQ(subdivision.back()[], );
HandleRangefinder(sensor_id, subdivision_time, frame_id, subdivision);
}
} void SensorBridge::HandleRangefinder(
const std::string& sensor_id, const carto::common::Time time,
const std::string& frame_id, const carto::sensor::TimedPointCloud& ranges) {
const auto sensor_to_tracking =
tf_bridge_.LookupToTracking(time, CheckNoLeadingSlash(frame_id));
if (sensor_to_tracking != nullptr) {
trajectory_builder_->AddSensorData(
sensor_id, cartographer::sensor::TimedPointCloudData{
time, sensor_to_tracking->translation().cast<float>(),
carto::sensor::TransformTimedPointCloud(
ranges, sensor_to_tracking->cast<float>())});
}
}

SensorBridge

 ToPointCloudWithIntensities(const sensor_msgs::PointCloud2& message) {
PointCloudWithIntensities point_cloud;
// We check for intensity field here to avoid run-time warnings if we pass in
// a PointCloud2 without intensity.
if (PointCloud2HasField(message, "intensity")) {
pcl::PointCloud<pcl::PointXYZI> pcl_point_cloud;
pcl::fromROSMsg(message, pcl_point_cloud);
for (const auto& point : pcl_point_cloud) {
point_cloud.points.emplace_back(point.x, point.y, point.z, .f);
point_cloud.intensities.push_back(point.intensity);
}
} else {
pcl::PointCloud<pcl::PointXYZ> pcl_point_cloud;
pcl::fromROSMsg(message, pcl_point_cloud); // If we don't have an intensity field, just copy XYZ and fill in
// 1.0.
for (const auto& point : pcl_point_cloud) {
point_cloud.points.emplace_back(point.x, point.y, point.z, .f);
point_cloud.intensities.push_back(1.0);
}
}
return std::make_tuple(point_cloud, FromRos(message.header.stamp));
}

msg_conversion.cc

  查看CollatedTrajectoryBuilder的AddSensorData方法,在CollatedTrajectoryBuilder的头文件中,包括4个覆写的AddSensorData(x,x)方法,方法中通过sensor::MakeDispatchable转换为Dispatchable<DataType>类型。

 void AddSensorData(const std::string& sensor_id, const sensor::TimedPointCloudData& timed_point_cloud_data) override
{
AddSensorData(sensor::MakeDispatchable(sensor_id, timed_point_cloud_data));
} void AddSensorData(const std::string& sensor_id, const sensor::ImuData& imu_data) override
{
AddSensorData(sensor::MakeDispatchable(sensor_id, imu_data));
} void AddSensorData(const std::string& sensor_id, const sensor::OdometryData& odometry_data) override
{
AddSensorData(sensor::MakeDispatchable(sensor_id, odometry_data));
} void AddSensorData(const std::string& sensor_id, const sensor::FixedFramePoseData& fixed_frame_pose_data) override
{
AddSensorData(sensor::MakeDispatchable(sensor_id, fixed_frame_pose_data));
}

  最终定位到了sensor_collator_对象的方法。

void CollatedTrajectoryBuilder::AddSensorData( std::unique_ptr<sensor::Data> data)
{
sensor_collator_->AddSensorData(trajectory_id_, std::move(data));
}

  


  查看几个类CollatedTrajectoryBuilder,mapping::GlobalTrajectoryBuilder

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

  mapping::GlobalTrajectoryBuilder构造函数

GlobalTrajectoryBuilder(const LocalTrajectoryBuilderOptions& options, const int trajectory_id,
PoseGraph* const pose_graph, const LocalSlamResultCallback& local_slam_result_callback)
: trajectory_id_(trajectory_id), pose_graph_(pose_graph),
local_trajectory_builder_(options), local_slam_result_callback_(local_slam_result_callback)
{}

  注意这里的继承关系:

    class CollatedTrajectoryBuilder : public TrajectoryBuilderInterface

    class GlobalTrajectoryBuilder : public mapping::TrajectoryBuilderInterface

  在mapping_2d和mapping_3d两个命名空间下分别存在2个local_trajectory_builder_类,实现了局部的扫描匹配和子图构建。代码在cartographer\cartographer\internal文件夹下。

  另外一个重要的Node类变量是extrapolators_,该对象在Node类的处理Odometry和IMU数据时都有用到,作用是位姿推算。在文一种Node::AddTrajectory方法中调用了AddExtrapolator(trajectory_id, options);

 std::map<int, ::cartographer::mapping::PoseExtrapolator> extrapolators_;
void Node::AddExtrapolator(const int trajectory_id, const TrajectoryOptions& options)
{
constexpr double kExtrapolationEstimationTimeSec = 0.001; // 1 ms
CHECK(extrapolators_.count(trajectory_id) == );
const double gravity_time_constant =
node_options_.map_builder_options.use_trajectory_builder_3d()
? options.trajectory_builder_options.trajectory_builder_3d_options()
.imu_gravity_time_constant()
: options.trajectory_builder_options.trajectory_builder_2d_options()
.imu_gravity_time_constant();
extrapolators_.emplace(
std::piecewise_construct, std::forward_as_tuple(trajectory_id),
std::forward_as_tuple(
::cartographer::common::FromSeconds(kExtrapolationEstimationTimeSec),
gravity_time_constant));
}

map的emplace方法,高效插入。http://en.cppreference.com/w/cpp/container/map/emplace

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

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

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

  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. iOS - UIAlertController三种显示提示框代码

    UIAlertView在IOS 8以上版本已经过时了,官方推荐我们使用UIAlertController代替UIAlertView.UIActionSheet 1、UIAlertController显 ...

  2. 【SpringCloud微服务实战学习系列】服务治理Spring Cloud Eureka

    Spring Cloud Eureka是Spring Cloud Netflix微服务中的一部分,它基于NetFlix Sureka做了二次封装,主要负责完成微服务架构中的服务治理功能. 一.服务治理 ...

  3. Office Web Apps Server

    Office Web Apps Server Office Web Apps Server 是一款 Office 服务器产品,可提供针对 Office 文件的基于浏览器的文件查看和编辑服务.Offic ...

  4. YAML入门

    概要 YAML(是YAML Ain't Markup Language的缩写,尾音的发音类似Camel)是一种序列化数据的语言(类似json, xml),使用轻量高可读性的语法描述list, dict ...

  5. TCP通信粘包问题分析和解决

    转载至https://www.cnblogs.com/kex1n/p/6502002.html 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发 ...

  6. solus 系统 - 安装编译工具

    将会安装 gcc,make等工具 sudo eopkg install -c system.devel VirtualBox工具 https://solus-project.com/articles/ ...

  7. zabbix监控告警Received empty response from Zabbix Agent Assuming that agent dropped connection

    zabbix监控告警Received empty response from Zabbix Agent Assuming that agent dropped connection错误 查看zabbi ...

  8. 在CentOS中安装arial字体

    验证码不能正常显示是因为 linux 没有字体 1. widonws下载字体文件到Linux windows的字体比较多,其字体文件位于 C:\WINDOWS\Fonts . 从其中copy相应的字体 ...

  9. update set from 语句用法

    关键字: update set from 下面是这样一个例子: 两个表a.b,想使b中的memo字段值等于a表中对应id的name值     表a:id, name               1   ...

  10. Maven Tomcat Plugin

    <!-- 本地Tomcat --> <dependency> <groupId>org.apache.tomcat.maven</groupId> &l ...