使用行为树控制机器人(二) —— 黑板


使用行为树控制机器人(一) —— 节点
使用行为树控制机器人(二) —— 黑板
使用行为树控制机器人(三) —— 通用端口

使用行为树控制机器人(一) —— 节点 实现了节点执行,但节点间是封闭的无法进行数据传递的,此文的目的即是解决节点间的数据传输问题 —— 黑板。
学习时参考链接:ROS机器人行为树教程

一、概念

黑板” 是一个简单的键/值存储,由树的所有节点共享。黑板上的一个 "条目 "是一个键/值对。输入端口可以读取黑板上的一个条目,而输出端口可以写入一个条目。
下面创建一个行为树逻辑:创建一个ThinWhatToSay节点 使用一个输出端口通过键 topic 写入黑板条目,然后创建另一个节点SaySomething 使用一个输入端口通过键 topic 读取黑板条目该键指向的对应值并将其内容进行打印 (下图中键 topic 其值为"The answer is 42")。

在这里插入图片描述

其中,Script Input 用于调试,可使用内置的名为"Script"的动作将一个静态值写入条目中。

<Script code=" topic1:='message by script' " />

二、黑板功能实现

1. 功能实现

1.1 头文件定义

#ifndef BEHAVIOR_TREE_NODES_H
#define BEHAVIOR_TREE_NODES_H

#include "behaviortree_cpp/bt_factory.h"
#include <iostream>

namespace BT
{

// 带输入端口的同步动作节点
class SaySomething : public SyncActionNode
{
public:
  SaySomething(const std::string& name, const NodeConfig& config);

  // 必须实现静态端口声明方法
  static PortsList providedPorts();

  // 节点执行函数
  NodeStatus tick() override;
};

// 带输出端口的同步动作节点
class ThinkWhatToSay : public SyncActionNode
{
public:
  ThinkWhatToSay(const std::string& name, const NodeConfig& config);

  // 端口声明
  static PortsList providedPorts();

  // 节点执行函数
  NodeStatus tick() override;
  std::string _info = "The answer is 42";
};

} // namespace BT

#endif // BEHAVIOR_TREE_NODES_H

注意自定义的TreeNode有输入和/或输出端口时,这些端口必须在静态方法中声明,可以使用模板方法TreeNode::getInput(key)来读取端口消息的输入。

static PortsList providedPorts();

1.2 源文件实现

#include "behavior_tree_nodes.h"

using namespace BT;

// SaySomething 实现
SaySomething::SaySomething(const std::string& name, const NodeConfig& config) : SyncActionNode(name, config)
{}

PortsList SaySomething::providedPorts()
{
  return { InputPort<std::string>("message") };
}

NodeStatus SaySomething::tick()
{
  // 直接使用 BT::Expected 类型
  BT::Expected<std::string> msg = getInput<std::string>("message");
  
  // 检查是否成功获取输入
  if (!msg)
  {
    throw BT::RuntimeError("missing required input [message]: " + msg.error());
  }
  
  // 输出消息
  std::cout << "Robot says: " << msg.value() << std::endl;
  return NodeStatus::SUCCESS;
}

// ThinkWhatToSay 实现
ThinkWhatToSay::ThinkWhatToSay(const std::string& name, const NodeConfig& config) : SyncActionNode(name, config)
{}

PortsList ThinkWhatToSay::providedPorts()
{
  return {
      InputPort<std::string>("info"),  // 添加输入端口
      OutputPort<std::string>("text")
      };
}

NodeStatus ThinkWhatToSay::tick() {

  auto msg = getInput<std::string>("info");
  
  // 检查是否成功获取输入
  if (!msg)
  {
    throw BT::RuntimeError("missing required input [info]: " + msg.error());
  }
  _info = msg.value();

  setOutput("text", _info);
  return NodeStatus::SUCCESS;
}

1.3 main文件实现

#include "behavior_tree_nodes.h"
#include "behaviortree_cpp/bt_factory.h"

int main(int argc, char *argv[])
{
  std::string think_info="The answer is 43";
  BT::BehaviorTreeFactory factory;
  factory.registerNodeType<BT::SaySomething>("SaySomething");
  factory.registerNodeType<BT::ThinkWhatToSay>("ThinkWhatToSay");

  if (argc > 1) {
    think_info = argv[1];
    std::cout << "Using command line argument: " << think_info << std::endl;
  } else {
    std::cout << "Using default value: " << think_info << std::endl;
  }

  try {
    auto tree = factory.createTreeFromFile("../trees/my_tree.xml");
    tree.rootBlackboard()->set("topic1", think_info); // 设置黑板参数
    std::cout << "------ Behavior Tree Structure ------" << std::endl;
    BT::printTreeRecursively(tree.rootNode());
    std::cout << "------------------------------------" << std::endl;
    tree.tickWhileRunning();
  } catch (const std::exception& e) {
    std::cerr << "Error: " << e.what() << std::endl;
    return 1;
  }

  return 0;
}

1.4 my_tree.xml 实现

在这里插入图片描述

行为树实现逻辑如上,xml文件定义如下,使用相同的键(topic) 将输出端口与输入端口 “连接” 起来:

<root BTCPP_format="4" main_tree_to_execute="MainTree">
  <BehaviorTree ID="MainTree">
    <Sequence name="root_sequence">
      <!-- Script code 注释,可命令动态传参 -->
      <Script code=" topic1:='message by script' " />
      <!-- 直接传递字符串 -->
      <SaySomething name="say_hello" message="hello"/>
      
      <!-- 通过黑板传递 -->
      <ThinkWhatToSay name="think" text="{topic}" info="{topic1}"/>
      <SaySomething name="say_answer" message="{topic}"/>
    </Sequence>
  </BehaviorTree>
</root>

2. 执行结果

上述行为树执行结果如下:
在这里插入图片描述

如不想通过 <Script code=" topic1:='message by script' " /> ,而是通过命令动态传参,且只需将其注释,重新运行即可。

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值