行为树BehaviorTree学习记录1_基本概念

本文介绍了行为树BehaviorTree的基本概念,如其与有限状态机的区别,以及序贯、回退和修饰节点的运作方式。文章还提及了Groot2工具和行为树在游戏NPC和Nav2导航中的应用。
摘要由CSDN通过智能技术生成

(为免误导,特免责声明如下:本文所有内容,只是基于个人当前理解和实际做法,后面亦会有更正和修订,但任何版本都不免有个人能力有限、理解有误或者工作环境不同的状况,故文中内容仅供参考。任何人都可以借鉴或者直接使用代码片段,但对任何直接引用或者借鉴产生的技术问题等后果,作者不承担任何责任。)

1 什么是行为树BehaviorTree

BT Tree是有限状态机的升级,跟状态机一样基本也是根据条件,激活某个状态,执行相应的任务。
不同的是它采用树状结构来设置自动化流程,行为树的结构可以用xml文件配置,即在编译后方便的调整自动体的行为模式。
BT Tree目前广泛应用于一些自动控制的领域甚至是游戏的NPC,Nav2的导航也大量使用BT Tree。

参考链接:https://www.behaviortree.dev/
该维护团体还开发了Groot2,是一个GUI,可用于设计BT Tree的结构、实时监控系统运行状态和记录日志。

2 行为树结构

基本概念

  • tick
    类似于信号(滴答、令牌、打勾),由树的根节点发出,通过树结构一直传到叶节点(leaf node)。传到哪个节点就执行哪个节点(TreeNode)。

  • 节点状态
    任何节点(TreeNode)获得tick后,都执行相应的操作(回调函数)完成相应的任务后返回的状态,只能是:

    • SUCCESS 成功
    • FAILURE 失败
    • RUNNING 还在运行
  • 子节点
    如果节点(TreeNode)有一个或多个子节点,则由此节点的类型,决定如何把tick传到子节点执行。比如
    这个节点是顺序节点(sequence node 是最简单的控制节点)则它的子节点按先后顺序依次获得tick,依次执行,结束以后返回,如果所有页节点都返回成功,则它也将状态从running改为success
    查看动画示意

  • 叶节点
    最底层没有子节点的就是叶节点,叶节点完成实际的任务,比如与系统其他部分沟通交互。比如action节点就是最普通的叶节点。

3节点类型

节点类继承关系

  • 所有节点都继承自:TreeNode。
  • ControlNode 控制节点,有1个或多个子节点。
  • DecorateNode 修饰节点,只有一个子节点,它可以修改子节点的结果(比如取反)或者让子节点运行多次等。
  • 条件节点(ConditionNode)和行为节点(ActionNode)都是页节点,不能再有子节点
  • ConditionNode不能返回runnig,不能改变系统,(就是做条件判断,返回真假,真就是成功,假就是失败)
  • ActionNode主要用来完成真正的任务,又可以进一步分为同步ActionNode和异步ActionNode。同步节点会阻塞进程,直到任务结束返回成功或者失败,而异步节点可以先返回running,需要多次tick执行直到有真正的结果返回。

目前系统可用的节点有:

(1)SequenceNode顺序控制节点

在这里插入图片描述
顺序控制节点:按顺序tick子节点,前一个子节点返回成功,则tick下一个子节点,所有的成功则返回成功。如果中间某个子节点返回失败,则不需要tick其它子节点,直接返回失败。
除了普通的Sequence,还有 SequenceWithMemory 和 ReactiveSequence
他们的共同特点有:
- 在tick第一个子节点前,先把状态改为running
- 如果一个子节点返回成功,tick下一个子节点
- 如果最后一个子节点也返回成功,则整个顺序节点返回成功
他们的区别在于有子节点返回失败(failure)或运行中(running)是:

类型子节点返回失败 failure子节点返回运行中running
Sequence重新开始再次tick
ReactiveSequence重新开始重新开始
SequenceWithMemory再次tick再次tick

重新开始:意味着下次被tick的时候会从第一个子节点开始tick。
再次tick:当这个sequence再次被tick的时候,直接tick上次返回失败或者在running的子节点,而前面已经返回成功的子节点不再被tick。

举例

  • sequence
    在这里插入图片描述
    这是游戏里面一个狙击手的动作,前两个是条件节点 condition node,任何一个子节点,不管是前面的条件判断还是后面的动作节点Action node失败,下一次都是从第一个子节点开始,但如果是action node返回进行中running,下次就不从第一个子节点开始了(注意应该是异步action,同步的话只有等到成功或者失败才会返回),比如现在正在瞄准中,下次进来就不再做前面的条件判断了。

  • ReactiveSequence
    在这里插入图片描述
    ApproachEnemy是异步动作节点,会返回RUNNING。由于是ReactiveSequence,所以子节点isEnemyVisible 每次进来都会进行条件判断,如果某次判断为失败 (i,e, “FAILURE”), 则ApproachEnemy会被停止。

  • SequenceWithMemory
    这种控制节点用于,当某个子节点运行过并返回成功后,以后不想再运行该子节点的情况。比如
    在这里插入图片描述
    这是一个巡逻机器人,必须每个地点巡逻一次,如果只是B位置失败了,下次就不用再tick巡逻A这个action了。
    但是,电池是否正常(isBatteryOK )这种节点就必须每次都检查,所以它的父节点应该是个ReactiveSequence.

(2)Fallbacks回退控制节点

这类节点在其他的架构下可能被称为选择性控制或者优先控制等,目的是尝试不同的策略,直到找到一种可行的,(有某个子节点返回成功)。目前可用的有2种:

  • Fallback
  • ReactiveFallback
    他们的相同点是:
    • 在tick第一个子节点前,先把状态改为running
    • 如果一个子节点返回失败,则tick下一个子节点
    • 如果所有的子节点都返回失败,则返回失败,并停止(halt)所有子节点。
    • 如果某一个子节点返回成功,则整个节点停止下来返回成功,并停止(halt)所有子节点。
      不同的是:
类型子节点返回运行中running
Fallback再次tick
ReactiveFallback重新开始

“重新开始” 是指从第一个子节点开始整个fallback重新启动直接点.

“再次tick” 是指下次这个控制节点被tick时,相同的子节点会被tick.而这个子节点前面的兄弟节点(那些返回失败的子节点)将不会被tick.

举例

  • Fallback
    在这里插入图片描述
  • ReactiveFallback
    这种控制节点用在我们希望中断任何可能在进行的异步子节点,以检测前面的条件判断子节点是否发生了状态变化,比如从上次的失败变为可以成功了.

ReactiveFallback

上面这个例子,是一个角色(游戏里的NPC)需要休息最多8个小时,如果前面的条件判断子节点(是否休息好了)返回成功,则直接返回成功,终止正在进行中(running)的异步子节点(睡觉)。

(3)Decorators修饰器

修饰器节点至少有一个子节点,修饰器决定了它的子节点是否,何时被tick已经tick多少次。

  • Inverter 翻转器
    tick子节点一次,如果子节点返回成功则翻转为失败,如果子节点返回失败则返回成功,如果子节点返回运行中,也返回运行中。

  • ForceSuccess 强制成功
    子节点返回running,也返回running,除此之外不论子节点返回成功还是失败,一律返回成功

  • ForceFailure 强制失败
    子节点返回running,也返回running,除此之外不论子节点返回成功还是失败,一律返回失败

  • Repeat 重复
    在获得一次tick的时候,重复tick它的子节点最多到N次,N由输入口:num_cycles设定。
    如果每次tick子节点返回的都是成功,在N次后,返回成功。
    如果中间某次子节点返回是失败,则不继续tick下去,直接返回失败。
    如果子节点返回运行中,节点也返回运行中,在下一次再被tick的时候继续tick子节点,并不会增加重复次数。

  • RetryUntilSuccessful 重复到成功
    只要子节点返回是失败,就重复Tick子节点最多到N次,N由输入口:num_attempts传入。
    N次后如果还是失败就返回失败。
    如果某一次子节点返回成功,则打断不再继续tick了,直接返回成功。
    如果子节点返回运行中,节点也返回运行中,在下一次再被tick的时候继续tick子节点,并不会增加重复次数。

  • KeepRunningUntilFailure保持运行直到失败
    这种节点总数返回失败(在子节点返回失败时)或者运行中(子节点返回成功或者运行中)。

  • Delay延迟
    在过了一个指定的时间段(由输入端口:delay_msec传入)后Tick 它的子节点.
    如果子节点返回运行中,它也返回运行中,将会在下一次被tick的时候tick它的子节点. 否则把子节点的返回状态向上返回。

  • RunOnce 运行一次
    这种节点用于你只想让某个子节点运行一次的情况.
    如果子节点是个异步的,会tick到它返回成功或失败。(没懂,是当同步处理?阻塞在这里?目前的理解是:还是继续tick,因为还是第一次执行没完成
    在第一次执行完后,你可以把输入端口:then_skip 设置为:
    - TRUE (default),这个节点在将来会被跳过.
    - FALSE, 同步返回子级返回的相同状态,永远。(不太理解,目前的理解是:不执行动作,而是直接返回子节点第一次执行的结果。)

  • PreCondition预置条件
    Cf. Introduction to the Scripting language

  • SubTree子树
    Cf. Compose behaviors using Subtrees.

  • 其他需要再c++理注册的装饰器

  • ConsumeQueue 消耗型队列

  • SimpleDecoratorNode 简单装饰器节点(自定义装饰器)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值