行为树基础教程:创建你的第一个行为树
如何创建你自己的动作节点(ActionNodes)
创建TreeNode的最常用(也是推荐)的方式是通过继承。
示例:自定义同步动作节点(无端口)
// 示例的自定义同步动作节点
class ApproachObject : public BT::SyncActionNode {
public:
ApproachObject(const std::string& name) :
BT::SyncActionNode(name, {})
{}
// 必须重写tick()虚拟函数
BT::NodeStatus tick() override {
std::cout << "ApproachObject: " << this->name() << std::endl;
return BT::NodeStatus::SUCCESS;
}
};
- 任何TreeNode实例都有一个
name
。这个标识符旨在易于人类阅读,且不需要是唯一的。 tick()
方法是实际动作发生的地方。它必须始终返回一个NodeStatus
,即RUNNING、SUCCESS或FAILURE。
或者,我们可以使用依赖注入来创建TreeNode,给定一个函数指针(即“functor”)。
示例:使用函数指针创建TreeNode
using namespace BT;
// 简单函数返回NodeStatus
BT::NodeStatus CheckBattery() {
std::cout << "[ Battery: OK ]" << std::endl;
return BT::NodeStatus::SUCCESS;
}
// 将GripperInterface类的方法open()和close()封装成TreeNode
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; // 共享信息
};
我们可以从这些functors创建SimpleActionNode
:
CheckBattery()
GripperInterface::open()
GripperInterface::close()
使用XML动态创建树
考虑以下XML文件,名为my_tree.xml:
<root BTCPP_format="4">
<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>
- 你必须首先在
BehaviorTreeFactory
中注册自定义的TreeNode,然后从文件或文本加载XML。 - XML中使用的标识符必须与注册TreeNode时使用的一致。
- 属性“name”表示实例的名称;它是可选的。
代码实现
#include "behaviortree_cpp/bt_factory.h"
// 包含自定义节点定义的文件
#include "dummy_nodes.h"
using namespace DummyNodes;
int main() {
// 使用BehaviorTreeFactory注册自定义节点
BehaviorTreeFactory factory;
// 通过继承创建节点的推荐方式
factory.registerNodeType<ApproachObject>("ApproachObject");
// 使用函数指针注册SimpleActionNode
// 可以使用C++11 lambdas或std::bind
factory.registerSimpleCondition("CheckBattery", [](BT::TreeNode&) { return CheckBattery(); });
// 也可以使用类的方法创建SimpleActionNodes
GripperInterface gripper;
factory.registerSimpleAction("OpenGripper", [&gripper](BT::TreeNode&){ return gripper.open(); });
factory.registerSimpleAction("CloseGripper", [&gripper](BT::TreeNode&){ return gripper.close(); });
// 在部署时(即运行时,但只在开始时一次)创建树
auto tree = factory.createTreeFromFile("./my_tree.xml");
// 要“执行”树,你需要“触发”它
// 触发将根据树的逻辑传播到子节点
// 在这个例子中,因为Sequence的所有子节点都返回SUCCESS,所以执行了整个序列
tree.tickWhileRunning();
return 0;
}
/* 预期输出:
[ Battery: OK ]
GripperInterface::open
ApproachObject: approach_object
GripperInterface::close
*/