3. 服务通信
任务目标
本案例实现ROS2中最基础的服务通信(请求-响应模式):创建一个服务端节点,提供两个整数相加的服务;创建一个客户端节点,向服务端发送两个整数并接收求和结果。通过本案例掌握ROS2服务通信的完整流程:接口定义、服务端实现、客户端实现、编译配置与运行验证。
环境与依赖
- 已安装ROS2(Humble或Foxy版本均可,本案例以Humble为例)
- 已创建ROS2工作空间
~/ros2_ws/src - 需要
example_interfaces包(ROS2默认自带),其中包含AddTwoInts服务接口(int64 a + int64 b → int64 sum)
实现步骤
在 ~/ros2_ws/src 下创建功能包 service_demo,包含服务端节点 server_node 和客户端节点 client_node。两个节点均使用Python编写,通过 example_interfaces/srv/AddTwoInts 完成通信。
关键完整代码
文件1:service_demo/service_demo/server_node.py(服务端)
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class AddTwoIntsServer(Node):
def __init__(self):
super().__init__('add_two_ints_server')
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 = AddTwoIntsServer()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
文件2:service_demo/service_demo/client_node.py(客户端)
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class AddTwoIntsClient(Node):
def __init__(self):
super().__init__('add_two_ints_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 = AddTwoIntsClient()
# 发送请求:3 + 5
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()
文件3:service_demo/setup.py(入口配置,仅显示关键部分)
from setuptools import setup
import os
from glob import glob
package_name = 'service_demo'
setup(
name=package_name,
version='0.0.1',
packages=[package_name],
data_files=[
('share/ament_index/resource_index/packages', ['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
(os.path.join('share', package_name), glob('launch/*.launch.py'))
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='your_name',
maintainer_email='your_email@example.com',
description='ROS2 service communication demo',
license='Apache-2.0',
entry_points={
'console_scripts': [
'server_node = service_demo.server_node:main',
'client_node = service_demo.client_node:main',
],
},
)
编译与运行
# 1. 编译
cd ~/ros2_ws
colcon build --packages-select service_demo
source install/setup.bash
# 2. 运行服务端(终端1)
ros2 run service_demo server_node
# 3. 运行客户端(终端2,需先source环境)
source ~/ros2_ws/install/setup.bash
ros2 run service_demo client_node
验证方法与判定标准
- 服务端终端输出:启动后显示“服务端已启动,等待请求...”;收到请求后显示“收到请求: 3 + 5 = 8”。
- 客户端终端输出:显示“调用结果: 3 + 5 = 8”。
- 命令行验证:在第三个终端执行
ros2 service list应看到/add_two_ints;执行ros2 service type /add_two_ints应显示example_interfaces/srv/AddTwoInts。 - 判定通过条件:客户端输出结果为8,且服务端日志同步显示收到请求并完成计算,即证明请求-响应通信闭环成功。
常见问题
- 找不到
example_interfaces:执行sudo apt install ros-humble-example-interfaces安装。 - 客户端提示“等待服务端上线”后卡住:确认服务端已成功启动且服务名称一致(均为
add_two_ints)。 - 编译报错找不到包:确认
package.xml中已声明对example_interfaces和rclpy的依赖。