乐于分享
好东西不私藏

机器人系统——ROS2文档—编写简单的服务以及客户端节点(Python)

机器人系统——ROS2文档—编写简单的服务以及客户端节点(Python)

目标:使用 Python 创建并运行服务和客户端节点。教程级别:入门级时间: 20 分钟

目录

  • 背景
  • 前置条件
  • 任务
    • 创建包
    • 编写服务节点
    • 编写客户端节点
    • 4  构建并运行
  • 总结
  • 后续步骤
  • 相关资料

背景

节点使用服务进行通信时,发送数据请求的节点称为客户端节点,响应请求的节点称为服务节点。请求和响应的结构由.srv文件决定。

这里使用的示例是一个简单的整数加法系统;一个节点请求两个整数的和,另一个节点返回结果。

前置条件

在之前的教程中,您学习了如何创建工作空间创建包

任务

1创建包

打开一个新终端并加载您的 ROS 2 安装,以便ros2命令能够工作。

导航到在上一个教程中创建的ros2_ws目录。

回想一下,包应该创建在src目录中,而不是工作空间的根目录。导航到ros2_ws/src并创建一个新包:

$ ros2 pkg create --build-type ament_python --license Apache-2.0 py_srvcli --dependencies rclpy example_interfaces

您的终端将返回一条消息,确认您的包py_srvcli及其所有必要的文件和文件夹已创建。

--dependencies参数会自动将必要的依赖行添加到package.xmlexample_interfaces是包含.srv 文件的包,您将需要该文件来构建请求和响应的结构:

int64 a
int64 b
---
int64 sum

前两行是请求的参数,横线下面是响应。

1.1 更新 package.xml

由于您在包创建期间使用了--dependencies选项,因此不必手动向package.xml添加依赖项。

不过,像往常一样,确保将描述、维护者电子邮件和名称以及许可证信息添加到package.xml中。

<description>Python client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache-2.0</license>

1.2 更新 setup.py

将相同的信息添加到setup.py文件的maintainermaintainer_emaildescriptionlicense字段:

maintainer='Your Name',
maintainer_email='you@email.com',
description='Python client server tutorial',
license='Apache-2.0',

2编写服务节点

ros2_ws/src/py_srvcli/py_srvcli目录中,创建一个名为service_member_function.py的新文件,并粘贴以下代码:

from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response
def main():
try:
with rclpy.init():
minimal_service = MinimalService()
rclpy.spin(minimal_service)
except (KeyboardInterrupt, ExternalShutdownException):
pass
if __name__ == '__main__':
main()

2.1 检查代码

第一个import语句从example_interfaces包导入AddTwoInts服务类型。接下来的import语句导入必要的 ROS 2 Python 客户端库接口。

from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node

MinimalService类构造函数初始化节点,名称为minimal_service。然后,它创建一个服务并定义类型、名称和回调。

def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)

服务回调的定义接收请求数据,将其求和,并将和作为响应返回。

def add_two_ints_callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b))
return response

最后,main 类初始化 ROS 2 Python 客户端库,实例化MinimalService类来创建服务节点,并旋转节点以处理回调。

2.2 添加入口点

为了允许ros2 run命令运行您的节点,您必须将入口点添加到位于ros2_ws/src/py_srvcli目录中的setup.py

'console_scripts':括号之间添加以下行:

'service = py_srvcli.service_member_function:main',

3编写客户端节点

ros2_ws/src/py_srvcli/py_srvcli目录中,创建一个名为client_member_function.py的新文件,并粘贴以下代码:

from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node
class MinimalClientAsync(Node):
def __init__(self):
super().__init__('minimal_client_async')
self.cli = self.create_client(AddTwoInts, 'add_two_ints')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = AddTwoInts.Request()
def send_request(self):
self.req.a = 41
self.req.b = 1
return self.cli.call_async(self.req)
def main(args=None):
try:
with rclpy.init(args=args):
minimal_client = MinimalClientAsync()
future = minimal_client.send_request()
rclpy.spin_until_future_complete(minimal_client, future)
response = future.result()
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(minimal_client.req.a, minimal_client.req.b, response.sum))
except (KeyboardInterrupt, ExternalShutdownException):
pass
if __name__ == '__main__':
main()

3.1 检查代码

与服务代码一样,我们首先导入必要的库。

from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.executors import ExternalShutdownException
from rclpy.node import Node

MinimalClientAsync类构造函数初始化节点,名称为minimal_client_async。构造函数定义创建了一个与服务节点具有相同类型和名称的客户端。类型和名称必须匹配,客户端和服务才能通信。构造函数中的while循环每秒检查一次是否有与客户端类型和名称匹配的服务可用。最后它创建一个新的AddTwoInts请求对象。

def __init__(self):
super().__init__('minimal_client_async')
self.cli = self.create_client(AddTwoInts, 'add_two_ints')
while not self.cli.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting again...')
self.req = AddTwoInts.Request()

在构造函数下面是send_request方法,它将发送请求并旋转直到收到响应或失败。

def send_request(self):
self.req.a = 41
self.req.b = 1
return self.cli.call_async(self.req)

最后我们有main方法,它构造一个MinimalClientAsync对象,使用传入的命令行参数发送请求,调用rclpy.spin_until_future_complete等待结果,并记录结果。

def main(args=None):
try:
with rclpy.init(args=args):
minimal_client = MinimalClientAsync()
future = minimal_client.send_request()
rclpy.spin_until_future_complete(minimal_client, future)
response = future.result()
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(minimal_client.req.a, minimal_client.req.b, response.sum))
except (KeyboardInterrupt, ExternalShutdownException):
pass

警告:不要在 ROS 2 回调中使用rclpy.spin_until_future_complete。有关更多详细信息,请参阅同步死锁文章

3.2 添加入口点

与服务节点一样,您也必须添加一个入口点才能运行客户端节点。

setup.py文件的entry_points字段应该如下所示:

entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main',
],
},

构建并运行

最佳实践是在工作空间根目录(ros2_ws)运行rosdep来检查构建前是否缺少依赖项:

Linux

$ rosdep install -i --from-path src --rosdistro lyrical -y

导航回工作空间根目录ros2_ws,并构建您的新包:

$ colcon build --packages-select py_srvcli

打开一个新终端,导航到ros2_ws,并加载设置文件:

Linux

$ source install/setup.bash

现在运行服务节点:

$ ros2 run py_srvcli service

该节点将等待客户端的请求。

打开另一个终端,再次从ros2_ws内部加载设置文件。启动客户端节点。客户端向服务发送请求,服务计算和并返回结果:

$ ros2 run py_srvcli client
[INFO] [minimal_client_async]: Result of add_two_ints: for 41 + 1 = 42

返回到服务节点正在运行的终端。您将看到它在收到请求时发布了日志消息:

[INFO] [minimal_service]: Incoming request
a: 41 b: 1

在服务端终端中输入Ctrl+C以停止节点旋转。

总结

您创建了两个节点,用于通过服务请求和响应数据。您将它们的依赖项和可执行文件添加到包配置文件中,以便能够构建和运行它们,从而看到服务/客户端系统的运行。

后续步骤

在最后几个教程中,您一直在使用接口通过话题和服务传递数据。接下来,您将学习如何创建自定义接口

相关资料

  •  Python 编写服务和客户端有几种方法;请查看ros2/examples仓库中的minimal_clientminimal_service包。
  • 在本教程中,您在客户端节点中使用了call_async() API 来调用服务。还有一个可用于 Python 的服务调用 API,称为同步调用。我们不建议使用同步调用,但如果您想了解更多关于它们的信息,请阅读同步与异步客户端指南。

本文档完整翻译自 ROS 2 Lyrical 官方文档,仅用于学习交流。