编写简单的服务和客户端 (Python) [待校准@9468]
Goal目标: using使用Python创建和运行服务和客户端节点。 [待校准@9469]
教程级别: 初学者 [Alyssa@7088]
Time时间: 20 20分钟 [待校准@7181]
背景
当 nodes 使用 services 进行通信时,发送数据请求的节点被调用到客户端节点,响应请求的节点是服务节点。请求和响应的结构由 .srv
文件决定。 [待校准@9395]
这里使用的示例是一个简单的整数加法系统; 一个节点请求两个整数的总和,另一个节点响应结果。 [待校准@9396]
先决条件
在之前的教程中,你学习了如何 create a workspace and create a package 。 [待校准@9349]
任务
1创建包
打开一个新的终端和 source your ROS 2 installation ,这样 ros2
命令就可以工作了。 [待校准@9214]
导航到 dev_ws
目录创建 previous tutorial 。 [待校准@9215]
重新调用应该在 src
目录中创建的包,而不是工作区的根目录。导航到 dev_ws/src
并创建一个新的包: [待校准@9216]
ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
您的终端将返回一条消息,验证您的包 py_srvcli
及其所有必要的文件和文件夹的创建。 [待校准@9470]
[需手动修复的语法] --dependencies
参数将自动调用y将必要的依赖关系添加到 package.xml
。 example_interfaces
是包含 the .srv file 的套餐,您需要构建您的请求和回复: [待校准@9471]
int64 a
int64 b
---
int64 sum
前两行是请求的参数,破折号下面是响应。 [待校准@9399]
1.1更新 package.xml
[待校准@8003]
因为您在包创建过程中使用了 --dependencies
选项,所以您不必手动向 package.xml
添加依赖项。 [待校准@9472]
但是,与往常一样,请确保将描述、维护人员电子邮件和姓名以及许可信息添加到 package.xml
。 [待校准@9220]
<description>Python client server tutorial</description>
<maintainer email="you@email.com">Your Name</maintainer>
<license>Apache License 2.0</license>
1.2更新 setup.py
[待校准@9473]
添加相同信息 setup.py
文件 maintainer
, maintainer_email
, description
和 license
字段: [待校准@9474]
maintainer='Your Name',
maintainer_email='you@email.com',
description='Python client server tutorial',
license='Apache License 2.0',
2写入服务节点 [待校准@9400]
在 dev_ws/src/py_srvcli/py_srvcli
目录内,创建一个调用 service_member_function.py
的新文件,并将以下代码粘贴到: [待校准@9475]
from example_interfaces.srv import AddTwoInts
import rclpy
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(args=None):
rclpy.init(args=args)
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
if __name__ == '__main__':
main()
2.1检查代码 [待校准@8007]
第一个 import
语句从 example_interfaces
包中导入 AddTwoInts
服务类型。以下 import
语句导入ROS 2 Python客户端库,并指定调用 Node
类。 [待校准@9476]
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
[需手动修复的语法] MinimalService
类构造函数初始化名称为 minimal_service
的节点。然后,它创建一个服务并定义类型、名称和调用。 [待校准@9477]
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
服务调用back的定义接收请求数据,对其求和,并将总和作为响应返回。 [待校准@9478]
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
最后,主类初始化ROS 2 Python客户端库,实例化 MinimalService
类以创建服务节点,并旋转节点以处理调用。 [待校准@9479]
2.2添加入口点 [待校准@9263]
要允许 ros2 run
命令运行您的节点,您必须将入口点添加到 setup.py
(位于 dev_ws/src/py_srvcli
目录中)。 [待校准@9480]
在 “'console_scripts':” 括号中添加以下一行: [待校准@9481]
'service = py_srvcli.service_member_function:main',
3写入客户端节点 [待校准@9413]
在 dev_ws/src/py_srvcli/py_srvcli
目录内,创建一个调用 client_member_function.py
的新文件,并将以下代码粘贴到: [待校准@9482]
import sys
from example_interfaces.srv import AddTwoInts
import rclpy
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 = int(sys.argv[1])
self.req.b = int(sys.argv[2])
self.future = self.cli.call_async(self.req)
def main(args=None):
rclpy.init(args=args)
minimal_client = MinimalClientAsync()
minimal_client.send_request()
while rclpy.ok():
rclpy.spin_once(minimal_client)
if minimal_client.future.done():
try:
response = minimal_client.future.result()
except Exception as e:
minimal_client.get_logger().info(
'Service call failed %r' % (e,))
else:
minimal_client.get_logger().info(
'Result of add_two_ints: for %d + %d = %d' %
(minimal_client.req.a, minimal_client.req.b, response.sum))
break
minimal_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
3.1检查代码 [待校准@9375]
对客户来说,唯一不同的 import
声明是 import sys
。客户端节点代码使用 sys.argv 来访问请求的命令行输入参数。 [待校准@9483]
构造函数定义创建与服务节点具有相同类型和名称的客户端。类型和名称必须匹配,客户端和服务才能进行通信。 [待校准@9484]
构造函数中的 while
循环检查与客户端类型和名称匹配的服务是否每秒可用一次。 [待校准@9485]
构造函数下方是请求定义,后跟 main
。 [待校准@9486]
客户的 main
唯一显著的区别是 while
环。只要系统正在运行,循环就会检查 future
,看看是否有来自服务的响应。如果服务已发送响应,则结果将写入日志消息中。 [待校准@9487]
3.2添加入口点 [待校准@9463]
与服务节点一样,您还必须添加一个入口点才能运行客户端节点。 [待校准@9488]
[需手动修复的语法] setup.py
文件的 entry_points
字段应该如下所示: [待校准@9489]
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main',
],
},
4构建和运行 [待校准@7896]
在构建之前,最好在工作区 ( dev_ws
) 的根目录下运行 rosdep
,以检查是否缺少依赖项: [待校准@9228]
rosdep install -i --from-path src --rosdistro foxy -y
rosdep仅在Linux上运行,因此您可以跳到下一步。 [待校准@9229]
rosdep仅在Linux上运行,因此您可以跳到下一步。 [待校准@9229]
导航回工作区的根目录, dev_ws
,并构建新的包: [待校准@9230]
colcon build --packages-select py_srvcli
colcon build --packages-select py_srvcli
colcon build --merge-install --packages-select py_srvcli
打开一个新的终端,导航到 dev_ws
,源文件安装文件: [待校准@9231]
. install/setup.bash
. install/setup.bash
call install/setup.bat
现在运行服务节点: [待校准@9423]
ros2 run py_srvcli service
节点将等待客户端的请求。 [待校准@9490]
打开另一个终端,再次从 dev_ws
内部源文件。启动客户端节点,后跟由空格分隔的任意两个整数: [待校准@9491]
ros2 run py_srvcli client 2 3
例如,如果您选择 2
和 3
,客户将收到如下回复: [待校准@9426]
[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5
返回到服务节点正在运行的终端。您将看到它在收到请求时发布了日志消息: [待校准@9492]
[INFO] [minimal_service]: Incoming request
a: 2 b: 3
在服务器终端中输入 “ctrl + c” 以停止节点旋转。 [待校准@9428]
总结
您创建了两个节点来通过服务请求和响应数据。您将它们的依赖项和可执行文件添加到包配置文件中,以便您可以构建和运行它们,从而使您可以看到服务/客户端系统正在工作。 [待校准@9493]
下一步
在最近的几个教程中,您一直在利用界面跨话题和服务传递数据。接下来,你将学习如何 create custom interfaces 。 [待校准@9430]