Cartographer总结(1:整体框架)
Git上开源的cartographer分为cartographer工程和其ros封装即cartographer_ros两部分。cartographer_ros部分只是对cartographer的封装应用,相当于一个example,可以用来理解cartographer的接口如何使用。
cartographer使用了goolge自带的一些库,如 ceres, glog 等,还有一些非google开发的依赖库,如 Protobuf,Lua 等。最新版本的cartographer使用了更多google开发的库。这些goolge的库使用起来方不方便因人而异,我个人的使用感觉还是很不方便的,尤其是在安装过程中,版本的要求较多,跨平台能力一般。
*Protobuf: 是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。
*Lua: 用来配置cartographer的各项参数。
cartographer的官网可以找到整体流程图:
该流程图简单表示了算法内部的数据流程,实际使用的类要比流程图中多一些。
根据我的总结,cartographer的类分层顺序为:map_builder->global_trajectory_builder->local_trajectory_builder
这其中包含了2d和3d两种情况,分别实例化2d和3d的相关类。(todo: 列出相关类)
为什么要有trajectory的概念呢?这正是cartographer的伟大之处,它支持多次建图,和純定位(pure localization),同时稍加改进,cartographer将可以解决slam中的某些long-term问题。即同一场景不同时间建图时的场景变化的问题,在历史地图上进行增量建图的问题。这一切都得益于trajectory的概念。
Global_slam负责整张map的维护,包括回环检测和后端优化。
Local_slam负责submap的生成,前端的算法。
在cartographer_ros中最主要的是在node_main.cpp中:
1.创建一个cartographer的map_builder,此类是建图的基础,几乎所有功能都在该类内部:
auto map_builder =
cartographer::common::make_unique<cartographer::mapping::MapBuilder>(
node_options.map_builder_options);
2. 创建一个node,并将map_builder传入node中:
Node node(node_options, std::move(map_builder), &tf_buffer);
3. 如果有需要载入的pbstream文件,进行文件读取和数据反序列化:
if (!FLAGS_load_state_filename.empty()) {
node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
}
4. 使用默认的topic创建trajectory:
if (FLAGS_start_trajectory_with_default_topics) {
node.StartTrajectoryWithDefaultTopics(trajectory_options);
}
5. 运行ros的回调,即订阅topics的过程:
::ros::spin();
至此,启动算法的全部流程完成,还有两个结束建图时运行的函数:
node.FinishAllTrajectories();//结束所有的trajectory
node.RunFinalOptimization();//运行最终优化
在添加trajectory的过程中,分为以下3个步骤,都是非常重要的环节,分布初始化了cartographer的Extrapolator和SensorSamplers,这两个功能都是cartographer自带的;第三个是基于ros的topic订阅,在ros::spin()的作用下订阅topics:
/// 1
AddExtrapolator(trajectory_id, options);
/// 2
AddSensorSamplers(trajectory_id, options);
/// 3
LaunchSubscribers(options, topics, trajectory_id);
关于cartographer的时间
注意!cartographer内部使用的时间为:::cartographer::common::Time ,是UTC时间不是Unix时间,除此之外还与ROS的time不同。
ROS的time使用的是seconds + nano-seconds,而 ::cartographer::common::Time 使用的是seconds + 100*nano-seconds,不要混淆。
所以使用的时候需要进行转换,cartographer 中已经给出了 ::ros::Time ToRos 和 ::cartographer::common::Time FromRos 两者之间互相转换的函数。