大家好,欢迎大家关注我的知乎专栏慢慢悠悠小马车


BehaviorTreeFactory

定义在BehaviorTree.CPP/include/behaviortree_cpp_v3/bt_factory.h,主要包含3个容器来保存数据。

/*** @brief The BehaviorTreeFactory is used to create instances of a* TreeNode at run-time.* Some node types are "builtin", whilst other are used defined and need* to be registered using a unique ID.*/
class BehaviorTreeFactory {
private:std::unordered_map<std::string, NodeBuilder> builders_;std::unordered_map<std::string, TreeNodeManifest> manifests_;std::set<std::string> builtin_IDs_;
}

以BehaviorTree.CPP/examples/t03_generic_ports.cpp为例,在构造BehaviorTreeFactory实例后立即打印这3个容器的元素的名称,可以发现输出结果仅顺序不同,说明Factory构造后默认包含了内建的29个nodes。

builders[1]=Switch4
builders[2]=Switch6
builders[3]=BlackboardCheckDouble
builders[4]=BlackboardCheckInt
builders[5]=SubTree
builders[6]=KeepRunningUntilFailure
builders[7]=Switch5
builders[8]=ReactiveSequence
builders[9]=Parallel
builders[10]=Delay
builders[11]=SetBlackboard
builders[12]=SequenceStar
builders[13]=Fallback
builders[14]=AlwaysSuccess
builders[15]=ReactiveFallback
builders[16]=Sequence
builders[17]=Switch3
builders[18]=Switch2
builders[19]=AlwaysFailure
builders[20]=IfThenElse
builders[21]=WhileDoElse
builders[22]=SubTreePlus
builders[23]=ForceSuccess
builders[24]=Inverter
builders[25]=BlackboardCheckString
builders[26]=RetryUntilSuccesful
builders[27]=ForceFailure
builders[28]=Repeat
builders[29]=Timeoutmanifests[1]=Switch4
manifests[2]=Switch6
manifests[3]=BlackboardCheckDouble
manifests[4]=BlackboardCheckInt
manifests[5]=SubTree
manifests[6]=KeepRunningUntilFailure
manifests[7]=Switch5
manifests[8]=ReactiveSequence
manifests[9]=Parallel
manifests[10]=Delay
manifests[11]=SetBlackboard
manifests[12]=SequenceStar
manifests[13]=Fallback
manifests[14]=AlwaysSuccess
manifests[15]=ReactiveFallback
manifests[16]=Sequence
manifests[17]=Switch3
manifests[18]=Switch2
manifests[19]=AlwaysFailure
manifests[20]=IfThenElse
manifests[21]=WhileDoElse
manifests[22]=SubTreePlus
manifests[23]=ForceSuccess
manifests[24]=Inverter
manifests[25]=BlackboardCheckString
manifests[26]=RetryUntilSuccesful
manifests[27]=ForceFailure
manifests[28]=Repeat
manifests[29]=TimeoutbuiltinNodes[1]=AlwaysFailure
builtinNodes[2]=AlwaysSuccess
builtinNodes[3]=BlackboardCheckDouble
builtinNodes[4]=BlackboardCheckInt
builtinNodes[5]=BlackboardCheckString
builtinNodes[6]=Delay
builtinNodes[7]=Fallback
builtinNodes[8]=ForceFailure
builtinNodes[9]=ForceSuccess
builtinNodes[10]=IfThenElse
builtinNodes[11]=Inverter
builtinNodes[12]=KeepRunningUntilFailure
builtinNodes[13]=Parallel
builtinNodes[14]=ReactiveFallback
builtinNodes[15]=ReactiveSequence
builtinNodes[16]=Repeat
builtinNodes[17]=RetryUntilSuccesful
builtinNodes[18]=Sequence
builtinNodes[19]=SequenceStar
builtinNodes[20]=SetBlackboard
builtinNodes[21]=SubTree
builtinNodes[22]=SubTreePlus
builtinNodes[23]=Switch2
builtinNodes[24]=Switch3
builtinNodes[25]=Switch4
builtinNodes[26]=Switch5
builtinNodes[27]=Switch6
builtinNodes[28]=Timeout
builtinNodes[29]=WhileDoElse

在factory添加了2个node后,再次打印,builders和manifests就多了2个对应的元素。 registerNodeType()的入参,就表明了实际节点类型T(CalculateGoal)在树中的名称(同样是CalculateGoal)。建议大家令实际类型和入参ID相同,可以减少很多迷惑。

factory.registerNodeType<CalculateGoal>("CalculateGoal");
factory.registerNodeType<PrintTarget>("PrintTarget");
/** registerNodeType is the method to use to register your custom TreeNode.**  It accepts only classed derived from either ActionNodeBase, DecoratorNode,*  ControlNode or ConditionNode.*/
template <typename T>
void registerNodeType(const std::string& ID);

在加载tree之前,开发者必须先将自定义的node注册进入factory,才能正确解析xml。1个factory可以包含多个tree。

auto tree = factory.createTreeFromText(xml_text);

NodeBuilder

NodeBuilder定义在BehaviorTree.CPP/include/behaviortree_cpp_v3/bt_factory.h,使用了建造者模式,可以理解为模板化的node构造函数,是偏特化的模板类定义。根据构造函数的参数个数区别,选择不同的builder,返回一个智能指针。

/// The term "Builder" refers to the Builder Pattern (https://en.wikipedia.org/wiki/Builder_pattern)
typedef std::function<std::unique_ptr<TreeNode>(const std::string&, const NodeConfiguration&)>NodeBuilder;// 检查T是否有带参(const std::string&)的默认构造函数
template <typename T>
using has_default_constructor = typename std::is_constructible<T, const std::string&>;// 检查T是否有带参(const std::string&, const NodeConfiguration&)的构造函数
template <typename T>
using has_params_constructor =typename std::is_constructible<T, const std::string&, const NodeConfiguration&>;// 对应T既有默认构造函数,又有带2个参数构造函数的情况,即上述2条判断都为true。注意make_unique的参数区分
// 定义了1个T类型的指针,默认值是nullptr。这不重要,重要的是enable_if的条件检查
template <typename T>
inline NodeBuilder
CreateBuilder(typename std::enable_if<has_default_constructor<T>::value &&has_params_constructor<T>::value>::type* = nullptr) {return [](const std::string& name, const NodeConfiguration& config) {// Special case. Use default constructor if parameters are emptyif (config.input_ports.empty() && config.output_ports.empty() &&has_default_constructor<T>::value) {return std::make_unique<T>(name);}return std::make_unique<T>(name, config);};
}// 对应T只有带2参构造函数的情况,注意new的参数
template <typename T>
inline NodeBuilder
CreateBuilder(typename std::enable_if<!has_default_constructor<T>::value &&has_params_constructor<T>::value>::type* = nullptr) {return [](const std::string& name, const NodeConfiguration& params) {return std::unique_ptr<TreeNode>(new T(name, params));};
}// 对应T只有默认构造函数(带1参)的情况,注意new的参数
template <typename T>
inline NodeBuilder
CreateBuilder(typename std::enable_if<has_default_constructor<T>::value &&!has_params_constructor<T>::value>::type* = nullptr) {return [](const std::string& name, const NodeConfiguration&) {return std::unique_ptr<TreeNode>(new T(name));};
}

语法参考资料:

​​is_constructible - C++ Reference

std::enable_if - cppreference.com

std::enable_if 的几种用法 ← Yee

C++11新特性--std::enable_if和SFINAE - 简书

Blackboard

定义在BehaviorTree.CPP/include/behaviortree_cpp_v3/blackboard.h,是树中nodes传输数据的方式,所有nodes共享。每棵树都有自己的blackboard,开发者需要显式的在不同的父树、子树的blackboard间创建映射关联。这个操作无需通过代码实现,只需要在xml中编辑。同样,nodes执行顺序也是在xml中设定的,这使得调试时可以节省大量的程序编译时间。Blackboard类中存储数据的容器有3个。

private:std::unordered_map<std::string, Entry> storage_;  // 保存键值对// 指向父blackboard的指针,会与本blackboard有重映射关系std::weak_ptr<Blackboard> parent_bb_; // weak_ptr可以避免循环引用// 保存映射的内外对应关系std::unordered_map<std::string,std::string> internal_to_external_;

Entry就是blackboard保存的1个元素。

struct Entry {Any value;  // port存储的值const PortInfo port_info; // port的其他信息,不含名字和值
}

Port

Port是节点间交换数据的机制,通过Blackboard的相同key联系起来。节点的ports的数量、名称、类型等,在编译时就已知了,体现在xml文件中。

如果一个节点有输入/输出port,必须在providedPorts()函数中声明。

获取port的值可以用getInput(),设置port的值可以用setOutput()。

typedef std::unordered_map<std::string, PortInfo> PortsList;static PortsList providedPorts();template <typename T>
Result getInput(const std::string& key, T& destination) const;template <typename T>
Optional<T> getInput(const std::string& key) const;template <typename T>
Result setOutput(const std::string& key, const T& value);
class PortInfo {
private:PortDirection _type;const std::type_info* _info;// 将port输入/输出的string自动转换为指定类型的函数StringConverter _converter;std::string description_; // port的含义描述std::string default_value_; // port没有设置值时的默认值
}
typedef std::function<Any(StringView)> StringConverter;

PortDirection分为INPUT、OUTPUT、INOUT 3种。开发者最好不要对输入的port和数据做任何假设,推荐在tick()中周期性的读取输入值,以应对外界变化,而不是只在构造函数或初始化时只读取一次保存下来。保存历史信息会伤害行为树的reactive特性,使得节点的行为不仅与现在情景有关,还与过去有关。

template <typename T>
inline T convertFromString(StringView /*str*/);

当在xml中通过SetBlackboard设置port后,代码中运行getInput()会后台调用convertFromString(),将读入的string转换为对应的类型。BehaviorTree.CPP/include/behaviortree_cpp_v3/basic_types.h中实现了StirngView向string, const char*, int, unsigned, long, unsigned long, float, double, vector<int>, bool, NodeStatus, Nodetype, PortDirection等内建类型的转换。如果开发者想转换为自定义类型的话,可以参考basic_types.cpp的代码。

树中的nodes间互相读写port是不会触发convertFromString()的,毕竟这是一个string到type的单向转换。

// 转换为枚举类型
template <>
NodeType convertFromString<NodeType>(StringView str) {if (str == "Action") return NodeType::ACTION;if (str == "Condition") return NodeType::CONDITION;if (str == "Control") return NodeType::CONTROL;if (str == "Decorator") return NodeType::DECORATOR;if (str == "SubTree" || str == "SubTreePlus") return NodeType::SUBTREE;return NodeType::UNDEFINED;
}
struct Position2D {double x, y;
};// 转换为自定义类型
template <>
inline Position2D convertFromString(StringView str) {// real numbers separated by semicolonsauto parts = splitString(str, ';');if (parts.size() != 2) {throw RuntimeError("invalid input)");} else {Position2D output;output.x = convertFromString<double>(parts[0]);output.y = convertFromString<double>(parts[1]);return output;}
}

std::optional - cppreference.com

std::any - cppreference.com

C++17之std::any_janeqi1987的专栏-CSDN博客_std::any

BT4:库中基本类型——Factory和Blackboard相关推荐

  1. Swift HandyJSON库中的类型相互转换的实现

    前言 阅读优秀的开源框架,对提升自己的能力有很大帮助.HandyJSON库就是其中的优秀框架之一, 本文介绍一下HandyJSON库是如何处理类型间相互转换的. 我们在开发时,常见的类型转换如下: D ...

  2. python怎么显示提示_Python中的类型提示(中)

    Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 3.接口存根文件 这个选项允许你如下图一般保存你的代码: 并在原文件的旁边添加一个扩展名为pyi的文件: ...

  3. 为什么写了value属性 jq赋值value值不显示_[Go基础]理解 Go 标准库中的 atomic.Value 类型

    转载声明 文章作者:喵叔 上次更新:2019-03-15 许可协议:CC BY-NC-ND 4.0(转载请注明出处) 原文链接:https://blog.betacat.io/post/golang- ...

  4. java jceks 密钥_Java中不同类型的密钥库(Keystore) – 概述

    阅读: 877 密钥库是用于存储加密密钥和证书的存储工具 ,最常用于SSL通信,以证明服务器和客户端的身份.密钥库可以是文件或硬件设备.有三种类型的条目可以存储在密钥库中,取决于密钥库的类型,这三种类 ...

  5. 计算机oleaut32.dll,OLEAUT32.dll模块中处理类型库的相关函数可导致代码执行 -电脑资料...

    tombkeeper[Base64Decode("dG9tYmtlZXBlckB4Zm9jdXMub3Jn")] 2009.10.1 刚下载完几部不错的片子,但是考虑到做人要讲信用 ...

  6. 如何将AD类型的封装导成Allegro库中的封装

    1.首先使用的是LCSC下载的封装,以电容0201封装为例.下载下来格式如下: 2.中间需要使用PADS转一下格式,转成asc格式的.AD的不知道可不可以直接转,我也是之前在某个教程中学的,可能操作复 ...

  7. Openzwave库中对Zwave产品配置文件的使用

    Openzwave库中对Zwave产品配置文件的使用 在openzwave库中通过配置文件定义一些可配置参数,对于每一个zwave命令类来说,我们都可以通过配置文件定义这些参数:在openzwave中 ...

  8. 全面理解Python中的类型提示(Type Hints)

    众所周知,Python 是动态类型语言,运行时不需要指定变量类型.这一点是不会改变的,但是2015年9月创始人 Guido van Rossum 在 Python 3.5 引入了一个类型系统,允许开发 ...

  9. Dictionary作为数据源绑定,调用c++库中返回为BYTE*的函数,listView项排序

    最近在做一个电子档案管理的项目.现在还处于初期,只是做一个简单demo拿去跟客户演示.至于最后谈不谈得下来,到底做不做,反正我是不看好,但没因为这样就马马虎虎.草草了事.这个项目算是b/s加c/s混合 ...

最新文章

  1. UI自动化测试中的页面定位问题,年薪50W软件测试工程师为你解答
  2. 学习深度网络需要直观的感知
  3. MySQL—相关子查询
  4. LeetCode Algorithm 811. 子域名访问计数
  5. 操作系统【六】虚拟内存
  6. 用idea新建springboot项目遇到的@Restcontroller不能导入的问题
  7. Django模板自定义标签和过滤器,模板继承(extend),Django的模型层
  8. python函数传递列表_python传递列表作为函数参数
  9. for...in、for...of、forEach()有什么区别
  10. python网络爬虫从入门到精通吕云翔pdf_Python 网络爬虫从入门到精通
  11. 中国邮递员问题的深入剖析与算法实现(附例题及MATLAB、LINGO代码)
  12. 系统集成项目管理工程师11《项目风险管理》
  13. html古诗竖版,古诗词竖版图片
  14. Web.xml 错误或异常页面配置
  15. [解题报告]【第16题】给定 n,打印一个直角边为 n 的等边直角三角形
  16. 用Redis存取两个人的共同好友名单
  17. 为什么unity中我的模型是红颜色的
  18. 【Vue】webpack的基本使用
  19. 烤仔的朋友们丨政策暖风吹来,国内公链们的春天来了?
  20. MEEGO系统七大优势

热门文章

  1. wallhaven壁纸下载-selenium版本
  2. SAPABAP金色传说:KE30报表增加特征筛选字段的示例方法
  3. xp 恢复 简体中文 美式键盘
  4. NoteExpree对参考文献格式修改
  5. Halcon学习---深度学习篇segment2~训练模型。
  6. iOS开发简记(1):指定APP的图标与启动图
  7. dy是怎么算收益的?武汉新时标文化传媒
  8. 无需括号的xss payload
  9. 一日一技:看视频用这个太爽了!自动实时翻译英语视频
  10. HLS / Chisel 利用CORDIC双曲系统实现平方根计算