ROS2 服务通信
服务概念
1. 什么是服务
在 ROS 2 中,**服务(Service)**是一种同步式请求—响应通信机制。它适合处理"调用一次,返回一次结果"的任务。
例如:
- 请求当前两个数的和;
- 请求机器人执行一次固定操作;
- 请求返回当前某项状态信息。
与话题通信不同,服务通信不是持续广播,而是:
- 客户端发起请求;
- 服务端接收请求并处理;
- 服务端返回结果;
- 客户端获得响应。
text
客户端发送请求 → 服务端处理 → 返回响应2. 服务与话题的区别
话题(Topic)
- 适合连续发送数据
- 是异步通信
- 一般用于传感器数据、状态流等持续信息交换
服务(Service)
- 适合一次请求、一次返回
- 更像函数调用
- 一般用于命令触发、状态查询、参数计算等任务
3. 服务端与客户端的关系
在 ROS 2 服务通信中:
- 服务端(Service Server):负责接收请求并返回响应;
- 客户端(Client):负责发送请求并等待结果。
说明示例
- 客户端发送两个整数 a=2, b=3;
- 服务端执行加法运算;
- 服务端返回结果 sum=5;
- 客户端打印结果。
4. 服务通信的基本构成
一个 ROS 2 Python 服务系统通常包括以下内容:
- 导入 rclpy 与 Node;
- 导入服务接口类型,例如 example_interfaces.srv.AddTwoInts;
- 在服务端节点中调用
create_service(); - 在客户端节点中调用
create_client(); - 服务端编写请求处理回调函数;
- 客户端构造请求并发送;
- 运行节点并验证服务响应。
学习内容六:服务通信实践
(一)创建实验功能包
bash
ros2 pkg create --build-type ament_python learn_service --dependencies rclpy example_interfaces这里使用 example_interfaces 中的 AddTwoInts 服务接口。
这个服务接口的客户端的数据是两个 int64 的数,分别为 a 和 b,服务端的数据是 int64 的 sum,结构如下:
srv
int64 a
int64 b
---
int64 sum(二)编写客户端节点
在功能包下新建文件 service_client.py:
python
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from example_interfaces.srv import AddTwoInts # 自定义的服务接口
class AddClient(Node):
def __init__(self, node_name, service_name):
super().__init__(node_name)
self.client_ = self.create_client(AddTwoInts,service_name) # 创建服务客户端对象(服务接口类型,服务名)
while not self.client_.wait_for_service(timeout_sec=1.0):# 循环等待服务器端成功启动
self.get_logger().warn('service not available, waiting again...')
self.request_ = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self, a, b): # 创建一个发送服务请求的函数
self.request_.a = a
self.request_.b = b
self.future = self.client_.call_async(self.request_) # 发送请求数据
return self.future
def main(args = None):
rclpy.init(args=args)
client_node = AddClient('service_adder_client',service_name='add_two_ints') # 创建ROS2节点对象并进行初始化
a = 10
b = 20
future = client_node.send_request(a, b) # 发送服务请求,比如说求解10+20
rclpy.spin_until_future_complete(client_node,future) # 阻塞当前线程,直到某个 Future 对象完成(即收到服务响应、动作结果等)。
if future.result() is not None:
response = future.result() # future.result() 获取服务端返回的结果
client_node.get_logger().info(f'第一个加数为:{a},第二个加数为{b},加和的结果为: {response.sum}') # response.sum是返回的数据的变量名是sum
else:
client_node.get_logger().error('服务请求失败')
client_node.destroy_node()
rclpy.shutdown()(三)编写服务端节点
在功能包下新建文件 service_server.py:
python
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from example_interfaces.srv import AddTwoInts # 自定义的服务接口
class adderServer(Node):
def __init__(self, node_name, service_name):
super().__init__(node_name)
self.server_ = self.create_service(AddTwoInts,service_name, self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_callback(self, request:AddTwoInts.Request, response:AddTwoInts.Response): # 创建回调函数,执行收到请求后对数据的处理
response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
self.get_logger().info(f'收到请求,计算{request.a} + {request.b}')
return response # 反馈应答信息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
server_node = adderServer("service_adder_server",service_name='add_two_ints') # 创建ROS2节点对象并进行初始化
rclpy.spin(server_node) # 循环等待ROS2退出
server_node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口(四)修改 setup.py
python
entry_points={
'console_scripts': [
'service_adder_client = learn_service.service_client:main',
'service_adder_server = learn_service.service_server:main',
],
},(五)编译功能包
返回工作空间根目录:
bash
colcon build
source install/setup.bash(六)运行服务端与客户端节点
1. 运行服务端节点
打开第一个终端,到工作空间根目录:
bash
source install/setup.bash
ros2 run learn_service service_adder_server服务端启动后会一直运行,等待客户端请求。
2. 运行客户端节点
打开第二个终端,到工作空间根目录:
bash
source install/setup.bash
ros2 run learn_service service_adder_client这表明客户端成功发送了请求,服务端成功进行了处理,并返回了结果。可以看出服务端一直在运行。
使用命令行工具验证服务通信
1. 查看当前服务列表
bash
ros2 service list2. 查看服务类型
bash
ros2 service type /add_two_ints3. 直接通过命令行调用服务
本质是通过命令行模拟了一个客户端:
bash
# 格式 ros2 service call 服务名 数据接口 "{字典形式表示的请求数据}"
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 4, b: 5}"服务通信成功的条件
要实现服务通信,至少需要满足以下条件:
说明必须满足的条件
- 客户端和服务端的服务名一致;
- 客户端和服务端的服务类型一致;
- 服务端已经处于运行状态;
- 客户端能够成功连接服务。