You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:17:18