1294.40 FallbackWithMemory의 재진입 규칙

1. 재진입의 정의

FallbackWithMemory의 재진입(re-entry)이란, 이전 Tick에서 자식이 RUNNING을 반환하여 Fallback이 RUNNING 상태인 경우, 다음 Tick에서 current_child_idx에 기억된 인덱스부터 평가를 재개하는 동작을 의미한다. 이 재진입 규칙에 의해 이전 Tick에서 FAILURE를 반환한 자식들의 재평가가 생략된다(Colledanchise & Ogren, 2018).

2. 재진입 규칙의 상세

2.1 규칙 1: RUNNING 자식 인덱스에서 재개

이전 Tick에서 자식 c_k가 RUNNING을 반환한 경우, 다음 Tick에서 인덱스 k부터 평가를 시작한다. 인덱스 0부터 k-1까지의 자식은 평가되지 않는다.

Tick N:
  children[0] → FAILURE
  children[1] → FAILURE
  children[2] → RUNNING
  current_child_idx ← 2
  → RUNNING

Tick N+1: (인덱스 2에서 재진입)
  children[0] → (건너뜀)
  children[1] → (건너뜀)
  children[2] → RUNNING
  → RUNNING

2.2 규칙 2: 건너뛰어진 자식은 재평가되지 않음

재진입 시 인덱스 0부터 k-1까지의 자식은 이전 Tick에서 FAILURE를 반환한 자식들이다. 이들은 재진입 Tick에서 재평가되지 않으므로, 해당 자식들의 상태 변화(FAILURE에서 SUCCESS로의 전환)를 감지하지 못한다.

Tick N:
  children[0](CondA) → FAILURE    (조건 미충족)
  children[1](ActB)  → RUNNING
  current_child_idx ← 1

Tick N+1: (CondA가 SUCCESS로 변경되었으나...)
  children[0](CondA) → (건너뜀 — 변화 미감지)
  children[1](ActB)  → RUNNING
  → RUNNING

2.3 규칙 3: RUNNING 자식이 FAILURE를 반환하면 다음으로 진행

재진입한 자식이 이번 Tick에서 FAILURE를 반환하면, 다음 인덱스의 자식으로 진행한다. 이전에 건너뛰어진 자식으로 되돌아가지 않는다.

Tick N:
  children[0] → FAILURE
  children[1] → RUNNING    (current_child_idx=1)

Tick N+1:
  children[1] → FAILURE    (RUNNING에서 FAILURE로 전환)
  children[2] → SUCCESS    (다음 대안으로 진행)
  current_child_idx ← 0
  → SUCCESS

2.4 규칙 4: SUCCESS 또는 전체 FAILURE 시 인덱스 초기화

어떤 자식이 SUCCESS를 반환하거나 모든 자식이 FAILURE를 반환하면, current_child_idx가 0으로 초기화된다. 다음 Tick(Fallback이 다시 IDLE에서 시작)에서는 첫 번째 자식부터 평가한다.

Tick N:
  children[1] → SUCCESS    (재진입 후 성공)
  current_child_idx ← 0    (초기화)
  → SUCCESS

Tick N+1: (새로운 평가 주기)
  children[0] → ...        (인덱스 0부터 시작)

3. 재진입 규칙의 실행 추적

3.1 전체 흐름 추적

children: [CondCheck, AsyncRecoveryA, AsyncRecoveryB, SafeStop]

Tick 1:
  i=0: CondCheck → FAILURE         (정상 상태 아님)
  i=1: AsyncRecoveryA → RUNNING    (복구 A 시도 중)
  current_child_idx ← 1
  → RUNNING

Tick 2: (인덱스 1에서 재진입)
  i=1: AsyncRecoveryA → RUNNING    (복구 A 계속)
  → RUNNING

Tick 3: (인덱스 1에서 재진입)
  i=1: AsyncRecoveryA → FAILURE    (복구 A 실패)
  i=2: AsyncRecoveryB → RUNNING    (복구 B 시도 시작)
  current_child_idx ← 2
  → RUNNING

Tick 4: (인덱스 2에서 재진입)
  i=2: AsyncRecoveryB → SUCCESS    (복구 B 성공)
  current_child_idx ← 0
  → SUCCESS

이 추적에서 주목할 점:

  • Tick 2에서 CondCheck가 건너뛰어진다 (정상 상태로 변했을 수 있지만 미확인).
  • Tick 3에서 AsyncRecoveryA 실패 후 AsyncRecoveryB로 자동 진행된다.
  • Tick 4에서 CondCheck와 AsyncRecoveryA가 모두 건너뛰어진다.

4. SequenceWithMemory 재진입 규칙과의 대칭성

규칙SequenceWithMemoryFallbackWithMemory
재진입 위치RUNNING 자식 인덱스RUNNING 자식 인덱스
건너뛰는 자식이전 SUCCESS 자식이전 FAILURE 자식
진행 조건현재 자식 SUCCESS현재 자식 FAILURE
종료 및 초기화FAILURE 또는 전체 SUCCESSSUCCESS 또는 전체 FAILURE

두 변형의 재진입 규칙은 구조적으로 동일하며, 건너뛰는 대상과 진행/종료 조건에서 SUCCESS와 FAILURE가 교환된 관계이다.

5. 재진입 규칙의 장점과 한계

5.1 장점

  1. 불필요한 재시도 방지: 이미 실패한 대안을 매 Tick마다 재시도하지 않으므로 자원을 절약한다.
  2. 비동기 작업의 안정적 진행: RUNNING 자식에 대한 Tick이 앞쪽 자식의 재평가 없이 직접 전달되므로, 비동기 작업의 진행이 안정적이다.
  3. 예측 가능한 Tick 비용: RUNNING 상태에서 Tick당 1개의 노드만 방문하므로, 자식 수에 무관하게 Tick 비용이 일정하다.

5.2 한계

  1. 우선순위 높은 대안의 가용성 변화 미감지: 이전에 FAILURE였던 앞쪽 자식이 SUCCESS로 변경되어도 감지하지 못한다. 더 나은 대안이 가용해졌는데도 현재 대안을 계속 시도한다.
  2. 조건 재검사 불가: 앞쪽에 배치된 조건 노드가 건너뛰어지므로, 조건 기반의 대안 전환이 불가능하다.
  3. 환경 변화에 대한 반응 지연: 환경이 변화하여 이전 대안이 다시 성공 가능해져도, 현재 대안의 시도가 완료될 때까지 감지되지 않는다.

이러한 한계가 문제가 되는 경우에는 ReactiveFallback을 사용하여 매 Tick마다 처음부터 재평가해야 한다.


참고 문헌

  • Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
  • Faconti, D. (2022). BehaviorTree.CPP documentation and API reference. https://www.behaviortree.dev/