4. 行为树入门
在多机器人任务编排中,我们经常需要描述“先导航到目标点,再抓取物体,若失败则重试,若电量低则先回充”这类带有条件、顺序与恢复逻辑的任务。行为树(Behavior Tree,BT)正是为此类任务而生的结构化决策模型。它把复杂任务拆解为可复用的节点,通过树形组合表达执行顺序、条件分支与失败恢复,相比传统有限状态机,更适合表达层次化、可组合的机器人行为。
一、核心原理:从“状态跳转”到“树形组合”
行为树是一棵有向树,叶子节点是实际执行动作或判断条件的执行节点,内部节点是控制子节点执行顺序与返回结果的控制节点。每个节点执行后都会向父节点返回三种状态之一:
- SUCCESS(成功):节点完成目标,例如导航到达目标点。
- FAILURE(失败):节点未能完成目标,例如路径被完全阻塞。
- RUNNING(运行中):节点尚未结束,需要下一个 tick 继续执行,例如机器人正在移动。
行为树采用周期性 tick(滴答)驱动:每个控制周期从根节点开始,按树结构向下传播 tick,直到叶子节点返回状态,再逐层向上汇总。这种“自顶向下 tick、自底向上返回”的机制,使行为树天然支持异步、长时任务与中断恢复。
行为树 = 节点类型 + 返回状态 + tick 传播规则 + 组合逻辑
其中组合逻辑决定了“先做什么、失败后做什么、什么条件下切换分支”。
二、节点类型:控制节点与执行节点
行为树节点通常分为两大类。控制节点负责组织子节点,执行节点负责实际动作或条件判断。
| 节点类型 | 类别 | 执行语义 | 典型用途 |
|---|---|---|---|
| Sequence(顺序) | 控制节点 | 依次执行子节点,全部成功才返回 SUCCESS;任一失败立即返回 FAILURE | “导航→抓取→放置”固定流程 |
| Fallback / Selector(选择) | 控制节点 | 依次尝试子节点,任一成功即返回 SUCCESS;全部失败才返回 FAILURE | “主路径失败则走备用路径” |
| Parallel(并行) | 控制节点 | 同时 tick 多个子节点,按成功/失败阈值决定整体结果 | “边走边检测障碍物” |
| Decorator(装饰) | 控制节点 | 对单个子节点附加条件,如 Inverter、Retry、Timeout | “失败重试 3 次”“超时取消” |
| Action(动作) | 执行节点 | 执行具体动作,返回 SUCCESS / FAILURE / RUNNING | 导航、抓取、语音播报 |
| Condition(条件) | 执行节点 | 判断条件是否成立,返回 SUCCESS 或 FAILURE | “电量是否低于 20%” |
在 ROS2 中,常用 BehaviorTree.CPP 库实现行为树,节点通过 XML 描述,动作节点可封装为 ROS2 Action Client。例如一个简单的巡逻任务可以写成:
<root main_tree_to_execute="MainTree">
<BehaviorTree ID="MainTree">
<Fallback>
<Sequence>
<Condition ID="BatteryOK"/>
<Action ID="NavigateToGoal"/>
<Action ID="GraspObject"/>
</Sequence>
<Action ID="ReturnToCharge"/>
</Fallback>
</BehaviorTree>
</root>
这段 XML 表达的逻辑是:如果电量正常,则依次导航并抓取;否则直接回充。Fallback 节点保证了“主任务失败或条件不满足时,自动切换到回充分支”。
三、执行语义:tick、状态返回与恢复
行为树的执行语义有三个关键点。第一,tick 是周期性的,通常由 ROS2 定时器或执行器驱动,频率可为 10 Hz 或 100 Hz。第二,RUNNING 状态会保留执行现场,例如导航动作返回 RUNNING 后,下一次 tick 不会重新启动导航,而是继续等待其完成。第三,控制节点会记忆当前执行位置,Sequence 在某个子节点返回 RUNNING 后会记住该位置,下次 tick 从该子节点继续,而不是从头开始。
这种机制使得行为树非常适合长时任务。例如多机器人协同搬运中,机器人 A 等待机器人 B 到位,A 的等待节点返回 RUNNING,A 不会反复重新发起等待,而是持续检查 B 的状态,直到条件满足后继续执行后续动作。
RUNNING 不是失败,也不是成功,而是“还没做完”。
控制节点必须正确处理 RUNNING,否则会导致任务重复启动或状态丢失。
四、与有限状态机的对比
有限状态机(FSM)通过“状态 + 转移条件”描述行为,适合状态数量少、转移关系清晰的场景。行为树则通过“树形组合 + 返回状态”描述行为,更适合层次化、可复用、可动态调整的任务。两者对比如下:
| 对比维度 | 有限状态机 FSM | 行为树 BT |
|---|---|---|
| 组织方式 | 状态与转移边 | 树形节点组合 |
| 可复用性 | 状态难以跨任务复用 | 子树可封装复用 |
| 扩展性 | 新增状态易导致转移爆炸 | 新增分支对原树影响小 |
| 失败恢复 | 需显式设计恢复状态 | Fallback/Retry 天然支持 |
| 可读性 | 状态多时图复杂 | 层次清晰,接近任务描述 |
| 适用场景 | 简单状态切换、模式管理 | 复杂任务编排、多机器人协同 |
需要强调的是,行为树并非完全取代 FSM。在机器人系统中,底层模式切换(如手动/自动/急停)仍常用 FSM,而上层任务编排更适合行为树。两者可以结合:行为树的动作节点内部封装一个 FSM,从而兼顾底层稳定性与上层灵活性。
五、案例与常见误区
假设一个多机器人仓库场景:机器人需要“前往货架→识别货物→抓取→送往打包区”。用行为树可以这样组织:根节点为 Sequence,依次为 NavigateToShelf、DetectObject、GraspObject、NavigateToPack。其中 GraspObject 可加 Retry 装饰器,失败重试 3 次;DetectObject 可加 Timeout,超时则走 Fallback 切换到人工求助分支。
常见误区有三个。第一,把 RUNNING 当成 FAILURE,导致动作被反复重启。第二,在 Sequence 中放置条件节点却不理解其返回语义,条件失败会直接中断整个序列。第三,过度嵌套,树太深会降低可读性与调试效率,应通过子树封装和黑板(Blackboard)共享数据来简化结构。
总结来看,行为树通过节点类型、返回状态与 tick 传播规则,把复杂任务拆解为可组合、可复用、可恢复的树形结构。在多机器人行为树任务编排中,掌握 Sequence、Fallback、Decorator 与 Action/Condition 的语义,是后续实现协同任务、动态调度与故障恢复的基础。