1294.41 FallbackWithMemory의 마지막 Running 자식 기억

1. RUNNING 자식 기억의 메커니즘

FallbackWithMemory가 RUNNING을 반환할 때, 내부 상태 변수 current_child_idx에 RUNNING을 반환한 자식의 인덱스가 저장된다. 이 인덱스는 다음 Tick에서 평가 시작점으로 사용되며, Fallback이 SUCCESS, FAILURE, 또는 Halt 상태가 될 때까지 유지된다(Colledanchise & Ogren, 2018).

2. 기억의 생성

2.1 최초 RUNNING 발생

children: [ActA, AsyncActB, ActC]

Tick 1:
  i=0: ActA → FAILURE
  i=1: AsyncActB → RUNNING    ← 기억 생성
  current_child_idx ← 1       ← 인덱스 1 저장
  → RUNNING

자식 AsyncActB(인덱스 1)가 RUNNING을 반환하면, current_child_idx에 1이 저장된다.

2.2 기억의 갱신

RUNNING 자식이 FAILURE를 반환하고 후속 자식이 RUNNING을 반환하면, 기억이 갱신된다.

Tick 1:
  i=0: ActA → FAILURE
  i=1: AsyncActB → RUNNING
  current_child_idx ← 1    ← 인덱스 1 기억

Tick 2:
  i=1: AsyncActB → FAILURE  (이전 대안 실패)
  i=2: AsyncActC → RUNNING  (다음 대안 시도)
  current_child_idx ← 2    ← 인덱스 2로 갱신

기억은 항상 가장 최근에 RUNNING을 반환한 자식의 인덱스를 반영한다.

3. 기억의 유지

3.1 RUNNING 상태 지속

RUNNING 자식이 여러 Tick에 걸쳐 RUNNING을 계속 반환하면, 기억은 동일한 인덱스를 유지한다.

Tick 1: current_child_idx ← 2 (children[2] → RUNNING)
Tick 2: current_child_idx = 2  (children[2] → RUNNING, 유지)
Tick 3: current_child_idx = 2  (children[2] → RUNNING, 유지)
Tick 4: current_child_idx = 2  (children[2] → RUNNING, 유지)

이 기간 동안 인덱스 0과 1의 자식은 한 번도 평가되지 않는다.

4. 기억의 소멸

4.1 SUCCESS에 의한 소멸

어떤 자식이 SUCCESS를 반환하면, 기억이 초기화된다.

Tick N: current_child_idx = 2
        children[2] → SUCCESS
        current_child_idx ← 0    ← 기억 소멸
        → SUCCESS

4.2 전체 FAILURE에 의한 소멸

모든 남은 자식이 FAILURE를 반환하면, 기억이 초기화된다.

Tick N: current_child_idx = 2
        children[2] → FAILURE
        children[3] → FAILURE
        (더 이상 자식 없음)
        current_child_idx ← 0    ← 기억 소멸
        → FAILURE

4.3 Halt에 의한 소멸

외부에서 Halt가 호출되면, 기억이 강제 초기화된다.

현재 상태: current_child_idx = 2, children[2] RUNNING

halt() 호출:
  children[2].halt()    ← RUNNING 자식 Halt
  current_child_idx ← 0 ← 기억 소멸

Halt에 의한 기억 소멸은 상위 노드가 이 Fallback을 중단시킬 때 발생하며, 다음 활성화 시 첫 번째 자식부터 다시 평가하도록 보장한다.

5. 기억이 미치는 영향

5.1 Tick당 노드 방문 수 감소

RUNNING 자식의 인덱스가 k이고 RUNNING이 M Tick 동안 지속되는 경우:

  • 기억 있음 (FallbackWithMemory): Tick당 1개 노드 방문 → 총 k + M
  • 기억 없음 (ReactiveFallback): Tick당 k+1개 노드 방문 → 총 (k+1) \times M

5.2 상태 일관성 보장

RUNNING 자식에 대한 Tick이 앞쪽 자식의 평가 없이 직접 전달되므로, 비동기 작업의 연속성이 보장된다. 앞쪽 자식의 재평가로 인한 예기치 않은 Halt가 발생하지 않는다.

5.3 대안 전환 불가

기억된 인덱스 이전의 자식은 재평가되지 않으므로, 이전에 FAILURE였던 대안이 성공 가능해져도 자동 전환되지 않는다. 이는 기억 메커니즘의 의도된 동작이자 한계이다.

6. SequenceWithMemory의 RUNNING 기억과의 대칭성

특성SequenceWithMemoryFallbackWithMemory
기억 대상RUNNING 자식 인덱스RUNNING 자식 인덱스
건너뛰는 자식의 이전 상태SUCCESSFAILURE
기억 소멸 조건FAILURE 또는 전체 SUCCESSSUCCESS 또는 전체 FAILURE
Halt 시 동작인덱스 초기화인덱스 초기화

두 노드의 RUNNING 기억 메커니즘은 구조적으로 동일하며, 논리적 역할(SUCCESS/FAILURE)만 교환된다.

7. 구현 수준의 기억 관리

BehaviorTree.CPP v4에서 FallbackNode의 기억 관리는 다음과 같이 구현된다(Faconti, 2022).

class FallbackNode : public ControlNode
{
private:
    size_t current_child_idx_;    // RUNNING 자식 인덱스 기억

    void halt() override
    {
        current_child_idx_ = 0;   // Halt 시 기억 소멸
        ControlNode::halt();
    }
};

current_child_idx_는 단일 정수 변수로 구현되며, 메모리 오버헤드는 sizeof(size_t) 바이트(일반적으로 8바이트)에 불과하다. 이 최소한의 상태 유지가 FallbackWithMemory의 재진입 동작 전체를 가능하게 한다.


참고 문헌

  • 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/