ROS 2 Humble Nav2 生命周期教程:启动流程、状态监控、超时诊断与自愈设计
适用版本:ROS 2 Humble、Nav2 1.x。其他发行版的总体思路相同,但参数和源码实现可能不同。
适合读者:已经能启动 Nav2,但对
configure、activate、get_state、change_state、bond、respawn以及偶发启动失败还不够清楚的开发者。
1. 学完本文能解决什么问题?
阅读本文后,你应该能够回答:
- Nav2 为什么要先
configure,再activate? - Lifecycle Manager 与各个 Nav2 节点之间谁是客户端、谁是服务端?
get_state和change_state分别做什么?autostart、use_respawn和bond有什么区别?- 为什么节点已经切换成功,管理器仍可能认为启动失败?
- Service 使用
RELIABLE,为什么仍会出现 response timeout? - 遇到生命周期启动失败时,如何判断真正失败的环节?
- 如何设计比"单次失败就中止"更可靠的状态对账和有限重试?
本文先建立通用知识,再给出标准排障步骤,最后用一次真实测试验证结论。
2. 先分清三层:进程、生命周期、系统编排
很多误解来自把下面三层混在一起。
2.1 进程层
负责创建和销毁 Linux 进程,例如:
text
ros2 launch
systemd
supervisor
Docker/Kubernetes
自研节点管理器
进程崩溃后是否重新创建,属于这一层。
2.2 生命周期层
负责一个已经存在的节点当前处于什么业务状态:
text
unconfigured
inactive
active
finalized
节点进程存在,不代表它已经能够导航。例如 controller_server 进程可能存在,但仍停留在 unconfigured。
2.3 系统编排层
负责:
- 按依赖顺序启动多个节点;
- 判断整套系统是否健康;
- 超时后重试、清理或重启;
- 统一提供
starting/running/failed状态。
Nav2 的 lifecycle_manager 属于生命周期编排器,但它不是完整的进程守护系统。
text
外层进程管理器 / ros2 launch
│ 创建进程、进程退出后决定是否重启
▼
Nav2 lifecycle_manager
│ configure / activate / deactivate / cleanup
▼
controller、planner、bt_navigator 等 Lifecycle Node
3. ROS 2 Lifecycle 状态机
Lifecycle Node 的常见主状态如下:
| 状态 | ID | 含义 |
|---|---|---|
unconfigured |
1 | 节点进程存在,但业务资源尚未配置 |
inactive |
2 | 配置完成,但尚未正式工作 |
active |
3 | 已激活,可以参与导航 |
finalized |
4 | 生命周期已结束 |
最常见的启动路径:
text
unconfigured
│ configure
▼
configuring
│ on_configure() 成功
▼
inactive
│ activate
▼
activating
│ on_activate() 成功
▼
active
停止和清理路径:
text
active
│ deactivate
▼
inactive
│ cleanup
▼
unconfigured
3.1 configure 通常做什么?
不同节点实现不同,常见工作包括:
- 读取和校验参数;
- 加载控制器、规划器、行为树等插件;
- 创建 costmap、TF buffer、订阅者和发布者;
- 申请内存和其他资源;
- 检查依赖资源。
configure 成功后进入 inactive。此时资源基本准备好,但节点还没有正式执行导航业务。
3.2 activate 通常做什么?
常见工作包括:
- 激活 Lifecycle Publisher;
- 启动定时器或业务循环;
- 开始接受 Action 请求;
- 建立与生命周期管理器的 bond。
只有进入 active,节点才算真正可用。
4. Nav2 的典型生命周期启动顺序
Nav2 官方 Humble 导航栈通常包含:
text
controller_server
smoother_server
planner_server
behavior_server
bt_navigator
waypoint_follower
velocity_smoother
如果没有启动 velocity_smoother,受管节点可能只有前 6 个。
Lifecycle Manager 会执行两轮串行操作。
第一轮:依次 configure
text
controller_server → inactive
smoother_server → inactive
planner_server → inactive
behavior_server → inactive
bt_navigator → inactive
waypoint_follower → inactive
第二轮:依次 activate
text
controller_server → active
smoother_server → active
planner_server → active
behavior_server → active
bt_navigator → active
waypoint_follower → active
任意一步失败,默认会停止当前 bringup,不再继续后面的节点。
这里的顺序由 Lifecycle Manager 的 node_names 参数决定,不是节点之间互相协商出来的。
5. 客户端和服务端如何判断?
每个 Lifecycle Node 都有两个关键 Service:
text
/<node_name>/get_state
/<node_name>/change_state
调用它们时:
text
lifecycle_manager:客户端
Lifecycle Node:服务端
例如:
text
lifecycle_manager
│ request: configure
▼
/controller_server/change_state
│
│ controller_server 执行 on_configure()
│
└── response ──→ lifecycle_manager
但调用下面这个 Service 时:
text
/lifecycle_manager_navigation/manage_nodes
角色会变成:
text
RViz、UI或外部程序:客户端
lifecycle_manager:服务端
所以"谁是客户端"必须结合当前 Service 判断。
6. get_state 与 change_state
6.1 get_state:只读查询
bash
ros2 lifecycle get /controller_server
可能输出:
text
unconfigured [1]
inactive [2]
active [3]
get_state 没有业务副作用,因此适合有限重试。
6.2 change_state:有副作用的状态迁移
bash
ros2 lifecycle set /controller_server configure
ros2 lifecycle set /controller_server activate
ros2 lifecycle set /controller_server deactivate
ros2 lifecycle set /controller_server cleanup
change_state 会触发节点执行实际回调,例如 on_configure() 或 on_activate(),因此不能在结果未知时无脑重复发送。
6.3 Nav2 Manager 的典型单节点逻辑
Nav2 Humble 的核心逻辑可以简化为:
text
发送一次 change_state
↓
等待 response
↓
调用一次 get_state 验证目标状态
↓
成功后处理下一个节点
等待 Service 出现可能有循环,但针对一次迁移,请求本身不是默认连续发送三次。
7. 常用检查命令
7.1 列出 Lifecycle Node
bash
ros2 lifecycle nodes
7.2 查询节点状态
bash
ros2 lifecycle get /controller_server
ros2 lifecycle get /planner_server
ros2 lifecycle get /bt_navigator
批量检查示例:
bash
for node in \
controller_server \
smoother_server \
planner_server \
behavior_server \
bt_navigator \
waypoint_follower
do
printf '%s: ' "$node"
ros2 lifecycle get "/$node"
done
7.3 查看可用迁移
bash
ros2 lifecycle list /controller_server
7.4 查看生命周期 Service
bash
ros2 service list -t | grep -E 'get_state|change_state|manage_nodes'
7.5 调用 Nav2 整组启动
ManageLifecycleNodes 命令值:
text
STARTUP = 0
PAUSE = 1
RESUME = 2
RESET = 3
SHUTDOWN = 4
启动整组:
bash
ros2 service call \
/lifecycle_manager_navigation/manage_nodes \
nav2_msgs/srv/ManageLifecycleNodes \
'{command: 0}'
调试单节点时可以使用 ros2 lifecycle set;正常启动整套 Nav2 时优先通过 Lifecycle Manager,避免绕过编排顺序。
8. 如何持续观察真实状态迁移?
只看 get_state 是状态快照,可能错过中间过程。Lifecycle Node 还会发布迁移事件:
bash
ros2 topic echo \
/controller_server/transition_event \
lifecycle_msgs/msg/TransitionEvent
重点观察:
text
start_state.label
transition.label
goal_state.label
正常 configure:
text
unconfigured → configuring
configuring → inactive
正常 activate:
text
inactive → activating
activating → active
排查偶发故障时,应在重启前就开始订阅,而不是等错误发生后才订阅。
9. 两类超时必须分开
9.1 服务端发送 response 超时
典型日志:
text
failed to send response to /controller_server/change_state (timeout):
client will not receive response
含义是:
text
服务端已经收到request
→ 回调可能已经执行
→ 准备发送response
→ DDS发送/端点匹配阶段超时
→ 客户端不会收到这次response
这条日志本身不能证明生命周期回调失败。
9.2 客户端等待 response 超时
典型日志:
text
service client: async_send_request failed
含义是客户端发出请求后,等待 future 完成失败或超过调用期限。
两者可能形成因果链:
text
服务端response发送失败
↓
客户端一直收不到response
↓
客户端等待超时
10. Service 默认是 Reliable,不是 Best Effort
ROS 2 Service 默认 QoS:
text
History: KEEP_LAST
Depth: 10
Reliability: RELIABLE
Durability: VOLATILE
Lifecycle 的 /get_state、/change_state 使用 Service 默认 QoS,因此通常也是 RELIABLE。
Reliable 不等于调用绝对成功
Reliable 主要保证:在 Writer 和 Reader 已经正确匹配、资源可用且实体仍然存在时,对历史样本进行确认和必要的重传。
它不保证:
- DDS 端点一定及时完成发现和匹配;
- 写入队列永远有资源;
- 对端在写入时仍然存在;
- 写调用永远不返回超时;
- Service 具备事务级"恰好一次"语义。
所以出现 response timeout,不等于 QoS 被改成了 Best Effort。
11. Fast DDS 的 max_blocking_time
Fast DDS 的 Reliable DataWriter 有一个 max_blocking_time,常见默认值约为:
text
100 ms
它限制的不是:
text
on_configure()最多执行100ms
客户端最多等待response 100ms
而是:
服务端回调完成后,当前 response 写入 DDS 时最多允许阻塞多久。
在 rmw_fastrtps 的 Service response 路径中,服务端会根据请求携带的 GUID,确认 response writer 是否已经与对应客户端 response reader 匹配。
text
客户端发送request
↓
节点执行Lifecycle回调
↓
节点状态切换成功
↓
服务端准备发送response
↓
等待对应response reader匹配
↓
超过max_blocking_time
↓
send_response返回timeout
因此:
text
configure执行2秒,response立即写入成功
→ 正常
configure执行10ms,response写入等待超过100ms
→ response发送失败
准确说法是"response 没有成功写入 DDS",不是"已经发送的 Reliable 数据包超过100ms后被删除"。
为什么需要这个上限?
如果客户端已经退出、Reader一直无法匹配或者写队列长期堵塞,没有上限就可能让服务端 executor 线程永久卡在 send_response()。
这会影响其他 Service、订阅回调、定时器甚至控制循环。因此 Fast DDS 选择在超过上限后返回错误,避免应用线程无限阻塞。
对于低频但关键的 Lifecycle Service,适当延长该时间可能降低概率,但这属于缓解措施,不能代替上层状态对账。
12. response丢失后,如何判断节点到底成功没有?
遇到:
text
failed to send response to /xxx/change_state (timeout)
应立即查询:
bash
ros2 lifecycle get /xxx
并结合此前持续订阅的:
text
/xxx/transition_event
判断表:
| 观察结果 | 说明 |
|---|---|
| 已进入目标状态 | 回调成功,response传输失败 |
| 仍在原状态 | 回调可能未执行或迁移被拒绝 |
停在 configuring/activating |
回调可能耗时过长或卡住 |
进入 errorprocessing |
回调执行过程中发生真实错误 |
| 节点消失 | 进程退出、被清理或DDS尚未重新发现 |
为了减少单次查询本身偶发失败,可以短间隔查询多次:
bash
for i in 1 2 3 4 5; do
date '+%H:%M:%S.%3N'
timeout 3 ros2 lifecycle get /controller_server
sleep 0.3
done
13. 为什么 get_state 容易恢复,change_state 更麻烦?
13.1 get_state 是幂等只读操作
text
第一次查询失败
→ 再查一次
→ 不会改变节点状态
因此健康检查器通常可以不断轮询,直到成功或耗尽总超时。
13.2 change_state 有副作用
假设发送 configure 后没有收到 response:
text
情况A:节点没有执行,仍是unconfigured
情况B:节点已经执行成功,当前是inactive
直接重发 configure 时:
- 情况A下是合理重试;
- 情况B下会对
inactive节点请求非法迁移。
因此应该先对账:
text
change_state response异常
↓
多次get_state查询真实状态
↓
已经到目标状态 → 按成功处理
仍在原状态 → 有条件地重发
处于过渡状态 → 等待后复查
进入error → cleanup/reset/restart
14. autostart、bond 和 use_respawn
| 机制 | 负责什么 | 不负责什么 |
|---|---|---|
autostart |
第一次启动后自动 configure、activate | 不负责进程崩溃重启 |
bond |
检测 active 后的节点失联 | 不创建新进程 |
use_respawn |
子进程退出后由 launch重新创建 | 不处理进程仍活着时的Service超时 |
14.1 autostart
autostart=true 表示 Lifecycle Manager 启动后自动执行整组 startup。
如果为 false,节点进程仍会被 launch 创建,但生命周期通常停在 unconfigured,需要外部调用 manage_nodes。
14.2 bond
节点成功激活后,会与 Lifecycle Manager 建立 bond 心跳:
text
lifecycle_manager ← 心跳 → controller_server
← 心跳 → planner_server
← 心跳 → bt_navigator
某个节点失联超过 bond_timeout 后,管理器会认为系统不再完整,并采取降级或关闭动作。
查看 bond:
bash
ros2 topic info /bond
查看管理器参数:
bash
ros2 param get /lifecycle_manager_navigation bond_timeout
ros2 param get /lifecycle_manager_navigation attempt_respawn_reconnection
ros2 param get /lifecycle_manager_navigation bond_respawn_max_duration
14.3 use_respawn
节点在 launch 文件里通常类似:
python
Node(
package='nav2_controller',
executable='controller_server',
respawn=use_respawn,
respawn_delay=2.0,
)
只有进程真正退出时才会触发 respawn。
下面这种情况不会触发:
text
节点进程仍然存在
→ change_state已经执行
→ response没有送达
所以打开 use_respawn 不能解决 Lifecycle Service response timeout。
15. Lifecycle不是节点互相监测、互相拉起
Nav2 使用集中式管理:
text
lifecycle_manager
│
┌─────────┬───────┼────────┬─────────┐
▼ ▼ ▼ ▼ ▼
controller smoother planner behavior bt_navigator ...
这些节点不会互相创建进程。
一个完整的高可用系统通常需要组合:
text
Lifecycle状态机
+ Lifecycle Manager编排
+ Bond失联检测
+ Launch respawn或外部进程守护
+ 状态对账与有限重试
+ 整体健康检查
Lifecycle主要提供状态一致性和安全顺序,不等于 systemd 或 Kubernetes 式的完整自愈系统。
16. 通用排障教程
下面这套步骤可以用于大多数 Nav2 Lifecycle 启动问题。
步骤1:记录运行环境
bash
echo "$ROS_DISTRO"
echo "$RMW_IMPLEMENTATION"
echo "$ROS_DOMAIN_ID"
printenv FASTRTPS_DEFAULT_PROFILES_FILE
ros2 pkg prefix nav2_lifecycle_manager
步骤2:确认管理节点名单和参数
bash
ros2 param get /lifecycle_manager_navigation node_names
ros2 param get /lifecycle_manager_navigation autostart
ros2 param get /lifecycle_manager_navigation bond_timeout
不要假设顺序,一定要读取运行时参数。
步骤3:重启前启动迁移事件监听
bash
ros2 topic echo \
/controller_server/transition_event \
lifecycle_msgs/msg/TransitionEvent
如果问题随机发生,建议同时监听全部受管节点,并给输出加时间戳。
步骤4:保存服务端和管理器日志
重点搜索:
text
failed to send response
async_send_request failed
Failed to change state
Failed to bring up
Aborting bringup
步骤5:错误发生后立即查询真实状态
bash
ros2 lifecycle get /发生异常的节点
连续查询多次,区分真实状态与单次查询超时。
步骤6:确认是否继续处理下一个节点
例如 controller_server/change_state 超时后:
- 如果没有出现
smoother_server: unconfigured → configuring,说明管理器卡在 controller; - 如果 smoother 已经开始配置,此警告可能来自另一个客户端或旧请求。
步骤7:区分进程故障和通信故障
bash
ps -ef | grep controller_server
ros2 node list | grep controller_server
ros2 lifecycle get /controller_server
text
进程存在 + 节点存在 + 状态正确
→ 更像Service response或管理器认知问题
进程不存在
→ 真实进程崩溃或被外层清理
步骤8:做可控A/B测试
按优先级可以测试:
- 降低同时启动的节点数量;
- 延长启动间隔;
- 暂停额外的高频
get_state健康轮询; - 比较 Fast DDS 与 Cyclone DDS;
- 调整 Fast DDS Writer 的阻塞时间;
- 检查 SHM、网卡白名单、发现服务器和残留进程。
每次只改变一个变量,否则无法判断真正影响因素。
17. 更可靠的状态对账设计
17.1 get_state策略
text
单次超时不判死
→ 重试3~5次
→ 使用200ms、500ms、1s等退避间隔
→ 连续失败才判定节点不可达
17.2 change_state策略
text
发送一次change_state
↓
正常收到response → get_state验证
↓
response异常 → 不立即重发
↓
多次get_state对账
| 实际状态 | 建议 |
|---|---|
| 已到目标状态 | 视为成功,继续后续节点 |
| 仍是原状态 | 在次数限制内重发一次 |
| 正在过渡 | 等待后复查 |
errorprocessing |
cleanup/reset或重启 |
| 节点消失 | 交给进程守护层处理 |
17.3 什么时候才清理整套Nav2?
建议在以下情况才清理整栈:
- 对账和有限重试全部耗尽;
- 节点进入不可恢复错误状态;
- 关键进程退出且无法 respawn;
- TF、地图、插件等前置依赖持续不可用;
- 多个节点状态不一致且无法安全 reset。
不要因为一次只读查询超时就立即杀掉整栈。
18. 实测案例:22轮连续重启
下面保留一个真实案例,用于说明如何用证据判断,而不是把整篇教程写成个人日志。
18.1 测试结果
| 类型 | 轮数 |
|---|---|
| 完全正常 | 15 |
| 有超时警告但最终成功 | 5 |
| Nav2整体启动失败 | 2 |
一次失败由 smoother_server/get_state 客户端异常触发;另一次发生在:
text
/controller_server/change_state
18.2 关键时间线
text
15:23:56.737 controller_server: unconfigured → configuring
15:23:57.604 controller_server: configuring → inactive
15:23:57.704 change_state response发送timeout
随后独立查询:
text
controller_server: inactive [2]
结论:
text
状态切换成功
→ response写入阶段超时
→ manager没有收到确认
→ 没有继续配置smoother_server
→ 外层120秒健康检查到期
→ 整套Nav2被清理
从 inactive 到 response timeout 约 100 ms,也与 Fast DDS 默认 max_blocking_time 的实现路径吻合。
这个案例说明:
failed to send response不能直接等价为"生命周期回调失败"。必须结合 transition event 和实际状态判断。
19. 常见误区
误区1:进程存在就代表导航启动成功
错误。进程可能仍是 unconfigured 或 inactive。
误区2:change_state response超时说明状态没有改变
错误。节点可能已经到达目标状态,只是response没有送达。
误区3:Reliable保证消息绝对必达
错误。端点发现、资源限制、实体生命周期和写入超时仍可能导致API失败。
误区4:打开use_respawn可以解决Service超时
错误。respawn只在进程退出后触发。
误区5:bond会把崩溃节点重新创建
错误。bond负责检测失联,创建新进程需要launch respawn或外部守护器。
误区6:change_state超时后直接重发最可靠
错误。第一次可能已经生效,应先查询真实状态。
误区7:只增大所有超时时间就能根治
错误。延长超时只能降低部分竞态概率,还可能扩大阻塞影响。上层仍需要状态对账。
20. 排障检查清单
text
[ ] ROS_DISTRO、RMW_IMPLEMENTATION、ROS_DOMAIN_ID是否一致
[ ] lifecycle_manager实际管理哪些node_names
[ ] autostart、bond_timeout、use_respawn运行值是什么
[ ] 所有Lifecycle Service是否已经出现
[ ] transition_event最后到达哪个状态
[ ] 服务端是否出现failed to send response
[ ] 客户端是否出现async_send_request failed
[ ] 异常后ros2 lifecycle get返回什么
[ ] 下一个节点是否开始configure/activate
[ ] 节点进程是否仍然存在
[ ] 是否有外层健康检查不断调用get_state
[ ] 是否有旧进程、旧DDS端点或快速重复launch
[ ] 是否只在某一种RMW或Fast DDS配置下出现
[ ] 是否通过单变量A/B测试验证过假设
21. 总结
Nav2 Lifecycle 的核心目标是:
text
让多个导航节点按照确定顺序进入一致状态,
在结果不确定时停止后续操作,避免残缺系统继续运行。
它不是完整的进程自愈系统。高可用还需要:
text
进程守护
+ bond失联检测
+ get_state有限重试
+ change_state结果对账
+ 整栈健康检查和恢复策略
排查生命周期问题时,最重要的原则是把三件事分开:
text
节点是否执行了回调
节点真实状态是否改变
response是否成功到达管理器
只有把日志、transition_event 和 get_state 三类证据结合起来,才能判断究竟是业务回调失败、进程崩溃,还是"操作成功但确认消息没有送达"。
参考资料
-
ROS 2 Managed Nodes / Lifecycle Design
-
ROS 2 Humble Quality of Service Settings
https://docs.ros.org/en/humble/Concepts/Intermediate/About-Quality-of-Service-Settings.html
-
Nav2 Lifecycle Manager
https://github.com/ros-navigation/navigation2/tree/humble/nav2_lifecycle_manager
-
Fast DDS Reliability QoS Policy
-
ROS 2 Service response timeout讨论