描述

cartographer学习笔记(二)

这一篇主要分析node_main.cc的代码

主函数main()

  • 主函数                                                                                                                                                                                                                                                                        
    int main(int argc, char** argv) {
      google::InitGoogleLogging(argv[0]);
      google::ParseCommandLineFlags(&argc, &argv, true);
    
      CHECK(!FLAGS_configuration_directory.empty())
          << "-configuration_directory is missing.";
      CHECK(!FLAGS_configuration_basename.empty())
          << "-configuration_basename is missing.";
    
      ::ros::init(argc, argv, "cartographer_node");
      ::ros::start();
    
      cartographer_ros::ScopedRosLogSink ros_log_sink;
      cartographer_ros::Run();
      ::ros::shutdown();
    }
    
  • 1.
  • google::InitGoogleLogging(argv[0]); // 初始化 glog
    google::ParseCommandLineFlags(&argc, &argv, true); // 初始化 gflags
    

     都是google开发的glog的函数,两句话是初始化,用于输出log信息,标准的分级为:INFO, WARNING, ERROR, FATAL。不同级别的日志信息会输出到不同文件,同时高级别的日志也会写入到低级别中。

    LOG(INFO) << "Hello, GOOGLE!";  // INFO 级别的日志
    LOG(ERROR) << "ERROR, GOOGLE!";  // ERROR 级别的日志
    

     gflags用于在shell里面设置gflags的值,通过命令行参数 -v 50 这样子来指定 VLOG 输出的级别,-v 50 意味着只有小于 50 以下的 VLOG 才会被输出(这里不会影响 LOG(INFO)这些)

    LOG(INFO) << "Hello, GOOGLE!";
    VLOG(100) << "VLOG INFO 100";
    VLOG(50) << "VLOG INFO 50";
    VLOG(10) << "VLOG INFO 10";
    

    我在整个项目里搜索了一下,没有使用过VLOG(100)这种形式,也就是说cartographer是没有日志级别的

  • 2.
CHECK(!FLAGS_configuration_directory.empty())
    << "-configuration_directory is missing.";
CHECK(!FLAGS_configuration_basename.empty())
    << "-configuration_basename is missing.";

我没找到CHECK()函数的定义,简单分析,如果CHECK()里面是false,则打印输出,大概是这个意思

3.

::ros::init(argc, argv, "cartographer_node");
::ros::start();

节点初始化,节点命名为cartographer_node
节点启动

4.

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

ScopedRosLogSink的定义在cartographer_ros/ros_log_sink.h文件中

namespace cartographer_ros {
// Makes Google logging use ROS logging for output while an instance of this
// class exists.
class ScopedRosLogSink : public ::google::LogSink {
 public:
  ScopedRosLogSink();
  ~ScopedRosLogSink() override;

  void send(::google::LogSeverity severity, const char* filename,
            const char* base_filename, int line, const struct std::tm* tm_time,
            const char* message, size_t message_len) override;

  void WaitTillSent() override;

 private:
  bool will_die_;
};
}  // namespace cartographer_ros

使谷歌日志记录(glog)使用ROS日志记录输出,这是一个实例类存在,暂时没有太仔细去看,因为我再一次没有在代码中找到ros_log_sink,也就是说,这个类根本没有使用。

5.

::ros::shutdown();

关闭节点

Run()

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

namespace cartographer_ros {
namespace {

void Run() {
  constexpr double kTfBufferCacheTimeInSeconds = 10.;
  tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
  tf2_ros::TransformListener tf(tf_buffer);
  NodeOptions node_options;
  TrajectoryOptions trajectory_options;
  std::tie(node_options, trajectory_options) =
      LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);

  auto map_builder =
      cartographer::mapping::CreateMapBuilder(node_options.map_builder_options);
  Node node(node_options, std::move(map_builder), &tf_buffer,
            FLAGS_collect_metrics);
  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();

  node.FinishAllTrajectories();
  node.RunFinalOptimization();

  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }
}

}  // namespace
}  // namespace cartographer_ros

1.
DEFINE_bool和DEFINE_string没什么可说的,google的api,功能是读取shell命令的参数

2.

constexpr double kTfBufferCacheTimeInSeconds = 10.;里的constexpr关键字是C++11/14编译规则,简单来说就是让编译器在编译时就编译成字面值
具体的可以自行去学习,

3.

tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};

使用tf2消息的大部分工作通过tf2_ros::Buffer类完成,其通过tf2_ros::TransformListener完成对tf2_ros::Buffer类的初始化和构造,并订阅相应tf消息,后续操作都是用的tf2_ros::Buffer类的成员函数完成:

整句话的意思是,tf变换的历史要保存10秒

3.

tf2_ros::TransformListener tf(tf_buffer);
负责tf数据接收

4.

节点参数和轨迹参数

NodeOptions node_options;
TrajectoryOptions trajectory_options;

定义在文件cartographer_ros/node_options.h

namespace cartographer_ros {

// Top-level options of Cartographer's ROS integration.
struct NodeOptions {
  ::cartographer::mapping::proto::MapBuilderOptions map_builder_options;
  std::string map_frame;
  double lookup_transform_timeout_sec;
  double submap_publish_period_sec;
  double pose_publish_period_sec;
  double trajectory_publish_period_sec;
  bool publish_to_tf = true;
  bool publish_tracked_pose = false;
  bool use_pose_extrapolator = true;
};

NodeOptions CreateNodeOptions(
    ::cartographer::common::LuaParameterDictionary* lua_parameter_dictionary);

std::tuple<NodeOptions, TrajectoryOptions> LoadOptions(
    const std::string& configuration_directory,
    const std::string& configuration_basename);
}  // namespace cartographer_ros

定义在文件cartographer_ros/trajectory_options.h

namespace cartographer_ros {

struct TrajectoryOptions {
  ::cartographer::mapping::proto::TrajectoryBuilderOptions
      trajectory_builder_options;
  std::string tracking_frame;
  std::string published_frame;
  std::string odom_frame;
  bool provide_odom_frame;
  bool use_odometry;
  bool use_nav_sat;
  bool use_landmarks;
  bool publish_frame_projected_to_2d;
  int num_laser_scans;
  int num_multi_echo_laser_scans;
  int num_subdivisions_per_laser_scan;
  int num_point_clouds;
  double rangefinder_sampling_ratio;
  double odometry_sampling_ratio;
  double fixed_frame_pose_sampling_ratio;
  double imu_sampling_ratio;
  double landmarks_sampling_ratio;
};

TrajectoryOptions CreateTrajectoryOptions(
    ::cartographer::common::LuaParameterDictionary* lua_parameter_dictionary);

}  // namespace cartographer_ros

5.

加载参数

td::tie(node_options, trajectory_options) =
      LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);

std::tie把node_options和trajectory_options整合成一个std::tuple,从而实现批量赋值(node_options和 trajectory_options一起赋值)

建图指针

auto map_builder =
      cartographer::mapping::CreateMapBuilder(node_options.map_builder_options);

::cartographer::mapping::proto::MapBuilderOptions map_builder_options定义在cartographer/cartographer/mapping/map_builder_interface.h中

cartographer::mapping::CreateMapBuilder定义在cartographer/cartographer/mapping/map_builder.h中,构造函数就只有一个输入变量,就是map_builder_options


7.

Node node(node_options, std::move(map_builder), &tf_buffer,
            FLAGS_collect_metrics);

Node定义在cartographer_ros/node.h

8.

  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();

  node.FinishAllTrajectories();
  node.RunFinalOptimization();

  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }

如果node.LoadState、node.StartTrajectoryWithDefaultTopics、node.FinishAllTrajectories、node.RunFinalOptimization、node.SerializeState的定义都在cartographer_ros/node.h中