# ROS2基础教程 - 8 参数
在 ROS1 中,有参数服务器(Parameter Server),它是一个集中式的存储系统,用于存储和检索参数。节点可以通过这个参数服务器共享配置数据,例如常量和设置。
而在 ROS2 中,取消了 Master 和集中式的参数服务器,每个节点都有自己的参数管理系统。参数直接与节点绑定,节点可以声明、获取和更新自己的参数。这种设计使得参数管理更加分散和灵活,适应了分布式系统的需求。
那么 ROS2 中如何在节点之间共享参数呢?
除了可以使用话题、服务、共享文件等方式,还可以通过 SyncParametersClient
或 AsyncParametersClient
访问和修改其他节点的参数。
# 8.1 查看和设置节点参数
# 1 查看参数命令
我们可以使用如下命令查看参数有哪些命令:
# 可以查看参数有哪些命令
ros2 param
2
将小海龟的两个节点运行起来,使用如下命令可以查看运行的节点有哪些参数:
# 查看节点有哪些参数:
ros2 param list
2
可以看到有两个节点,每个节点下有很多的参数:
查看到节点有哪些参数后,可以使用如下命令查看某个参数的描述信息:
# 查看turtlesim节点下background_b参数的描述性信息
ros2 param describe turtlesim background_b
2
# 2 获取和设置参数
我们还可以对参数值进行获取和修改。
我们可以使用如下命令查看当前参数的值:
# 查看turtlesim节点下background_b参数的值
ros2 param get turtlesim background_b
2
修改参数的值:
# 查看turtlesim节点下background_b参数的值
ros2 param set turtlesim background_b 100
2
在上面的命令中,修改了背景颜色蓝色通道的值,所以小海龟的窗口的背景颜色会发生变化。
# 3 导出和导入参数
上面对单个参数进行获取和设置,有时候节点的参数比较多,一个一个修改不太方便,我们还可以将某个节点所有的参数导出到一个 yaml 文件,然后修改这个文件,修改完成,再将这个文件导入到节点中。
将 turtlesim
节点的参数导入到 turtlesim.yaml
文件中:
# 将turtlesim节点所有的参数和值打印到终端
ros2 param dump turtlesim
# 将turtlesim节点所有的参数和值,导出到 turtlesim.yaml 文件中
ros2 param dump turtlesim >> turtlesim.yaml
2
3
4
5
所有的参数会导出到文件中,然后可以修改导出后的文件。
修改完成,可以使用如下命令导入到节点:
# 配置文件中的参数加载入节点
ros2 param load turtlesim turtlesim.yaml
2
下面通过代码来演示,如何在节点中声明、获取、修改、监听参数的变化。
# 8.2 Python实现参数
# 1 创建功能包
首先在 工作空间/src
创建一个功能包,我这里叫 param_python
:
ros2 pkg create --build-type ament_python param_python
# 2 创建节点文件
然后下面在 param_python/param_python
目录下创建节点文件。
例如 param_node.py
,内容如下:
import rclpy
from rclpy.node import Node
from rcl_interfaces.msg import SetParametersResult
from rclpy.parameter import Parameter
import rclpy.parameter
class MyNode(Node):
def __init__(self):
super().__init__('param_node') # 构造函数,节点名为"param_node"
# 声明一个名为 "my_param" 的参数,默认值为 "Hello, ROS2!"
self.declare_parameter('my_param', 'Hello, ROS2!')
# 获取参数值并打印
param_value = self.get_parameter('my_param').get_parameter_value().string_value
self.get_logger().info(f'Initial parameter value: {param_value}')
# 订阅参数变化的回调
self.add_on_set_parameters_callback(self.parameter_callback)
# 创建一个定时器,每秒打印参数值
self.timer_print = self.create_timer(1.0, self.timer_print_callback)
# 创建一个定时器,每 5 秒重置参数值
self.timer_reset = self.create_timer(5.0, self.timer_reset_callback)
# 每秒打印参数值的回调函数
def timer_print_callback(self):
param_value = self.get_parameter('my_param').get_parameter_value().string_value
self.get_logger().info(f'Current parameter value: {param_value}')
# 每 5 秒重置参数值的回调函数
def timer_reset_callback(self):
# 重置参数值为 "Hello, ROS2!"
new_param = Parameter('my_param', Parameter.Type.STRING, 'Hello, ROS2!')
self.set_parameters([new_param])
# 获取参数值并打印
param_value = self.get_parameter('my_param').get_parameter_value().string_value
self.get_logger().info(f'Parameter reset to: {param_value}')
# 参数变化的回调函数
def parameter_callback(self, parameters):
for param in parameters:
if param.name == 'my_param':
self.get_logger().info(f'Parameter changed to: {param.value}')
return SetParametersResult(successful=True, reason='')
def main(args=None):
rclpy.init(args=args) # 初始化ROS2
node = MyNode() # 创建节点
rclpy.spin(node) # 运行节点
rclpy.shutdown() # 关闭ROS2
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
在上面的代码中,使用 self.declare_parameter('my_param', 'Hello, ROS2!')
声明参数。然后使用 self.get_parameter('my_param')
获取参数,并创建了一个监听参数变化的回调。
创建了两个定时器,一个每隔1秒打印一次参数值,另一个每隔5秒重置一下参数。
所以上面的代码涉及到参数的声明、获取、修改 和 监听参数的变化。
# 4 配置节点
在功能包下的 setup.py 中配置两个节点,在 entry_points
中配置:
entry_points={
'console_scripts': [
'param_node = param_python.param_node:main',
],
},
2
3
4
5
# 5 构建项目
现在代码已经编写完成了,需要构建项目。
在工作空间下执行如下命令:
colcon build
构建完成,会在 install
目录下生成文件。
# 6 运行节点
首先执行 source
命令,在工作空间下执行:
source install/local_setup.sh
上面的命令是让 ROS 找到我们的功能包,已经在 HelloWorld 章节说过了。
然后打开一个终端,启动服务端节点:
# 启动节点,不传递参数
ros2 run param_python param_node
# 在启动节点的时候,也可以给my_param传递参数
ros2 run param_python param_node --ros-args -p my_param:='hello foooor'
2
3
4
5
此时可以看到不停的打印参数和重置参数的日志。
再打开另一个命令行,可以使用如下命令修改上面程序中声明的 my_param
参数:
# 将my_param的值修改为Hello Foooor
ros2 param set param_node my_param 'Hello Foooor'
2
可以通过日志看到参数的值被修改为 Hello Foooor
了。
# 8.3 C++实现参数
# 1 创建功能包
首先在 工作空间/src
创建一个功能包,我这里叫 param_cpp
:
ros2 pkg create --build-type ament_cmake param_cpp
# 2 创建节点文件
在 param_cpp/src
下创建节点文件,例如创建节点文件 param_node.cpp
,内容如下:
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
class MyNode : public rclcpp::Node
{
public:
MyNode() : Node("param_node") // 节点名称为 "param_node"
{
// 声明一个名为 "my_param" 的参数,默认值为 "Hello, ROS2!"
this->declare_parameter<std::string>("my_param", "Hello, ROS2!");
// 获取参数值并打印
std::string param_value = this->get_parameter("my_param").as_string();
RCLCPP_INFO(this->get_logger(), "Initial parameter value: %s", param_value.c_str());
// 订阅参数变化的回调
this->add_on_set_parameters_callback(
std::bind(&MyNode::parameter_callback, this, std::placeholders::_1));
// 创建定时器,每秒打印参数值
timer_print_ = this->create_wall_timer(
std::chrono::seconds(1),
std::bind(&MyNode::timer_print_callback, this));
// 创建定时器,每 5 秒重置参数值
timer_reset_ = this->create_wall_timer(
std::chrono::seconds(5),
std::bind(&MyNode::timer_reset_callback, this));
}
private:
// 每秒打印参数值的回调函数
void timer_print_callback()
{
std::string param_value = this->get_parameter("my_param").as_string();
RCLCPP_INFO(this->get_logger(), "Current parameter value: %s", param_value.c_str());
}
// 每 5 秒重置参数值的回调函数
void timer_reset_callback()
{
// 重置参数值为 "Hello, ROS2!"
rclcpp::Parameter new_param("my_param", "Hello, ROS2!");
this->set_parameters({new_param});
// 获取参数值并打印
std::string param_value = this->get_parameter("my_param").as_string();
RCLCPP_INFO(this->get_logger(), "Parameter reset to: %s", param_value.c_str());
}
// 参数变化的回调函数
rcl_interfaces::msg::SetParametersResult parameter_callback(const std::vector<rclcpp::Parameter> ¶meters)
{
for (const auto ¶m : parameters)
{
if (param.get_name() == "my_param")
{
RCLCPP_INFO(this->get_logger(), "Parameter changed to: %s", param.as_string().c_str());
}
}
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
result.reason = "";
return result;
}
// 声明两个定时器
rclcpp::TimerBase::SharedPtr timer_print_;
rclcpp::TimerBase::SharedPtr timer_reset_;
};
int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MyNode>());
rclcpp::shutdown();
return 0;
}
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
在上面的代码中,使用 this->declare_parameter<std::string>("my_param", "Hello, ROS2!");
声明参数。然后使用 this->get_parameter("my_param")
获取参数,并创建了一个监听参数变化的回调。
创建了两个定时器,一个每隔1秒打印一次参数值,另一个每隔5秒重置一下参数。
所以上面的代码涉及到参数的声明、获取、修改 和 监听参数的变化。
# 4 配置CMakeLists.txt
在功能包下的 CMakeLists.txt 中,添加节点配置,可以添加在 find_package(ament_cmake REQUIRED)
下面。
find_package(rclcpp REQUIRED)
# 添加可执行文件,指向 param_node.cpp
add_executable(param_node src/param_node.cpp)
ament_target_dependencies(param_node rclcpp)
# 安装可执行文件
install(TARGETS
param_node
DESTINATION lib/${PROJECT_NAME}
)
2
3
4
5
6
7
8
9
10
11
上面配置了 param_node
节点。
# 5 构建项目
现在代码已经编写完成了,需要构建项目。
在工作空间下执行如下命令:
colcon build
构建完成,会在 install
目录下生成文件。
# 6 运行节点
首先执行 source
命令,在工作空间下执行:
source install/local_setup.sh
上面的命令是让 ROS 找到我们的功能包,已经在 HelloWorld 章节说过了。
然后打开一个终端,启动服务端节点:
# 启动节点,不传递参数
ros2 run param_cpp param_node
# 在启动节点的时候,也可以给my_param传递参数
ros2 run param_cpp param_node --ros-args -p my_param:='hello foooor'
2
3
4
5
此时可以看到不停的打印参数和重置参数的日志。
再打开另一个命令行,可以使用如下命令修改上面程序中声明的 my_param
参数:
# 将my_param的值修改为Hello Foooor
ros2 param set param_node my_param 'Hello Foooor'
2
可以通过日志看到参数的值被修改为 Hello Foooor
了。