ROS2 Python动作服务器使用Rate对象引发阻塞问题咨询
ROS2动作服务器中Rate对象使用问题解答
核心结论
不建议在动作服务器的execute_callback回调函数内直接使用create_rate()创建的Rate对象,否则会因阻塞节点主循环导致时钟更新停滞,进而引发rate.sleep()无限阻塞。
问题原因
ROS2的Rate对象依赖节点的时钟机制和回调队列正常运转:
execute_callback本身运行在动作服务器的回调线程中,若在此回调内调用rate.sleep(),会占用该线程并阻塞节点的主循环(spin/spin_once)。- 节点主循环被阻塞后,无法处理时钟更新消息(尤其是使用仿真时间时),
Rate的睡眠逻辑会因无法获取有效时钟信号而陷入无限等待。 - 而
time.sleep()是系统级睡眠,不依赖ROS2的时钟和回调机制,因此能正常执行。
解决方案
方案1:使用time.sleep()(适用于系统时间场景)
如果你的节点使用真实系统时间,直接替换rate.sleep()为time.sleep(step_t)即可,代码逻辑无需大幅修改:
# 替换原rate.sleep()为 time.sleep(step_t)
方案2:独立线程执行动作逻辑(适用于仿真时间/需ROS时钟同步场景)
若需依赖ROS2时钟(比如仿真环境),需将动作执行逻辑放到独立线程中,让execute_callback尽快返回,保证节点主循环正常运转:
import threading from rclpy.action import ActionServer, GoalResponse # 其他必要导入... class LinearControlServer(Node): def __init__(self): super().__init__('linear_control_server') self._action_server = ActionServer( self, LinearControl, 'linear_control', self.execute_callback) self._execution_thread = None def execute_callback(self, goal_handle): print("Received new goal") # 启动独立线程处理动作执行 self._execution_thread = threading.Thread( target=self._run_goal_execution, args=(goal_handle,) ) self._execution_thread.start() # 立即接受目标并返回,不阻塞回调 return GoalResponse.ACCEPT def _run_goal_execution(self, goal_handle): curr_pos = goal_handle.request.initial_position goal_pos = goal_handle.request.goal_position vel = goal_handle.request.linear_velocity dist = math.fabs(curr_pos - goal_pos) rate = self.create_rate(50) step_t = 1.0/50.0 feedback_msg = LinearControl.Feedback() while dist > 1e-2: print("dist: ", dist) print("curr_pos: ", curr_pos) curr_pos += vel * step_t dist = math.fabs(curr_pos - goal_pos) feedback_msg.distance = dist goal_handle.publish_feedback(feedback_msg) rate.sleep() print("Motion done") goal_handle.succeed()
注意事项
- 使用仿真时间时,必须确保节点主循环能持续处理
/clock话题消息,否则任何依赖ROS时钟的组件都会失效。 - 若使用独立线程,需注意线程安全问题,比如避免多个线程同时修改节点内的共享变量。
内容的提问来源于stack exchange,提问作者Jonathan
相关产品推荐
相关产品推荐

