基于yaml-cpp的C++参数服务器设计

前言

  • 今天分享一个基于yaml设计的参数服务器的小设计,主要是为了解决在ROS2节点运行过程中希望动态修改参数并且把参数保存下来的逻辑实现。使用ROS2自带的参数服务器查询一次需要10ms,而且使用launch对yaml的加载由于需要install到共享目录下需要重新编译这对于程序参数调试是及其不友好的。
  • 本文介绍的设计包含以下功能:
    • 查询10个参数小于0.1ms
    • 可根据用户自定参数类型自动进行类型转换,转换出错时自动返回类型默认值(用户可指定)
    • 实时动态修改参数同步进C++程序变量参数,无需额外编译

yaml-cpp介绍与配置

  • YAML-CPP是一个开源的C++库,用于处理YAML数据。YAML是一种人类友好的数据序列化格式,它结合了JSON的易读性和Python的简洁性。YAML-CPP库使得C++程序能够轻松地读取和写入YAML格式的数据。
  • 配置说明:
    • ubuntu ROS2一般有自带这个功能包,使用该程序只需添加依赖即可
    sudo apt-get -y install libyaml-cpp-dev
    
    • CmakeLists配置
      • 备注:std::filesystem属于C++17,需要添加编译要求
        • set(CMAKE_CXX_STANDARD 17)
        • set(CMAKE_CXX_STANDARD_REQUIRED ON)
       # CmakeLists.txt中添加下述依赖即可
       find_package(yaml-cpp REQUIRED)
       add_executable(your_node src/your_node.cpp)
       target_link_libraries(your_node yaml-cpp)
       ament_target_dependencies(your_node yaml-cpp)
      

代码示例

  • 那来看看这个小设计
//ParamsServer.hpp
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
#include <chrono>
#include <filesystem>

/// @brief  参数服务器
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 {
                // 使用YAML-CPP的as方法转换为指定类型
                return it->second.as<T>();
            } catch (const YAML::TypedBadConversion<T>& e) {
                // 如果转换失败,输出错误信息并返回默认值
                std::cerr << "Error converting parameter " << parameterName << ": " << e.what() << std::endl;
            }
        }
        return defaultValue;
    }
   // 设置YAML文件路径的方法
    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) {
                // 保存原始的YAML节点,不进行类型转换
                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_; // 使用YAML::Node保存原始数据
    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左右
      在这里插入图片描述

使用

  • 用户调用(API):
    • #include "ParamsServer.hpp":需要必须包含头文件(内容见上)
    • getParameter<参数类型>("参数名字", "参数默认值");:获取yaml参数
    • setYamlFilePath("新参数路径");:修改yaml文件路径
  • 创建yaml参数文件
# config.yaml
string_param: "hello world"
int_param: 111
float_param: 42.0
double_param: 0.0
bool_param: true
  • C++代码使用
//单例模型获取
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); 
  • 支持动态修改yaml路径
    params_server->setYamlFilePath("new_config.yaml");
    

总结

  • 小工具,希望对大家有帮助,如有错误,欢迎指正,谢谢支持
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值