这段时间把项目做了一部分后,对最近学习的内容进行总结归纳。
首先看main函数部分,先对Glog库进行初始化。
google::InitGoogleLogging(argv[0])
该库为一个程序级的日志系统库,提供基于C++风格的流以及各种辅助宏的日志API。可以根据严重性进行分级(分别为INFO,WARNING,ERROR以及FATAL)来选择性记录日志。同时提供了多个便利的宏来处理特定关系的判断。接着来解析参数:
google::ParseCommandLineFlags(&argc, &argv, true);
argc和argv为main主函数传入的参数,第三个参数remove_flag用来判断,在标识解析后是否要从参数列表移除来释放空间。true代表函数处理完成后,参数信息中将删除gflags相关参数,而状态false不删除gflags参数,只会调整argv中存储内容的顺序,并且把flag放在命令行参数的前面。其中使用到了gflags工具,需要包含以下头文件:
#include "gflags/gflags.h"
GFlags是谷歌开源的命令行标记库,主要支持bool, int32, int64, uint64, double, string等类型的参数。定义参数通过DEFINE_type 宏实现,格式为DEFINE_xxx(变量名,默认值,描述方法的帮助)。被定义好后便可以通过FLAGS_变量名来访问对应的参数了。
DEFINE_bool(collect_metrics, false,
"Activates the collection of runtime metrics. If activated, the "
"metrics can be accessed via a ROS service.");
还可以在头文件中通过DECLARE_变量名声明(与extern效果一致)变为全局变量。
再回到前边可以看到定义的一些常用的变量configuration_directory/basename等。collect_metrics为度量的集合,在激活后可以通过ros服务来访问度量。
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.");
接下来用Glog库中的宏CHECK()去判断结果式是否为真,如果为真则程序继续运行,否则将会打印后面的信息,然后退出程序, 处理过程和FATAL比较像。
CHECK(!FLAGS_configuration_directory.empty())
<< "-configuration_directory is missing.";
CHECK(!FLAGS_configuration_basename.empty())
<< "-configuration_basename is missing.";
ros节点的初始化以及start函数(如果在创建任何NodeHandle之前不需要启用ros相关的线程,网络时可以不用)。
::ros::init(argc, argv, "cartographer_node");
::ros::start();
然后调用函数ros_log_sink这个类中的send函数进行消息的输出。
cartographer_ros::ScopedRosLogSink ros_log_sink;
// 调用Run函数
cartographer_ros::Run();
// 结束ROS相关的线程, 网络等
::ros::shutdown();
复习并且拓展一些额外的学习内容:
函数指针:本质为一个指针,声明格式为:类型说明符(*函数名)(参数列表)
指针函数:返回值类型是指针的函数,本质为一函数。格式为:类型标识符*函数名(参数列表)
多态的表现形式是父类指针可以根据具体指向的子类对象,来执行不同的函数,这个过程是发生在运行时的。拥有纯虚函数的类不可以实例化成、对象。基类的析构函数要为虚函数,否则有的类不会析构。子类构造时,会进行基类的初始化,再进行子类的初始化。
遇到ros::spinOnce函数前不会去执行订阅消息的回调函数,运行完回调函数后继续向下执行。
target_link_libraries是将要生成的目标文件(静态库,动态库,可执行文件)的依赖库与目标文件作链接。所有传感器数据最终都要转到tracking_frame下,published_frame是cartographer发布tf的最下方的一个坐标系。当use_odometry为true时,则此时一定要存在odom的坐标系,这个坐标系可以是cartographer发的,也可以不是。当provide_odom_frame为true时,cartographer发布的tf树为map_frame->odom_frame->published_frame。
下一篇将重点介绍Run函数。