当前位置:   article > 正文

Cartographer学习记录:cartographer源码3D SLAM部分(一)_cartographer 3d slam

cartographer 3d slam

在经历过官方数据集自录数据集的验证后,我们可以清楚的看到cartographer的3D建图效果,下面一段时间我将开展3D SLAM源码部分的阅读和注释,在此做个记录,如果有小伙伴发现不对的地方,希望能够及时告知我,以便我及时改正。

以个人习惯而言,我阅读代码采用的策略是先看头文件,熟悉类内包括的函数及成员变量,然后对实现文件采用的是深度优先搜索+广度优先搜索的方式,深度优先搜索是指从主线程出发,对调用函数逐段深入;广度优先搜索主要指呈现形式,把深度优先搜索得到的调用放在该文件中以便后续查看方便,下面我将先从cartographer的上层ROS封装开始逐步深入阅读。

一.node_main.cc文件

该文件是cartographer_ros的主体文件,所以选择从该文件进行逐步深入阅读。

1.文件的一开始是调用Google 开源的命令行标记处理库gflags,gflags主要支持的参数类型包括bool, int32, int64, uint64, double, string等,定义参数通过DEFINE_type宏实现,参数分别为参数名,参数默认值以及提示信息,定义完成后即可通过FLAGS_name访问相应的参数,例如下面出现的“FLAGS_configuration_directory”和“FLAGS_configuration_basename”就对应此处的“configuration_directory”和“configuration_basename”。

  1. DEFINE_bool(collect_metrics, false,
  2. "Activates the collection of runtime metrics. If activated, the "
  3. "metrics can be accessed via a ROS service.");
  4. DEFINE_string(configuration_directory, "",
  5. "First directory in which configuration files are searched, "
  6. "second is always the Cartographer installation to allow "
  7. "including files from there.");
  8. DEFINE_string(configuration_basename, "",
  9. "Basename, i.e. not containing any directory prefix, of the "
  10. "configuration file.");
  11. DEFINE_string(load_state_filename, "",
  12. "If non-empty, filename of a .pbstream file to load, containing "
  13. "a saved SLAM state.");
  14. DEFINE_bool(load_frozen_state, true,
  15. "Load the saved state as frozen (non-optimized) trajectories.");
  16. DEFINE_bool(
  17. start_trajectory_with_default_topics, true,
  18. "Enable to immediately start the first trajectory with default topics.");
  19. DEFINE_string(
  20. save_state_filename, "",
  21. "If non-empty, serialize state and write it to disk before shutting down.");

2.从底下的main函数出发进行阅读,main函数里面主要进行一些初始化,运行和结束操作,初始化包括InitGoogleLogging、ParseCommandLineFlags以及ros的init函数,运行是本文件内定义的run函数,结束则是ros的shutdown函数,(ps:ros前面跟的::表示全局作用域下的调用)。初始化和结束操作大致了解下作用就可以,关键是run函数,直接关系到如何从ros上层节点一点一点进入下层cartographer算法层面。

  1. int main(int argc, char** argv) {
  2. // 初始化Google的日志库glog
  3. google::InitGoogleLogging(argv[0]);
  4. // 调用gflags进行参数的初始化,第一个和第二个参数为main函数的参数基数和参数值,第三个参数名为remove_flags,如果为false,则表示会将argv的参数值按照flags进行分隔、重新排序,如果为true, gflags会移除argv中分隔过的参数
  5. google::ParseCommandLineFlags(&argc, &argv, true);
  6. // Google的日志库glog里提供的CHECK系列的宏,检测某个条件是否为真,形式为CHECK(condition)
  7. // condition如果不为真,则打印后面的表达式信息以及“check failed:" #condition "”
  8. // 然后退出程序,出错后的处理过程和FATAL比较像
  9. CHECK(!FLAGS_configuration_directory.empty())
  10. << "-configuration_directory is missing.";
  11. CHECK(!FLAGS_configuration_basename.empty())
  12. << "-configuration_basename is missing.";
  13. // cartographer的主ROS节点"cartographer_node"的初始化
  14. ::ros::init(argc, argv, "cartographer_node");
  15. // 启动ROS,显式调用该函数表示在创建任何NodeHandle实例之前启动ROS相关的线程, 网络等
  16. ::ros::start();
  17. // 使用ROS_INFO进行glog消息的输出
  18. cartographer_ros::ScopedRosLogSink ros_log_sink;
  19. // 开始运行cartographer_ros
  20. cartographer_ros::Run();
  21. // 结束ROS相关的线程,网络等
  22. ::ros::shutdown();
  23. }

3.Run函数流程:利用LoadOptions函数加载配置->将配置作为CreateMapBuilder的参数创建map_builder对象->将map_builder和配置node_options作为参数构造node类对象->node类对象订阅topic信息开始制定路径->node类对象结束所有处于活动状态的轨迹并执行全局优化。

Run函数中的重要的调用函数包括CreateMapBuilder()、StartTrajectoryWithDefaultTopics()、FinishAllTrajectories()、RunFinalOptimization(),加载功用的一些函数就不做过多的介绍。

  1. // cartographer_ros命名空间
  2. namespace cartographer_ros {
  3. namespace {
  4. void Run() {
  5. // 定义tf发布的时间间隔
  6. constexpr double kTfBufferCacheTimeInSeconds = 10.;
  7. // 实例化tf2_ros::Buffer对象tf_buffer
  8. tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
  9. // 开启监听tf的独立线程
  10. tf2_ros::TransformListener tf(tf_buffer);
  11. // node_options结构体,包含地图帧名设置、接收tf的timeout时间设置、子图发布周期设置、位姿发布周期设置、路径发布周期设置
  12. NodeOptions node_options;
  13. // TrajectoryOptions结构体,包含跟踪帧名设置、发布帧名设置、里程计帧名设置、雷达扫描数量设置、点云数量设置等等
  14. TrajectoryOptions trajectory_options;
  15. // 补充——c++11引入的std::tie()函数可以将变量连接到一个给定的tuple上,生成一个元素类型全是引用的tuple
  16. // 将LoadOptions 获取到的lua文件内参数值分别赋给 node_options 和 trajectory_options
  17. // LoadOptions 函数在node_options.h 中定义
  18. std::tie(node_options, trajectory_options) =
  19. LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);
  20. // MapBuilder类是cartographer内完整的SLAM算法类包含前端(TrajectoryBuilders,scan to submap)与后端(用于查找回环的PoseGraph)
  21. // CreateMapBuilder函数利用lua文件内设置的参数值传感指向MapBuilder类实例化对象以便后续嗲用相关成员函数
  22. auto map_builder =
  23. cartographer::mapping::CreateMapBuilder(node_options.map_builder_options);
  24. // 补充——c++11引入的std::move 是将对象的状态或者所有权从一个对象转移到另一个对象,
  25. // 只是转移,没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能
  26. // 右值引用是用来支持转移语义的,转移语义可以将资源(堆,系统对象等)从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高C++应用程序的性能
  27. // 在程序中对临时对象的维护(创建和销毁)对性能有严重影响.
  28. // Node类的初始化,将ROS的topic传入MapBuilder
  29. // Node 在/cartographer_ros/cartographer_ros/cartographer/node.h 中定义
  30. // 在该构造函数中订阅了很多传感器的topic,收集传感器数据
  31. Node node(node_options, std::move(map_builder), &tf_buffer,
  32. FLAGS_collect_metrics);
  33. // 根据前面DEFINE_type定义决定是否加载pbstream地图文件
  34. if (!FLAGS_load_state_filename.empty()) {
  35. node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
  36. }
  37. // 使用默认topic(在node_constants.h文件中定义相关话题名称)开始制定轨迹
  38. if (FLAGS_start_trajectory_with_default_topics) {
  39. node.StartTrajectoryWithDefaultTopics(trajectory_options);
  40. }
  41. // ROS 消息回调处理函数,ros::spinonce()表示只调用一次订阅的消息,后续程序还可以继续执行,若想要一直订阅则需要加上循环,所以相对spin()具有灵活性,可以控制频率和消息池大小;
  42. ::ros::spin();
  43. // 结束所有处于活动状态的轨迹
  44. node.FinishAllTrajectories();
  45. // 当所有的轨迹结束时,再执行一次全局优化
  46. node.RunFinalOptimization();
  47. // 如果DEFINE_type定义save_state_filename非空,就保存pbstream文件
  48. if (!FLAGS_save_state_filename.empty()) {
  49. node.SerializeState(FLAGS_save_state_filename,
  50. true /* include_unfinished_submaps */);
  51. }
  52. }
  53. } // namespace
  54. } // namespace cartographer_ros

接下来的文章将先结合CreateMapBuilder()函数和node的构造函数进行突破,逐段深入解读,然后按照StartTrajectoryWithDefaultTopics()、FinishAllTrajectories()、RunFinalOptimization()的顺序对node类的成员函数依次解读,其中各文件之间相关的一些地方我也会指出来方便各位小伙伴理解,希望能够在记录的过程中不断纠错和进步。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号