ROS2开发笔记——service样例(三)

2023-05-17 Views862字4 min read

一、创建包

服务的通信方式就是客户端发起其服务请求,服务端收到请求后响应。在这里,我们通过一个加法求解的样例演示怎么进行服务开发,客户端发起请求——“我想知道a+b=?”,服务端收到a和b这两个数后把它们相加并返回给客户端。根据问题的定义,那么这个服务的数据结构就应该如下所示,

int64 a
int64 b
---
int64 sum

上半部定义了请求的两个整型变量,下半部定义响应结果。该数据结构定义在了example_interfaces包里面,就是为了方便我们学习,后面我们也会学习怎么自定义需要的各类数据结构。

除了上述的数据结构包,我们还需要依赖rclpy这个包,知道了这些信息,我们就可以在新建包的时候指定这些依赖包,

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

更新package.xml和setup.py中的信息,

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

二、编写服务端节点

在ros2_ws/src/py_srvcli/py_srvcli目录下创建一个service_member_function.py文件,

from example_interfaces.srv import AddTwoInts # 数据结构类型

import rclpy # ROS2 python客户端
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():
    rclpy.init()

    minimal_service = MinimalService()

    rclpy.spin(minimal_service)

    rclpy.shutdown()


if __name__ == '__main__':
    main()

我们在创建包的时候已经指定了依赖,所以现在我们只需要给程序添加执行入口,

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

三、编写客户端节点

在ros2_ws/src/py_srvcli/py_srvcli目录下创建一个client_member_function.py文件,

import sys # 导入sys,以便能够通过sys.argv在命令行读取输入

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, a, b): # 定义怎么发送请求
        self.req.a = a
        self.req.b = b
        self.future = self.cli.call_async(self.req) # 异步请求(非阻塞)
        rclpy.spin_until_future_complete(self, self.future) # 等待服务端应答
        return self.future.result()


def main():
    rclpy.init()

    minimal_client = MinimalClientAsync()
    response = minimal_client.send_request(int(sys.argv[1]), int(sys.argv[2])) # 从命令行读取输入,并发送服务请求
    minimal_client.get_logger().info(
        'Result of add_two_ints: for %d + %d = %d' %
        (int(sys.argv[1]), int(sys.argv[2]), response.sum))

    minimal_client.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

添加程序执行入口,

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

四、编译与运行

检查依赖,

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

编译

$ colcon build --packages-select py_srvcli

启动服务端

$ source install/setup.bash
$ ros2 run py_srvcli service

在新的终端启动客户端

$ source install/setup.bash
$ ros2 run py_srvcli client 2 3
[INFO] [minimal_client_async]: Result of add_two_ints: for 2 + 3 = 5

回到服务端窗口,我们可以看到刚刚输出了以下提示信息,

[INFO] [minimal_service]: Incoming request
a: 2 b: 3
EOF