行为树(Behavior Tree):从 ROS 机器人到 Unity 游戏 AI 的统一决策范式
行为树(Behavior Tree):从 ROS 机器人到 Unity 游戏 AI 的统一决策范式
作者按:本文是"每日一学"系列之一。今天我们聊一个在机器人和游戏 AI 领域都被广泛使用的决策架构——行为树(Behavior Tree, BT)。我们将以 ROS 机器人编程为主线,再延伸到 Unity 中的实践。
一、为什么需要行为树?
在早期的游戏 AI 和机器人决策系统中,有限状态机(Finite State Machine, FSM) 是最常用的方案。它简单直观:定义若干状态,再定义状态之间的跳转条件。
但 FSM 存在一个致命问题:状态数量增加时,跳转边会以平方级膨胀。当你的机器人需要"巡逻 → 发现目标 → 追踪 → 抓取 → 返航 → 充电"等一系列复杂行为时,FSM 会变成一张难以维护的"意大利面图"。
行为树则用树形结构 + 组合节点优雅地解决了这个问题,具备三大优势:
- 模块化:每个节点是独立的行为单元,可复用、可组合。
- 可读性强:树形结构天然适合可视化编辑与调试。
- 响应式(Reactive):每个 tick 都重新评估,能实时响应环境变化。
二、行为树的核心概念
2.1 节点类型
行为树的节点主要分为四大类:
| 节点类型 | 作用 | 典型代表 |
|---|---|---|
| 控制节点(Control) | 控制子节点的执行流程 | Sequence、Selector、Parallel |
| 装饰节点(Decorator) | 修饰单个子节点的行为 | Inverter、Retry、Timeout |
| 条件节点(Condition) | 判断某种条件 | IsBatteryLow、IsObstacleAhead |
| 动作节点(Action) | 执行具体行为 | MoveTo、Grasp、Speak |
2.2 三种状态返回值
每个节点在被 tick 时会返回以下三种状态之一:
SUCCESS:执行成功FAILURE:执行失败RUNNING:仍在执行中
2.3 两个最重要的控制节点
Sequence(顺序节点):相当于逻辑"AND",从左到右依次执行子节点,遇到 FAILURE 立即返回失败,全部成功才返回成功。
Selector / Fallback(选择节点):相当于逻辑"OR",从左到右依次执行子节点,遇到 SUCCESS 立即返回成功,全部失败才返回失败。
三、ROS 中的行为树:BehaviorTree.CPP 与 Nav2
在 ROS 2 生态中,最主流的行为树库是 BehaviorTree.CPP,它也是 Nav2(导航栈) 的核心调度引擎。Nav2 中的路径规划、避障恢复、目标重试等行为,全部由行为树编排。
3.1 一个机器人巡逻的 XML 示例
BehaviorTree.CPP 支持用 XML 描述行为树:
<root main_tree_to_execute="MainTree">
<BehaviorTree ID="MainTree">
<Fallback name="root">
<Sequence name="LowBatteryRecovery">
<IsBatteryLow/>
<MoveTo goal="charging_station"/>
<Charge/>
</Sequence>
<Sequence name="Patrol">
<MoveTo goal="waypoint_A"/>
<MoveTo goal="waypoint_B"/>
<MoveTo goal="waypoint_C"/>
</Sequence>
</Fallback>
</BehaviorTree>
</root>
这棵树的逻辑非常清晰:优先检查电量,低电量则回去充电;否则按 A→B→C 巡逻。
3.2 自定义动作节点(C++)
#include "behaviortree_cpp/bt_factory.h"
using namespace BT;
class MoveTo : public SyncActionNode {
public:
MoveTo(const std::string& name, const NodeConfig& config)
: SyncActionNode(name, config) {}
static PortsList providedPorts() {
return { InputPort<std::string>("goal") };
}
NodeStatus tick() override {
std::string goal;
if (!getInput("goal", goal)) {
return NodeStatus::FAILURE;
}
// 这里调用 ROS 2 的 Action Client,发送导航目标
ROS_INFO("Moving to: %s", goal.c_str());
// ... 省略实际导航调用 ...
return NodeStatus::SUCCESS;
}
};
int main(int argc, char** argv) {
BehaviorTreeFactory factory;
factory.registerNodeType<MoveTo>("MoveTo");
auto tree = factory.createTreeFromFile("./patrol.xml");
tree.tickWhileRunning();
return 0;
}
3.3 Nav2 中的实战应用
Nav2 的默认导航行为树(navigate_to_pose_w_replanning_and_recovery.xml)包含:
- 主流程:周期性重新规划 → 跟随路径
- 恢复行为:当导航失败时,依次尝试清除局部代价地图 → 清除全局代价地图 → 原地旋转 → 后退
这种"主行为 + 恢复行为"的模式正是行为树的经典应用:用 Fallback 把"成功路径"和"恢复策略"组合在一起。
3.4 调试利器:Groot2
Groot2 是 BehaviorTree.CPP 配套的可视化编辑器,可以拖拽设计行为树,并在运行时实时高亮当前 tick 的节点,对调试复杂机器人行为极其有用。
四、Unity 中的行为树:游戏 AI 的另一面
行为树在游戏行业的应用其实更早,育碧、Bungie 等大厂在《光环 2》《刺客信条》等作品中都广泛使用。
4.1 Unity 常用方案
- Behavior Designer(Asset Store 付费):功能最全,支持可视化编辑、变量共享、协程动作。
- NPBehave(开源):基于代码声明式构建,轻量级。
- Unity Behavior(官方包,2024 起):Unity 官方推出的 muse 行为图,逐渐成为新标准。
4.2 NPBehave 代码示例
using NPBehave;
public class EnemyAI : MonoBehaviour {
private Root behaviorTree;
void Start() {
behaviorTree = new Root(
new Selector(
// 分支1:看到玩家就追击攻击
new Sequence(
new Condition(() => CanSeePlayer()),
new Action(() => ChasePlayer()),
new Condition(() => InAttackRange()),
new Action(() => Attack())
),
// 分支2:默认巡逻
new Sequence(
new Action(() => PatrolNextWaypoint()),
new Wait(2.0f)
)
)
);
behaviorTree.Start();
}
}
4.3 ROS vs Unity:同一思想的两种语境
| 维度 | ROS 机器人 | Unity 游戏 |
|---|---|---|
| 典型动作 | 导航、抓取、传感器读取 | 追击、攻击、寻路 |
| Tick 频率 | 通常 10–50 Hz | 与帧率绑定(60+ Hz) |
| 执行模型 | 异步 Action(长耗时) | 协程 / 帧驱动 |
| 黑板共享 | ROS 参数 / Topic | ScriptableObject / 内置 Blackboard |
| 调试工具 | Groot2 | Behavior Designer Inspector |
值得注意的是,Unity Robotics Hub 项目把 ROS 和 Unity 连接起来,可以用 Unity 做机器人仿真,再用 ROS 端的行为树驱动决策——两个世界正在融合。
五、行为树的进阶话题
掌握基础后,可以继续学习以下几个进阶方向:
- 黑板(Blackboard):节点之间通过共享的键值存储传递数据,避免硬耦合。
- 并行节点(Parallel):同时执行多个子节点,在"边走边说话"这类场景中很有用。
- 响应式 vs 一次性:是每个 tick 都重新评估条件,还是仅在状态切换时评估?
- 行为树 + 效用函数(Utility AI):用打分机制选择最优分支,比纯优先级更智能。
- 行为树 + LLM:2024 年起,已经有研究用大模型动态生成行为树,让机器人具备"听人话办事"的能力。
六、今日小结
行为树本质上是一种用树形结构组织"决策—执行"流程的范式。它在 ROS 机器人(Nav2、BehaviorTree.CPP)和 Unity 游戏 AI 中都是事实标准,因为它同时满足了模块化、可视化、可响应这三大需求。
给你的一个动手作业 🛠️:
用 BehaviorTree.CPP(或 NPBehave)实现一个"扫地机器人"行为树:在电量充足时清扫 → 检测到障碍则绕行 → 电量低于 20% 自动返航充电。先画 XML / 树图,再用代码实现,体验"先设计后编码"的流程。
明天见,继续我们的"每日一学"!🚀
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)