ROS2 入门应用 请求和应答(Python)


1. 创建功能包

1. 创建功能包

《ROS2 入门应用 工作空间》中已创建和加载了ros2_ws工作空间
《ROS2 入门应用 元功能包(C++)》中已创建和加载了my_package功能包

那么就创建一个独立的py_srvcli功能包来做服务的请求和应答的功能

cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces

--dependencies可选参数将会自动在package.xmlCMakeLists.txt中添加必要的依赖行

依赖example_interfaces包,将会使用到example_interfacessrv文件,两位整数求和服务:

int64 a
int64 b
---
int64 sum

2. 创建源文件

进入py_srvcli功能包的py_srvcli文件夹

cd ~/ros2_ws/src/py_srvcli/py_srvcli

2.1. 服务端

新建service_member_function.py服务端源文件

nano service_member_function.py

复制以下内容到文件中:

from example_interfaces.srv import AddTwoInts

import rclpy
from rclpy.node import Node


class MinimalService(Node):
    '''
    服务端节点类
    '''
    def __init__(self):
        # 初始化节点
        super().__init__('minimal_service')

        # 初始化服务端,服务类型AddTwoInts,服务add_two_ints,回调函数add_two_ints_callback
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)

    def add_two_ints_callback(self, request, response):
        '''
        服务端回调函数
        '''
        # 从请求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():
    # 初始化ROS2
    rclpy.init()

    # 创建服务端节点
    minimal_service = MinimalService()

    # 运行服务端节点
    rclpy.spin(minimal_service)

    # 退出ROS2
    rclpy.shutdown()


if __name__ == '__main__':
    main()

2.2. 客户端

新建client_member_function.py客户端源文件

nano client_member_function.py

复制以下内容到文件中:

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')

        # 初始化客户端,服务类型AddTwoInts,服务add_two_ints
        self.cli = self.create_client(AddTwoInts, 'add_two_ints')

        # 搜索服务节点,间隔1s
        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():
    # 初始化ROS2
    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))

    # 销毁节点,退出ROS2
    minimal_client.destroy_node()
    rclpy.shutdown()


if __name__ == '__main__':
    main()

3. 添加依赖关系

创建功能包时,已通过--dependencies可选参数自动在package.xml中添加必要的依赖行

<depend>rclpy</depend>
<depend>example_interfaces</depend>

4. 添加入口点

打开 setup.py 文件
entry_points入口字段的console_scripts控制台脚本的括号中添加以下两行:

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

添加一个入口点py_srvcli功能包的service_member_function源文件的main函数,并命名为service
添加一个入口点py_srvcli功能包的client_member_function源文件的main函数,并命名为client


5. 编译和运行

进入工作空间根目录

cd ~/ros2_ws

在编译之前检查缺失的依赖项(可跳过):

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

编译:

colcon build --packages-select py_srvcli

打开一个新终端,运行服务端节点准备应答:

ros2 run py_srvcli service

打开一个新终端,运行客户端节点发送请求:

ros2 run py_srvcli client 1 2

# [INFO] [minimal_client_async]: Result of add_two_ints: for 1 + 2 = 3

谢谢