行为树类似于状态机,只是一种机制,在正确的条件下,正确的时间来唤醒回调函数。

进一步,我们使用callback和tick来相互替换;

这些回调函数里面具体执行什么取决于你;

怎样创建你自己的ActionNodes

默认的方法是采用继承的方式来实现一个TreeNode:

//Example of custom SyncActionNode(synchronous action) without ports.
class ApproachObject : public BT::SyncActionNode
{
public:
ApproachObject(const std::string& name) :
BT::SyncActionNode(name, {})
{}

//You must override the virtual function tick()
BT::NodeStatus tick() override
{
std::out << "ApproachObject: " << this->name() <<std::endl;
return BT::NodeStatus::SUCCESS;
}
};

任何一个TreeNode的实例需要有一个名字,但不要求是唯一的;

方法tick()是实际行为发生的地方。它必须总是返回一个节点状态,如RUNNING,SUCCESS或者FAILURE。

另外,也可以用依赖注入的方式创建一个TreeNode,给定一个函数指针(如functor)

对functor唯一的要求是具有如下之一的表示:

BT::NodeStatus myFunction()
BT::NodeStatus myFunction(BT::TreeNode& self)

例如:

using namespace BT;

// Simple function that return a NodeStatus
BT::NodeStatus CheckBattery()
{
std::cout << "[ Battery: OK ]" << std::endl;
return BT::NodeStatus::SUCCESS;
}

// We want to wrap into an ActionNode the methods open() and close()
class GripperInterface
{
public:
GripperInterface(): _open(true) {}

NodeStatus open() {
_open = true;
std::cout << "GripperInterface::open" << std::endl;
return NodeStatus::SUCCESS;
}

NodeStatus close() {
std::cout << "GripperInterface::close" << std::endl;
_open = false;
return NodeStatus::SUCCESS;
}

private:
bool _open; // shared information
};

我们可以构建一个SimpleActionNode从下面的functors中的任何一个:

  • CheckBattery()
  • GripperInterface::open()
  • GripperInterface::close()

用一个XML来动态创建一棵树

XML文件命名为my_tree.xml:

<root main_tree_to_execute = "MainTree" >
<BehaviorTree ID="MainTree">
<Sequence name="root_sequence">
<CheckBattery name="check_battery"/>
<OpenGripper name="open_gripper"/>
<ApproachObject name="approach_object"/>
<CloseGripper name="close_gripper"/>
</Sequence>
</BehaviorTree>
</root>

You can find more details about the XML schema ​​here​​.

首先比如注册我们自定义的TreeNodes到BehaviorTreeFactory中,然后从文件或者text中加载XML;

在XML中使用的标志符必须与注册TreeNodes中使用的一致;

属性name表示实例的名字,它是可选的;

#include "behaviortree_cpp_v3/bt_factory.h"

// file that contains the custom nodes definitions
#include "dummy_nodes.h"

int main()
{
// We use the BehaviorTreeFactory to register our custom nodes
BehaviorTreeFactory factory;

// Note: the name used to register should be the same used in the XML.
using namespace DummyNodes;

// The recommended way to create a Node is through inheritance.
factory.registerNodeType<ApproachObject>("ApproachObject");

// Registering a SimpleActionNode using a function pointer.
// you may also use C++11 lambdas instead of std::bind
factory.registerSimpleCondition("CheckBattery", std::bind(CheckBattery));

//You can also create SimpleActionNodes using methods of a class
GripperInterface gripper;
factory.registerSimpleAction("OpenGripper",
std::bind(&GripperInterface::open, &gripper));
factory.registerSimpleAction("CloseGripper",
std::bind(&GripperInterface::close, &gripper));

// Trees are created at deployment-time (i.e. at run-time, but only
// once at the beginning).

// IMPORTANT: when the object "tree" goes out of scope, all the
// TreeNodes are destroyed
auto tree = factory.createTreeFromFile("./my_tree.xml");

// To "execute" a Tree you need to "tick" it.
// The tick is propagated to the children based on the logic of the tree.
// In this case, the entire sequence is executed, because all the children
// of the Sequence return SUCCESS.
tree.tickRoot();

return 0;
}

/* Expected output:
*
[ Battery: OK ]
GripperInterface::open
ApproachObject: approach_object
GripperInterface::close
*/