Composition of Behaviors with Subtree
使用子树进行行为组合
我们可以将大规模行为组合起来,通过将较小和可重复使用的行为组合成更大的行为。
换句话说,我们想要创建__层次__行为树。
这可以通过在XML中定义多个树结构来实现,包括一个嵌套在另一个中。
CrossDoor behavior
这个例子是受到一篇关于行为树的流行文章的启发article about behavior trees.
这也是第一个使用 Decorators
和Fallback
功能的实际示例。
<root main_tree_to_execute = "MainTree">
<BehaviorTree ID="DoorClosed">
<Sequence name="door_closed_sequence">
<Inverter>
<IsDoorOpen/>
</Inverter>
<RetryUntilSuccessful num_attempts="4">
<OpenDoor/>
</RetryUntilSuccessful>
<PassThroughDoor/>
</Sequence>
</BehaviorTree>
<BehaviorTree ID="MainTree">
<Fallback name="root_Fallback">
<Sequence name="door_open_sequence">
<IsDoorOpen/>
<PassThroughDoor/>
</Sequence>
<SubTree ID="DoorClosed"/>
<PassThroughWindow/>
</Fallback>
</BehaviorTree>
</root>
为了提高可读性,我们的自定义节点使用一行代码进行注册:
CrossDoor::RegisterNodes(factory);
BT库提供的默认节点,如Fallback
,已经被BehaviorTreeFactory
注册。
可能会注意到,我们将树的一个相当复杂的分支封装在一个称为DoorClosed
的独立树中,当门关闭时执行。
期望的行为是:
- 如果门是打开的,就
PassThroughDoor
- 如果门是关闭的,尝试最多4次
OpenDoor
,然后PassThroughDoor
- 如果无法打开关闭的门,就
PassThroughWindow
Loggers
在C++的一侧,我们不需要做任何事情来构建可重用的子树。
因此我们借此机会介绍另一个新功能:BehaviorTree.CPP : Loggers.
一个记录器是一种用于显示、记录和/或发布树中任何状态变化的机制。
int main()
{
using namespace BT;
BehaviorTreeFactory factory;
// register all the actions into the factory
// We don't show how these actions are implemented, since most of the
// times they just print a message on screen and return SUCCESS.
// See the code on Github for more details.
factory.registerSimpleCondition("IsDoorOpen", std::bind(IsDoorOpen));
factory.registerSimpleAction("PassThroughDoor", std::bind(PassThroughDoor));
factory.registerSimpleAction("PassThroughWindow", std::bind(PassThroughWindow));
factory.registerSimpleAction("OpenDoor", std::bind(OpenDoor));
factory.registerSimpleAction("CloseDoor", std::bind(CloseDoor));
factory.registerSimpleCondition("IsDoorLocked", std::bind(IsDoorLocked));
factory.registerSimpleAction("UnlockDoor", std::bind(UnlockDoor));
// Load from text or file...
auto tree = factory.createTreeFromText(xml_text);
// This logger prints state changes on console
StdCoutLogger logger_cout(tree);
// This logger saves state changes on file
FileLogger logger_file(tree, "bt_trace.fbl");
// This logger stores the execution time of each node
MinitraceLogger logger_minitrace(tree, "bt_trace.json");
#ifdef ZMQ_FOUND
// This logger publish status changes using ZeroMQ. Used by Groot
PublisherZMQ publisher_zmq(tree);
#endif
printTreeRecursively(tree.rootNode());
//while (1)
{
NodeStatus status = NodeStatus::RUNNING;
// Keep on ticking until you get either a SUCCESS or FAILURE state
while( status == NodeStatus::RUNNING)
{
status = tree.tickRoot();
// IMPORTANT: you must always add some sleep if you call tickRoot()
// in a loop, to avoid using 100% of your CPU (busy loop).
// The method Tree::sleep() is recommended, because it can be
// interrupted by an internal event inside the tree.
tree.sleep( milliseconds(10) );
}
if( LOOP )
{
std::this_thread::sleep_for( milliseconds(1000) );
}
}
return 0;
}