1 应用场景
参数服务器在ROS中主要用于实现不同节点之间的数据共享。参数服务器相当于是独立于所有节点的一个公共容器,可以将数据存储在该容器中,被不同的节点调用,当然不同的节点也可以往其中存储数据,关于参数服务器的典型应用场景如下:
- 导航实现时,会进行路径规划,比如: 全局路径规划,设计一个从出发点到目标点的大致路径。本地路径规划,会根据当前路况(突变情况)生成时时的行进路径。
上述场景中,全局路径规划和本地路径规划时,就会使用到参数服务器
- 全局路径规划时,需要参考小车的尺寸,比如限高,本地路径规划,需要参考小车尺寸,比如汽车动力学。我们可以将这些尺寸信息存储到参数服务器,全局路径规划节点与本地路径规划节点都可以从参数服务器中调用这些参数
- 参数服务器,一般适用于存在数据共享的一些应用场景
概念 —— 作用 —— 案例
- 概念:以共享的方式实现不同节点之间数据交互的通信模式
- 作用:存储一些多节点共享的数据,类似于全局变量
- 案例:实现参数增删改查操作
2 参数服务器模型
参数服务器实现是最为简单的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master (管理者)
- Talker (参数设置者)
- Listener (参数调用者)
- ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数
- Talker向 master 注册参数,Listener向master申请获取参数,master查找参数,并且将值响应给listener
整个流程由以下步骤实现:
-
Talker 设置参数
Talker 通过 RPC 向参数服务器发送参数(包括参数名与参数值),ROS Master 将参数保存到参数列表中。 -
Listener 获取参数
Listener 通过 RPC 向参数服务器发送参数查找请求,请求中包含要查找的参数名。 -
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<std::string>)
获取所有的键,并存储在参数 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<std::string>)
- 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}
)