ROS2——参数的使用

上回说到, Organization给每个人免费送2个汉堡, 有一天Organization正在营业中, 汉堡突然供不应求了, 领导决定临时改变规则, 之后的每个人只能领取一个汉堡. 但是此时节点已经在运行中, 该如何改变这个值呢? 这就需要用到参数parameters

参数简介

参数是节点的配置参数值。你可以认为参数是节点配置的一部分。参数为整数,浮点数,布尔值,字符串和列表。在ROS2中,每个节点都有自己的参数。所有参数都是可动态重新配置的,并且是基于ROS2服务构建的。

在这个案例中, 每个人领取的汉堡数量就可以是Organization节点的一个参数.

下面, 我们将修改这段服务程序, 通过修改参数来改变人均汉堡数

修改服务程序

新建Organization_with_parameter.cpp文件

代码由Organization.cpp修改而来, 不同的地方均使用// CHANGE:标注出了, 请读者自行与上节代码进行对比.

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
#include "rclcpp/rclcpp.hpp"
#include "service_interfaces/srv/calculate.hpp"

using std::placeholders::_1;
using std::placeholders::_2;

class Organization : public rclcpp::Node
{

public:
Organization() : Node("Organization"), NumOfAll(100)
{
RCLCPP_INFO(this->get_logger(), "大家好, 我们是热心组织, 我们只给poorer发汉堡.");
callback_group_organization = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
Organization_Server = this->create_service<service_interfaces::srv::Calculate>("Calculate",
std::bind(&Organization::organization_callback,this,_1,_2),
rmw_qos_profile_services_default,
callback_group_organization);

// CHANGE: 声明参数
this->declare_parameter<int>("NumOfEachPerson", NumOfEachPerson);
}

private:
size_t NumOfAll;

// CHANGE: 声明每个人分得的汉堡数, 默认为2
int NumOfEachPerson = 2;

rclcpp::CallbackGroup::SharedPtr callback_group_organization;
rclcpp::Service<service_interfaces::srv::Calculate>::SharedPtr Organization_Server;

void organization_callback(const service_interfaces::srv::Calculate::Request::SharedPtr request,
const service_interfaces::srv::Calculate::Response::SharedPtr response)
{
if(request->status == "Poorer")
{
RCLCPP_INFO(this->get_logger(), "收到一个来自%s的请求,他家有%d个人.", request->status.c_str(), request->num_of_people);

// CHANGE: 更新参数
this->get_parameter("NumOfEachPerson", NumOfEachPerson);

// CHANGE: 计算应给汉堡数量,由参数给定
unsigned int NumOfRequired = request->num_of_people * NumOfEachPerson;

if(NumOfRequired > NumOfAll)
{
RCLCPP_INFO(this->get_logger(), "当前汉堡库里只剩%d个汉堡啦! 已经不够分了, 请明日再来.", NumOfRequired);
response->success = false;
}
else
{
NumOfAll -= NumOfRequired;
response->num_of_hamburger = NumOfRequired;
response->success = true;
RCLCPP_INFO(this->get_logger(), "成功送出%d个汉堡~ 还剩余%d个汉堡", NumOfRequired, NumOfAll);
}
}
else
{
response->success = false;
response->num_of_hamburger = 0;
RCLCPP_INFO(this->get_logger(), "收到一个非法请求,这人是个%s, 不满足送汉堡资格.", request->status.c_str());
}
}
};

int main(int argc, char **argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<Organization>();
rclcpp::executors::MultiThreadedExecutor exector;
exector.add_node(node);
exector.spin();
rclcpp::shutdown();
return 0;
}

Cmakelist.txt

添加:

1
2
add_executable(Organization_with_parameters_node src/Organization_with_parameters.cpp)
ament_target_dependencies(Organization_with_parameters_node rclcpp service_interfaces)

添加:Organization_with_parameters_node

1
2
3
4
5
install(TARGETS
...
Organization_with_parameters_node
DESTINATION lib/${PROJECT_NAME}
)

package.xml

不用修改

编译

--packages-select指定编译customer_and_kfc功能包

1
colcon build --packages-select poor_and_organization

刷新环境

1
source ~/.bashrc

运行

新建一个终端窗口, 运行带参数的Organization服务端节点

1
ros2 run poor_and_organization Organization_with_parameters_node

再另新建一个终端, 运行Poor客户端节点

一开始每人领取两个汉堡, 直接运行客户端:

1
ros2 run poor_and_organization Poor_node Poorer 5

Organization服务端: 成功发出10个汉堡

Parameters-Organization1

Poor客户端: 成功领取10个汉堡

Parameters-change-Poor1

此时需要修改参数, 另起一个终端, 运行:

1
ros2 param set /Organization NumOfEachPerson 1

提示如下即为成功

Parameters-change

再次运行客户端

1
ros2 run poor_and_organization Poor_node Poorer 50

Poor客户端: 50个人领取了50个汉堡

Parameters-change-Poor2

Organization服务端: 成功发出50个汉堡

Parameters-Organization2

由此可见, 参数成功被修改了, 但此时的参数不会被保留, 参数所在的节点重启后即恢复至初始值. 那如果一个节点的参数特别多, 修改完之后想将当前的参数保存下来供下次调用应该怎么操作呢? 请继续阅读.

参数常用命令

使用 ros2 param

查看参数列表

1
ros2 param list

Parameters-ros2-param-list

也可具体到某个节点

1
ros2 param list /Organization

查看参数描述

1
ros2 param describe /Organization NumOfEachPerson

Parameters-ros2-param-describe-Organization-NumOfEachPerson

获取参数值

ros2 param get <node_name> <parameter_name>

1
ros2 param get /Organization NumOfEachPerson

Parameters-ros2-param-get-Organization-NumOfEachPerson-2

设置参数值

ros2 param set <node_name> <parameter_name> <value>

1
ros2 param set /Organization NumOfEachPerson 1

再次获取参数值, 发生了改变:

Parameters-ros2-param-get-Organization-NumOfEachPerson-1

保存参数

ros2 param dump <node_name>

1
ros2 param dump /Organization

在当前终端的根目录下, 可以看到增加了一个Organization.yaml文件, 打开此文件

1
2
3
4
/Organization:
ros__parameters:
NumOfEachPerson: 1
use_sim_time: false

有了这个参数文件, 就可以通过这个文件设置节点的参数

加载参数

节点启动后加载参数

ros2 param load <node_name> <parameter_file>

1
ros2 param load /Organization ./Organization.yaml

返回如下表示加载成功:

Parameters-ros2-param-load-Organization-Organization-yaml

节点启动前加载参数

ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>

1
ros2 run poor_and_organization Organization_with_parameters_node --ros-args --params-file ./Organization.yaml 

请读者尝试自行获取一下当前的NumOfEachPerson值作为练习.