描述
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();
}
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是没有日志级别的
CHECK(!FLAGS_configuration_directory.empty())
<< "-configuration_directory is missing.";
CHECK(!FLAGS_configuration_basename.empty())
<< "-configuration_basename is missing.";
我没找到CHECK()函数的定义,简单分析,如果CHECK()里面是false,则打印输出,大概是这个意思
::ros::init(argc, argv, "cartographer_node");
::ros::start();
节点初始化,节点命名为cartographer_node
节点启动
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
,也就是说,这个类根本没有使用。
::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
DEFINE_bool
和DEFINE_string
没什么可说的,google的api,功能是读取shell命令的参数
constexpr double kTfBufferCacheTimeInSeconds = 10.;
里的constexpr
关键字是C++11/14编译规则,简单来说就是让编译器在编译时就编译成字面值
具体的可以自行去学习,
tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
使用tf2消息的大部分工作通过tf2_ros::Buffer类完成,其通过tf2_ros::TransformListener完成对tf2_ros::Buffer类的初始化和构造,并订阅相应tf消息,后续操作都是用的tf2_ros::Buffer类的成员函数完成:
整句话的意思是,tf变换的历史要保存10秒
tf2_ros::TransformListener tf(tf_buffer);
负责tf数据接收
节点参数和轨迹参数
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
加载参数
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
Node node(node_options, std::move(map_builder), &tf_buffer,
FLAGS_collect_metrics);
Node
定义在cartographer_ros/node.h
中
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
中