导航2特定节点介绍

警告

在刚开始时,词汇可能是一个令人困惑的重要问题。
  • 在讨论BT时,``Node``与ROS 2上下文中的``Node``完全不同。

  • 在BT的上下文中,ActionNode 不一定与ROS 2上下文中的Action服务器连接(但通常会连接)。

有许多定制的Nav2 BT节点可用于以Nav2特定方式使用。下面将介绍一些常用的Nav2节点。完整的定制BT节点列表可在 nav2_behavior_tree插件文件夹 中找到。配置指南 也可能非常有用。

动作节点

  • ComputePathToPose - 计算到位姿的行为服务器客户端(规划器接口)

  • FollowPath - FollowPath 动作服务器客户端(控制器接口)

  • Spin, Wait, Backup - 行为服务器客户端

  • ClearCostmapService - ClearCostmapService 服务器客户端

完成时,这些动作节点将返回 SUCCESS,如果动作服务器认为动作已正确完成,则返回 RUNNING,否则返回 FAILURE。请注意,在上面的列表中,ClearCostmapService 动作节点不是动作服务器客户端,而是服务客户端。

条件节点

  • GoalUpdated - 检查目标话题上的目标是否已更新

  • GoalReached - 检查是否已达到目标

  • InitialPoseReceived - 检查是否已收到 intial_pose 话题上的姿态

  • isBatteryLow - 通过监听电池主题来检查电池是否低

上述条件节点列表可用于探测系统的特定方面。通常情况下,如果条件为真,则它们将返回 SUCCESS,否则返回 FAILURE。在默认的Nav2 BT中使用的关键条件是 GoalUpdated,它在特定子树中以异步方式进行检查。此条件节点允许描述行为:“如果目标已更新,则我们必须重新规划”。条件节点通常与ReactiveFallback节点配对使用。

装饰节点

  • 距离控制器 - 每当机器人行驶一定距离时,将对子节点进行触发

  • Rate Controller - 控制子节点以恒定频率进行刻度。刻度频率是一个公开端口

  • 目标更新器 - 通过行为树上的端口更新子节点的目标

  • Single Trigger - 仅对子节点进行一次刻度,并在所有后续刻度中返回``FAILURE``

  • 速度控制器 - 控制其子节点以与机器人速度成比例的频率触发

Control: PipelineSequence

当子节点返回“运行中”时,“PipelineSequence”控制节点将重新触发之前的子节点。该节点类似于“序列”节点,但其附加属性是在“当前”节点之前的子节点将被重新触发(类似于管道中的水流)。如果任何子节点返回“失败”,所有子节点都将停止运行,父节点也将返回“失败”。当序列中的**最后一个节点**返回“成功”时,该节点将停止运行并返回“成功”。

为了进一步解释,这里有一个使用PipelineSequence的示例BT。


../../_images/control_pipelineSequence.png

<root main_tree_to_execute="MainTree">
    <BehaviorTree ID="MainTree">
        <PipelineSequence>
            <Action_A/>
            <Action_B/>
            <Action_C/>
        </PipelineSequence>
    </BehaviorTree>
</root>
  1. Action_A``Action_B``和``Action_C``都处于“空闲”状态。

  2. 当父PipelineSequence首次被执行时,假设``Action_A``返回``RUNNING``。父节点现在将返回``RUNNING``,而且不会执行其他节点。


../../_images/control_pipelineSequence_RUNNING_IDLE_IDLE.png

  1. 现在,让我们假设``Action_A``返回“成功”,然后``Action_B``将被触发并返回“运行中”。``Action_C``尚未被触发,因此返回“空闲”。


../../_images/control_pipelineSequence_SUCCESS_RUNNING_IDLE.png

  1. Action_A``再次被执行并返回``RUNNINGAction_B``重新被执行并返回``SUCCESS,因此BT继续执行首次执行``Action_C``。假设``Action_C``返回``RUNNING``。重新执行``Action_A``是使PipelineSequence有用的关键。


../../_images/control_pipelineSequence_RUNNING_SUCCESS_RUNNING.png

  1. 序列中的所有动作都将被重新触发。假设``Action_A``仍然返回“运行中”,而``Action_B``再次返回“成功”,``Action_C``现在在此触发中返回“成功”。序列现已完成,因此即使``Action_A``仍处于“运行中”,也会停止``Action_A``的运行。


../../_images/control_pipelineSequence_RUNNING_SUCCESS_SUCCESS.png

请注意,如果``Action_A``、Action_B``或``Action_C``在任何时候返回``FAILURE,则父节点也将返回``FAILURE``,并停止执行任何子节点。

有关 PipelineSequence 的详细信息,请参阅 PipelineSequence 配置指南

控制:恢复

Recovery 控制节点只有两个子节点,仅当第一个子节点返回 SUCCESS 时它才返回 SUCCESS。如果第一个子节点返回 FAILURE,则会执行第二个子节点。这个循环将继续,直到出现以下情况之一:

  • 第一个子节点返回``SUCCESS``(导致父节点返回``SUCCESS``)。

  • 第二个子节点返回 FAILURE``(导致父节点返回 ``FAILURE

  • 违反了``number_of_retries``输入参数的限制。

通常情况下,此节点用于连接一个操作和一个恢复操作,正如其名称所示。第一个操作通常是“主要”行为,第二个操作将在主要行为失败的情况下执行。通常,执行第二个子节点操作会提高第一个操作成功的机会。


../../_images/control_recovery_node.png

<root main_tree_to_execute="MainTree">
    <BehaviorTree ID="MainTree">
        <RecoveryNode number_of_retries="1">
            <ComputePathToPose/>
            <ClearLocalCostmap/>
        </RecoveryNode>
    </BehaviorTree>
</root>

在上面的例子中,假设``ComputePathToPose``失败。作为响应,ClearLocalCostmap``将被激活并返回``SUCCESS。现在,既然我们已经清除了代价地图,假设机器人能够正确计算路径并且``ComputePathToPose``现在返回``SUCCESS``。然后,父级RecoveryNode也将返回``SUCCESS``,BT将完成。

有关 RecoveryNode 的详细信息,请参阅 RecoveryNode 配置指南

控制:RoundRobin

RoundRobin 控制节点以循环方式执行其子节点,直到某个子节点返回 SUCCESS,此时父节点也将返回 SUCCESS。如果所有子节点返回 FAILURE,父节点 RoundRobin 也将返回 FAILURE

这是一个我们将用来介绍概念的BT示例。


../../_images/control_round_robin.png

<root main_tree_to_execute="MainTree">
    <BehaviorTree ID="MainTree">
        <RoundRobin>
            <Action_A/>
            <Action_B/>
            <Action_C/>
        </RoundRobin>
    </BehaviorTree>
</root>
  1. 所有节点都从 IDLE 状态开始。


../../_images/control_round_robin_IDLE_IDLE_IDLE.png

2. Upon tick of the parent node, the first child (Action_A) is ticked. Let's assume on tick the child returns RUNNING. In this case, no other children are ticked and the parent node returns RUNNING as well.


../../_images/control_round_robin_RUNNING_IDLE_IDLE.png

3. Upon the next tick, let's assume that Action_A returns FAILURE. This means that Action_B will get ticked next, and Action_C remains unticked. Let's assume Action_B returns RUNNING this time. That means the parent RoundRobin node will also return RUNNING.


../../_images/control_round_robin_FAILURE_RUNNING_IDLE.png

4. Upon this next tick, let's assume that Action_B returns SUCCESS. The parent RoundRobin will now halt all children and return SUCCESS. The parent node retains this state information, and will tick Action_C upon the next tick rather than start from Action_A like Step 2 did.


../../_images/control_round_robin_FAILURE_SUCCESS_IDLE.png

  1. 在这个时钟周期中,假设 Action_C 返回 RUNNING,父级 RoundRobin 也返回 RUNNING。其他节点都未执行。


../../_images/control_round_robin_FAILURE_SUCCESS_RUNNING.png

  1. 在最后一次激活时,假设``Action_C``返回``FAILURE``。父节点将循环并再次激活``Action_A``。Action_A``返回``RUNNING,父级RoundRobin节点也将返回``RUNNING``。除非所有子节点返回``FAILURE``,否则此模式将无限继续。


../../_images/control_round_robin_RUNNING_IDLE_FAILURE.png

有关 RecoveryNode 的更多详细信息,请参阅 RoundRobin 配置指南