datetime:2023/05/12 10:30
author:nzb

BT8:ActionNode及同步、异步

定义在BehaviorTree.CPP/include/behaviortree_cpp_v3/action_node.h,这个文件中定义了ActionNodeBaseSyncActionNodeSimpleActionNodeAsyncActionNodeStatefulActionNodeCoroActionNode等类,开发者自己定义的action node一般都会继承其中一个基类。

ActionNodeBase

最通用的action node基类,子类要实现executeTick()tick()halt()等函数。

SyncActionNode

继承自ActionNodeBase,同步action node,不会返回RUNNING,无需开发者实现halt()

NodeStatus SyncActionNode::executeTick() {
  auto stat = ActionNodeBase::executeTick();
  if (stat == NodeStatus::RUNNING) {
    throw LogicError("SyncActionNode MUST never return RUNNING");
  }
  return stat;
}

virtual void halt() override final { setStatus(NodeStatus::IDLE); }

SimpleActionNode

继承自SyncActionNode,常使用lambdasstd::bind构造std::function对象来构造SimpleActionNode,这个function就是tick()的内容。 这样开发者无需定义node,只需指定node类型IDtick()即可,SimpleConditionNode同理。

class SimpleActionNode : public SyncActionNode {
 public:
  typedef std::function<NodeStatus(TreeNode&)> TickFunctor;

  // You must provide the function to call when tick() is invoked
  SimpleActionNode(const std::string& name, TickFunctor tick_functor,
                   const NodeConfiguration& config);
  ~SimpleActionNode() override = default;
 protected:
  virtual NodeStatus tick() override final;
  TickFunctor tick_functor_;  // tick()执行的内容
};

NodeStatus SimpleActionNode::tick() {
  ...
  NodeStatus status = tick_functor_(*this);
  if (status != prev_status) {
    setStatus(status);
  }
  return status;
}

BehaviorTree.CPP/examples/t01_build_your_first_tree.cpp中有使用示例。

GripperInterface gripper;
// open()是GripperInterface类的成员函数
factory.registerSimpleAction("OpenGripper",
                               std::bind(&GripperInterface::open, &gripper));

register函数不会调用node的构造函数,其作用是向BehaviorTreeFactory注册类型,创建构造函数匹配的builder

// 不需要输入config
void BehaviorTreeFactory::registerSimpleAction(
    const std::string& ID, const SimpleActionNode::TickFunctor& tick_functor,
    PortsList ports) {
  NodeBuilder builder = [tick_functor, ID](const std::string& name,
                                           const NodeConfiguration& config) {
    return std::make_unique<SimpleActionNode>(name, tick_functor, config);
  };
  TreeNodeManifest manifest = {NodeType::ACTION, ID, std::move(ports)};
  registerBuilder(manifest, builder);
}
void BehaviorTreeFactory::registerBuilder(const TreeNodeManifest& manifest,
                                          const NodeBuilder& builder) {
  auto it = builders_.find(manifest.registration_ID);
  if (it != builders_.end()) {
    throw BehaviorTreeException("ID [", manifest.registration_ID,
                                "] already registered");
  }
  builders_.insert({manifest.registration_ID, builder});
  manifests_.insert({manifest.registration_ID, manifest});
}

AsyncActionNode

继承自ActionNodeBase,会在executeTick()函数中创建1个线程来执行tick(),通过halt_requested_变量监控节点是否被终止。 开发者需要在子类tick() 中周期性检查isHaltRequested()的返回值,以便及时终止执行。子类halt()要记得调用父类AsyncActionNode::halt()。 子类不必显式的返回RUNNING ,只需根据结果返回SUCCESS/FAILURE,未执行完成时会自动置位和返回RUNNING

NodeStatus BT::AsyncActionNode::executeTick() {
  // send signal to other thread.
  //  The other thread is in charge for changing the status
  if (status() == NodeStatus::IDLE) {
    setStatus(NodeStatus::RUNNING);
    halt_requested_ = false;
    thread_handle_ = std::async(std::launch::async, [this]() {
      try {
        setStatus(tick());
      } catch (std::exception&) {
        std::cerr << "\nUncaught exception from the method tick(): ["
                  << registrationName() << "/" << name() << "]\n"
                  << std::endl;
        exptr_ = std::current_exception();
        thread_handle_.wait();
      }
      return status();
    });
  }
  if (exptr_) {
    std::rethrow_exception(exptr_);
  }
  return status();
}

BehaviorTree.CPP/sample_nodes/movebase_node.hAsyncActionNode的使用示例。

参考链接

StatefulActionNode

继承自ActionNodeBase,像状态机的运行方式。如果节点在IDLE state就会调用onStart(),如果在RUNNING state就会调用onRunning(),如果被halt() 就会调用onHalted()

class StatefulActionNode : public ActionNodeBase {
 public:
  StatefulActionNode(const std::string& name, const NodeConfiguration& config)
      : ActionNodeBase(name, config) {}

  // do not override this method
  NodeStatus tick() override final;
  // do not override this method
  void halt() override final;

  /// method to be called at the beginning.
  /// If it returns RUNNING, this becomes an asychronous node.
  virtual NodeStatus onStart() = 0;

  /// method invoked by a RUNNING action.
  virtual NodeStatus onRunning() = 0;

  /// when the method halt() is called and the action is RUNNING, this method is
  /// invoked. This is a convenient place todo a cleanup, if needed.
  virtual void onHalted() = 0;
};
NodeStatus StatefulActionNode::tick() {
  const NodeStatus initial_status = status();  // 当前状态

  if (initial_status == NodeStatus::IDLE) {
    NodeStatus new_status = onStart();  // 状态跳转
    ...
    return new_status;
  }

  if (initial_status == NodeStatus::RUNNING) {
    NodeStatus new_status = onRunning();  // 状态跳转
    ...
    return new_status;
  }
  //  当前状态不是IDLE、RUNNING,那么就是SUCCESS/FAILURE,可以直接返回结果
  return initial_status;
}

void StatefulActionNode::halt() {
  if (status() == NodeStatus::RUNNING) {
    onHalted();  // 状态跳转
  }
  setStatus(NodeStatus::IDLE);
}

CoroActionNode

理解不透,略。

results matching ""

    No results matching ""