跳到正文

Wiki

BehaviorTree.CPP v3.8.7

約 8 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

1.BehaviorTree.CPP v3.8.7

對應 BehaviorTree.CPP 3.8 官方文檔:About。

行為樹(Behavior Tree,簡稱 BT)是一種組織機械人任務及其切換的方式。 BehaviorTree.CPP 則是用於實現行為樹的 C++ 庫; 本教程的程序使用 3.8.7 驗證,概念資料按官方 3.8 系列文檔學習,官方教程入口在這裏。

已經學過 FSM 後,可以從一個問題開始理解它:當導航、充電、任務取消等需求不斷增加時,怎樣把決策邏輯組織得清楚,並複用已有能力?

本篇先説明行為樹解決什麼問題,以及它與 FSM 的關係。節點狀態、tick 傳播和四類節點將在下一篇“基本概念”集中介紹。


2.從一個簡單的 FSM 開始

假設移動機械人只有等待目標、執行導航、到達目標三個階段:

IDLE → 收到目标 → NAVIGATING → 导航成功 → ARRIVED
                     └────── 导航失败 → IDLE

用 C++ 風格偽代碼表示:

enum class RobotState
{
    IDLE,
    NAVIGATING,
    ARRIVED
};

RobotState current_state = RobotState::IDLE;

void Update()
{
    switch (current_state)
    {
        case RobotState::IDLE:
            if (HasNavigationGoal())
            {
                StartNavigation();
                current_state = RobotState::NAVIGATING;
            }
            break;

        case RobotState::NAVIGATING:
            if (NavigationSucceeded())
            {
                StopRobot();
                current_state = RobotState::ARRIVED;
            }
            else if (NavigationFailed())
            {
                current_state = RobotState::IDLE;
            }
            // 尚未结束时保留当前状态,下次 Update() 再检查
            break;

        case RobotState::ARRIVED:
            // 到达后的处理按具体任务补充
            break;
    }
}

外部週期性調用 Update()。啓動導航的接口快速返回,機械人移動期間,每次更新只檢查當前結果,不在函數里一直等待。

這種任務用 FSM 很容易描述:狀態明確,轉換條件也少。


3.複雜任務需要統一組織決策邏輯

任務增加後,同一條規則可能影響多個狀態:

新需求 可能影響的任務 需要決定什麼
電量不足 等待、導航、執行任務 是否暫停當前任務,前往充電
緊急停止請求 導航、充電、執行任務 是否中止當前操作並停止
定位丟失 導航、返回充電點 是否暫停移動、嘗試恢復定位
收到新目標或取消請求 導航 是否取消舊請求、切換任務
導航失敗 普通任務、返回充電點 是否重試、放棄或採用其他處理

例如,低電量發生在導航期間,可能需要先取消導航,再前往充電;發生在等待期間,則沒有正在執行的導航需要取消。

如果每個狀態都各自檢查電量、取消請求和定位,規則就容易分散。修改一條策略時,需要檢查多個狀態及其進入、退出邏輯。

FSM 可以通過公共事件處理、分層組織等方式管理這些需求。學習行為樹,是為了掌握另一種組織複雜任務的工具:用有層次的任務結構表達條件、優先級和執行關係。

3.1.區分底層能力與任務決策

機械人系統可以先按職責分成兩層:

任务决策层
    ├── 当前应该执行哪个任务?
    ├── 应该去普通目标还是充电点?
    └── 失败或条件变化后怎样处理?
            ↓ 调用
能力模块
    ├── Navigation       导航到指定目标
    ├── Localization     提供定位及其有效性
    ├── Battery          提供电量和充电信息
    ├── Sensors          提供环境数据
    └── Actuators        执行运动等指令

導航模塊主要解決“給定目標,如何到達”;任務決策層決定“現在是否導航、去哪裏、失敗怎麼辦”。行為樹適合組織這部分任務決策,具體功能仍由相應模塊實現。


4.用層次結構表達機械人任務

先用一個概念結構表達任務優先級:

RobotBehavior
├── EmergencyHandling     有紧急停止请求时,停止机器人
├── EnergyManagement      电量低时,处理充电
├── ExecuteMission        有任务时,执行导航等操作
└── WaitForTask           其余情况下,等待任务

這張圖描述的是預期策略,不是可以直接運行的行為樹。實際哪些條件會重新檢查、怎樣選擇分支、如何中止舊任務,需要由具體節點和執行規則實現。

任務可以繼續逐層展開:

RobotBehavior
├── EmergencyHandling
├── EnergyManagement
│   ├── CheckBattery
│   └── RechargeTask
│       ├── NavigateToChargingStation
│       └── StartCharging
└── ExecuteMission
    ├── CheckGoal
    └── NavigateToGoal

閲讀整體任務時,只看第一層就能知道主要職責; 研究充電流程時,再展開 RechargeTask。 小任務可以組成大任務,一整棵子樹也可以成為更大任務的一部分。

這裏仍只展示任務分解,具體節點類型和成功、失敗時的執行規則後面再補充。


5.模塊化、複用與組合怎樣落到代碼上

官方把節點比作積木:每個節點封裝一種檢查或操作,再把節點組合成任務。各個特點可以對應到具體的設計選擇:

特點 在移動機械人中的做法
模塊化 Modularity 電量檢查、導航請求、停止操作分別封裝
可複用性 Reusability 普通目標導航和返回充電點複用同一種導航節點實現,配置不同目標
可組合性 Composability 將檢查、導航、充電組合成完整充電任務
關注點分離 Separation of Concerns 導航模塊負責到達目標,行為樹負責決定何時導航和如何處理結果

例如 IsBatteryLow 只檢查電量,NavigateToGoal 負責請求導航,StartCharging 負責啓動充電。這樣可以分別測試和替換各項能力,也能在不同任務中重新組合。

可複用的是節點實現或子樹設計,不意味着多處任務必須共享同一個正在運行的節點對象。具體怎樣創建和配置實例,在後面的編程示例中學習。

行為樹的樹形圖也有助於閲讀工作流程。 不過,完整理解仍要結合每種節點的執行語義;僅憑連線無法判斷所有運行細節。


6.反應性來自持續驅動與重新判斷

機械人環境會在任務執行期間變化。例如:

开始导航:电量和定位正常,存在目标
    ↓
导航过程中:收到取消请求
    ↓
重新判断:取消当前导航,转入后续处理

反應性(Reactive)指系統能夠在任務尚未完成時,根據變化調整行為。

前面學習的非阻塞 FSM,通過週期執行 Update() 檢查事件和任務進度。

行為樹通過外部持續發起 tick,按照樹的規則推進任務:

執行方式 一次更新主要做什麼
FSM 的 Update() 根據當前狀態處理事件,決定是否轉移
行為樹的一次 tick 從根節點進入,按各節點的規則選擇並執行當前需要的節點

對於持續任務,每次調用應及時交還控制權,方便後續繼續決策。 導航系統可以在兩次更新之間繼續工作,決策層不必堵在一次調用中等待機械人到達。

行為樹能否及時響應條件變化,取決於驅動頻率、哪些條件被重新 tick,以及動作是否支持中止。 把條件節點放進樹裏,不代表它會自動持續監控;具體的響應式規則將在後續示例中學習。


7.狀態機FSM與行為樹BT可以配合使用

選擇組織方式時,可以先看問題的結構:

場景 常見做法
設備具有明確階段,如關閉、啓動、運行、故障 使用 FSM 表達階段及其轉換
任務包含多種條件、優先級和可複用子流程 使用行為樹組織任務
上層需要組合任務,某個底層模塊內部又有明確階段 上層使用行為樹,模塊內部保留 FSM

例如:

RobotBehavior              行为树:组织整体任务
└── NavigateToGoal         动作节点:调用导航模块
    └── NavigationModule   模块内部:用 FSM 管理运行阶段

這張圖表達軟件調用與組織關係。NavigationModule 是外部模塊,不是動作節點下面的行為樹子節點。

兩種工具都能組織任務決策,適用範圍也有重疊。 現有 FSM 結構清晰時,可以繼續使用;任務需要更多層次和組合時,再考慮引入行為樹。


8.v3.8.7 教程的學習順序

本篇作為導讀,後面按六個完整主題組織主線。階段 1 集中建立概念,階段 2 從最小程序逐步增加功能,統一使用 BehaviorTree.CPP 3.8.7、C++20 和 VSCode CMake Tools。

文檔 主要內容 所在階段
基本概念 節點、狀態、tick 傳播和四類節點 階段 1
控制與裝飾規則 順序、備選方案、重試、重複和超時 階段 1
第一個完整程序 先銜接概念,再從最小 demo 逐步增加條件和端口 階段 1 收尾,進入階段 2
節點之間傳數據 輸入輸出、自定義類型和外部 Blackboard 階段 2
持續任務與中止 持續導航、多次 tick、響應式檢查和取消 階段 2
組合實際任務 子樹映射、多 XML 文件和完整任務的成功、失敗路徑 階段 2 收尾

實踐按自己的完成情況推進:先運行當前版本,看到輸出並理解執行過程,再增加下一步功能。各篇預留了 VSCode 截圖位置,完整程序使用模擬移動機械人數據。

完成主線後,可以繼續 使用Groot1編輯與調試,學習圖形編輯、實時觀察和日誌回放。這篇屬於工具實踐,繼續使用相同的 BehaviorTree.CPP 3.8.7 工程。

新讀者也可以從 v4.10.0 教程 開始;已經完成本組實踐後,閲讀 v3 與 v4 區別及遷移,再學習 Groot2。