• 第2章 ROS 通信机制 3 —— 参数服务器 plumbing_param_server


    1 应用场景

    参数服务器在ROS中主要用于实现不同节点之间的数据共享参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据,关于参数服务器的典型应用场景如下:

    • 导航实现时,会进行路径规划,比如: 全局路径规划,设计一个从出发点到目标点的大致路径。本地路径规划,会根据当前路况(突变情况)生成时时的行进路径

    上述场景中,全局路径规划和本地路径规划时,就会使用到参数服务器

    • 全局路径规划时,需要参考小车的尺寸,比如限高,本地路径规划,需要参考小车尺寸,比如汽车动力学。我们可以将这些尺寸信息存储到参数服务器全局路径规划节点与本地路径规划节点都可以从参数服务器中调用这些参数
    • 参数服务器,一般适用于存在数据共享的一些应用场景

    概念 —— 作用 —— 案例

    • 概念:以共享的方式实现不同节点之间数据交互的通信模式
    • 作用存储一些多节点共享的数据,类似于全局变量
    • 案例:实现参数增删改查操作

    2 参数服务器模型

    参数服务器实现是最为简单的,该模型如下图所示,该模型中涉及到三个角色:

    • ROS Master (管理者)
    • Talker (参数设置者)
    • Listener (参数调用者)
    • ROS Master 作为一个公共容器保存参数Talker 可以向容器中设置参数,Listener 可以获取参数

    在这里插入图片描述

    • Talker向 master 注册参数,Listener向master申请获取参数,master查找参数,并且将值响应给listener

    整个流程由以下步骤实现:

    1. Talker 设置参数
      Talker 通过 RPC 向参数服务器发送参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。

    2. Listener 获取参数
      Listener 通过 RPC 向参数服务器发送参数查找请求,请求中包含要查找的参数名。

    3. ROS Master 向 Listener 发送参数值
      ROS Master 根据步骤2请求提供的参数名查找参数值,并将查询结果通过 RPC 发送给 Listener。

    参数可使用数据类型:

    • 32-bit integers:4个字节的整型数据
    • booleans:布尔数据
    • strings:字符文本
    • doubles:浮点类型数据
    • iso8601 dates:时间类型数据
    • lists:列表,单列集合
    • base64-encoded binary data:编码的二进制数据
    • dictionary:字典,键值组成,双列集合
    • 注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据

    实现不同节点之间的数据共享

    参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,不同的节点也可以往其中存储数据。

    • rosparam list:可以列出参数服务器里的参数
    • rosparam get /type:得到类型参数的具体值
    • rosparam get /type_param:得到类型参数的具体值

    3 参数操作(C++)

    • 需求:实现参数服务器参数的增删改查操作。
    • 在 C++ 中实现参数服务器数据的增删改查,可以通过两套 API 实现:
    • ros::NodeHandle
    • ros::param

    3.1 增与改 demo01_param_set.cpp

    #include"ros/ros.h"
    /*
        需要实现参数的新增和修改
        需求:首先设置机器人的共享参数,类型,半径(0.15m)
            再修改半径(0.2m)
        实现:
            ros::NodeHandle
                setParam("键",值)
            ros::param
                set("键",值)
        修改,只需要继续调研 setParam 或者 set函数,保证键是已经存在的,那么值会覆盖
    */
    
    int main(int argc, char *argv[])
    {
        // 初始化ROS节点
        ros::init(argc,argv,"set_param_c");
    
        // 创建ROS节点句柄
        ros::NodeHandle nh;
    
        // 参数增-------------------------
        // 方案1:nh
        nh.setParam("type","xiaoHuang");// 类型
        nh.setParam("radius",0.15);// 半径
        // 方案2:ros::param
        ros::param::set("type_param","xiaoBai");
        ros::param::set("radius_param",0.15);
    
        // 参数改-------------------------
        // 方案1:nh
        nh.setParam("radius",0.2);// 执行完这行产生参数的覆盖,相当于修改参数
        
        // 方案2:ros::param
        ros::param::set("radius_param",0.25);
    
        return 0;
    }
    

    编译执行-参数查找(重要!!!)

    • 编译 Ctrl + Shift + B
    • 启动 roscore(窗口1)
    roscore
    
    • 启动窗口2
    cd demo01_ws/
    source ./devel/setup.bash
    rosrun plumbing_param_server demo01_param_set
    
    • 启动窗口3
    rosparam list
    

    在这里插入图片描述

    • 获得修改后的参数

    在这里插入图片描述

    3.2 NodeHandle&rosparam 查 demo02_param_get.cpp

    #include "ros/ros.h"
    /*
        演示参数查询
        实现:
            ros::NodeHandle--------------------
                param(键,默认值) 
                存在,返回对应结果,否则返回默认值
    
                getParam(键,存储结果的变量)
                    存在,返回 true,且将值赋值给参数2
                    若果键不存在,那么返回值为 false,且不为参数2赋值
    
                getParamCached键,存储结果的变量)--提高变量获取效率
                    存在,返回 true,且将值赋值给参数2
                    若果键不存在,那么返回值为 false,且不为参数2赋值
    
                getParamNames(std::vector)
                    获取所有的键,并存储在参数 vector 中 
    
                hasParam(键)
                    是否包含某个键,存在返回 true,否则返回 false
    
                searchParam(参数1,参数2)
                    搜索键,参数1是被搜索的键,参数2存储搜索结果的变量
    
            ros::param-------------------------
                set("键",值)
    */
    
    int main(int argc, char *argv[])
    {
        // 设置编码
        setlocale(LC_ALL,"");
    
        // 初始化ROS节点
        ros::init(argc,argv,"get_param_c");
    
        // 创建节点句柄
        ros::NodeHandle nh;
    
        // ros::NodeHandle--------------------
        // 1.param
        double radius = nh.param("radius",0.5);//查询键为radius的值,没有的话返回0.5
        ROS_INFO("radius = %.2f",radius);
    
        // 2.getParam
        double radius2 = 0.0;
        bool result = nh.getParam("radius",radius2);// 第1个是键,第2个是存储结果的变量
        if(result)
        {
            ROS_INFO("获取的半径是:%.2f",radius2);
        }else{
            ROS_INFO("被查询的变量不存在");
        }
    
        // 3.getParamCached 与getParam类似,底层性能有提升,一般测试下,看不出来
        // double radius3 = 1.0;
        // bool result = nh.getParamCached("radius",radius3);
        // if(result)
        // {
        //     ROS_INFO("获取的半径是:%.2f",radius3);
        // }else{
        //     ROS_INFO("被查询的变量不存在");
        // }
    
        // 4.getParamNames
        std::vector<std::string> names;
        nh.getParamNames(names);
        // 遍历names,获取每个键的名称
        for(auto &&name : names)
        {
                ROS_INFO("遍历的元素:%s",name.c_str());//转化成c风格的字符串
        }
    
        // 5.hasParam 判断元素是否存在
        bool flag1 = nh.hasParam("radius");
        bool flag2 = nh.hasParam("radiusxxx");
        ROS_INFO("radius 存在吗? %d",flag1);
        ROS_INFO("radiusxxx 存在吗? %d",flag2);
    
        // 6.searchParam 搜索键
        std::string key;
        nh.searchParam("radius",key);
        ROS_INFO("搜索结果:%s",key.c_str());
    
        // ros::param-------------------------
        double radius_param = ros::param::param("radius",10.0);// 如果查询不到,就用默认值
        // 注意:如果上面结果为0.00, 需要把radius后面的数写成浮点数,如果是整数,就一直是0.00
        ROS_INFO("radius_param = %.2f",radius_param);
    
        std::vector<std::string> names_param;
        ros::param::getParamNames(names_param);
        for(auto &&name : names_param)
        {
            ROS_INFO("键:%s",name.c_str());// 转成c风格
        }
    
        return 0;
    }
    

    编译执行

    • 编译 Ctrl + Shift + B
    • 启动 roscore(窗口1)
    roscore
    
    • 启动窗口2
    cd demo01_ws/
    source ./devel/setup.bash
    rosrun plumbing_param_server demo01_param_set(不变)
    
    • 启动窗口3
    cd demo01_ws/
    source ./devel/setup.bash
    rosrun plumbing_param_server demo02_param_get
    

    ros::NodeHandle 方式查询

    • param(键,默认值)
    • getParam(键,存储结果的变量) 返回 bool类型

    在这里插入图片描述

    • getParamNames(std::vector)
    • type_param、type、radius_param、radius都是自己设置的

    在这里插入图片描述

    • hasParam 判断元素是否存在 (键)

    在这里插入图片描述

    • searchParam 搜索键

    在这里插入图片描述

    ros::param 方式查询

    • param方式

    在这里插入图片描述

    3.3 删 demo03_param_del.cpp

    #include"ros/ros.h"
    
    /*
        实现:
            ros::NodeHandle
                deleteParam()
            ros::param
                del()
    */
    
    int main(int argc, char *argv[])
    {
        setlocale(LC_ALL,"");
        ros::init(argc,argv,"param_del_c");
    
        ros::NodeHandle nh;
    
        //删除:NodeHandle
        bool flag1 = nh.deleteParam("radius");
        if(flag1)
        {
            ROS_INFO("删除成功");
        }else{
            ROS_INFO("删除失败");
        }
    
        //删除:ros::param
        bool flag2 = ros::param::del("radius_param");
        if(flag2)
        {
            ROS_INFO("radius_param删除成功");
        }else{
            ROS_INFO("radius_param删除失败");
        }
        return 0;
    }
    

    编译执行

    • 编译 Ctrl + Shift + B
    • 启动 roscore(窗口1)
    roscore
    
    • 启动窗口2
    cd demo01_ws/
    source ./devel/setup.bash
    rosrun plumbing_param_server demo01_param_set(不变)
    
    • 启动窗口3
    cd demo01_ws/
    source ./devel/setup.bash
    rosrun plumbing_param_server demo03_param_del
    

    在这里插入图片描述

    • 启动窗口4 —— 少了 radius 和 radius_param
    cd demo01_ws/
    source ./devel/setup.bash
    rosparam list
    

    在这里插入图片描述

    3.4 配置 CMakeLists.txt

    add_executable(demo01_param_set src/demo01_param_set.cpp)
    add_executable(demo02_param_get src/demo02_param_get.cpp)
    add_executable(demo03_param_del src/demo03_param_del.cpp)
    
    target_link_libraries(demo01_param_set
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo02_param_get
      ${catkin_LIBRARIES}
    )
    target_link_libraries(demo03_param_del
      ${catkin_LIBRARIES}
    )
    

    4 参数操作(Python)

    4.1 增与改 demo01_param_set_p.py

    #! /usr/bin/env python
    
    import rospy
    """
        演示参数的新增与修改
        需求:子阿参数服务器中设置机器人属性:型号,半径
    
    """
    
    if __name__ == "__main__":
        # 初始化ros节点
        rospy.init_node("param_set_p")
    
        # 新增参数
        rospy.set_param("type_p","xiaoHuangChe") # 设置 键,值
        rospy.set_param("radius_p",0.5)
        
        # 修改参数
        rospy.set_param("radius_p",0.2) # 后面的语句会覆盖前面的值
    

    4.2 NodeHandle&rosparam 查 demo02_param_get_p.py

    #! /usr/bin/env python
    
    import rospy
    """
         参数服务器操作之查询_Python实现:    
            get_param(键,默认值)
                当键存在时,返回对应的值,如果不存在返回默认值
            get_param_cached 
                和get_param 使用一致,效率高
            get_param_names
                获取所有参数键的集合
            has_param
                判断是否包含某个键
            search_param
                查找某个参数的键,并返回完整的键名
    
    """
    
    if __name__ == "__main__":
    
        rospy.init_node("get_param_p")
        # 1.get_param 根据键获取参数的值
        radius1 = rospy.get_param("radius_p",0.5)
        radius2 = rospy.get_param("radius_pxxx",0.5)
    
        rospy.loginfo("radius1 = %.2f",radius1)
        rospy.loginfo("radius2 = %.2f",radius2)
    
        # 2.get_param_cached  效率比第1个高
        radius3 = rospy.get_param("radius_p",0.5)
        radius4 = rospy.get_param("radius_pxxx",0.5)
    
        rospy.loginfo("radius3 = %.2f",radius1)
        rospy.loginfo("radius4 = %.2f",radius2)
    
        # 3.get_param_names 遍历包
        names = rospy.get_param_names()
        for name in names:
            rospy.loginfo("name = %s",name)
    
        # 4.has_param 判断某个键是否存在
        flag1 = rospy.has_param("radius_p")
        if flag1:
            rospy.loginfo("radius_p 存在")
        else:
            rospy.loginfo("radius_p 不存在")
    
        flag2 = rospy.has_param("radius_pxxx")
        if flag2:
            rospy.loginfo("radius_pxxx 存在")
        else:
            rospy.loginfo("radius_pxxx 不存在")
    
        # 5.search_param 查询是否存在,存在则返回键名
        key = rospy.search_param("radius_p")
        rospy.loginfo("key = %s",key)
    

    4.3 删 demo03_param_del_p.py

    #! /usr/bin/env python
    
    import rospy
    """
        演示参数删除:
            delete_param()
    """
    
    if __name__ == "__main__":
        rospy.init_node("del_param_p")
    
        # 使用try捕获异常,使它不显示错误(第2次删除会抛出异常)
        try:
            # 删除参数
            rospy.delete_param("radius_p")
        except Exception as e:
            rospy.loginfo("被删除的参数不存在")
    

    4.4 设置权限

    • 终端下进入 scripts 执行
    chmod +x *.py
    ll
    

    4.5 CMakeLists.txt

    catkin_install_python(PROGRAMS
      scripts/demo01_param_set_p.py
      scripts/demo02_param_get_p.py
      scripts/demo03_param_del_p.py
      DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
    )
    
  • 相关阅读:
    面试官:Spring中获取Bean有几种方式?
    STM32F4移植SPI注意事项
    read 方法为什么返回 int 类型
    【洛谷】P2661 信息传递
    智慧城市中的数字孪生:数字孪生技术助力智慧城市提高公共服务水平
    【C++】内存分区模型
    Gateway路由的配置方式
    springboot+旅游管理系统 毕业设计-附源码261117
    前端token知识梳理:token如何存储?token过期如何处理?如何无感刷新token?
    着巨大风险!力荐这套「软件安装和环境配置手册」,看了直呼NB
  • 原文地址:https://blog.csdn.net/qq_42731062/article/details/127039850