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

本文主要分析BehaviorTree.CPP/src/xml_parsing.cpp的内容,因为函数代码都很长,就省略了代码,大家可以与源文件对照理解。搞清楚行为树的解析、加载、构建过程,有利于对其设计思路有更深刻的理解,但是对行为树的使用影响不大,可以跳过。

我认为行为树的精华在于blackboard的设计,实现了节点间、树间的数据共享,但代码层次较深,理解花费时间较多,待以后补充,大家可以期待一下。


1、BehaviorTreeFactory::createTreeFromText()

树的加载和创建由createTreeFromText() 实现,该函数的第2个参数具有默认参数,即初创建的blackboard,是一个局部变量,但是由智能指针指向它。因此,只要引用计数大于0,该变量仍然不会释放,可以访问得到。

Tree createTreeFromText(const std::string& text,Blackboard::Ptr blackboard = Blackboard::create());
Tree BehaviorTreeFactory::createTreeFromText(const std::string& text,Blackboard::Ptr blackboard) {XMLParser parser(*this);// 加载和解析文本,检查各项元素是否符合BT的概念要求。parser.loadFromText(text);// 创建树和所有节点的实例,构造好树之间、节点之间的父子关系,port的映射关系等。auto tree = parser.instantiateTree(blackboard);// 将树的节点信息绑定给树实例变量tree.manifests = this->manifests();return tree;
}

createTreeFromText() 主要有3部分。其中的manifests包含了树的所有节点类型信息,其实节点的builder和manifest在树建立之前已经通过register函数传给factory变量了。

template <typename T>
void registerNodeType(const std::string& ID, PortsList ports) {...registerBuilder(CreateManifest<T>(ID, ports), CreateBuilder<T>());
}
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});
}

2、XMLParser::loadFromText()

具体由XMLParser::Pimpl::loadDocImpl()执行,主要有如下几个步骤。

  1. 第1个for循环,递归加载本xml中所include的子树xml文件,先加载子树,再加载外层树,相当于深度优先搜索。
  2. 第2个for循环,遍历本xml文件中的树的名称或ID(相当于树的根节点),保存在类XMLParser::Pimpl的成员变量tree_roots中。
  3. 第3、4个for循环,将构造树之前就注册的所有节点,和2中读取的树的根节点,都存入局部变量std::set<std::string> registered_nodes; 然后将其传入VerifyXML()。
  4. VerifyXML()负责检查树的设计要求是否满足。检查项有:
    1. TreeNodesModel标签是否合法,主要用于Groot可视化。
    2. 各种node的子节点数量是否合法,是否有ID。比如ControlNode至少有1个子节点,DecoratorNode只有1个子节点,Subtree没有子节点。
    3. 是否有未注册的不认识的节点。
    4. 针对非subtree节点进行递归检查。
    5. 是否指定main_tree_to_execute 标签。如果有多个BehaviorTree,则必须指定main_tree_to_execute,如果只有1个BehaviorTree,就不需要指定。

3、XMLParser::instantiateTree()

分为2个部分。

  1. 构造了行为树的实例——局部变量output_tree,将传入的blackboard(即上文创建的智能指针指向的blackboard)保存入 output_tree.blackboard_stack。
  2. 调用recursivelyCreateTree(),传入主树(最外层树)的ID、tree局部变量、blackboard(还是刚才同一个智能指针)、TreeNode指针(空指针nullptr,作为根节点)。
Tree XMLParser::instantiateTree(const Blackboard::Ptr& root_blackboard) {Tree output_tree;...// first blackboardoutput_tree.blackboard_stack.push_back(root_blackboard);_p->recursivelyCreateTree(main_tree_ID, output_tree, root_blackboard,TreeNode::Ptr());return output_tree;
}

接下来对recursivelyCreateTree()展开分析。

4、XMLParser::Pimpl::recursivelyCreateTree()

函数内递归执行recursiveStep(),注意第1个参数是父节点。

void BT::XMLParser::Pimpl::recursivelyCreateTree(const std::string& tree_ID, Tree& output_tree, Blackboard::Ptr blackboard,const TreeNode::Ptr& root_parent) {std::function<void(const TreeNode::Ptr&, const XMLElement*)> recursiveStep;recursiveStep = [&](const TreeNode::Ptr& parent, const XMLElement* element) {...};auto root_element = tree_roots[tree_ID]->FirstChildElement();// start recursionrecursiveStep(root_parent, root_element);
}

recursiveStep()分为3部分。

  1. 调用XMLParser::Pimpl::createNodeFromXML()创建节点实例,将该实例保存在树的std::vector<TreeNode::Ptr> nodes 成员变量中。
  2. 如果该节点是SUBTREE类型的,细分SubtreeNode和SubtreePlusNode来处理。
    1. 如果是SubtreeNode,就根据__shared_blackboard的值来创建blackboard,并添加映射信息,然后递归调用recursivelyCreateTree()来创建子树。

    2. 如果是SubtreePlusNode,就根据__autoremap的值来创建blackboard的port的映射,然后递归调用recursivelyCreateTree()来创建子树。

  3. 如果该节点不是SUBTREE类型的,递归调用recursiveStep(),并把该节点作为接下来待创建节点的父节点。如果该节点没有其他包含的元素了,就不再递归了,从recursiveStep()返回,进而从recursivelyCreateTree()返回,进而从instantiateTree() 返回。

5、createNodeFromXML()

  1. 对非subtree的节点,将port映射的key和value保存入局部变量PortsRemapping port_remap。
  2. 对于有remap的节点,在blackboard中通过Blackboard::setPortInfo() 添加port映射信息, 并在父树的blackboard的相同key也保存相同port信息。基于此,实现了父子树之间的blackboard对相同key的同一性关联。
  3. 使用manifest中保存的信息,初始化NodeConfiguration。即在NodeConfiguration的input_ports和output_ports集合中添加存在外部映射的port。
  4. 对于不存在外部映射的port,对其中的InputPort赋默认值,并存入NodeConfiguration的input_ports集合中。
  5. config构造完成,调用 instantiateTreeNode() 来实例化子节点。
  6. 若传入的父节点有效,根据父节点的类型,为其添加子节点。

BT12:从xml创建加载行为树的过程分析相关推荐

  1. c#如何实现从xml中加载树目录,并且显示完整的Text

    谈到xml,相信有一定编程基础的童鞋们都比较清楚了.xml文件格式类似于嵌套的样式,这种独特的层次结构很容易使我们联想到目录树,因此这不禁让我们对两者之间产生一种牵线的想法.这不禁,使我们有这么一个问 ...

  2. Extjs中加载异步树的最简单例子实现

    <!-- Author:Lovingshu&&Mr Samael Date:2012/06/25 Remark:Teach You How To Build A AsyncTre ...

  3. web.xml 配置 加载顺序

    web.xml 的加载顺序是:context-param -> listener -> filter -> servlet . 过滤器执行顺序是根据filter-mapping ,不 ...

  4. web.xml 组件加载顺序

    转载自  web.xml 组件加载顺序 在配置项目组件的过程中, 了解Tomcat加载组件顺序很有必要. 例如某些框架如Quartz的集群功能需要数据库的支持, 数据库的加载肯定要在框架组件加载之前. ...

  5. 伟景行citymaker-----01.javascript打开本地模型CEP,加载目录树,加载要素类

    以下所有代码基于 CityMaker_IE_Plugin_vConnect8.0.171127.exe 版本 该版本只能使用IE打开,建议使用IE11 下载代码案例 1.打开cep模型代码 1.1  ...

  6. html如何添加加载动画效果,CSS3创建加载动画效果

    加载动画在网页设计中是很常见的.用户们都希望网页加载又快又流畅而不是盯着屏幕苦等,而加载动画能够在内容加载完成前给用户视觉反馈,从而能够吸引用户而不让他们直接放弃继续浏览你的网站. 创建加载效果所需的 ...

  7. “安装centOS7.0出现‘你没有创建加载器第一阶段设备,你没有创建可引导分区’,并提示可用空间不足”的解决方案

    前两天花费了很大力气安装CentOS7,终于安装成功了,现在把过程中遇到的问题和大家分享一下,希望能对大家有帮助. 具体问题如下: 1.已经留出足够的未分配磁盘空间,在CentOS7安装界面却显示&q ...

  8. Spring源码分析(1) —— 从Xml的加载到解析

    题外话: 接口&多态 我有一辆自行车,每天骑着它去上班 package com.zhao.SpringIoc;public class Bike {public void go() {Syst ...

  9. Ehcache二级缓配置永不过期,缓存失效问题 ---- 原因没有加载ehcache.xml配置文件,加载了默认的ehcache-failsafe.xml配置文件

    问题: 在ehcache.xml配置文件中的参数 配置的空闲时间,过期时间都很长, 或者说配置的永不过期, 但是在测试项目过程中, 程序还是会有重新查数据库, 配置参数,配置文件好像不生效问题; 解决 ...

最新文章

  1. crf linux使用教程,Linux下CRF++的使用
  2. linux显示界面指令规范,linux的常规操作命令
  3. google i/o_Google I / O 2017最有希望的突破
  4. nginx做负载均衡,解决多机器多gpu卡服务对外暴露一个接口问题
  5. 历时 4 年,阿里云推出金融核心系统转型实践书
  6. 台式计算机如何封存,一种计算机用声卡封存装置的制作方法
  7. R费希尔精确检验(Fisher‘s exact test)
  8. java case 字符_Java中Switch Case使用字符串
  9. EPLAN2.9程序安装及注意事项
  10. CUDA编程学习笔记 之 页锁定内存(固定内存Pinned Memory)
  11. 二氯甲烷废气处理吸附工艺
  12. Java实用工具类-将汉字转为拼音
  13. ios射击类游戏简单代码射击
  14. 极光推送完整流程测试
  15. xorg介绍 xorg和桌面环境的关系
  16. 计算机文化基础知识在未来工作中的应用论文,计算机文化基础论文
  17. (总结)什么是HTML语义化
  18. 【软件测试】白盒测试方法与黑盒测试方法的区别
  19. redux与flux
  20. PSGAN 网络再修改

热门文章

  1. 2021“新四跨”演示成功 中兴推进5G汽车电子网络商用
  2. matlab基础训练—索引
  3. TripletsLoss不是解决距离的银色子弹,而是打开度量学习的大门钥匙
  4. 基于javaweb的网盘系统设计和实现(java+ssm+jpa)
  5. mysql中存储过程 解决参数作为表名
  6. 嵌入式软件工程师介绍
  7. BM(Berlekamp-Massey)算法学习笔记
  8. 孙溟㠭先生篆刻作品“千载寿”
  9. 聚酯纤维,这种面料到底好不好?
  10. 【IT简史】ATT(贝尔电话公司)