3. 服务通信实现

任务目标

本案例实现一个ROS2加法服务:服务端节点接收两个整数请求,返回其和;客户端节点发送请求并等待响应,验证ROS2服务通信的同步请求-响应机制。

环境准备

需要已安装ROS2(Humble或更新版本),并创建好工作空间。本案例使用Python编写,无需额外依赖包。

实现步骤

在功能包中创建两个Python文件:add_service.py(服务端)和add_client.py(客户端)。服务端使用rclpy创建服务,客户端使用rclpy创建客户端并同步调用服务。

关键完整代码

# add_service.py
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddService(Node):
    def __init__(self):
        super().__init__('add_service')
        self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback)
        self.get_logger().info('服务端已启动,等待请求...')

    def add_callback(self, request, response):
        response.sum = request.a + request.b
        self.get_logger().info(f'收到请求: {request.a} + {request.b} = {response.sum}')
        return response

def main(args=None):
    rclpy.init(args=args)
    node = AddService()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()
# add_client.py
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts

class AddClient(Node):
    def __init__(self):
        super().__init__('add_client')
        self.cli = self.create_client(AddTwoInts, 'add_two_ints')
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.get_logger().info('等待服务端启动...')
        self.req = AddTwoInts.Request()

    def send_request(self, a, b):
        self.req.a = a
        self.req.b = b
        future = self.cli.call_async(self.req)
        rclpy.spin_until_future_complete(self, future)
        if future.result() is not None:
            return future.result().sum
        else:
            self.get_logger().error('服务调用失败')
            return None

def main(args=None):
    rclpy.init(args=args)
    client = AddClient()
    result = client.send_request(3, 5)
    if result is not None:
        client.get_logger().info(f'调用结果: 3 + 5 = {result}')
    client.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

编译与运行

  1. 将上述两个文件放入功能包的scripts目录,并赋予执行权限:chmod +x scripts/add_service.py scripts/add_client.py
  2. 在setup.py的entry_points中添加:
entry_points={
    'console_scripts': [
        'add_service = scripts.add_service:main',
        'add_client = scripts.add_client:main',
    ],
},
  1. 编译并source环境:
cd ~/ros2_ws
colcon build --packages-select your_package_name
source install/setup.bash

验证方法

打开两个终端,分别运行以下命令:

# 终端1
ros2 run your_package_name add_service

# 终端2
ros2 run your_package_name add_client

通过条件:

  • 终端1显示“收到请求: 3 + 5 = 8”
  • 终端2显示“调用结果: 3 + 5 = 8”
  • 预期输出为8,与代码逻辑严格一致

也可使用命令行工具验证服务通信:

ros2 service list  # 应看到 /add_two_ints
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 10, b: 20}"

预期返回sum: 30。

常见问题

  • 找不到服务:确认服务端已启动且服务名一致,使用ros2 service list检查。
  • 客户端阻塞:确保服务端正在运行,客户端wait_for_service会等待服务出现。
  • 编译错误:检查setup.py中scripts路径是否正确,以及是否已安装example_interfaces包。