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

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

2023年6月22日发(作者:)

BT12:从xml创建加载⾏为树的过程分析⼤家好,欢迎⼤家关注我的知乎专栏本⽂主要分析/src/xml_的内容,因为函数代码都很长,就省略了代码,⼤家可以与源⽂件对照理解。搞清楚⾏为树的解析、加载、构建过程,有利于对其设计思路有更深刻的理解,但是对⾏为树的使⽤影响不⼤,可以跳过。我认为⾏为树的精华在于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的概念要求。 omText(text); // 创建树和所有节点的实例,构造好树之间、节点之间的⽗⼦关系,port的映射关系等。 auto tree = tiateTree(blackboard); // 将树的节点信息绑定给树实例变量 sts = this->manifests(); return tree;}createTreeFromText() 主要有3部分。其中的manifests包含了树的所有节点类型信息,其实节点的builder和manifest在树建⽴之前已经通过register函数传给factory变量了。template void registerNodeType(const std::string& ID, PortsList ports) { ... registerBuilder(CreateManifest(ID, ports), CreateBuilder());}void BehaviorTreeFactory::registerBuilder(const TreeNodeManifest& manifest, const NodeBuilder& builder) { auto it = builders_.find(ration_ID); if (it != builders_.end()) { throw BehaviorTreeException("ID [", ration_ID, "] already registered"); } builders_.insert({ration_ID, builder}); manifests_.insert({ration_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::setregistered_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_oard_stack。2. 调⽤recursivelyCreateTree(),传⼊主树(最外层树)的ID、tree局部变量、blackboard(还是刚才同⼀个智能指针)、TreeNode指针(空指针nullptr,作为根节点)。Tree XMLParser::instantiateTree(const Blackboard::Ptr& root_blackboard) { Tree output_tree; ... // first blackboard output_oard__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 recursiveStep; recursiveStep = [&](const TreeNode::Ptr& parent, const XMLElement* element) { ... }; auto root_element = tree_roots[tree_ID]->FirstChildElement(); // start recursion recursiveStep(root_parent, root_element);}recursiveStep()分为3部分。1. 调⽤XMLParser::Pimpl::createNodeFromXML()创建节点实例,将该实例保存在树的std::vector 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. 若传⼊的⽗节点有效,根据⽗节点的类型,为其添加⼦节点。

发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1687386160a6203.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信