文章目录
- 0. 概述
- 1. 设计方案
- 1.1 状态机图 (FSM)
- 1.2 行为树图 (BT)
- 1.3 整体架构图
- 1.4 其它
- 2. 代码实现
- 2.1 代码结构
- 2.2 CMakeLists.txt
- 2.3 FSM 实现 src/fsm.hpp
- 2.4 行为树实现 src/behavior_tree.hpp
- 2.5 主程序 src/main.cpp
- 2.6 代码解释
- 3. 结果
0. 概述
用纯 C++ 实现一个简单的状态机和行为树示例,不依赖外部库。
结合有限状态机和行为树,设计一个简单的门控制逻辑。该逻辑不仅展示了 FSM 和 BT 的结合应用,还引入随机性和条件判断。
进一步阅读: 使用 TinyFSM 和 BehaviorTree.CPP 构建状态机与行为树示例
本文示例代码: https://gitee.com/liudegui/simple-fsm-bt-example
1. 设计方案
设计方案的图示表示,包括状态机图、行为树图以及整体架构图。
1.1 状态机图 (FSM)
状态机图展示了门的不同状态以及状态之间的转换。
状态机(FSM) 将用于管理门的不同状态,并定义相应的状态转换逻辑。定义以下状态和事件:
-
状态
LOCKED
(锁定)UNLOCKED
(解锁)CLOSED
(关闭)OPENED
(打开)HALF_OPEN
(半开)JAMMED
(卡住)
-
事件
lock
:将门锁定unlock
:将门解锁open
:将门打开close
:将门关闭halfOpen
:将门半开jam
:将门卡住unjam
:将门解卡
每个状态都有相应的处理方法,确保在状态转换时执行相应的操作
1.2 行为树图 (BT)
行为树图展示了行为树的结构和各个行为节点。
行为树(BT) 将用于定义门的行为序列,包含多个行为节点,每个节点对应一个具体的操作。包含以下行为节点:
- 行为节点
UnlockAction
:尝试解锁门OpenAction
:尝试打开门HalfOpenAction
:尝试将门半开JamAction
:尝试将门卡住UnjamAction
:尝试将门解卡LockAction
:尝试锁定门
每个行为节点在执行之前会检查当前门的状态,以确保操作的逻辑性。行为树中的每个步骤都有一定的随机性(50% 的概率)以模拟现实中行为的不确定性。
1.3 整体架构图
整体架构图展示了 FSM 和 BT 如何结合在一起工作。
1.4 其它
-
整体架构图:
-
主程序调用行为树,行为树顺序执行各个行为节点。
-
每个行为节点根据当前状态与状态机(DoorFSM)进行交互,执行相应的状态转换。
-
状态机管理门的状态,处理各种事件,并在状态之间进行转换。
-
-
输出信息:
为了让系统的行为更具可读性,我们在每个行为节点中添加详细的输出信息,说明当前正在尝试执行的操作。例如:Step 0: Attempting to unlock the door...
Step 1: Attempting to open the door...
Skipping step 1 due to random choice.
这些图示展示了 FSM 和 BT 的结合应用,能够更直观地理解整个系统的设计和工作流程。
2. 代码实现
2.1 代码结构
.
├── CMakeLists.txt
├── src
│ ├── main.cpp
│ ├── fsm.hpp
│ └── behavior_tree.hpp
2.2 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(SimpleFSM_BT_DoorExample)
set(CMAKE_CXX_STANDARD 17)
add_executable(SimpleFSM_BT_DoorExample
src/main.cpp
)
2.3 FSM 实现 src/fsm.hpp
#ifndef FSM_HPP
#define FSM_HPP
#include <iostream>
class DoorFSM {
public:
enum State { LOCKED, UNLOCKED, CLOSED, OPENED, HALF_OPEN, JAMMED };
DoorFSM() : state(LOCKED) {
}
void lock() {
if (state != LOCKED) {
state = LOCKED;
std::cout << "Door is now locked." << std::endl;
}
}
void unlock() {
if (state == LOCKED) {
state = UNLOCKED;
std::cout << "Door is now unlocked." << std::endl;
}
}
void open() {
if (state == CLOSED || state == UNLOCKED || state == HALF_OPEN) {
state = OPENED;
std::cout << "Door is now opened." << std::endl;
}
}
void close() {
if (state == OPENED || state == HALF_OPEN) {
state = CLOSED;
std::cout << "Door is now closed." << std::endl;
}
}
void halfOpen() {
if (state == CLOSED || state == OPENED) {
state = HALF_OPEN;
std::cout << "Door is now half open." << std::endl;
}
}
void jam() {
if (state != JAMMED) {
state = JAMMED;
std::cout << "Door is now jammed." << std::endl;
}
}
void unjam() {
if (state == JAMMED) {
state = CLOSED;
std::cout << "Door is now unjammed and closed." << std::endl;
}
}
State getState() const {
return state;
}
private:
State state;
};
#endif // FSM_HPP
2.4 行为树实现 src/behavior_tree.hpp
#ifndef BEHAVIOR_TREE_HPP
#define BEHAVIOR_TREE_HPP
#include <cstdlib>
#include <ctime>
#include <functional>
#include <vector>
#include "fsm.hpp"
class BehaviorTree {
public:
using Action = std::function<void()>;
explicit BehaviorTree(DoorFSM& fsm) : fsm(fsm), currentStep(0) {
actions.push_back([this, &fsm]() {
std::cout << "Step 0: Attempting to unlock the door..." << std::endl;
if (fsm.getState() == DoorFSM::LOCKED) {
fsm.unlock();
}
});
actions.push_back([this, &fsm]() {
std::cout << "Step 1: Attempting to open the door..." << std::endl;
if (fsm.getState() == DoorFSM::UNLOCKED || fsm.getState() == DoorFSM::CLOSED) {
fsm.open();
}
});
actions.push_back([this, &fsm]() {
std::cout << "Step 2: Attempting to half open the door..." << std::endl;
if (fsm.getState() == DoorFSM::OPENED) {
fsm.halfOpen();
}
});
actions.push_back([this, &fsm]() {
std::cout << "Step 3: Attempting to jam the door..." << std::endl;
if (fsm.getState() == DoorFSM::HALF_OPEN) {
fsm.jam();
}
});
actions.push_back([this, &fsm]() {
std::cout << "Step 4: Attempting to unjam the door..." << std::endl;
if (fsm.getState() == DoorFSM::JAMMED) {
fsm.unjam();
}
});
actions.push_back([this, &fsm]() {
std::cout << "Step 5: Attempting to lock the door..." << std::endl;
if (fsm.getState() == DoorFSM::CLOSED) {
fsm.lock();
}
});
std::srand(std::time(nullptr)); // 初始化随机数生成器
}
void run() {
while (currentStep < actions.size()) {
if (std::rand() % 2 == 0) { // 50%的概率执行当前步骤
actions[currentStep]();
} else {
std::cout << "Skipping step " << currentStep << " due to random choice." << std::endl;
}
currentStep++;
}
currentStep = 0;
}
private:
DoorFSM& fsm;
std::vector<Action> actions;
int currentStep;
};
#endif // BEHAVIOR_TREE_HPP
2.5 主程序 src/main.cpp
#include "behavior_tree.hpp"
#include "fsm.hpp"
#include <thread>
int main() {
DoorFSM fsm;
BehaviorTree bt(fsm);
while (true) {
bt.run(); // 执行带有随机性的行为树
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 0;
}
2.6 代码解释
-
状态机(FSM):定义了
DoorFSM
类,包含六个状态(LOCKED
,UNLOCKED
,CLOSED
,OPENED
,HALF_OPEN
,JAMMED
)和相应的方法。 -
行为树(BT):行为树在每个动作节点中捕获
fsm
对象的引用,确保在执行 lambda 函数时可以访问fsm
对象。通过[this, &fsm]
捕获this
指针和fsm
引用。 -
条件判断和随机性:行为树在每一步操作之前,先检查当前状态是否允许执行该操作,同时保留了随机性。
-
跳过步骤:如果当前步骤被随机选择跳过,则输出
Skipping step X due to random choice.
,其中X
是当前步骤的编号。
通过这种方式,我们可以确保行为树中的每个动作节点都能正确访问 fsm
对象,并根据当前状态执行相应的操作。再次尝试编译和运行程序,这些修改应该可以解决捕获问题。
3. 结果
行为树会在执行每一步之前检查当前状态,确保操作的逻辑一致性。这使得系统在引入随机性的同时,保持状态转换的合理性。现在运行程序时,会看到更加合理的状态变化输出:
Step 0: Attempting to unlock the door...
Door is now unlocked.
Step 1: Attempting to open the door...
Door is now opened.
Step 2: Attempting to half open the door...
Door is now half open.
Step 3: Attempting to jam the door...
Door is now jammed.
Step 4: Attempting to unjam the door...
Door is now unjammed and closed.
Step 5: Attempting to lock the door...
Door is now locked.
Skipping step 0 due to random choice.
Step 1: Attempting to open the door...
Step 2: Attempting to half open the door...
Step 3: Attempting to jam the door...
Skipping step 4 due to random choice.
Step 5: Attempting to lock the door...
Step 0: Attempting to unlock the door...
Door is now unlocked.
Skipping step 1 due to random choice.
Step 2: Attempting to half open the door...
Step 3: Attempting to jam the door...
Step 4: Attempting to unjam the door...
Step 5: Attempting to lock the door...