行为树介绍

编程中的抽象已经将我们对计算机的使用从基本的算术运算发展到使用模型表示复杂的现实世界现象。更具体地说,对于机器人技术来说,抽象已经将我们从低级的执行器控制和基本传感转变为对更高层次的概念行为的推理,正如《机器人系统学》一文中所定义的那样。

在自治系统中,我们已经看到了除了“普通”编程之外的一大堆抽象,用于行为建模和执行。您可能会在文献中找到一些常见的程序,包括远程反应程序Petri 网有限状态机 (FSM) 和行为树 (BT)。根据我的经验,FSM 和 BT 是您今天最常看到的两个抽象概念。

在这篇文章中,我将介绍行为树及其所有术语,将它们与有限状态机进行对比,分享一些示例和软件库,如果您想了解更多信息,将一如既往地为您留下一些资源。

什么是行为树?

行为树的节点按照大类可分为:根节点(Root)、行为节点(Action)、条件节点(Conditional)、组合节点(Composite)、装饰节点(Decorator)。其中,组合节点包括了选择节点与序列节点。常用的非叶节点有选择节点和序列节点。常用的叶节点有条件节点和动作节点(行为节点)

根节点Root:行为树的最顶部节点,也是入口节点。

行为(动作)节点ActionNode:没有子节点,用以执行具体行为的节点,当动作完成后会返回“成功”;当动作无法完成会返回“失败”;如果动作正在进行中,会返回“运行中”。

条件节点ConditionNode:条件节点代表了一个判断,没有子节点,条件满足会返回“成功”状态;否则返回“失败”状态。

组合节点用于连接多个子节点,比较常见的组合节点有选择节点与序列节点。

选择节点Fallback

选择节点按照自左向右的顺序计算每个子节点,一旦某个子节点返回了“成功”或“运行中”的状态,那么选择节点就会立刻将自身的状态相应地更改为“成功”或“运行中”,并不再执行后面的节点。选择节点类似于一种按照一定顺序判断的“或”逻辑,一旦存在一个成功执行的子节点,后续的子节点将不再执行。

序列节点Sequence

序列节点按照自左向右的顺序计算每个子结点,一旦某个子结点返回了“失败”或“运行中”的状态,那么序列节点就会立刻将自身的状态相应地更改为“失败”或“运行中”,并不再执行后面的节点。序列节点类似于“且”的逻辑,每个子节点按照顺序依次执行,一旦有一个子节点未能成功执行,后续的子节点将不会被调用。如果按照顺序所有的节点都返回了成功的状态,那么序列节点的状态对应被更新为“成功”。

装饰节点DecoratorNode:只有一个子节点,用以执行特定逻辑。

正如上面所介绍的,有几个抽象可以帮助设计自治代理的复杂行为。通常,它们由一组有限的实体组成,这些实体映射到我们系统中的特定行为或操作模式,例如,“向前移动”、“关闭抓手”、“闪烁警告灯”、“前往充电站”。每个模型类都有一组规则,这些规则描述了代理应何时执行这些行为,更重要的是,代理应如何在它们之间切换。

行为树 (BT) 就是这样一种抽象,我将通过以下特征来定义它:

  1. 行为树是树 (duh):它们从根节点开始,设计为按特定顺序遍历,直到达到最终状态(成功或失败)。
  2. 叶节点是可执行行为:每个叶子都会做一些事情,无论是简单的检查还是复杂的操作,都会输出状态(成功、失败或正在运行)。换句话说,叶节点是将 BT 连接到特定应用程序的较低级别代码的地方。
  3. 内部节点控制树遍历:树的内部(非叶)节点将接受其子节点的结果状态,并应用自己的规则来指示接下来应该扩展哪个节点。

行为树实际上始于视频游戏行业,用于定义非玩家角色 (NPC) 的行为:虚幻引擎和 Unity(该领域的两大力量)都有专门的 BT 创作工具。这并不奇怪;BT 的一大优点是它们易于编写和修改,即使在运行时也是如此。但是,与其他一些抽象相比,这牺牲了设计反应行为(例如,模式切换)的易用性,您将在本文后面看到。

从那时起,BT也进入了机器人领域,因为机器人越来越有能力完成不仅仅是简单的重复性任务。毫无疑问,这里最好的资源是 Michele Colledanchise 和 Petter Ögren 的教科书“机器人和 AI 中的行为树:简介”。

行为树术语

让我们深入研究一下行为树中的术语。虽然该语言在文献和各种软件库中并不是标准的,但我将在很大程度上遵循《机器人和人工智能中的行为树》中的定义。

乍一看,这些是构成行为树的节点类型以及它们如何以图形方式表示:

行为树在称为刻度的离散更新步骤中执行。当 BT 被tick时,通常以某个指定的速率,其子节点会根据树的构造方式递归tick。节点tick后,它会向其父节点返回一个状态,该状态可以是 Success、Failure 或 Running。

执行节点是 BT 的叶子,可以是 Action 节点,也可以是 Condition 节点。唯一的区别是条件节点只能在单个刻度内返回 Success 或 Failure,而动作节点可以跨越多个刻度,并且可以返回 Running,直到它们达到最终状态。通常,条件节点表示简单的检查(例如,“夹持器是否打开?”),而动作节点表示复杂的操作(例如,“打开门”)。

控制节点是内部节点,并定义如何根据其子节点的状态遍历 BT。重要的是,控制节点的子节点可以是执行节点,也可以是控制节点本身。Sequence、Fallback 和 Parallel 节点可以有任意数量的子节点,但它们处理所述子节点的方式不同。装饰器节点必须有一个子节点,并使用一些自定义策略修改其行为。

请查看下面的图片,了解不同的控制节点是如何工作的。

序列节点按顺序执行子节点,直到一个子节点返回 Failure 或所有子节点返回 Success。

回退节点按顺序执行子节点,直到其中一个子节点返回 Success 或所有子节点返回 Failure。这些节点是为自治代理设计恢复行为的关键。

并行节点将“并行”执行其所有子节点。这是用引号括起来的,因为它不是真正的并行性;在每次滴答时,每个子节点将按顺序单独滴答。当至少有 M 个子节点(介于 1 到 N 之间)成功时,并行节点返回 Success,当所有子节点都失败时,并行节点返回 Failure。

装饰器节点使用自定义策略修改单个子节点。装饰器有自己的一套规则,用于更改“装饰节点”的状态。例如,“Invert”装饰器会将 Success 更改为 Failure,反之亦然。虽然装饰器可以增加行为树库的灵活性,但应该尽可能坚持使用标准控制节点和通用装饰器,以便其他人可以轻松理解其设计。

机器人示例:搜索对象

理解上一节中所有术语和图形的最佳方法是通过示例。假设我们有一个移动机器人,它必须在家庭环境中搜索特定物体。假设机器人事先知道所有的搜索位置;换句话说,它已经有了一个可以运作的世界模型。

让我们从简单开始。如果只有一个位置(我们称之为 A),那么 BT 是必要操作的简单序列:转到该位置,然后查找对象。

选择将导航表示为动作节点,因为机器人可能需要一些时间才能移动(在此过程中返回 Running)。另一方面,我们将视觉表示为一个条件节点,假设机器人可以在物体到达目的地后从单个图像中检测到物体。我承认,这完全是为了显示每个执行节点的一个而设计好的。

你应该知道的一个非常常见的设计原则在书中被定义为明确的成功条件。简单来说,您几乎应该在采取行动之前进行检查。例如,如果您已经处于特定位置,为什么不在开始导航操作之前检查您是否已经在那里呢?

显式成功条件使用回退节点,并在操作之前有一个条件。仅当未满足成功条件时,受保护的操作才会执行 - 在此示例中,如果机器人不在位置 A

我们的机器人可能在具有多个位置的环境中运行,我们的想法是查看所有可能的位置,直到我们找到感兴趣的对象。这可以通过引入根级回退节点并按某些指定顺序对每个位置重复上述行为来实现。

我们还可以使用 Fallback 节点来定义反应性行为;也就是说,如果一个行为不起作用,请尝试下一个行为,依此类推。

最后,假设我们不想寻找单个物体,而是要考虑几个物体——比如苹果和橙子。这种组合条件的用例可以使用并行节点来完成,如下所示。

  • 如果我们接受苹果或橙子(“OR”条件),那么如果一个节点返回 Success,则我们成功。
  • 如果我们同时需要一个苹果和一个橙子(“AND”条件),那么如果两个节点都返回 Success,我们就会成功。
  • 如果我们关心对象的顺序,例如,你必须先找到一个苹果,然后才能找到一个橙子,那么这可以通过一个序列节点来完成。

当然,也可以并行编写动作 - 例如,原地转弯,直到连续 5 次tick检测到一个人。虽然我希望例子足够简单,可以理解基础知识,但我强烈建议查看文献,以获取真正展示 BT 功能的更复杂的示例。

重新审视机器人示例:装饰器和黑板

我不知道你是怎么想的,但看着上面的BT让我有些不安。这只是在回退节点下多次复制和粘贴的相同行为。如果有 20 个不同的位置,并且每个位置的行为涉及的不仅仅是两个简化的执行节点,那会怎样?事情可能很快就会变得一团糟。

在大多数面向 BT 的软件库中,可以将这些执行节点定义为共享资源的参数化行为(例如,用于导航的同一 ROS 动作客户端,或用于视觉的对象检测器)。同样,可以编写代码来自动构建复杂的树,并从现成的子树库中组合它们。因此,问题不在于效率,而在于可读性。

此 BT 还有另一种实现方式,可以扩展到许多其他应用程序。基本思路如下:

  • 介绍装饰器:不要为每个位置复制相同的子树,而是使用单个子树并使用策略对其进行修饰,该策略会重复该行为直到成功。
  • 在每次迭代时更新目标位置:假设您现在有一个要访问的目标位置的“队列”,因此在子树的每次迭代中,您都会从该队列中弹出一个元素。如果队列最终是空的,那么我们的 BT 就会失败。

在大多数 BT 中,我们经常需要一些共享数据的概念,例如我们正在讨论的位置队列。这就是黑板概念的用武之地:你会在大多数BT库中找到黑板结构,它们实际上只是一个公共存储区域,个人行为可以在这里读取或写入数据。

我们的示例 BT 现在可以按如下方式重构。我们引入了一个“GetLoc”操作,该操作从我们的已知位置队列中弹出一个位置,并将其作为某个参数target_location写入黑板。如果队列为空,则返回 Failure;否则,它将返回 Success。然后,处理导航的下游节点可以使用此target_location参数,该参数在子树重复时会更改。

可以使用 Blackboards 执行许多其他任务。这是我们示例的另一个扩展:假设在找到一个物体后,机器人应该与它检测到的物体(如果有的话)交谈。因此,“FoundApple”和“FoundOrange”条件可以写入黑板中的located_objects参数,随后的“Speak”操作将相应地读取它。例如,如果机器人需要拾取检测到的物体,并且根据物体的类型具有不同的操作策略,则可以应用类似的解决方案。

行为树与有限状态机

作为一个对“BT与FSM有何不同”进行了很多思考的人,我想重申它们都有其优点和缺点,你能做的最好的事情就是了解什么时候一个问题更适合一个或另一个(或两者兼而有之)。

《机器人与人工智能中的行为树》一书以更严谨的方式扩展了这些思想,但以下是我试图总结关键思想的尝试:

  • 从理论上讲,可以将任何内容表示为 BT、FSM、其他抽象之一或纯代码。然而,每种模型都有其自身的优点和缺点,因为它们旨在帮助更大规模的设计。
  • 具体到BT与FSM,模块化和反应性之间存在权衡。通常,BT 更容易组合和修改,而 FSM 在设计反应行为方面具有优势。

让我们使用另一个机器人示例来更深入地进行这些比较。假设我们有一个拣选任务,机器人必须移动到一个物体上,通过关闭抓手来抓住它,然后回到它的原始位置。可以在下面找到 BT 和 FSM 的并排比较。对于像这样的简单设计,两种实现都相对干净且易于遵循。

行为树(左)和有限状态机(右)用于我们的机器人拾取示例。

现在,如果我们想修改此行为,会发生什么?假设我们首先要检查预抓取位置是否有效,并在必要时进行纠正,然后再关闭夹持器。使用 BT,我们可以直接沿着我们想要的动作序列插入一个子树,而使用 FSM,我们必须重新连接多个过渡。这就是我们声称 BT 非常适合模块化的意思。

对 BT(左)和 FSM(右)的修改,如果我们想添加预抓取校正行为。

另一方面,存在反应性问题。假设我们的机器人使用有限的电源运行,因此如果电池电量不足,它必须在返回任务之前返回充电站。您可以使用 BT 实现类似此类操作,但使用 FSM 更容易实现完全反应行为(即,无论机器人位于何处,电池状态都会导致机器人充电......即使它看起来有点凌乱。

在“混乱”的注释中,行为树狂热者倾向于将“意大利面条状态机”的论点作为你永远不应该使用FSM的理由。我认为这不是一个公平的比较。分层有限状态机 (HFSM) 的概念由来已久,如果您遵循良好的设计实践,有助于避免此问题,如下所示。但是,确实,在 HFSM 中管理转换仍然比在 BT 中添加或删除子树更困难。

已经定义了特定的结构,使 BT 对这些应用更具反应性。例如,存在“反应序列”的概念,即使它们返回了成功,仍然可以在序列中勾选先前的子项。在我们的示例中,如果该操作序列期间的任何时候电池电量不足,这将允许我们终止带有 Failure 的子树,这可能是我们想要的。

向 BT 添加电池检查和充电操作很容易,但请注意,此检查不是反应性的——它仅在序列开始时发生。实现更多的反应性会使 BT 的设计复杂化,但使用反应序列等结构是可行的。

FSM 可以通过允许定义任何两种状态之间的转换来允许这种反应。

分层 FSM 可以清理图表。在本例中,我们定义了一个名为“标称”的超态,从而在正常运行和充电之间定义了两种明确的操作模式。

两全其美:高级模式开关由 FSM 处理,模式特定行为由 BT 管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖者(视觉算法工程师)

绝对物超所值的干货

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值