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

2.3 参数服务器

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

关于参数服务器的典型应用场景如下:导航实现时,会进行路径规划,比如:全局路径规划,设计一个从出发点到目标点的大致路径。本地路径规划,会根据当前路况生成时时的行进路径。

上述场景中,全局路径规划和本地路径规划时,就会使用到参数服务器:路径规划时,需要参考小车的尺寸,我们可以将这些尺寸信息存储到参数服务器,全局路径规划节点与本地路径规划节点都可以从参数服务器中调用这些参数参数服务器,一般适用于存在数据共享的一些应用场景。

概念:以共享的方式实现不同节点之间数据交互的通信模式。

作用:存储一些多节点共享的数据,类似于全局变量。

2.3.1参数服务器理论模型

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

  • ROS Master (管理者)
  • Talker (参数设置者)
  • Listener (参数调用者)

ROS Master 作为一个公共容器保存参数,Talker 可以向容器中设置参数,Listener 可以获取参数。

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

1.Talker设置参数

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

2.Listener获取参数

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

3.ROS Master向Listener发送参数值

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

参数可使用数据类型:

  • 32-bit integers
  • booleans
  • strings
  • doubles
  • iso8601 dates
  • lists
  • base64-encoded binary data
  • 字典

注意:参数服务器不是为高性能而设计的,因此最好用于存储静态的非二进制的简单数据

2.3.2 参数操作(C++)

需求:实现参数服务器参数的增删改查操作。

在 C++ 中实现参数服务器数据的增删改查,可以通过两套 API 实现:

  • ros::NodeHandle
  • ros::param

下面为具体操作演示

1.参数服务器新增(修改)参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/*
参数服务器操作之新增与修改(二者API一样)_C++实现:
在 roscpp 中提供了两套 API 实现参数操作
ros::NodeHandle
setParam("键",值)
ros::param
set("键","值")
示例:分别设置整形、浮点、字符串、bool、列表、字典等类型参数
修改(相同的键,不同的值)
*/
#include "ros/ros.h"
int main(int argc, char *argv[])
{
ros::init(argc, argv, "set_update_param");
std::vector<std::string> stus;
stus.push_back("zhangsan");
stus.push_back("李四");
stus.push_back("王五");
stus.push_back("孙大脑袋");
std::map<std::string, std::string> friends;
friends["guo"] = "huang";
friends["yuang"] = "xiao";
// NodeHandle-------------------------------------------------------
ros::NodeHandle nh;
nh.setParam("nh_int", 10); // 整型
nh.setParam("nh_double", 3.14); // 浮点型
nh.setParam("nh_bool", true); // bool
nh.setParam("nh_string", "hello NodeHandle"); // 字符串
nh.setParam("nh_vector", stus); // vector
nh.setParam("nh_map", friends); // map
// 修改演示(相同的键,不同的值)
nh.setParam("nh_int", 10000);
// param-------------------------------------------------------
ros::param::set("param_int", 20);
ros::param::set("param_double", 3.14);
ros::param::set("param_string", "Hello Param");
ros::param::set("param_bool", false);
ros::param::set("param_vector", stus);
ros::param::set("param_map", friends);
// 修改演示(相同的键,不同的值)
ros::param::set("param_int", 20000);
return 0;
}

配置CMakeLists.txt,后面的操作同样需要设置不再说明

1
2
3
4
5
6
add_executable(addParam 
src/addParam.cpp
)
target_link_libraries(addParam
${catkin_LIBRARIES}
)

编译之后运行

1
roscore

新终端

1
2
source ./devel/setup.bash
rosrun 包名 addParam

这样参数就会加入参数服务器,下面的命令查看参数

1
rosparam list

2.参数服务器获取参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
参数服务器操作之查询_C++实现:
在 roscpp 中提供了两套 API 实现参数操作
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 ----- 与 NodeHandle 类似
*/
#include "ros/ros.h"
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "get_param");
// NodeHandle-------------------------------------------------------
/*
ros::NodeHandle nh;
// param 函数
int res1 = nh.param("nh_int",100); // 键存在
int res2 = nh.param("nh_int2",100); // 键不存在
ROS_INFO("param获取结果:%d,%d",res1,res2);
// getParam 函数
int nh_int_value;
double nh_double_value;
bool nh_bool_value;
std::string nh_string_value;
std::vector stus;
std::map friends;
nh.getParam("nh_int",nh_int_value);
nh.getParam("nh_double",nh_double_value);
nh.getParam("nh_bool",nh_bool_value);
nh.getParam("nh_string",nh_string_value);
nh.getParam("nh_vector",stus);
nh.getParam("nh_map",friends);
ROS_INFO("getParam获取的结果:%d,%.2f,%s,%d",
nh_int_value,
nh_double_value,
nh_string_value.c_str(),
nh_bool_value
);
for (auto &&stu : stus)
{
ROS_INFO("stus 元素:%s",stu.c_str());
}
for (auto &&f : friends)
{
ROS_INFO("map 元素:%s = %s",f.first.c_str(), f.second.c_str());
}
// getParamCached()
nh.getParamCached("nh_int",nh_int_value);
ROS_INFO("通过缓存获取数据:%d",nh_int_value);
//getParamNames()
std::vector param_names1;
nh.getParamNames(param_names1);
for (auto &&name : param_names1)
{
ROS_INFO("名称解析name = %s",name.c_str());
}
ROS_INFO("----------------------------");
ROS_INFO("存在 nh_int 吗? %d",nh.hasParam("nh_int"));
ROS_INFO("存在 nh_intttt 吗? %d",nh.hasParam("nh_intttt"));
std::string key;
nh.searchParam("nh_int",key);
ROS_INFO("搜索键:%s",key.c_str());
*/
// param-------------------------------------------------------
ROS_INFO("++++++++++++++++++++++++++++++++++++++++");
int res3 = ros::param::param("param_int", 20); // 存在
int res4 = ros::param::param("param_int2", 20); // 不存在返回默认
ROS_INFO("param获取结果:%d,%d", res3, res4);
// getParam 函数
int param_int_value;
double param_double_value;
bool param_bool_value;
std::string param_string_value;
std::vector<std::string> param_stus;
std::map<std::string, std::string> param_friends;
ros::param::get("param_int", param_int_value);
ros::param::get("param_double", param_double_value);
ros::param::get("param_bool", param_bool_value);
ros::param::get("param_string", param_string_value);
ros::param::get("param_vector", param_stus);
ros::param::get("param_map", param_friends);
ROS_INFO("getParam获取的结果:%d,%.2f,%s,%d",
param_int_value,
param_double_value,
param_string_value.c_str(),
param_bool_value);
for (auto &&stu : param_stus)
{
ROS_INFO("stus 元素:%s", stu.c_str());
}
for (auto &&f : param_friends)
{
ROS_INFO("map 元素:%s = %s", f.first.c_str(), f.second.c_str());
}
// getParamCached()
ros::param::getCached("param_int", param_int_value);
ROS_INFO("通过缓存获取数据:%d", param_int_value);
// getParamNames()
std::vector<std::string> param_names2;
ros::param::getParamNames(param_names2);
for (auto &&name : param_names2)
{
ROS_INFO("名称解析name = %s", name.c_str());
}
ROS_INFO("----------------------------");
ROS_INFO("存在 param_int 吗? %d", ros::param::has("param_int"));
ROS_INFO("存在 param_intttt 吗? %d", ros::param::has("param_intttt"));
std::string key;
ros::param::search("param_int", key);
ROS_INFO("搜索键:%s", key.c_str());
return 0;
}

3.参数服务器删除参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
参数服务器操作之删除_C++实现:
ros::NodeHandle
deleteParam("键")
根据键删除参数,删除成功,返回 true,否则(参数不存在),返回 false
ros::param
del("键")
根据键删除参数,删除成功,返回 true,否则(参数不存在),返回 false
*/
#include "ros/ros.h"
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
ros::init(argc, argv, "delete_param");
ros::NodeHandle nh;
bool r1 = nh.deleteParam("nh_int");
ROS_INFO("nh 删除结果:%d", r1);
bool r2 = ros::param::del("param_int");
ROS_INFO("param 删除结果:%d", r2);
return 0;
}

2.3.3 参数操作(Python)

1.参数服务器新增(修改)参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#! /usr/bin/env python
"""
参数服务器操作之新增与修改(二者API一样)_Python实现:
"""
import rospy
if __name__ == "__main__":
rospy.init_node("set_update_paramter_p")
# 设置各种类型参数
rospy.set_param("p_int",10)
rospy.set_param("p_double",3.14)
rospy.set_param("p_bool",True)
rospy.set_param("p_string","hello python")
rospy.set_param("p_list",["hello","haha","xixi"])
rospy.set_param("p_dict",{"name":"hulu","age":8})
# 修改
rospy.set_param("p_int",100)

2.参数服务器获取参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#! /usr/bin/env python
"""
参数服务器操作之查询_Python实现:
get_param(键,默认值)
当键存在时,返回对应的值,如果不存在返回默认值
get_param_cached
get_param_names
has_param
search_param
"""
import rospy
if __name__ == "__main__":
rospy.init_node("get_param_p")
#获取参数
int_value = rospy.get_param("p_int",10000)
double_value = rospy.get_param("p_double")
bool_value = rospy.get_param("p_bool")
string_value = rospy.get_param("p_string")
p_list = rospy.get_param("p_list")
p_dict = rospy.get_param("p_dict")
rospy.loginfo("获取的数据:%d,%.2f,%d,%s",
int_value,
double_value,
bool_value,
string_value)
for ele in p_list:
rospy.loginfo("ele = %s", ele)
rospy.loginfo("name = %s, age = %d",p_dict["name"],p_dict["age"])
# get_param_cached
int_cached = rospy.get_param_cached("p_int")
rospy.loginfo("缓存数据:%d",int_cached)
# get_param_names

names = rospy.get_param_names()
for name in names:
rospy.loginfo("name = %s",name)
rospy.loginfo("-"*80)
# has_param
flag = rospy.has_param("p_int")
rospy.loginfo("包含p_int吗?%d",flag)
# search_param
key = rospy.search_param("p_int")
rospy.loginfo("搜索的键 = %s",key)

3.参数服务器删除参数

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /usr/bin/env python
"""
参数服务器操作之删除_Python实现:
rospy.delete_param("键")
键存在时,可以删除成功,键不存在时,会抛出异常
"""
import rospy
if __name__ == "__main__":
rospy.init_node("delete_param_p")
try:
rospy.delete_param("p_int")
except Exception as e:
rospy.loginfo("删除失败")