您主要有两种方法来创建行为树。
- 静态地,在编译时。
- 动态地,在运行时,即解析文件。
强烈建议你使用后一种方法,但为了完整性,我们将描述前者。
如何创建自己的ActionNodes
你能在这里找到源码:sample_nodes/dummy_nodes.h.
// Example of custom SyncActionNode (synchronous Action)
class ApproachObject: public BT::SyncActionNode
{
public:
ApproachObject(const std::string& name):
BT::SyncActionNode(name) {}
// You must override this virtual function
BT::NodeStatus tick() override
{
std::cout << "ApproachObject: " << this->name() << std::endl;
return BT::NodeStatus::SUCCESS;
}
};
如你所见:
-
TreeNode的任何实例都有一个名称。该标识符旨在用户可读,并且不需要是唯一的。
-
tick()是实际Action发生的地方。它必须返回NodeStatus,即RUNNING,SUCCESS或FAILURE。
-
halt()用于停止异步Action。ApproachObject不需要它。
或者,我们可以使用依赖项注入来创建一个指定函数指针的TreeNode。
仿函数的唯一要求是具有以下任一特征:
BT::NodeStatus myFunction()
BT::NodeStatus myFunction(BT::TreeNode& self)
例如:
using namespace BT;
NodeStatus SayHello() {
std::cout << "Robot says Hello" << std::endl;
return NodeStatus::SUCCESS;
}
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;
};
我们可以从以下这些仿函数构建一个SimpleActionNode
:
- SayHello()
- GripperInterface::open()
- GripperInterface::close()
静态树
让我们创建TreeNodes的实例并将它们组成一个树。
BT::SequenceNode
是库提供的内置ControlNode。BT::SimpleActionNode
是一个通过仿函数创建的同步ActionNode。DummyNodes::ApproachObject
是我们用户定义的ActionNode。
#include "dummy_nodes.h"
int main()
{
using namespace BT;
using namespace DummyNodes;
GripperInterface gi;
SequenceNode sequence_root("sequence");
SimpleActionNode say_hello("action_hello", std::bind(SayHello) );
SimpleActionNode open_gripper("open_gripper",
std::bind( &GripperInterface::open, &gi) );
SimpleActionNode close_gripper("close_gripper",
std::bind( &GripperInterface::close, &gi) );
ApproachObject approach_object("approach_object");
// Add children to the sequence.
// They will be executed in the same order they are added.
sequence_root.addChild(&say_hello);
sequence_root.addChild(&open_gripper);
sequence_root.addChild(&approach_object);
sequence_root.addChild(&close_gripper);
sequence_root.executeTick();
return 0;
}
/* Expected output:
Robot says: "Hello!!!"
GripperInterface::open
ApproachObject: approach_object
GripperInterface::close
*/
动态创建的树
将以下XML存储在my_tree.xml文件中
请注意,以下语法也有效:
<root main_tree_to_execute = "MainTree" >
<BehaviorTree ID="MainTree">
<Sequence name="root_sequence">
<SayHello name="action_hello"/>
<OpenGripper name="open_gripper"/>
<ApproachObject name="approach_object"/>
<CloseGripper name="close_gripper"/>
</Sequence>
</BehaviorTree>
</root>
我们必须首先将自定义TreeNode注册到其中BehaviorTreeFactory
,然后从文件或文本加载XML。
XML中使用的标识符必须与用于注册TreeNode的标识符一致。
属性“name”表示实例的名称,它是可选的。
#include "behaviortree_cpp/xml_parsing.h"
#include "Blackboard/blackboard_local.h"
#include "dummy_nodes.h"
int main()
{
using namespace BT;
using namespace DummyNodes;
GripperInterface gi;
BehaviorTreeFactory factory;
factory.registerSimpleAction("SayHello", std::bind(SayHello) );
factory.registerSimpleAction("OpenGripper",
std::bind( &GripperInterface::open, &gi));
factory.registerSimpleAction("CloseGripper",
std::bind( &GripperInterface::close, &gi));
factory.registerNodeType<ApproachObject>("ApproachObject");
// IMPORTANT: when the object "tree" goes out of scope,
// all the TreeNodes instances are destroyed
auto tree = buildTreeFromFile(factory, "./my_tree.xml");
tree.root_node->executeTick();
return 0;
}
/* Expected output:
Robot says: "Hello!!!"
GripperInterface::open
ApproachObject: approach_object
GripperInterface::close
*/