How to create a BehaviorTree
行为树类似于状态机,只是一种机制,在正确的条件下,正确的时间来唤醒回调函数。
进一步,我们使用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 */