行为树BehaviorTree学习记录2_简单顺序控制实例

文章介绍了如何在行为树(BT)结构中创建自定义动作节点,包括通过继承和依赖注入两种方式。详细解释了如何注册节点,以及如何根据XML文件构建和运行行为树实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(为免误导,特免责声明如下:本文所有内容,只是基于个人当前理解和实际做法,后面亦会有更正和修订,但任何版本都不免有个人能力有限、理解有误或者工作环境不同的状况,故文中内容仅供参考。任何人都可以借鉴或者直接使用代码片段,但对任何直接引用或者借鉴产生的技术问题等后果,作者不承担任何责任。)

1 BT tree结构

在这里插入图片描述
一个顺序控制节点下面4个子节点:第一个是条件判断节点,后三个动作节点
就是会判断电池状态,如果正常,依次执行打开夹具,靠近物体,闭合夹具的动作,如果某一子节点返回失败,则直接返回失败,不再继续。

2 创建自己的动作节点

  • 类继承
// 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::cout << "ApproachObject: " << this->name() << std::endl;
    return BT::NodeStatus::SUCCESS;
  }
};

上面代码创建了一个自己的动作节点类,继承自同步动作节点。必须要重载的虚函数是tick(),本例子只是输出对象的名称。其返回值必须是RUNNING, SUCCESS或FAILURE三者之一。

  • 行为绑定简单动作类

除了这种从动作节点继承来生成自己的动作节点类的方法,还有简单的生成动作节点的方法,就是dependency injection(依赖注入?依赖绑定,就是用具有一下结构的函数去绑定到BT自己的类上(它的tick函数被替代了)
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
};

可以用
CheckBattery()
GripperInterface::open()
GripperInterface::close()
任意一个函数创建简单动作节点SimpleActionNode。

3实例

如果行为树的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>

在代码中如果创建与xml文件定义的BT树结构一样的对象,并tick运行呢:

2.1) 注册类

首先必须把自定义的节点类注册到行为树工厂(BehaviorTreeFactory ),这样它读取xml文件后,才知道如何根据xml文件的名称去创建对应的类的对象。
从xml文件可以看出需要注册自己的类有CheckBattery ,OpenGripper ,ApproachObject 和CloseGripper 。
根据第2部分,注册方法有两种:

  • 第一种是类继承的ApproachObject 类,其注册方法是:factory.registerNodeType(“ApproachObject”);
    <>里面的C++的类名,“”里面的是这个类的标签,与xml文件里一致,这样读到xml文件的这个标签,它才知道去创建哪个类的对象。

  • 第二种是简单action类的注册,通过依赖注入的方式,绑定tick函数生成不同的简单动作类,如:
    factory.registerSimpleCondition(“CheckBattery”, & { return CheckBattery(); });
    第一个参数“”里的是类标签,与xml文件里的一致。后面的lambda函数会绑定作为这个简单动作类的tick回调。

    2.2) 读取树结构文件创建树对象
    auto tree = factory.createTreeFromFile(“./my_tree.xml”);

    2.3) 运行该树对象

tree.tickWhileRunning();

#include "behaviortree_cpp/bt_factory.h"

// file that contains the custom nodes definitions
#include "dummy_nodes.h"
using namespace DummyNodes;

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

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

  // Registering a SimpleActionNode using a function pointer.
  // You can use C++11 lambdas or std::bind
  factory.registerSimpleCondition("CheckBattery", [&](TreeNode&) { return CheckBattery(); });

  //You can also create SimpleActionNodes using methods of a class
  GripperInterface gripper;
  factory.registerSimpleAction("OpenGripper", [&](TreeNode&){ return gripper.open(); } );
  factory.registerSimpleAction("CloseGripper", [&](TreeNode&){ return gripper.close(); } );

  // 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.tickWhileRunning();

  return 0;
}

/* Expected output:
*
  [ Battery: OK ]
  GripperInterface::open
  ApproachObject: approach_object
  GripperInterface::close
*/
### ROS2行为树的使用教程 #### 构建环境准备 为了在ROS2环境中成功运行和开发基于`BehaviorTree.CPP`的行为树,需先准备好相应的开发环境。对于ROS2而言,应采用`colcon`工具来构建工作空间中的软件包[^3]。 ```bash cd ros2_ws colcon build ``` #### 安装 `BehaviorTree.CPP` 库与可视化工具 Groot2 安装并配置好`BehaviorTree.CPP`库以及其配套使用的图形界面编辑器Groot2是必要的前置条件之一。通过这些工具能够更便捷地设计、测试及监控行为树的工作流程[^2]。 #### 创建自定义消息类型 某些特定应用场景下可能需要用到定制的消息格式,比如`sentry_interfaces::msg::RobotHP`这样的健康状态报告信息。可以通过克隆现成的例子仓库或者手动创建所需的消息文件来满足项目需求。 #### 行为树结构实例——搜寻物体案例 针对具体的任务场景如搜寻目标物,在ROS2平台上利用`BehaviorTree.CPP`实现时,可通过如下方式组织行为节点: - **根节点** (`Fallback`):尝试依次执行子节点直到有一个返回成功; - **序列节点** (`Sequence`):按顺序逐一调用内部的任务单元,只有当全部完成才视为整体操作结束; 具体到代码层面,则表现为一段XML形式描述的行为树定义语句[^4]: ```xml <root main_tree_to_execute = "MainTree"> <BehaviorTree ID="MainTree"> <Fallback name="root"> <!-- 单独的任务项 --> <Task1 name="task_1"/> <Task2 name="task_2"/> <!-- 多个连续动作组成的序列 --> <Sequence> <Task3 name="task_3"/> <Task4 name="task_4"/> </Sequence> </Fallback> </BehaviorTree> </root> ``` 此片段展示了如何组合不同的控制流组件以适应复杂多变的实际作业要求。 #### 日志记录与性能分析功能 值得一提的是,该框架还集成了强大的日志追踪机制,允许使用者轻松捕获整个决策过程的关键事件,并对其进行深入剖析,从而辅助后续迭代改进算法表现[^1]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值