返回专辑
·Johan·4 分钟阅读

Lifecycle Bringup:按依赖激活,而不是 Timer 睡五秒

沿硬件→状态→感知→规划的边编排;失败要挡住下游假 active。

Lifecycle Bringup:按依赖激活,而不是 Timer 睡五秒

1. sleep(5) 在 CI 上必碎

launch 文件里 TimerAction(period=5.0) 「等硬件起来」——开发机 3 秒够、CI 慢盘 8 秒不够;或者 5 秒够但掩盖了驱动 configure 失败,导航带着假 TF 假 active。Bringup 不是 launch 副作用,是安全状态机:沿依赖边激活,边断了下游停在 inactive,而不是带着旧数据跑。

2. 依赖 DAG:四组边界

把启动画成 DAG,节点为点,lifecycle 依赖为边:

text
硬件组 → 状态组 → 感知组 → 导航组
典型节点就绪条件
硬件ros2_control hardware + controller_managerconfigure + activate 成功
状态robot_state_publisher, static TF/robot_description + TF 树连通
感知lidar/camera driver, pointcloud/scan首帧数据 + stamp 合理
导航AMCL, costmap, planner, controller, bt_navigator上游 active + 地图加载

任何跨组「先启动再说」都应删除。Nav2 的 lifecycle_manager 维护节点列表,按序 configure → activate:

yaml
lifecycle_manager_navigation:
  ros__parameters:
    node_names:
      - map_server
      - amcl
      - controller_server
      - planner_server
      - behavior_server
      - bt_navigator
    autostart: true
    bond_timeout: 4.0

3. 事件驱动,不是魔法延时

lifecycle_manager 的「按组激活」往往够用——关键是组边界与依赖一致。上游 on_activate 成功后才触发下游 configure。若必须兼容非 lifecycle 第三方节点,用「话题就绪」当弱依赖:

python
# launch 中:等 /scan 首帧而非 sleep
from launch_ros.actions import Node
from launch.actions import RegisterEventHandler
from launch.event_handlers import OnProcessStart

并设超时失败(如 30 s),而不是无限等。超时失败应打进 /diagnostics,启动现场才知道卡在哪一组。第三方节点可用 OnMessageReceived 订阅 /robot_description/scan 首帧后再启动下游——弱依赖必须有超时与失败诊断,不能无限阻塞 launch。

4. 失败要挡住下游

硬件 configure 失败时,导航组不得假 active——否则 planner 用空 map、controller 用未标定 footprint 跑。测试:拔掉硬件插件或故意错配 URDF,观察下游停在 inactive,诊断可见原因。

bash
ros2 lifecycle get /controller_server    # 应为 inactive 或 unconfigured
ros2 lifecycle list /controller_server # 看合法 transition

热重启路径与冷启动一样要测:很多系统冷启动能起、热重启起不来,是 cleanup/activate 不对称或依赖假设「进程从零开始」。

5. 删掉 sleep 的替换步骤

每删一处 sleep,必须补一条依赖边或就绪条件 + 超时:

旧延时新条件验证
sleep 等 lidar/scan 首帧或 lifecycle active拔 lidar → 超时失败
sleep 等 mapmap_server lifecycle active无 map → 导航 inactive
sleep 等 controlhardware interface active错 URDF → 下游不激活

全部 sleep 删光却无条件,系统会快速随机失败——要把失败变成诊断,而不是让 CI 变红却无人懂。

6. bond 超时与整组 deactivate

lifecycle_managerbond_timeout 检测节点存活。节点 crash 后 manager 应触发整组 deactivate/cleanup,而不是留 zombie active。bond_timeout 过短误杀慢盘 CI;过长则 crash 后下游继续用 stale 数据。建议:开发机测 baseline,CI 慢盘 agent 加 50% 裕量,真机取较小值。室内典型 4–10 s。

7. launch 测试与 CI 集成

bringup 应用 launch_testing 做冒烟:断言 lifecycle 节点在 60 s 内全部 active,或明确失败在预期节点。CI 不应 sleep 等结果——用 WaitForTopics 或 lifecycle state 订阅。慢盘 agent 上跑同一测试,验证事件驱动不 flaky。热重启测试:active → deactivate → cleanup → configure → activate,全程下游不得提前 active。

8. ros2_control 与硬件组

硬件组不仅是 driver:controller_manager 加载控制器插件后,硬件接口才应 activate。顺序:robot_description 加载 → hardware interface configure → 控制器 spawn → activate。跳过 controller_manager 直接发 /cmd_vel,lifecycle 导航组即使 active 也会在真机上无响应。bringup DAG 要把 ros2_control 嵌进硬件组,不是平行另起。

joint_state_broadcaster 应在 diff_drive_controller 之前 spawn——否则 controller 读不到关节状态,activate 成功但 /odom 无输出。这类隐式依赖也要写进 DAG,不能假设「反正 eventually 会有数据」。

9. 验收与案例

  • 启动 DAG 文档化;每组有明确就绪条件。
  • 拔依赖(硬件/map/lidar):下游不得假 active;诊断指明卡在哪条边。
  • CI 跑冷启动与热重启;不得靠加长 sleep 蒙混。

某项目 bt_navigator 先 activate,AMCL 5 秒后仍 configure 失败——FollowPath 用空定位跑,撞墙。改为 lifecycle_manager 严格序后,AMCL 失败则导航组停在 inactive,启动日志一行定位根因。删一处 sleep,补一条边,胜过加十行 retry。

Bringup 是安全状态机的一部分。事件与状态,不做魔法延时。启动日志应能一行回答「卡在哪条边、哪个节点、何种 transition 失败」——否则只是把 sleep 换成了更隐蔽的等待。

← 全部文章

johan's blog