Subtrees and Loggers
翻译自:https://www.behaviortree.dev
Composition of Behaviors with Subtree
We can build large scale behavior composing together smaller and reusable behaviors into larger ones. [我们可以构建大规模行为,将更小且可重用的行为组合成更大的行为。]
In other words, we want to create hierarchical behavior trees. [换句话说,我们想要创建分层行为树。]
This can be achieved easily defining multiple trees in the XML including one into the other. [这可以很容易地在 XML 中定义多个树,包括一个到另一个。]
CrossDoor behavior
This example is inspired by a popular article about behavior trees. [这个例子的灵感来自一篇关于行为树的流行文章。]
It is also the first practical example that uses Decorators
and 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>
For readability, our custom nodes are registered with the one-liner: [为了便于阅读,我们的自定义节点使用单线注册:]
CrossDoor::RegisterNodes(factory);
Default nodes provided by the BT library such as Fallback are already registered by the BehaviorTreeFactory. [BT 库提供的默认节点(例如 Fallback)已经由 BehaviorTreeFactory 注册。]
It may be noticed that we incapsulated a quite complex branch of the tree, the one to execute when the door is closed, into a separate tree called DoorClosed
. [可能会注意到,我们将树的一个非常复杂的分支(门关闭时执行的分支)封装到一个名为 DoorClosed 的单独树中。]
The desired behavior is: [期望的行为是:]
- If the door is open,
PassThroughDoor
. [如果门打开,PassThroughDoor。] - If the door is closed, try up to 4 times to
OpenDoor
and, then,PassThroughDoor
. [如果门已关闭,请最多尝试 4 次 OpenDoor,然后再尝试 PassThroughDoor。] - If it was not possible to open the closed door,
PassThroughWindow
. [如果无法打开关闭的门,PassThroughWindow。]
Loggers
On the C++ side we don’t need to do anything to build reusable subtrees. [在 C++ 方面,我们不需要做任何事情来构建可重用的子树。]
Therefore we take this opportunity to introduce another neat feature of BehaviorTree.CPP : Loggers. [因此,我们借此机会介绍 BehaviorTree.CPP 的另一个简洁功能:记录器。]
A Logger is a mechanism to display, record and/or publish any state change in the tree. [记录器是一种显示、记录和/或发布树中任何状态变化的机制。]
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;
}