服务(Service)采用请求/响应(Request/Response)模式,是一种同步的、一对一的通信方式。客户端发送请求,服务端处理后返回响应。
| 特性 | Topic | Service |
|---|---|---|
| 通信模式 | 发布/订阅 | 请求/响应 |
| 方向 | 单向 | 双向 |
| 参与者 | 多对多 | 一对一 |
| 实时性 | 异步,持续流 | 同步,触发式 |
| 数据丢失 | 可能丢失 | 保证响应 |
| 适用场景 | 传感器数据流 | 开关、查询、配置 |
| 典型例子 | 激光扫描数据 | 触发拍照、获取状态 |
#!/usr/bin/env python3
"""机器人控制服务端 - 提供运动控制接口"""
import rclpy
from rclpy.node import Node
from example_interfaces.srv import SetBool, Trigger
from std_srvs.srv import SetBool as StdSetBool
import math
class RobotControlServer(Node):
"""机器人控制服务端"""
def __init__(self):
super().__init__('robot_control_server')
# 机器人状态
self.is_enabled = False
self.battery_level = 100.0
self.total_distance = 0.0
# 创建服务1:启用/禁用机器人
self.enable_srv = self.create_service(
SetBool, 'enable_robot', self.enable_callback
)
# 创建服务2:触发紧急停止
self.estop_srv = self.create_service(
Trigger, 'emergency_stop', self.estop_callback
)
# 创建服务3:获取状态
# 使用自定义回调
self.status_srv = self.create_service(
Trigger, 'get_status', self.status_callback
)
# 模拟电池消耗
self.battery_timer = self.create_timer(1.0, self.update_battery)
self.get_logger().info('🤖 机器人控制服务端已启动')
self.get_logger().info(' 可用服务: enable_robot, emergency_stop, get_status')
def enable_callback(self, request, response):
"""启用/禁用回调"""
self.is_enabled = request.data
response.success = True
if request.data:
response.message = '✅ 机器人已启用'
self.get_logger().info('机器人已启用')
else:
response.message = '⏸️ 机器人已禁用'
self.get_logger().info('机器人已禁用')
return response
def estop_callback(self, request, response):
"""紧急停止回调"""
self.is_enabled = False
response.success = True
response.message = '🛑 紧急停止已触发!机器人已禁用'
self.get_logger().error('紧急停止触发!')
return response
def status_callback(self, request, response):
"""状态查询回调"""
state = '启用' if self.is_enabled else '禁用'
response.success = True
response.message = (
f'状态: {state} | '
f'电量: {self.battery_level:.1f}% | '
f'总里程: {self.total_distance:.2f}m'
)
self.get_logger().info(f'状态查询: {response.message}')
return response
def update_battery(self):
"""模拟电池消耗"""
if self.is_enabled:
self.battery_level = max(0, self.battery_level - 0.5)
self.total_distance += 0.3
if self.battery_level < 10 and self.battery_level % 5 < 0.5:
self.get_logger().warn(f'⚠️ 电量低: {self.battery_level:.0f}%')
def main(args=None):
rclpy.init(args=args)
node = RobotControlServer()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""机器人控制客户端 - 调用控制服务"""
import sys
import rclpy
from rclpy.node import Node
from example_interfaces.srv import SetBool
from std_srvs.srv import Trigger
class RobotControlClient(Node):
"""机器人控制客户端"""
def __init__(self):
super().__init__('robot_control_client')
# 创建客户端
self.enable_client = self.create_client(SetBool, 'enable_robot')
self.estop_client = self.create_client(Trigger, 'emergency_stop')
self.status_client = self.create_client(Trigger, 'get_status')
# 等待服务可用
self.get_logger().info('等待服务可用...')
self.enable_client.wait_for_service(timeout_sec=5.0)
self.status_client.wait_for_service(timeout_sec=5.0)
self.get_logger().info('服务已就绪!')
def send_enable_request(self, enable: bool):
"""发送启用/禁用请求"""
request = SetBool.Request()
request.data = enable
self.get_logger().info(f'请求: {"启用" if enable else "禁用"}机器人...')
future = self.enable_client.call_async(request)
# 同步等待结果
rclpy.spin_until_future_complete(self, future)
return future.result()
def send_estop_request(self):
"""发送紧急停止请求"""
request = Trigger.Request()
self.get_logger().warn('请求: 紧急停止!')
future = self.estop_client.call_async(request)
rclpy.spin_until_future_complete(self, future)
return future.result()
def send_status_request(self):
"""查询状态"""
request = Trigger.Request()
future = self.status_client.call_async(request)
rclpy.spin_until_future_complete(self, future)
return future.result()
def main(args=None):
rclpy.init(args=args)
client = RobotControlClient()
# 示例操作序列
print('\n===== 机器人控制演示 =====')
# 1. 查询初始状态
result = client.send_status_request()
print(f'初始状态: {result.message}')
# 2. 启用机器人
result = client.send_enable_request(True)
print(f'启用结果: {result.message}')
# 3. 再次查询状态
result = client.send_status_request()
print(f'当前状态: {result.message}')
# 4. 禁用机器人
result = client.send_enable_request(False)
print(f'禁用结果: {result.message}')
# 5. 测试紧急停止
result = client.send_estop_request()
print(f'急停结果: {result.message}')
client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
// robot_control_server_cpp.cpp
#include "rclcpp/rclcpp.hpp"
#include "example_interfaces/srv/set_bool.hpp"
#include "std_srvs/srv/trigger.hpp"
#include <memory>
#include <string>
class RobotControlServerCpp : public rclcpp::Node {
public:
RobotControlServerCpp() : Node("robot_control_server_cpp"),
is_enabled_(false), battery_(100.0), distance_(0.0) {
// 创建服务
enable_srv_ = this->create_service<example_interfaces::srv::SetBool>(
"enable_robot_cpp",
std::bind(&RobotControlServerCpp::enable_callback, this,
std::placeholders::_1, std::placeholders::_2));
status_srv_ = this->create_service<std_srvs::srv::Trigger>(
"get_status_cpp",
std::bind(&RobotControlServerCpp::status_callback, this,
std::placeholders::_1, std::placeholders::_2));
RCLCPP_INFO(this->get_logger(), "C++机器人控制服务端已启动");
}
private:
void enable_callback(
const std::shared_ptr<example_interfaces::srv::SetBool::Request> request,
std::shared_ptr<example_interfaces::srv::SetBool::Response> response) {
is_enabled_ = request->data;
response->success = true;
response->message = request->data ? "✅ 机器人已启用" : "⏸️ 机器人已禁用";
RCLCPP_INFO(this->get_logger(), "%s", response->message.c_str());
}
void status_callback(
const std::shared_ptr<std_srvs::srv::Trigger::Request>,
std::shared_ptr<std_srvs::srv::Trigger::Response> response) {
std::string state = is_enabled_ ? "启用" : "禁用";
response->success = true;
response->message = "状态: " + state +
" | 电量: " + std::to_string(static_cast<int>(battery_)) + "%" +
" | 里程: " + std::to_string(distance_).substr(0, 5) + "m";
RCLCPP_INFO(this->get_logger(), "状态查询: %s", response->message.c_str());
}
rclcpp::Service<example_interfaces::srv::SetBool>::SharedPtr enable_srv_;
rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr status_srv_;
bool is_enabled_;
double battery_;
double distance_;
};
int main(int argc, char** argv) {
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<RobotControlServerCpp>());
rclcpp::shutdown();
return 0;
}
# my_interfaces/srv/ComputePath.srv
# Request(请求)
---
# Response(响应)
geometry_msgs/PoseStamped[] waypoints
float64 total_distance
float64 estimated_time
bool success
string message
# my_interfaces/srv/GetSensorData.srv
# Request
string sensor_name
int64 num_samples
---
# Response
float64[] samples
float64 mean
float64 std_dev
float64 min_val
float64 max_val
bool success
string error_message
--- 分隔。每部分定义字段,使用基本类型或已有消息类型。构建后会自动生成Python和C++的类。
#!/usr/bin/env python3
"""异步服务调用示例"""
import rclpy
from rclpy.node import Node
from example_interfaces.srv import SetBool
class AsyncServiceClient(Node):
def __init__(self):
super().__init__('async_service_client')
self.client = self.create_client(SetBool, 'enable_robot')
self.client.wait_for_service()
def call_async_with_callback(self, enable: bool):
"""异步调用,通过回调处理结果"""
request = SetBool.Request()
request.data = enable
future = self.client.call_async(request)
future.add_done_callback(self.response_callback)
def response_callback(self, future):
"""异步响应回调"""
try:
response = future.result()
self.get_logger().info(
f'响应: success={response.success}, '
f'message="{response.message}"'
)
except Exception as e:
self.get_logger().error(f'服务调用失败: {e}')
def main(args=None):
rclpy.init(args=args)
client = AsyncServiceClient()
# 异步调用不会阻塞
client.call_async_with_callback(True)
client.get_logger().info('请求已发送,等待响应...')
# 继续spin等待回调
rclpy.spin(client)
client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
| 命令 | 功能 | 示例 |
|---|---|---|
ros2 service list | 列出所有服务 | ros2 service list |
ros2 service type | 查看服务类型 | ros2 service type /enable_robot |
ros2 service call | 调用服务 | ros2 service call /enable_robot example_interfaces/srv/SetBool "{data: true}" |
ros2 service find | 查找服务类型 | ros2 service find example_interfaces/srv/SetBool |
ros2 interface show | 查看接口定义 | ros2 interface show example_interfaces/srv/SetBool |
创建一个计算器服务,支持加减乘除:
Calculator.srv:float64 a, float64 b, string operation → float64 result, bool success, string message创建一个图像捕获服务:
创建一个工作流:客户端先调用"初始化"服务,再调用"启动"服务,最后调用"停止"服务。每步依赖上一步的结果。
# 启动服务端
ros2 run my_pkg robot_control_server
# 在命令行调用服务
ros2 service call /enable_robot example_interfaces/srv/SetBool "{data: true}"
ros2 service call /get_status std_srvs/srv/Trigger "{}"
ros2 service call /emergency_stop std_srvs/srv/Trigger "{}"
经验值:+150 XP