尽量不要用行为树,除非公司里有个人,本来就很擅长行为树,那么可以交给他,其他人没必要参与了。行为树入门难,原因不是它本身多么复杂,而是网上的资料太少,尤其groot的资料更少,全靠自己摸索,一个并不复杂的问题,摸索一整天。有不少资料是游戏设计方面的,用于机器人领域的开源方案目前只有ROS2。资料这么少,肯定是有原因的。
最近从行为树版本3切换到版本4,发现有一些变化,又走了一遍坑之后,我还是认为最好不要用行为树。
准确地说,是BehaviorTree.CPP
和Groot
太垃圾,行为树本身在游戏行业已经用了很多年,不需要讨论其可用性。
行为树的优缺点
优点:
- 行为逻辑和状态数据分离(减少了耦合),节点写好以后可以反复利用
- 线性的方式扩展,所以扩展性好。添加或删除一个功能,可能只需添加或删除一个节点
- 黑板变量用起来很灵活
- 官方提供了与ROS2的接口,适合机器人的业务开发,而且是两套接口。
- 使用groot编辑,直观容易理解。但是Groot又有一大堆缺点,我在另一篇文章有总结
行为树的缺点
- 一个简单的操作都需要定义Action或Condition节点,行为树很容易做的复杂化
- 每次从根节点开始逻辑,若树的很庞大,效率将变得低下,占用CPU更多,注意行为树的刷新频率
- 黑板变量,有的人在XML文件里赋值,有的人在C++里赋值,我如果用groot2看行为树,无法看到C++里赋值的情况
- switch默认最多6个case,不能直接修改xml文件里的Switch,C++不识别。要想实现更多case,只能自己实现新的 Control 节点
- C++的逻辑风格是大多数人熟悉的,比如if else, while, 设计模式等等,一个人看另一个人写的代码并不难上手。但是行为树的逻辑类型很多,有的人还会自己定义控制节点和修饰节点。同样的功能,不同人实现的逻辑会很不同,互相难理解。我之前看两个人写的行为树,风格大为不同,很难理清思路。
Timeout
节点疑似不能生效
网上找不到修饰节点Timeout
的使用方法,从API里找到了说明:The TimeoutNode will halt() a running child if the latter has been RUNNING for more than a give time. The timeout is in milliseconds and it is passed using the port “msec”.
但是我反复试验,发现这个节点总不生效,它修饰的节点运行时间早就超过Timeout
规定时间了,但是不会停止
状态机的缺点
- 在项目中可能增加新状态、减少状态或者改变状态之间的迁移关系,如果状态越来越多,一点小修改都会产生很大的工作量,代码中会出现大量的判断跳转,耦合性太强。代码的逻辑会变得臃肿
- 与行为树最显著的区别:状态与执行内容是绑定在一起的。当执行内容需要在多个状态中执行时,各个状态下都需要放置执行内容的逻辑。当业务逻辑代码分散在各处时就不太好维护了
行为树与FSM的权衡
- 状态机是事件驱动的,对于状态机来说,每个时刻它都处于某种“状态”中,等待某个事件(转换)的发生。如果事件没有发生,那么继续保持在这个状态;
- 行为树都是自顶向下采用轮询的方式实现的。不太适合实现事件驱动的行为。例如,机器人在行走过程中陷入困境了,这明显是发生了事件,用状态机表示更合适。其实通过并行节点和修饰节点结合能够处理这种情况。
安装
安装依赖项,然后去github的release里下载最新的版本1
2
3
4
5sudo apt-get sqlite3
sudo apt-get install libzmq3-dev libboost-dev
sudo apt-get install libboost-coroutine-dev # 需要用到协程
sudo apt-get install qtbase5-dev libqt5svg5-dev libzmq3-dev libdw-dev
cmake老三样编译安装。
普通的cmake设置如下1
2
3
4
5
6
7
8
9
10cmake_minimum_required(VERSION 3.10.2)
project(simple_bt)
set(CMAKE_CXX_SaTANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(behaviortree_cpp)
add_executable(${PROJECT_NAME} "simple_bt.cpp")
target_link_libraries(${PROJECT_NAME} BT::behaviortree_cpp)
ROS2环境的设置如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34cmake_minimum_required(VERSION 3.8)
project(test_node)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
set(CMAKE_CXX_STANDARD 17)
add_compile_options(-Wextra -Wpedantic -Wno-unused-parameter -g)
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(ament_index_cpp)
find_package(behaviortree_cpp)
INCLUDE_DIRECTORIES(/home/user/catkin_ws/src/test_node/include)
add_executable(test_node src/test.cpp)
ament_target_dependencies(test_node
rclcpp
behaviortree_cpp
${BTCPP_LIBRARY}
)
install(TARGETS
test_node
DESTINATION lib/${PROJECT_NAME}
)
ament_package()
package.xml
如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<package format="3">
<name>test_node</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="user@todo.todo">zzp</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<depend>rclcpp</depend>
<depend>std_msgs</depend>
<depend>behaviortree_cpp</depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
ROS2行为树动态库默认安装在/opt/ros/galactic/lib/libbehaviortree_cpp_v3.so
,头文件在/opt/ros/foxy/include/behaviortree_cpp_v3