cartographer参数读取过程下
Kong Liangqian Lv6

在参数读取上中,我们说到了,lua的配置参数最后会通过std::tie传到两个变量中,分别是

  • 类为NodeOptions的变量 node_options
  • 类为TrajectoryOptions的变量trajectory_options

下面我们具体分析参数的读取过程

即从node_main.cc的

1
2
std::tie(node_options, trajectory_options) =
LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);

LoadOptions

开始,下面是LoadOption的具体内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
std::tuple<NodeOptions, TrajectoryOptions> LoadOptions(
const std::string& configuration_directory,
const std::string& configuration_basename) {
// 获取配置文件所在的目录
auto file_resolver =
absl::make_unique<cartographer::common::ConfigurationFileResolver>(
std::vector<std::string>{configuration_directory});

// 读取配置文件内容到code中
const std::string code =
file_resolver->GetFileContentOrDie(configuration_basename);

// 根据给定的字符串, 生成一个lua字典
cartographer::common::LuaParameterDictionary lua_parameter_dictionary(
code, std::move(file_resolver));

// 创建元组tuple,元组定义了一个有固定数目元素的容器, 其中的每个元素类型都可以不相同
// 将配置文件的内容填充进NodeOptions与TrajectoryOptions, 并返回
return std::make_tuple(CreateNodeOptions(&lua_parameter_dictionary),
CreateTrajectoryOptions(&lua_parameter_dictionary));
}

在参数配置上中,我们简单说明了参数的返回,和加载结果。下面首先看一下如何获取配置文件所在的目录

构造ConfigurationFileResolver对象

1
2
3
auto file_resolver =
absl::make_unique<cartographer::common::ConfigurationFileResolver>(
std::vector<std::string>{configuration_directory});

file_resolver是一个指向ConfigurationFileResolver类的只能指针,传入的参数是配置文件的目录,将其包装为一个vector,即只含有这一个目录的vector

1
2
3
4
5
6
7
8
9
10
11
class ConfigurationFileResolver : public FileResolver {
public:
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_;
};

ConfigurationFileResolver继承于FileResolver类,FileResolver是一个接口(见下小节),需要实现GetFullPathOrDie和GetFileContentOrDie两个方法,在这里有一个构造函数,explicit,可以防止隐式转换?还有一个私有变量来存储configuration_files_directories。

FileResolver

1
2
3
4
5
6
class FileResolver {
public:
virtual ~FileResolver() {}
virtual std::string GetFullPathOrDie(const std::string& basename) = 0;
virtual std::string GetFileContentOrDie(const std::string& basename) = 0;
};

此FIleResolver是一个文件处理的接口,有两个纯虚函数

  • GetFullPathOrDie: 获取路径
  • GetFileContentOrDie:获取文件内容

构造函数ConfigurationFileResolver

1
2
3
4
5
ConfigurationFileResolver::ConfigurationFileResolver(
const std::vector<std::string>& configuration_files_directories)
: configuration_files_directories_(configuration_files_directories) {
configuration_files_directories_.push_back(kConfigurationFilesDirectory);
}

构造函数中,一共两步骤

  1. 入参configurationfiles_directories传递给私有变量configuration_files_directories
  2. 把参数kConfigurationFilesDirectory加入到配置文件目录里面,即编译后的cartographer自己的配置文件的目录。因此每一次更改了lua文件之后,都需要重新编译才可以

因此其实私有成员变量configurationfiles_directories里面有两个目录地址

  1. 自己在launch文件里面指定的目录地址
  2. cartographer在编译之后的项目配置文件地址

kConfigurationFilesDirectory

注意,此参数是所在的文件目录在build_isolated/cartographer/common/config.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef CARTOGRAPHER_COMMON_CONFIG_H_
#define CARTOGRAPHER_COMMON_CONFIG_H_

namespace cartographer {
namespace common {

constexpr char kConfigurationFilesDirectory[] =
"/home/kong/lixiang_carto/cartographer_detailed_comments_ws/install_isolated/share/cartographer/configuration_files";
constexpr char kSourceDirectory[] = "/home/kong/lixiang_carto/cartographer_detailed_comments_ws/src/cartographer";

} // namespace common
} // namespace cartographer

#endif // CARTOGRAPHER_COMMON_CONFIG_H_

他是经过编译之后才有的文件,此文件是由src/cartographer/cartographer/common/config.h.cmake文件生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef CARTOGRAPHER_COMMON_CONFIG_H_
#define CARTOGRAPHER_COMMON_CONFIG_H_

namespace cartographer {
namespace common {

constexpr char kConfigurationFilesDirectory[] =
"@CARTOGRAPHER_CONFIGURATION_FILES_DIRECTORY@";
constexpr char kSourceDirectory[] = "@PROJECT_SOURCE_DIR@";

} // namespace common
} // namespace cartographer

#endif // CARTOGRAPHER_COMMON_CONFIG_H_

在这里配置了两个参数,一个是CARTOGRAPHER_CONFIGURATION_FILES_DIRECTORY,他表示编译之后cartographer配置文件的目录所在位置,因此每一次更改了lua文件之后,都需要重新编译才可以

下面的PROJECT_SOURCE_DIR表示项目的源代码目录位置

获取文件内容GetFileContentOrDie

1
2
3
// 读取配置文件内容到code中
const std::string code =
file_resolver->GetFileContentOrDie(configuration_basename);

file_resolver是上一节中的ConfigurationFileResolver对象,在此调用GetFileContentOrDie来获得文件内容

1
2
3
4
5
6
7
8
9
10
11
12
std::string ConfigurationFileResolver::GetFileContentOrDie(
const std::string& basename) {
CHECK(!basename.empty()) << "File basename cannot be empty." << basename;

// 根据文件名查找是否在给定文件夹中存在
const std::string filename = GetFullPathOrDie(basename);
std::ifstream stream(filename.c_str());

// 读取配置文件内容并返回
return std::string((std::istreambuf_iterator<char>(stream)),
std::istreambuf_iterator<char>());
}

首先通过函数GetFullPathOrDie来获得文件basename所在的路径,然后读取完整路径下的文件内容。

GetFullPathOrDie

首先通过函数GetFullPathOrDie来获得文件basename所在的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::string ConfigurationFileResolver::GetFullPathOrDie(
const std::string& basename) {
for (const auto& path : configuration_files_directories_) {
const std::string filename = path + "/" + basename;
std::ifstream stream(filename.c_str());
// 只要找到就退出
if (stream.good()) {
LOG(INFO) << "Found '" << filename << "' for '" << basename << "'.";
return filename;
}
}
// 如果找不到配置文件就退出整个程序
LOG(FATAL) << "File '" << basename << "' was not found.";
}

通过对两个配置路径(launch文件指定的以及cartographer自己的编译后的配置文件路径)进行for循环一个目录一个目录查看有没有这个文件,如果找到,就返回组合后的配置文件全路径

生成lua字典LuaParameterDictionary

1
2
3
// 根据给定的字符串, 生成一个lua字典
cartographer::common::LuaParameterDictionary lua_parameter_dictionary(
code, std::move(file_resolver));

构造函数 LuaParameterDictionary

1
2
3
4
5
6
7
8
9
10
/**
* @brief Construct a new Lua Parameter Dictionary:: Lua Parameter Dictionary object
*
* @param[in] code 配置文件内容
* @param[in] file_resolver FileResolver类
*/
LuaParameterDictionary::LuaParameterDictionary(
const std::string& code, std::unique_ptr<FileResolver> file_resolver)
: LuaParameterDictionary(code, ReferenceCount::YES,
std::move(file_resolver)) {}

这里传入的是字符串类型的配置文件,一个指向FileResolver的unique_ptr,下面是调用了另外一个构造函数,对lua文件进行解析,因为下面的内容需要对lua文件进行操作,就不再深究。实际自己书写的时候,只要搞一下yaml就可以了

创建结点CreateNodeOptions

1
2
3
4
// 创建元组tuple,元组定义了一个有固定数目元素的容器, 其中的每个元素类型都可以不相同
// 将配置文件的内容填充进NodeOptions与TrajectoryOptions, 并返回
return std::make_tuple(CreateNodeOptions(&lua_parameter_dictionary),
CreateTrajectoryOptions(&lua_parameter_dictionary));

最后是把参数分别赋值给两个参数类中,在上一节中已经说明

添加自己的参数

  1. lua文件里面的options中添加变量
  2. 判断自己的变量应该是属于NodeOptions还是TrajectoryOptions(一条轨迹的基础参数配置)
  3. 在相应的Create*Options函数里,添加对应的Option.xx = xxx

这样就可以完成新参数的读取了

 Comments