MapBuilder继承了抽象类MapBuilderInterface,是cartographer算法的核心类.MapBuilder包括了两个部分,其中TrajectoryBuilder用于Local Submap的建立与维护;PoseGraph部分用于Loop Closure.

源码中使用的变量是map_builder_
,属于MapBuilder类型,它的赋值在MapBuilderBridge的构造函数;变量map_builder_bridge_
的赋值又在Node的构造函数;最终是在node_main.cc
中的
1 2 3
| auto map_builder = cartographer::common::make_unique<cartographer::mapping::MapBuilder>( node_options.map_builder_options );
|
具体怎么实现不用管,知道这是个智能指针就行
成员变量
MapBuilder
私有成员变量
1 2 3 4 5 6 7 8 9 10 11
| const proto::MapBuilderOptions options_; common::ThreadPool thread_pool_;
std::unique_ptr<PoseGraph> pose_graph_;
unique_ptr<sensor::CollatorInterface> sensor_collator_;
vector<std::unique_ptr<mapping::TrajectoryBuilderInterface>> trajectory_builders_;
vector<proto::TrajectoryBuilderOptionsWithSensorIds> all_trajectory_builder_options_;
|
线程池和
pose_graph_
在这里。
由此可见MapBuilder
构造函数里,后端的线程就已经开始跑了
trajectory是机器人跑一圈时的轨迹,在这其中需要记录和维护传感器的数据。根据这个trajectory上传感器收集的数据,我们可以逐步构建出栅格化的地图Submap,但这个submap会随着时间或trajectory的增长而产生误差累积。
PoseGraph是用来进行全局优化,将所有的Submap紧紧tie在一起,构成一个全局的Map,消除误差累积。
构造函数
省略3D的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| MapBuilder::MapBuilder(const proto::MapBuilderOptions& options) : options_(options), thread_pool_(options.num_background_threads()) { pose_graph_ = absl::make_unique<PoseGraph2D>( options_.pose_graph_options(), absl::make_unique<optimization::OptimizationProblem2D>( options_.pose_graph_options().optimization_problem_options() ), &thread_pool_); if (options.collate_by_trajectory()) sensor_collator_ = absl::make_unique<sensor::TrajectoryCollator>(); else sensor_collator_ = absl::make_unique<sensor::Collator>(); }
|
在lua文件中找不到
collate_by_trajectory
这一项,默认没有开启,但在
map_builder_options.proto
中可以找到,如果我们想要配置这个选项,只需要在lua文件中添加
MAP_BUILDER.collate_by_trajectory = true
MapBuilder类在构造时会对线程池, 位姿图进行初始化, 在添加新轨迹时会对前端类的对象进行初始化, 初始化之后才会去判读这个轨迹是否存在初始位姿.
AddTrajectoryBuilder
这个函数可以说是cartographer中最重要的函数之一
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
| int MapBuilder::AddTrajectoryBuilder( const std::set<SensorId>& expected_sensor_ids, const proto::TrajectoryBuilderOptions& trajectory_options, LocalSlamResultCallback local_slam_result_callback) { const int trajectory_id = trajectory_builders_.size();
std::unique_ptr<LocalTrajectoryBuilder2D> local_trajectory_builder; if (trajectory_options.has_trajectory_builder_2d_options() ) { local_trajectory_builder = absl::make_unique<LocalTrajectoryBuilder2D>( trajectory_options.trajectory_builder_2d_options(), SelectRangeSensorIds(expected_sensor_ids) ); } DCHECK(dynamic_cast<PoseGraph2D*>(pose_graph_.get()) );
trajectory_builders_.push_back( absl::make_unique<CollatedTrajectoryBuilder>( trajectory_options, sensor_collator_.get(), trajectory_id, expected_sensor_ids,
CreateGlobalTrajectoryBuilder2D( std::move(local_trajectory_builder), trajectory_id, static_cast<PoseGraph2D*>(pose_graph_.get() ), local_slam_result_callback) ) );
|
一个MapBuilder的类对应了一次建图过程,在整个建图过程中,用于全局优化的PoseGraph的对象只有一个,即
pose_graph_
,而这个变量是在构造函数中就生成了。
每生成一个trajectory时都是调用AddTrajectoryBuilder
来创建的。一个trajectory
对应了机器人运行一圈,trajectory_id
取的是trajectory_builders_.size()
,所以开始是0.
在图建好后机器人可能多次运行,每一次运行都是新增一条trajectory,trajectory_id
就会+1. 因此,需要动态地维护一个trajectory的列表。
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
| MaybeAddPureLocalizationTrimmer(trajectory_id, trajectory_options, pose_graph_.get());
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()) ); } proto::TrajectoryBuilderOptionsWithSensorIds options_with_sensor_ids_proto; for (const auto& sensor_id : expected_sensor_ids) { *options_with_sensor_ids_proto.add_sensor_id() = ToProto(sensor_id); } *options_with_sensor_ids_proto.mutable_trajectory_builder_options() = trajectory_options;
all_trajectory_builder_options_.push_back(options_with_sensor_ids_proto); CHECK_EQ(trajectory_builders_.size(), all_trajectory_builder_options_.size()); return trajectory_id; }
|
has_initial_trajectory_pose
其他函数
SerializeState
: 保存地图到pbstream,序列化当前状态到一个proto流中
LoadState
: 加载pbstream地图,从一个proto流中加载SLAM状态,逐条trajectory恢复,恢复trajectory上的节点间的约束,恢复Submap
FinishTrajectory
: 结束指定id的轨迹,调用sensor_collator_->FinishTrajectory
和pose_graph_->FinishTrajectory
SubmapToProto
: 根据指定的submap_id来查询submap,把结果放到SubmapQuery::Response中。如果出现错误,返回error string; 成功则返回empty string. 对应服务kSubmapQueryServiceName
, 调用关系如下:
Node::HandleSubmapQuery
|
MapBuilderBridge::HandleSubmapQuery
|
MapBuilder::SubmapToProto