ROS2 Service通信——同步请求响应的适用场景

ROS2 Service通信——同步请求响应的适用场景 面试被问Service和Topic有什么区别什么时候用Service我说Service是请求-响应模式Topic是发布订阅。面试官说那在机器人系统里控制电机用Service还是Topic为什么这个问题其实考的是你对两种通信模式适用场景的理解。今天把Service讲透顺便对比一下它和Topic的选择逻辑。Service是什么Service是ROS2中的同步请求-响应通信机制。一个Node作为服务端Server提供某种服务其他Node作为客户端Client发起请求服务端处理完返回结果。跟打电话一样你拨过去请求对方接听并说话处理你听到回复响应。通话结束连接断开。一个典型的Service调用流程# 客户端代码 future self.client.call_async(request) rclpy.spin_until_future_complete(self, future) response future.result() print(f结果: {response.success})客户端发送request等待response拿到结果后继续执行。这是同步的——客户端在等待期间会被阻塞除非你用异步方式处理。Service的类型定义Service的类型由两部分组成请求Request和响应Response。用.srv文件定义# AddTwoInts.srv int64 a int64 b --- int64 sum横线上面是请求的字段下面是响应的字段。这个例子很简单客户端发两个数服务端返回它们的和。在机器人项目中常见的Service类型有std_srvs/SetBool——设置某个开关比如启动/停止传感器。请求是一个bool值响应是成功与否。nav_msgs/LoadMap——加载地图。请求是地图文件路径响应是加载结果。std_srvs/Trigger——触发某个动作。请求为空响应是成功与否和消息。你也可以自定义Service类型根据具体需求来设计请求和响应的字段。创建一个Service服务端代码from example_interfaces.srv import AddTwoInts class CalculatorNode(Node): def __init__(self): super().__init__(calculator) self.srv self.create_service( AddTwoInts, add_two_ints, self.add_callback) def add_callback(self, request, response): response.sum request.a request.b self.get_logger().info( f收到请求: {request.a} {request.b}) return response客户端代码class ClientNode(Node): def __init__(self): super().__init__(client) self.client self.create_client( AddTwoInts, add_two_ints) def send_request(self, a, b): request AddTwoInts.Request() request.a a request.b b self.future self.client.call_async(request) return self.future服务端通过create_service注册一个回调函数每当有客户端发来请求就调用这个回调。客户端通过call_async发送请求返回一个Future对象等Future完成就能拿到结果。Service vs Topic怎么选这是面试的重点。用Topic的场景数据持续产生、多个接收者、不需要返回值。传感器数据流激光雷达、IMU、里程计都是典型的Topic应用。控制指令cmd_vel也是Topic因为控制指令是持续发送的不需要返回值。用Service的场景一次性请求、需要返回值、调用频率不高。比如加载地图、查询系统状态、触发标定流程、获取当前位姿。这些操作不是持续进行的而且客户端需要等待结果才能继续下一步。一个判断标准如果调用方需要等待结果才能继续执行用Service。如果调用方只是单向发送数据、不关心结果用Topic。还有一个容易混淆的点不要用Service来控制高频动作。比如你想用Service来控制电机转速每次调用都要等响应返回才能发下一次延迟太高。这种情况应该用Topic持续发布速度指令。Service的局限性Service有一个很大的限制它是阻塞的。客户端发出请求后如果服务端处理很慢或者挂了客户端就会一直等下去。在机器人系统中这可能导致严重问题。假设你的导航Node调用一个Service来获取地图但地图服务挂了。导航Node就卡在那里整个导航系统瘫痪。解决办法有几个。第一设置超时时间。call_async返回的Future可以加超时处理超时了就报错而不是无限等待。if rclpy.spin_until_future_complete( self, future, timeout_sec5.0): response future.result() else: self.get_logger().error(Service调用超时)第二用异步方式处理。不要在回调里阻塞等待用Future的回调函数来处理结果。第三对于关键服务加一个健康检查机制。客户端定期检测服务端是否存活挂了就切换到备用方案。多客户端问题一个Service可以有多个客户端同时调用。ROS2会保证每次只有一个请求被处理单线程执行器情况下。如果多个客户端同时发请求后面的请求会排队等待。如果你的Service处理很慢比如一个图像处理服务多个客户端排队会导致延迟累积。解决方案是用回调组Callback Group或者多线程执行器让Service可以并发处理多个请求。面试中怎么聊面试官问Service你可以说Service是ROS2中的请求-响应通信机制适合一次性调用、需要返回值的场景比如加载地图、查询状态。和Topic的区别在于Topic是持续的数据流Service是按需调用。Service的局限是阻塞特性需要设置超时防止卡死。高频控制场景应该用Topic而不是Service。Service通信的局限性Service适合请求-响应模式的场景比如查询地图、请求路径规划等。但它有明确的局限性一是同步阻塞调用方会一直等待直到收到响应二是不适合高频调用每次调用都有连接建立的开销三是Service不支持一对多通信。在实际项目中如果只是简单的参数查询用Parameter Service更方便如果需要持续的数据流应该用Topic而不是反复调用Service。补充一点ROS2的Service支持多种序列化格式默认的CDR格式在大多数场景下够用但如果对性能有极高要求可以考虑切换到更轻量的序列化方案。给你的建议自己写一个简单的Service和Client跑通整个流程。然后故意让Service的回调函数sleep几秒看看客户端等待的行为。再想想你的项目里哪些地方应该用Service但现在用了Topic或者反过来。这种思考在面试中非常加分说明你不只是会用还理解设计决策。如果你的Service需要长时间运行比如路径规划考虑用Action代替。Action支持中途取消和进度反馈比Service的阻塞等待灵活得多。ROS2的Action底层也是基于Service实现的但加了反馈通道和取消机制。上一篇第114篇 ROS2 QoS策略——可靠性/持久性/截止时间的工程选型下一篇预告第116篇 ROS2 Action通信——长时任务与三阶段反馈机制