input and output ports

自定义的TreeNodes用于执行任意简单的或者复杂的软件代码。目的是提供一个更高抽象级别的接口。

因此,其概念上与函数并无不同。

与函数的相似之处:

传递参数到一个节点(inputs);

从一个节点中获取参数(outputs);

一个节点的输出可以作为另一个节点的输入;

BehaviorTree.CPP通过端口提供了一个基本的数据流机制,端口不仅使用简单而且灵活且类型安全。

输入端口:

一个有效的输入:

a、静态的字符串可以由节点解析;

b、指针指向黑板的入口,由一个key来识别;

黑板由一个简单的key/value存储器,可以由树中的所有节点来共享;

黑板入口是一个key/value对;

输入端口可以读黑板中的一个入口,而输出端口可以往黑板上去写一个入口项;

假设要创建一个ActionNode称之为SaySomething,其会打印一个给定的字符串到std::cout上。

那么上面的字符串可以通过一个输入端口称之为message的来传递;

下面可替换的XML语法:

<SaySomething name="first" message="hello world"/>

<SaySomething name="second" message="{greetings}"/>

在第一个节点中的属性message表示:

静态字符串hello world被传递到SaySomething的端口message;

这个消息是从XML文件中读取的,因此在运行时不能改变;

第二个语法表示:

读取黑板的入口greetings的当前值;这个值可以在运行时改变;

SaySomething行为节点(ActionNode)可以被以下实现:

//SyncActionNode (synchronous action) with an input port.
class SaySomething : public SyncActionNode
{
  public:
  //If your node has ports, you must use this constructor signature
  SaySomething(const std::string& name, const NodeConfiguration& config)
  : SyncActionNode(name, config){}

  //It is mandaroty to define this static method.
  static PortsList providedPorts()
  {
    //This action has a single input port called "message"
   // Any port must have a name. The type is optional.
   return {InputPort<std::string>("message")};
  }

  //As usual, you must override the virtual function tick()
  NodeStatus tick() override
 {
   Optional<std::string> msg = getInput<std::string>("message");
  //Check if optional is valid. If not, throw its error
  if(!msg)
  {
     throw BT::RuntiemError("missing required input[message] : ", msg.error());
  }

  //use the method value() to extract the valid message;
 std::cout <<"Robot says: " << msg.value() << std::endl;
  return NodeStatus::SUCCESS;  
 }
} 

在一个简单的函数中可以实现同样的功能;该函数将BT::TreeNode的一个实例作为输入为了能够访问输入端口的message:

//simple function that return a NodeStatus
BT::NodeStatus SaySomethingSimple(BT::TreeNode& self)
{
 Optional<std::string> msg = self.getInput<std::string>("message");
  // Check if optional is valid. If not, throw its error
  if (!msg)
  {
    throw BT::RuntimeError("missing required input [message]: ", msg.error());
  }
 //use the method value() to extract the valid message.
 std::cout <<"Robot says: " << msg.value() <<std::endl;
  return NodeStatus::SUCCESS;
}

当一个自定义的TreeNode有输入或者输出端口的时候,这些端口必须使用静态方法来声明:

static MyCustomNode::PortsList providedPorts();

从端口message中的输入可以使用模板方法TreeNode::getInput<T>(key)来获取;

该方法可能由于多种原因而失败。取决于用户去检查返回值的有效性,然后决定再做什么:

a.返回 NodeStatus::FAILURE

b.抛出异常;

c.使用不同的默认值;

注意:

强烈建议方法getInput()在tick()中实现,而不是在类的构造函数中实现;

c++代码禁止假设输入的类型,因为可能是静态的也可能是动态的。动态的输入可能在运行时发生变化,所以需要周期性读取。

输出端口

一个输入端口指向的黑板的入口项是有效的,只有当另一个节点已经往该同样的项中写了值。

下面是输出端口的例子:

class ThinkWhatToSay : public SyncActionNode
{
  public:
    ThinkWhatToSay(const std::string& name, const NodeConfiguration& config)
      : SyncActionNode(name, config)
    {
    }

    static PortsList providedPorts()
    {
        return { OutputPort<std::string>("text") };
    }

    // This Action writes a value into the port "text"
    NodeStatus tick() override
    {
        // the output may change at each tick(). Here we keep it simple.
        setOutput("text", "The answer is 42" );
        return NodeStatus::SUCCESS;
    }
};

可以替换使用的,大多数调试阶段,可能写一个静态的值到入口项通过使用内建的行为SetBlackboard。

<SetBlackboard output_key="the_answer" value="The answer is 42"/>

一个完整的例子,本例中一个Sequence中有5个action被执行:

a、行为1和4从输入端口message中读取静态字符串;

b、行为3和5读输入端口message从黑板中的the_answer项中读取;

c、行为2向黑板中的项the_answer中写入数据;

SaySomething2是一个简单的SimpleActionNode:

<root main_tree_to_execute = "MainTree" >
    <BehaviorTree ID="MainTree">
       <Sequence name="root_sequence">
           <SaySomething     message="start thinking..." />
           <ThinkWhatToSay   text="{the_answer}"/>
           <SaySomething     message="{the_answer}" />
           <SaySomething2    message="SaySomething2 works too..." />
           <SaySomething2    message="{the_answer}" />
       </Sequence>
    </BehaviorTree>
</root>
#include "behaviortree_cpp_v3/bt_factory.h"

// file that contains the custom nodes definitions
#include "dummy_nodes.h"

int main()
{
    using namespace DummyNodes;

    BehaviorTreeFactory factory;

    factory.registerNodeType<SaySomething>("SaySomething");
    factory.registerNodeType<ThinkWhatToSay>("ThinkWhatToSay");

    // SimpleActionNodes can not define their own method providedPorts().
    // We should pass a PortsList explicitly if we want the Action to 
    // be able to use getInput() or setOutput();
    PortsList say_something_ports = { InputPort<std::string>("message") };
    factory.registerSimpleAction("SaySomething2", SaySomethingSimple, 
                                 say_something_ports );

    auto tree = factory.createTreeFromFile("./my_tree.xml");

    tree.tickRoot();

    /*  Expected output:

        Robot says: start thinking...
        Robot says: The answer is 42
        Robot says: SaySomething2 works too...
        Robot says: The answer is 42
    */
    return 0;
}

通过指向黑板上的同一个项且该项的类型相同来彼此互连;

原文地址:https://www.cnblogs.com/gary-guo/p/14699666.html