(一) 从node_main.cc开始

先看main函数,省略google库的部分:

1
2
3
4
5
6
::ros::init(argc, argv, "cartographer_node");
::ros::start();

cartographer_ros::ScopedRosLogSink ros_log_sink;
cartographer_ros::Run();
::ros::shutdown();

实际上就是Run函数,流程如下:
Run函数流程.png

1
2
3
4
5
6
7
8
constexpr double kTfBufferCacheTimeInSeconds = 10.;
tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
tf2_ros::TransformListener tf(tf_buffer);
// 从lua文件加载配置
NodeOptions node_options;
TrajectoryOptions trajectory_options;
std::tie(node_options, trajectory_options) =
LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 核心变量: Cartographer的地图构建器
auto map_builder =
cartographer::common::make_unique<cartographer::mapping::MapBuilder>(
node_options.map_builder_options);
Node node(node_options, std::move(map_builder), &tf_buffer);
// 加载现有地图的情况
if (!FLAGS_load_state_filename.empty()) {
node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
}
// 核心函数
if (FLAGS_start_trajectory_with_default_topics) {
node.StartTrajectoryWithDefaultTopics(trajectory_options);
}
::ros::spin();

注意:MapBuilder类的对象在这里就实例化了,也是唯一的对象,而不是在以为的MapBuilderBridge

1
2
3
4
5
6
node.FinishAllTrajectories();
node.RunFinalOptimization();

if (!FLAGS_save_state_filename.empty()) {
node.SerializeState(FLAGS_save_state_filename);
}

显然我们需要看的函数是StartTrajectoryWithDefaultTopics

node.FinishAllTrajectories();node.RunFinalOptimization();是停止建图才执行的,一直到后端优化那里再分析。

proto机制

编写应用程序的时候,往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为序列化,而它的逆过程则可被称为反序列化。简单来说,序列化:将对象变成字节流的形式传出去。反序列化:从字节流恢复成原来的对象。

为什么要序列化:假如你有一个数据结构,里面存储的数据是经过很多其它数据通过非常复杂的算法生成的,由于数据量很大,算法又复杂,因此生成该数据结构所用数据的时间可能要很久(也许几个小时),生成该数据结构后又要用作其它的计算,那么你在调试阶段,每次运行个程序,就光生成数据结构就要花上这么长的时间,无疑代价是非常大的。如果你确定生成数据结构的算法不会变,那么就可以通过序列化技术生成数据结构数据存储到磁盘上,下次重新运行程序时只需要从磁盘上读取该对象数据即可,所花时间只有读一个文件的时间,节省了我们的开发时间。

Protobuf性能好,效率高;拥有代码生成机制,数据解析类自动生成;支持很多语言,包括C++、Java、Python;同时也是跨平台的,所以得到了广泛的应用。正常情况下你需要定义proto文件,然后使用IDL编译器编译成你需要的语言。目前proto存在2和3两个版本,最好用3.与2相比,3的改变有:移除了required字段、移除了缺省值等

proto的语法见proto文件语法Protobuf 终极教程

生成C++代码的命令是: protoc -I=. --cpp_out=. test.proto. -I指定protoc的搜索import的proto的文件夹,cpp_out是生成的目录,生成的文件类型是pb.hpb.cc。 对于Cartographer,proto文件在源码的cartographer/cartographer/,生成的pb.h文件在/usr/local/include/cartographer

以后会发现有些参数不在lua里,但在proto里,proto是 Cartographer原本的参数描述的方式,lua是catographer_ros的封装。proto定义了参数,lua具体描述参数的数值,他俩就跟class和实例化的对象一样

我尝试在lua中添加proto中特有的参数,比如collate_by_trajectory,结果运行Cartographer还是失败,可能还是不兼容。