Cartographer源码阅读(2):Node和MapBuilder对象
上文提到特别注意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对象的更多相关文章
- Cartographer源码阅读(4):Node和MapBuilder对象2
MapBuilder的成员变量sensor::Collator sensor_collator_; 再次阅读MapBuilder::AddTrajectoryBuilder方法.首先构造了mappin ...
- Cartographer源码阅读(1):程序入口
带着几个思考问题: (1)IMU数据的使用,如何融合,Kalman滤波? (2)图优化的具体实现,闭环检测的策略? (3)3D激光的接入和闭环策略? 1. 安装Kdevelop工具: http://b ...
- Cartographer源码阅读(6):LocalTrajectoryBuilder和PoseExtrapolator
LocalTrajectoryBuilder意思是局部轨迹的构建,下面的类图中方法的参数没有画进去. 注意其中的三个类:PoseExtrapolator类,RealTimeCorrelativeSca ...
- Cartographer源码阅读(5):PoseGraph位姿图
PoseGraph位姿图 mapping2D::PoseGraph类的注释: // Implements the loop closure method called Sparse Pose Adju ...
- Cartographer源码阅读(8):imu_tracker
IMU的输入为imu_linear_acceleration 和 imu_angular_velocity 线加速和角速度.最终作为属性输出的是方位四元数. Eigen::Quaterniond ...
- Cartographer源码阅读(9):图优化的前端——闭环检测
约束计算 闭环检测的策略:搜索闭环,通过匹配检测是否是闭环,采用了分支定界法. 前已经述及PoseGraph的内容,此处继续.位姿图类定义了pose_graph::ConstraintBuilder ...
- Cartographer源码阅读(3):程序逻辑结构
Cartographer早期的代码在进行3d制图的时候使用了UKF方法,查看现有的tag版本,可以转到0.1.0和0.2.0查看,包含kalman_filter文件夹. 文件夹中的pose_track ...
- Cartographer源码阅读(7):轨迹推算和位姿推算的原理
其实也就是包括两个方面的内容:类似于运动模型的位姿估计和扫描匹配,因为需要计算速度,所以时间就有必要了! 1. PoseExtrapolator解决了IMU数据.里程计和位姿信息进行融合的问题. 该类 ...
- koa源码阅读[0]
koa源码阅读[0] Node.js也是写了两三年的时间了,刚开始学习Node的时候,hello world就是创建一个HttpServer,后来在工作中也是经历过Express.Koa1.x.Koa ...
随机推荐
- Linux Platform驱动模型(三) _platform+cdev
平台总线是一种实现设备信息与驱动方法相分离的方法,利用这种方法,我们可以写出一个更像样一点的字符设备驱动,即使用cdev作为接口,平台总线作为分离方式: xjkeydrv_init():模块加载函数 ...
- QT 获取电脑时间
使用环境: VS2010 & QT Designer5 #include <QDateTime> //包含头文件 QDateTime local(QDateTime::curre ...
- A - Fire Net
Suppose that we have a square city with straight streets. A map of a city is a square board with n r ...
- CSS3 transition 属性过渡效果 详解
CSS3 transition 允许 CSS 元素的属性值在一定的时间区间内平滑地过渡.我们可以在不使用 Flash 动画或 JavaScript 的情况下,在元素从一种样式变换为另一种样式时为元素添 ...
- css3---2D效果 ---3D效果
CSS3边框: CSS3圆角:border-radius(**px 或 **%) 属性——创建边框线的圆角 CSS3盒子阴影:box-shadow属性——创建阴影 box-shadow:30px 0p ...
- d4
# s = '132a4b5c'# s1 = s[0]+s[2]+s[1]# print(s1)#使用while和for循环分别打印字符串s=’asdfer’中每个元素.s = 'fkld'# for ...
- css学习_css三大特性
css三大特性 1.层叠性(就近原则) 2.继承性(和文字有关的会继承) 3.优先级 (权重问题) 权重:0,0,0,0 0001 ---标签选择器(注意:即使有20个标签选择器也不会比一个伪类选 ...
- [No0000CF]想有一辈子花不完的钱?从了解“被动收入”开始吧
我想从理清自己所说被动收入的含义,开始创作此被动收入系列文章. 我更喜欢把被动收入较宽泛地定义为,甚至当你没有主动工作时,仍可赚取的收益.被动收入的另一个名称是剩余收入. 相比之下,当你停止工作时,通 ...
- centos7安装zabbix客户端并监控
zabbxi-agent安装及配置 1.安装zabbxi-agent yum install zabbix-agent -y 2.配置zabbxi-agent grep -n '^'[a-Z] /et ...
- Python开发【笔记】:列表转字典
列表转字典 it = [1,2,3,4] print(dict(zip(it, it))) # {1: 1, 2: 2, 3: 3, 4: 4} it = iter(it) print(dict(zi ...