1294.46 ReactiveFallback의 매 Tick 재평가 규칙
1. 매 Tick 재평가의 정의
ReactiveFallback의 매 Tick 재평가(per-tick re-evaluation) 규칙이란, Fallback이 RUNNING 상태인 동안 매번 Tick을 수신할 때마다 첫 번째 자식(인덱스 0)부터 순차적으로 모든 자식을 다시 평가하는 규칙이다. 이전 Tick에서의 자식 반환 결과는 기억되지 않으며, 매 Tick이 독립적인 완전한 평가 주기를 구성한다(Colledanchise & Ogren, 2018).
2. 재평가 규칙의 상세
2.1 규칙 1: 항상 인덱스 0부터 시작
이전 Tick에서 어떤 자식이 RUNNING을 반환했는지와 무관하게, 매 Tick에서 인덱스 0의 자식부터 평가를 시작한다.
Tick N: i=0(F) → i=1(F) → i=2(RUNNING) → RUNNING
Tick N+1: i=0(F) → i=1(F) → i=2(RUNNING) → RUNNING (0부터 재시작)
Tick N+2: i=0(S) → SUCCESS (대안 변화 감지)
2.2 규칙 2: FAILURE 자식도 재평가
FallbackWithMemory에서 건너뛰어지는 FAILURE 자식들이 ReactiveFallback에서는 매 Tick마다 다시 평가된다. 이를 통해 이전에 실패했던 대안의 가용성 변화를 지속적으로 확인한다.
2.3 규칙 3: SUCCESS 발생 시 RUNNING 자식 Halt
재평가 과정에서 이전에 FAILURE였던 자식이 SUCCESS를 반환하면, 현재 RUNNING 상태인 후속 자식에게 Halt를 전달한다.
Tick N:
children[0]: CondA → FAILURE
children[1]: ActB → RUNNING
ReactiveFallback → RUNNING
Tick N+1:
children[0]: CondA → SUCCESS ← 재평가에서 변화 감지
children[1]: ActB → Halt ← RUNNING 자식 중단
ReactiveFallback → SUCCESS
2.4 규칙 4: RUNNING 또는 SUCCESS 이후의 자식은 미평가
하나의 자식이 RUNNING 또는 SUCCESS를 반환하면, 그 이후의 자식에게는 Tick이 전달되지 않는다.
3. ReactiveSequence 재평가 규칙과의 대칭성
| 규칙 | ReactiveSequence | ReactiveFallback |
|---|---|---|
| 시작 인덱스 | 항상 0 | 항상 0 |
| 재평가 대상 | 이전 SUCCESS 자식 | 이전 FAILURE 자식 |
| 조기 종료 트리거 | FAILURE | SUCCESS |
| RUNNING 자식 Halt 원인 | 앞쪽 조건 FAILURE | 앞쪽 조건 SUCCESS |
| 계속 진행 상태 | SUCCESS | FAILURE |
4. 재평가에 의한 우선순위 감시
ReactiveFallback의 재평가 규칙은 앞쪽 자식을 우선순위 감시(priority monitor) 역할로 활용하는 패턴을 가능하게 한다. 우선순위가 높은 대안(앞쪽)이 가용해지면, 현재 진행 중인 낮은 우선순위 대안(뒤쪽)을 즉시 중단한다.
<ReactiveFallback>
<!-- 우선순위 감시 조건들 -->
<Condition ID="IsGoalAlreadyAchieved"/>
<Condition ID="IsFastPathAvailable"/>
<!-- 현재 진행 중인 대안 -->
<Action ID="UseSlowPath"/>
</ReactiveFallback>
매 Tick마다 목표 달성 여부와 빠른 경로의 가용성이 재평가된다. UseSlowPath 진행 중 빠른 경로가 가용해지면 UseSlowPath가 Halt된다.
5. 재평가의 비용 분석
5.1 Tick당 노드 방문 수
RUNNING 상태의 자식이 인덱스 k에 위치할 때, 매 Tick마다 k+1개의 노드가 방문된다.
자식 배열: [Cond₁, Cond₂, Cond₃, AsyncAct]
AsyncAct가 RUNNING일 때:
매 Tick 방문: Cond₁, Cond₂, Cond₃, AsyncAct → 4개
FallbackWithMemory에서는 AsyncAct만 방문하므로 1개이다.
5.2 총 비용 비교
비동기 작업이 M Tick 동안 RUNNING을 유지하는 경우:
- ReactiveFallback: (k+1) \times M 노드 방문
- FallbackWithMemory: k + 1 + (M-1) \times 1 = k + M 노드 방문
이 비용 구조는 ReactiveSequence와 SequenceWithMemory의 비용 구조와 동일하다.
6. 재평가와 조건-행동 패턴
ReactiveFallback에서 조건 노드를 앞에 배치하는 패턴은 “조건이 충족되면 행동을 중단“하는 의미론을 구현한다.
<ReactiveFallback>
<Condition ID="IsAtChargingStation"/>
<Action ID="NavigateToChargingStation"/>
</ReactiveFallback>
Tick 1: IsAtChargingStation→F, Navigate→R → RUNNING
Tick 2: IsAtChargingStation→F, Navigate→R → RUNNING
Tick 3: IsAtChargingStation→S → SUCCESS (Navigate Halt)
네비게이션 중 충전소에 도착하면(다른 요인에 의해 위치가 변경되거나 충전소가 이동해온 경우 등) 네비게이션이 즉시 중단된다.
7. 재평가와 액션 노드의 관계
ReactiveFallback에서 앞쪽에 배치된 액션 노드는 매 Tick마다 재실행된다. 이미 FAILURE를 반환한 액션이 다시 실행되면, onStart()부터 다시 시작하여 부수 효과가 반복 발생할 수 있다. 따라서 ReactiveFallback에서도 앞쪽에는 조건 노드(부수 효과 없는 순수 평가)만 배치하고, 비동기 액션은 뒤쪽에 배치하는 것이 설계 원칙이다(Faconti, 2022).
<!-- 권장 패턴: 조건 → 조건 → 액션 -->
<ReactiveFallback>
<Condition ID="CondA"/>
<Condition ID="CondB"/>
<Action ID="AsyncAction"/> <!-- 뒤쪽에 배치 -->
</ReactiveFallback>
<!-- 비권장 패턴: 액션이 앞에 위치 -->
<ReactiveFallback>
<Action ID="SomeAction"/> <!-- 매 Tick 재실행 — 위험 -->
<Action ID="AnotherAction"/>
</ReactiveFallback>
참고 문헌
- 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/