前言
- 今天分享一个基于yaml设计的参数服务器的小设计,主要是为了解决在ROS2节点运行过程中希望
动态修改参数
并且把参数保存下来
的逻辑实现。使用ROS2自带的参数服务器查询一次需要10ms,而且使用launch对yaml的加载由于需要install到共享目录下需要重新编译这对于程序参数调试是及其不友好的。 - 本文介绍的设计包含以下功能:
- 查询10个参数
小于0.1ms
- 可根据用户自定参数类型
自动进行类型转换
,转换出错时自动返回类型默认值(用户可指定) - 可
实时动态修改
参数同步进C++程序变量参数,无需额外编译
yaml-cpp介绍与配置
代码示例
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <filesystem>
class ParamsServer {
public:
static ParamsServer* getInstance() {
static ParamsServer* instance = new ParamsServer("config.yaml");
return instance;
}
ParamsServer(const ParamsServer&) = delete;
ParamsServer& operator=(const ParamsServer&) = delete;
template<typename T>
T getParameter(const std::string& parameterName, const T& defaultValue) {
std::filesystem::file_time_type lastWriteTime = std::filesystem::last_write_time(filename_);
if (lastWriteTime > lastLoadTime_) {
loadParameters();
}
auto it = parameters_.find(parameterName);
if (it != parameters_.end()) {
try {
return it->second.as<T>();
} catch (const YAML::TypedBadConversion<T>& e) {
std::cerr << "Error converting parameter " << parameterName << ": " << e.what() << std::endl;
}
}
return defaultValue;
}
void setYamlFilePath(const std::string& newPath) {
filename_ = newPath;
loadParameters();
}
private:
ParamsServer(const std::string& filename) : filename_(filename) {
loadParameters();
}
void loadParameters() {
std::ifstream file(filename_);
if (!file.is_open()) {
std::cerr << "Error opening file: " << filename_ << std::endl;
return;
}
try {
YAML::Node config = YAML::Load(file);
for (const auto& pair : config) {
parameters_[pair.first.as<std::string>()] = pair.second;
}
lastLoadTime_ = std::filesystem::last_write_time(filename_);
} catch (const YAML::Exception& e) {
std::cerr << "Error parsing YAML file: " << e.what() << std::endl;
}
}
std::string filename_;
std::map<std::string, YAML::Node> parameters_;
std::filesystem::file_time_type lastLoadTime_;
};
- 上述代码使用
单例模型
进行设计,熟悉设计模式
的应该不陌生 - loadParameters()函数:用于加载yaml文件
std::map<std::string, YAML::Node> parameters_;
:使用parameters_对yaml文件内容进行存储,这样避免实时进行文件读取耗时lastLoadTime_ = std::filesystem::last_write_time(filename_);
:记录本次读取文件时间,用于时间判断
- getParameter<>()函数:获取参数
- 判断
std::filesystem::file_time_type lastWriteTime = std::filesystem::last_write_time(filename_);
通过文件修改时间判断是否被修改,来判断是否需要重新加载参数,如果文件被修改,那么重新调用参数加载文件,否则使用存储的参数值(减少读取时间) - 使用模板,使用户可指定转换类型,调用yaml-cpp内置转换方法
it->second.as<T>()
对参数进行类型转换 - 如果转换出现问题
const YAML::TypedBadConversion<T>& e
,进入try-catch模块返回用户默认值
- 执行一次查询时间
- 进行读取时间约为0.1ms,正常读取参数在0.05ms左右
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/eaf50e6c5b3c4734aea7a573409a87e4.png)
使用
- 用户调用(API):
#include "ParamsServer.hpp"
:需要必须包含头文件(内容见上)getParameter<参数类型>("参数名字", "参数默认值");
:获取yaml参数setYamlFilePath("新参数路径");
:修改yaml文件路径
- 创建yaml参数文件
string_param: "hello world"
int_param: 111
float_param: 42.0
double_param: 0.0
bool_param: true
ParamsServer* params_server = ParamsServer::getInstance();
std::string string_param = params_server->getParameter<std::string>("string_param", "defaultValue");
int int_param = params_server->getParameter<int>("int_param", 0);
double double_param = params_server->getParameter<double>("double_param", 0.0);
float float_param = params_server->getParameter<double>("float_param", 0.0f);
bool bool_param = params_server->getParameter<bool>("bool_param", true);
总结
- 小工具,希望对大家有帮助,如有错误,欢迎指正,谢谢支持