Cartogragper source code reading

Posted by AlanG on Wed, 26 Jan 2022 22:29:22 +0100

Cartogragper source code reading

In the future, we will study and record the source code of Cartogragper

1, Knowledge reserve

1.gflag

Gflags concise tutorial
GFlags usage document
GFlags is an open source library for command line parameter processing, including C + + version and python version.
Unlike libraries such as getopt(), the definition of flag can be scattered in various source codes instead of being put together. A source file can define some of its own flags, which can be used by applications linked to the file. This makes it easy to reuse code. If different files define the same flag, an error will be reported when linking.
Example:
fgrep -l -f ./test ccc jjj
-l and - f are command line tags, while ccc and jjj are command line parameters because they do not start with -
-l is a tag without parameters, - f is a tag with parameters/ test, and gflags can parse this
These tags and parameters are stored in some data structures

See for specific usage Usage guide for gFlags

2.glog

Google glog is an application level log system library It provides a logging API based on C + + style streams and various auxiliary macros
See for specific usage glog User Guide

2, node_main.cc

DEFINE_bool at gflags Defined in H

  • The main parameter types supported by gflags include bool, int32, int64, uint64, double, string, etc
  • Define parameters through define_ The type macro is implemented. The meanings of the three parameters of the macro are the command line parameter name, the default value of the parameter, and the help information of the parameter
  • After the parameter is defined, it can be passed through FLAGS_name can access the corresponding parameters
DEFINE_bool(collect_metrics, false,
            "Activates the collection of runtime metrics. If activated, the "
            "metrics can be accessed via a ROS service.");
//configuration_directory in LX_ rs16_ 2d_ outdoor. In the launch file
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.");

First, focus on collect_metrics, which later appeared in:

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

Secondly, configuration_directory,configuration_basename appears in LX_ rs16_ 2d_ outdoor. In the launch file,

<node name="cartographer_node" pkg="cartographer_ros"
      type="cartographer_node" args="
          -configuration_directory $(find cartographer_ros)/configuration_files
          -configuration_basename lx_rs16_2d_outdoor.lua"

2.main function

int main(int argc, char** argv) {

  // note: initialize the glog Library
  google::InitGoogleLogging(argv[0]);
  
  // Use gflags to initialize parameters The third parameter is remove_flag
  // If true, gflags will remove the parse d parameters, otherwise gflags will keep these parameters, but the order of parameters may be adjusted
  google::ParseCommandLineFlags(&argc, &argv, true);

  /**
   * @brief glog The macro of the CHECK series provided in detects whether an expression is true
   * Detect the expression. If it is not true, print the following description and the information on the stack
   * Then exit the program. The processing process after error is more similar to FATAL
   */
  CHECK(!FLAGS_configuration_directory.empty())
      << "-configuration_directory is missing.";
  CHECK(!FLAGS_configuration_basename.empty())
      << "-configuration_basename is missing.";

  // Initialization of ros node
  ::ros::init(argc, argv, "cartographer_node");

  // Generally, you don't need to call it explicitly in your own code
  // However, if you want to start ROS related threads, networks, etc. before creating any NodeHandle instance, you can explicitly call this function
  ::ros::start();

  // Using ROS_INFO to output the glog message
  cartographer_ros::ScopedRosLogSink ros_log_sink;

  // Start running cartographer_ros
  cartographer_ros::Run();

  // End ROS related threads, networks, etc
  ::ros::shutdown();
}

3. Run function

namespace cartographer_ros {
namespace {

void Run() {
  constexpr double kTfBufferCacheTimeInSeconds = 10.;//Cache time
  tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
  // Open the independent thread that listens to tf
  tf2_ros::TransformListener tf(tf_buffer);

Summary of ROS time concept
Most of the work with tf2 messages is done through tf2_ The ROS:: buffer class is completed through tf2_ros::TransformListener completes tf2_ The initialization and construction of ROS:: buffer class, and subscribe to the corresponding tf message. tf2 is used for subsequent operations_ The member function of ROS:: buffer class is completed: tf2 coordinate conversion package of ROS

  • The first part of this code is to define some variable types, read the configuration file in the directory by calling the LoadOptions function, and assign some parameters to node_options, trajectory_options:
  NodeOptions node_options;
  TrajectoryOptions trajectory_options;

  // c++11: std::tie() function can connect variables to a given tuple and generate a tuple whose element types are all references

  // According to the content in Lua configuration file, it is node_options, trajectory_options assignment
  std::tie(node_options, trajectory_options) =
      LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);

3.1 LoadOptions() parsing:

Enter node_options.cc to see the definition of LoadOptions()*

* @param[in] configuration_directory Directory of configuration file
 * @param[in] configuration_basename Name of the configuration file
 * @return std::tuple<NodeOptions, TrajectoryOptions> Returns the configuration of nodes and tracks
 */
std::tuple<NodeOptions, TrajectoryOptions> LoadOptions(
    const std::string& configuration_directory,
    const std::string& configuration_basename) {
 

LoadOptions() can learn about the definitions of nodeoptions and trajectoryoption by itself. Their parameters are reflected in lua files

 // Get the directory where the configuration file is located
  auto file_resolver =
      absl::make_unique<cartographer::common::ConfigurationFileResolver>(
          std::vector<std::string>{configuration_directory});//{}configuration_ Put the directory into the vector after initialization

The definition of ConfigurationFileResolver is inherited from FileResolver

class ConfigurationFileResolver : public FileResolver {
 public:

  // C + + 11: the explicit keyword is used to prevent the implicit automatic conversion of class constructors
  explicit ConfigurationFileResolver(
      const std::vector<std::string>& configuration_files_directories);
  std::string GetFullPathOrDie(const std::string& basename) override;
  std::string GetFileContentOrDie(const std::string& basename) override;

 private:
  std::vector<std::string> configuration_files_directories_;
};

After entering the definition of ConfigurationFileResolver (), you can see that its parameters come from configuration_files_directories and kConfigurationFilesDirectory

ConfigurationFileResolver::ConfigurationFileResolver(
    const std::vector<std::string>& configuration_files_directories)
    : configuration_files_directories_(configuration_files_directories) {
  configuration_files_directories_.push_back(kConfigurationFilesDirectory);
// Read the contents of the configuration file into the code
  const std::string code =
      file_resolver->GetFileContentOrDie(configuration_basename);

View GetFileContentOrDie() definition:

std::string ConfigurationFileResolver::GetFileContentOrDie(
    const std::string& basename) {
  CHECK(!basename.empty()) << "File basename cannot be empty." << basename;

  // Find out whether it exists in the given folder according to the file name
  const std::string filename = GetFullPathOrDie(basename);
  std::ifstream stream(filename.c_str());

  // Read the contents of the configuration file and return
  return std::string((std::istreambuf_iterator<char>(stream)),
                     std::istreambuf_iterator<char>());

Generates a lua dictionary based on the given string

cartographer::common::LuaParameterDictionary lua_parameter_dictionary(
      code, std::move(file_resolver));

Need to go to Lua_ parameter_ dictionary. Go and see it in the library

Create a tuple, which defines a container with a fixed number of elements, in which each element type can be different
Fill the contents of the configuration file into NodeOptions and TrajectoryOptions, and return

 return std::make_tuple(CreateNodeOptions(&lua_parameter_dictionary),
                         CreateTrajectoryOptions(&lua_parameter_dictionary));

See the CreateNodeOptions and CreateTrajectoryOptions functions for yourself

Return to node_main.cc, enter map_builder.h and You can see in cc:

std::unique_ptr<MapBuilderInterface> CreateMapBuilder(
    const proto::MapBuilderOptions& options) {
  return absl::make_unique<MapBuilder>(options);

Here we need to explain std::unique_ptr definition, std::unique_ptr is a smart pointer introduced from C++11.
std:make_ unique<unique_ptr > is used to create and return unique_ptr to an object of the specified type, which is constructed with the specified parameters. For example, the parameter used in this sentence is node_options.map_builder_options, the specified return type is cartographer::mapping::MapBuilder

  • Define node
// c++11: std::move is to transfer the state or ownership of an object from one object to another, 
  // Just transfer, no memory relocation or memory copy, so it can improve utilization efficiency and performance
  // Right - valued references are used to support transfer semantics Transfer semantics can transfer resources (heap, system objects, etc.) from one object to another, 
  // This can reduce the creation, copy and destruction of unnecessary temporary objects, and greatly improve the performance of C + + applications
  // The maintenance (creation and destruction) of temporary objects has a serious impact on performance
 Node node(node_options, std::move(map_builder), &tf_buffer,
            FLAGS_collect_metrics);

Go deep into the node and add it next time

  • rest
// If the pbstream file is loaded, this function is executed
  if (!FLAGS_load_state_filename.empty()) {
    node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
  }

  // Start track with default topic
  if (FLAGS_start_trajectory_with_default_topics) {
    node.StartTrajectoryWithDefaultTopics(trajectory_options);
  }

  ::ros::spin();

  // End all active tracks
  node.FinishAllTrajectories();

  // When all the tracks are finished, perform another global optimization
  node.RunFinalOptimization();

  // If save_ state_ If the filename is not empty, save the pbstream file
  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }

summary

In the follow-up, we need to further summarize the content here, mainly including lua dictionary.

Topics: C++ slam cartographer