Skip to content

ROS2主要创新的2/3:服务(services)通信

01. 背景知识

服务(Service)是 ROS graph 中节点的另一种通信方法。服务基于 call-and-response 模型,而不是topic的 publisher-subscriber 模型。不同于 topic 让节点订阅数据流并获得持续更新, service 仅在客户端呼叫服务时提供数据。Service的call-and-response模型示意图如下:

Service的call-and-response模型示意图

多个节点call同一个服务

02. 准备工作

为了在每个新打开的终端里面使用 ROS2 的命令工具,我们需要用如下命令加载关于 ROS2 的配置信息:

shell
source /opt/ros/jazzy/setup.zsh

03. 学习任务

3.1 运行必要的节点

启动两个 turtlesim 节点 /turtlesim/teleop_turtle

打开新终端并运行:

shell
ros2 run turtlesim turtlesim_node

打开另一个终端并运行:

shell
ros2 run turtlesim turtle_teleop_key

3.2 ros2 service list

在新终端中运行 ros2 service list 命令将返回系统中当前活动的所有服务的列表:

shell
/clear
/kill
/reset
/spawn
/teleop_turtle/describe_parameters
/teleop_turtle/get_parameter_types
/teleop_turtle/get_parameters
/teleop_turtle/list_parameters
/teleop_turtle/set_parameters
/teleop_turtle/set_parameters_atomically
/turtle1/set_pen
/turtle1/teleport_absolute
/turtle1/teleport_relative
/turtlesim/describe_parameters
/turtlesim/get_parameter_types
/turtlesim/get_parameters
/turtlesim/list_parameters
/turtlesim/set_parameters
/turtlesim/set_parameters_atomically

将看到两个节点都有相同的 6 个服务,其名称中包含参数(parameters)。 ROS 2 中的几乎每个节点都有这些构建参数的基础设施服务。下一个教程中将有更多关于参数的信息。在本教程中,参数 services 将在讨论中省略。

现在,让我们专注于 turtlesim 特定的服务,/clear/kill/reset/spawn/turtle1/set_pen/turtle1/teleport_absolute/turtle1/teleport_relative。在之前的课程中,我们使用 rqt 与其中一些服务进行交互。

3.3 ros2 service type

服务具有描述服务的请求和响应数据结构的类型(type)。服务类型的定义与主题类型类似,但服务类型包含两个部分:一个消息用于请求,另一个用于响应。

要了解服务类型,请使用以下命令:

shell
ros2 service type <service_name>

我们来看看 turtlesim 的 /clear 服务。在新终端中,输入命令:

shell
ros2 service type /clear

输出结果如下:

shell
std_srvs/srv/Empty

Empty 类型表示服务调用在发出请求时不发送任何数据,在接收响应时不接收任何数据。

3.4 ros2 service list -t

要同时查看所有活动服务的类型,可以将 --show-types 选项(缩写为 -t)附加到 list 命令中:

shell
ros2 service list -t

这将返回:

shell
/clear [std_srvs/srv/Empty]
/kill [turtlesim/srv/Kill]
/reset [std_srvs/srv/Empty]
/spawn [turtlesim/srv/Spawn]
...
/turtle1/set_pen [turtlesim/srv/SetPen]
/turtle1/teleport_absolute [turtlesim/srv/TeleportAbsolute]
/turtle1/teleport_relative [turtlesim/srv/TeleportRelative]
...

3.5 ros2 service info

要查看特定服务的信息,请使用以下命令:

shell
ros2 service info <service_name>

这将返回服务类型以及服务客户端和服务器的计数。

例如,您可以找到 /clear 服务的客户端和服务器计数:

shell
ros2 service info /clear

这将返回:

shell
Type: std_srvs/srv/Empty
Clients count: 0
Services count: 1

3.6 ros2 service find

如果要查找特定类型的所有服务,可以使用以下命令:

shell
ros2 service find <type_name>

例如,可以找到所有 Empty typed 服务,如下所示:

shell
ros2 service find std_srvs/srv/Empty

这将返回:

shell
/clear
/reset

3.7 ros2 interface show

可以从命令行调用服务,但首先需要了解输入参数的结构。

shell
ros2 interface show <type_name>

/clear 服务的类型 Empty 尝试此操作:

shell
ros2 interface show std_srvs/srv/Empty

这将返回:

shell
---

---将请求结构(---之上)与响应结构(---之下)分开。但是,正如之前了解的那样,Empty 类型不会发送或接收任何数据。所以,自然而然地,它的结构是空白的。

让我们检查一下具有发送和接收数据的类型的服务,例如 /spawn。从 ros2 service list -t 的结果中,我们知道 /spawn 的类型是 turtlesim/srv/Spawn

要查看 /spawn 服务的请求和响应参数,请运行以下命令:

shell
ros2 interface show turtlesim/srv/Spawn

这将返回:

shell
float32 x
float32 y
float32 theta
string name # Optional.  A unique name will be created and returned if this is empty
---
string name

---行上方的信息告诉我们调用 /spawn 所需的参数。 xytheta 确定生成的海龟的 2D 姿势,而 name 显然是可选的。

在这种情况下,我们不需要了解该行下方的信息,但它可以帮助我们了解从呼叫中获得的响应的数据类型。

3.8 ros2 service call

现在,我们已经了解了什么是服务类型、如何查找服务类型以及如何查找该类型参数的结构。我们还可以使用以下方法调用服务:

shell
ros2 service call <service_name> <service_type> <arguments>

<arguments> 部分是可选的。此处 Empty 类型的服务没有任何参数:

shell
ros2 service call /clear std_srvs/srv/Empty

此命令将清除 turtlesim 窗口中绘制的任何线条。

利用service的call命令清除绘制的线条

现在让我们通过调用 /spawn 并设置参数来生成一个新的海龟。在从命令行进行的服务调用中输入 <arguments> 需要采用 YAML 语法。

输入命令:

shell
ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: ''}"

我们将获得这个方法样式的视图,其中包含所发生的事情,然后是服务响应:

shell
requester: making request: turtlesim.srv.Spawn_Request(x=2.0, y=2.0, theta=0.2, name='')

response:
turtlesim.srv.Spawn_Response(name='turtle2')

turtlesim 窗口将立即更新新生成的海龟:

生成新的海龟

3.9 ros2 service echo

要查看服务客户端和服务服务器之间的数据通信,我们可以使用以下方法echo服务:

shell
ros2 service echo <service_name | service_type> <arguments>

ros2 service echo 依赖于服务客户端和服务器的服务自检,默认情况下处于禁用状态。要启用它,用户必须在创建服务器客户端或服务器后调用 configure_introspection

启动 introspection_client and introspection_service 服务 Introspection 演示。

shell
ros2 launch demo_nodes_cpp introspect_services_launch.py

打开另一个终端并运行以下命令,以启用 introspection_clientintrospection_service 的服务自检。

shell
ros2 param set /introspection_service service_configure_introspection contents
ros2 param set /introspection_client client_configure_introspection contents

现在我们可以通过 ros2 service echo 看到 introspection_clientintrospection_service 之间的服务通信。

shell
ros2 service echo --flow-style /add_two_ints
info:
  event_type: REQUEST_SENT
  stamp:
    sec: 1709408301
    nanosec: 423227292
  client_gid: [1, 15, 0, 18, 250, 205, 12, 100, 0, 0, 0, 0, 0, 0, 21, 3]
  sequence_number: 618
request: [{a: 2, b: 3}]
response: []
---
info:
  event_type: REQUEST_RECEIVED
  stamp:
    sec: 1709408301
    nanosec: 423601471
  client_gid: [1, 15, 0, 18, 250, 205, 12, 100, 0, 0, 0, 0, 0, 0, 20, 4]
  sequence_number: 618
request: [{a: 2, b: 3}]
response: []
---
info:
  event_type: RESPONSE_SENT
  stamp:
    sec: 1709408301
    nanosec: 423900744
  client_gid: [1, 15, 0, 18, 250, 205, 12, 100, 0, 0, 0, 0, 0, 0, 20, 4]
  sequence_number: 618
request: []
response: [{sum: 5}]
---
info:
  event_type: RESPONSE_RECEIVED
  stamp:
    sec: 1709408301
    nanosec: 424153133
  client_gid: [1, 15, 0, 18, 250, 205, 12, 100, 0, 0, 0, 0, 0, 0, 21, 3]
  sequence_number: 618
request: []
response: [{sum: 5}]
---

04. 小结

节点可以使用 ROS 2 中的服务进行通信。与主题(一种单向通信模式,节点发布可由一个或多个订阅者使用的信息)不同,服务是一种请求/响应模式,其中客户端向提供服务的节点发出请求,服务处理请求并生成响应。

我们通常不想将服务 (service) 用于连续调用; 主题 (topic) 甚至行动 (action) 会更合适。

在本教程中,我们使用了命令行工具来识别、内省(introspect) 和调用服务。

附录:彻底搞明白如何从U盘启动Linux

  • Author:Zixuan HUANG