Lifecycle Bringup:按依赖激活,而不是 Timer 睡五秒
沿硬件→状态→感知→规划的边编排;失败要挡住下游假 active。

1. sleep(5) 在 CI 上必碎
launch 文件里 TimerAction(period=5.0) 「等硬件起来」——开发机 3 秒够、CI 慢盘 8 秒不够;或者 5 秒够但掩盖了驱动 configure 失败,导航带着假 TF 假 active。Bringup 不是 launch 副作用,是安全状态机:沿依赖边激活,边断了下游停在 inactive,而不是带着旧数据跑。
2. 依赖 DAG:四组边界
把启动画成 DAG,节点为点,lifecycle 依赖为边:
硬件组 → 状态组 → 感知组 → 导航组| 组 | 典型节点 | 就绪条件 |
|---|---|---|
| 硬件 | ros2_control hardware + controller_manager | configure + 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:
lifecycle_manager_navigation:
ros__parameters:
node_names:
- map_server
- amcl
- controller_server
- planner_server
- behavior_server
- bt_navigator
autostart: true
bond_timeout: 4.03. 事件驱动,不是魔法延时
lifecycle_manager 的「按组激活」往往够用——关键是组边界与依赖一致。上游 on_activate 成功后才触发下游 configure。若必须兼容非 lifecycle 第三方节点,用「话题就绪」当弱依赖:
# 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,诊断可见原因。
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 等 map | map_server lifecycle active | 无 map → 导航 inactive |
| sleep 等 control | hardware interface active | 错 URDF → 下游不激活 |
全部 sleep 删光却无条件,系统会快速随机失败——要把失败变成诊断,而不是让 CI 变红却无人懂。
6. bond 超时与整组 deactivate
lifecycle_manager 的 bond_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 换成了更隐蔽的等待。
相关
也可以看看
- ·3 分钟阅读
Lifecycle Node:把资源生命周期显式化,而不是构造即抢设备
configure/activate/cleanup 对称;关键路径用生命周期,工具节点保持简单。
- ·15 分钟阅读
ContentFilteredTopic:把过滤下推到中间件还是应用层
对比订阅端丢弃、应用层条件判断与 DDS 内容过滤的 CPU/带宽边界,说明表达式能力与发现时序限制;用高频率噪声话题与延迟尖峰验收过滤位置选择。
- ·16 分钟阅读
ROS 2 Action 取消语义:收到 cancel 不等于执行器已经停下
梳理 goal handle 状态机、取消回调与硬件停止之间的异步边界;用可中断执行循环、截止时间和终态发布构建契约并以注入验收。
johan's blog