ROS学习|Behavoir Tree(BT树)--基本概念

系列文章目录

Behavoir Tree(BT树)–基本概念
Behavoir Tree(BT树)–c++实现
Behavoir Tree(BT树)–c++实现第二节


最近学习BT树进行任务调度。记录和分享一下BT书的相关知识。文章大部分翻译自参考文献 与有限状态机不同的是,行为树是 控制“任务”执行流程的分层节点树。 # 基本概念 - 一个称为“ tick ”的信号被发送到树的根部并在树中传播,直到它到达叶节点。 - 接收到tick信号的 TreeNode 会执行它的回调callback。此回调必须返回 成功SUCCESS, 失败FAILURE或 正在运行RUNNING,如果操作是异步的并且需要更多时间来完成。 - 如果 TreeNode 有一个或多个子节点,它负责根据其状态、外部参数或前一个兄弟节点的结果对它们进行触发。 - LeafNodes叶节点,那些没有任何子节点的 TreeNodes,是实际的命令,即行为树与系统其余部分交互的地方 。动作节点是最常见的叶节点类型。 - 节点类型 **ControlNodes**是可以有1到N 个子节点的节点。一旦收到tick,该tick可以传播给一个或多个子节点。

DecoratorNodes类似于ControlNode,但它只能有一个子节点。

ActionNodes是叶节点,没有子节点。用户应该实现自己的ActionNodes来执行实际任务。

ConditionNodes等效于ActionNodes,但它们始终是原子的,即它们不能返回RUNNING。它们不应该改变系统的状态。
在这里插入图片描述

简单举例

为了更好地理解BehaviorTrees如何工作,让我们关注一些实际的例子。为简单起见,我们不会考虑操作返回RUNNING时会发生什么。
我们假设每个Action都是原子地和同步地执行的.
ControlNode:Sequence序列
让我们来说明BT如何使用最基本和最常用的ControlNode:SequenceNode。
ControlNode的子节点总是有序的 ; 是由ControlNode来考虑这个顺序。
在图形表示中,执行顺序是从左到右。
在这里插入图片描述
简而言之:

  • 如果子节点返回SUCCESS,请触发下一个。

  • 如果子节点返回FAILURE,则不再触发子节点并且向Sequence返回FAILURE。

  • 如果所有子节点都返回SUCCESS,则Sequence也返回SUCCESS。
    Decorators装饰器
    依据装饰节点的不同,该节点的作用可能是:

  • 传递/转换从子节点收到的结果

  • 停止子节点的执行

  • 重复触发子节点
    在这里插入图片描述
    图片里Inverter就是一个装饰节点,它将子节点的结果反转。图中的Inverter+子节点也可以表述为:“Is the door closed?”. Retry节点重复激发子节点num_attempts(图片里是5)次。
    左侧sequence节点的含义为:
    “如果门关上,尝试打开。
    尝试五次。如果不成功就会放弃并返回失败”
    第二个ControlNode:Fallback选择器
    Fallback,顾名思义,即处理子节点失败的情况。
    它的触发顺序为:

  • 若子节点失败,出发下一个

  • 若子节点成功,不在触发下一个,并返回成功

  • 若所有子节点均失败,Fallback节点返回失败。
    在这里插入图片描述
    这个例子可以解释为:

门开了吗?
如果没有,请尝试打开门。
门没开,如果您有钥匙,请解锁并打开门。
否则,砸门。
如果这些操作中的任何一个成功,则进入房间。

节点类型

sequence nodes 序列节点

上文已经介绍过,子节点返回success,sequence node依次触发下一个子节点;子节点返回failure,sequence node失败。
现有三种sequence node框架:sequence、sequenceStar、ReactiveSequence。
他们的相同点是:

  • 触发第一个子节点之前,节点的状态变为running
  • 子节点返回success,则触发下一个子节点
  • 若最后一个子节点返回success,所有子节点停止,并返回success
    他们的不同点是:
节点类型子节点failure子节点running
SequenceRestartTick again
ReactiveSequenceRestartRestart
SequenceStarTick againTick again

其中,“Restart”指整个序列从第一个子节点重新开始;“Tick again”指下次触发时,从当前子节点触发。先前返回success的子节点,不再触发。
Sequence伪代码示例
在这里插入图片描述

    status = RUNNING;
    // _index is a private member

    while(_index < number_of_children)
    {
        child_status = child[_index]->tick();

        if( child_status == SUCCESS ) {
            _index++;
        }
        else if( child_status == RUNNING ) {
            // keep same index
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            HaltAllChildren();
            _index = 0;
            return FAILURE;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    _index = 0;
    return SUCCESS;

ReactiveSequence伪代码示例
在这里插入图片描述
ApproachEnemy 是一个异步操作,它返回 RUNNING 直到最终完成。

条件 isEnemyVisible 将被多次调用,如果它变为false(即“FAILURE”),则 ApproachEnemy 将停止。

    status = RUNNING;

    for (int index=0; index < number_of_children; index++)
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            HaltAllChildren();
            return FAILURE;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    return SUCCESS;

SequenceStar伪代码示例
在这里插入图片描述
这个框图实现了让机器人依次巡逻A\B\C,并且每个点只巡逻一次。

    status = RUNNING;
    // _index is a private member

    while( index < number_of_children)
    {
        child_status = child[index]->tick();

        if( child_status == SUCCESS ) {
            _index++;
        }
        else if( child_status == RUNNING || 
                 child_status == FAILURE ) 
        {
            // keep same index
            return child_status;
        }
    }
    // all the children returned success. Return SUCCESS too.
    HaltAllChildren();
    _index = 0;
    return SUCCESS;

Fallback nodes 选择节点

类似地,fallnack node也有两种类型:Fallback和ReactiveFallback
相同点是:

  • 触发第一个子节点之前,节点的状态变为running
  • 子节点返回failure,则触发下一个子节点
  • 若最后一个子节点返回failure,所有子节点停止,并返回failure
  • 若任一子节点返回success,停止所有子节点,该节点返回success
    他们的不同点是:
节点类型子节点running
FallbackTick again
ReactiveFallbackRestart

其中,“Restart”指整个序列从第一个子节点重新开始;“Tick again”指下次触发时,从当前子节点触发。先前返回failure的子节点,不再触发。
fallback 示例伪代码
在这里插入图片描述
这个例子中我们采用不同的方法开门。第一次先检查门是否敞开

    // index is initialized to 0 in the constructor
    status = RUNNING;

    while( _index < number_of_children )
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            // Suspend execution and return RUNNING.
            // At the next tick, _index will be the same.
            return RUNNING;
        }
        else if( child_status == FAILURE ) {
            // continue the while loop
            _index++;
        }
        else if( child_status == SUCCESS ) {
            // Suspend execution and return SUCCESS.
            HaltAllChildren();
            _index = 0;
            return SUCCESS;
        }
    }
    // all the children returned FAILURE. Return FAILURE too.
    index = 0;
    HaltAllChildren();
    return FAILURE;

ReactiveFallback 示例伪代码
在这里插入图片描述
当前面的条件之一将其状态从 FAILURE 更改为 SUCCESS时,可以使用此 ControlNode中断异步子节点。这个例子中,角色最多睡8个小时。如果他睡够了,那么结点“areYouRested?”返回success,异步节点"timeout"和“sleep”将被中断。

    status = RUNNING;

    for (int index=0; index < number_of_children; index++)
    {
        child_status = child[index]->tick();

        if( child_status == RUNNING ) {
            // Suspend all subsequent siblings and return RUNNING.
            HaltSubsequentSiblings();
            return RUNNING;
        }

        // if child_status == FAILURE, continue to tick next sibling

        if( child_status == SUCCESS ) {
            // Suspend execution and return SUCCESS.
            HaltAllChildren();
            return SUCCESS;
        }
    }
    // all the children returned FAILURE. Return FAILURE too.
    HaltAllChildren();
    return FAILURE;

Decorators Nodes 装饰节点

一个装饰器节点只有一个子节点。它可以有以下几类:
InverterNode
反转子节点结果。但是子节点返回running时,同样返回running。
ForceSuccessNode
子节点返回running时,同样返回running;否则一律返回success。
ForceFailureNode
子节点返回running时,同样返回running;否则一律返回failure。
RepeatNode
只要子节点返回成功,触发子节点至N次。若子节点返回failure,中断循环并返回failure。
RetryNode
只要子节点返回失败,触发子节点至N次。若子节点返回failure,中断循环并返回success。

XML格式

XML Schema基础

下面是和一个简单任务树示例

 <root main_tree_to_execute = "MainTree" >
     <BehaviorTree ID="MainTree">
        <Sequence name="root_sequence">
            <SaySomething   name="action_hello" message="Hello"/>
            <OpenGripper    name="open_gripper"/>
            <ApproachObject name="approach_object"/>
            <CloseGripper   name="close_gripper"/>
        </Sequence>
     </BehaviorTree>
 </root>
  • 树的第一个tag是,必须有[main_tree_to_execute]属性。它至少含有一个的tag
  • tag 必须有[ID]属性
  • 当BT树含有多个BehavoirTree时 ,[main_tree_to_execute]属性是必须的,否则就是可选的
  • 每个节点都用一个tag表示。特别的:tag的名字是注册在Treenode的ID;[name]属性是该实例的名字,是可选的;Ports通过属性来定义。例子中,SaySomething的动作要求有message的输入port。

Ports 映射 和 指向 Blackboards entries的指针

输入输出port可以被重映射,通过使用Blackboards entry的名字,或者说使用BB的key/value对中的key。
一个BB key可以用{key_name}表示

 <root main_tree_to_execute = "MainTree" >
     <BehaviorTree ID="MainTree">
        <Sequence name="root_sequence">
            <SaySomething message="Hello"/>
            <SaySomething message="{my_message}"/>
        </Sequence>
     </BehaviorTree>
 </root>

在这个例子中,sequence第一个子节点打印“Hello”,第二个子节点读和写在 the entry of the blackboard 叫做“my_message”的值。

紧凑/清晰的格式

 <SaySomething               name="action_hello" message="Hello World"/>
 <Action ID="SaySomething"   name="action_hello" message="Hello World"/>

这两种写法都对,前者是紧凑格式syntax "compact"后者是清晰格式syntax “explicit”.
有些工具例如Groot要求清晰格式或者紧凑格式+额外的信息。例如:

 <root main_tree_to_execute = "MainTree" >
     <BehaviorTree ID="MainTree">
        <Sequence name="root_sequence">
           <SaySomething   name="action_hello" message="Hello"/>
           <OpenGripper    name="open_gripper"/>
           <ApproachObject name="approach_object"/>
           <CloseGripper   name="close_gripper"/>
        </Sequence>
    </BehaviorTree>

    <!-- the BT executor don't require this, but Groot does -->     
    <TreeNodeModel>
        <Action ID="SaySomething">
            <input_port name="message" type="std::string" />
        </Action>
        <Action ID="OpenGripper"/>
        <Action ID="ApproachObject"/>
        <Action ID="CloseGripper"/>      
    </TreeNodeModel>
 </root>

参考文献

源英文网站

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值