반환 상태 변환 데코레이터 (Return Status Transformation Decorators)
1. 개요
반환 상태 변환 데코레이터는 자식 노드가 반환한 상태(SUCCESS, FAILURE, RUNNING)를 다른 상태로 매핑하여 부모 노드에 전달하는 데코레이터 노드의 범주이다. 이 유형의 데코레이터는 자식 노드의 실행 자체를 변경하지 않으며, 오직 결과의 해석만을 수정한다. 자식 노드의 코드를 수정하지 않고도 행동 트리의 논리적 흐름을 조정할 수 있다는 점에서 핵심적인 구조적 유연성을 제공한다.
2. 반환 상태 변환의 형식적 모델
반환 상태 변환 데코레이터 D는 상태 변환 함수 f_D로 정의된다.
f_D : \mathcal{S} \rightarrow \mathcal{S}, \quad \mathcal{S} = \{\text{SUCCESS}, \text{FAILURE}, \text{RUNNING}\}
이 함수는 자식 노드의 반환 상태를 입력으로 받아, 변환된 상태를 출력한다. 내부 상태에 의존하지 않으므로 무상태(stateless) 변환이며, 동일한 입력에 대해 항상 동일한 출력을 생성한다.
기본 반환 상태 변환 데코레이터
Inverter
자식의 SUCCESS와 FAILURE를 상호 반전시킨다.
f_{\text{Inverter}}(s) = \begin{cases} \text{FAILURE} & \text{if } s = \text{SUCCESS} \\ \text{SUCCESS} & \text{if } s = \text{FAILURE} \\ \text{RUNNING} & \text{if } s = \text{RUNNING} \end{cases}
<Inverter>
<Condition ID="IsObstacleDetected"/>
</Inverter>
논리적 NOT 연산에 해당하며, 조건의 의미를 반전시키는 데 사용된다. 행동 트리에서 가장 빈번히 사용되는 데코레이터이다.
2.1 ForceSuccess
자식의 반환 상태를 항상 SUCCESS로 변환한다(RUNNING 제외).
f_{\text{ForceSuccess}}(s) = \begin{cases} \text{SUCCESS} & \text{if } s \in \{\text{SUCCESS}, \text{FAILURE}\} \\ \text{RUNNING} & \text{if } s = \text{RUNNING} \end{cases}
<ForceSuccess>
<Action ID="OptionalCleanup"/>
</ForceSuccess>
선택적(optional) 행동의 실패가 상위 Sequence를 중단시키지 않도록 하는 데 사용된다. 자식이 실패하더라도 전체 시퀀스는 계속 진행된다.
ForceFailure
자식의 반환 상태를 항상 FAILURE로 변환한다(RUNNING 제외).
f_{\text{ForceFailure}}(s) = \begin{cases} \text{FAILURE} & \text{if } s \in \{\text{SUCCESS}, \text{FAILURE}\} \\ \text{RUNNING} & \text{if } s = \text{RUNNING} \end{cases}
<ForceFailure>
<Action ID="AlwaysFailAction"/>
</ForceFailure>
상위 Fallback 노드에서 특정 분기를 항상 거부하게 하여, 후속 분기로의 전환을 강제하는 데 사용될 수 있다.
3. RUNNING 상태의 보존 원칙
모든 반환 상태 변환 데코레이터는 RUNNING 상태를 변환하지 않고 그대로 전달한다. 이 원칙의 근거는 다음과 같다.
- 비동기 실행의 보존:
RUNNING은 자식 노드가 비동기적으로 실행 중임을 나타내며, 이를 다른 상태로 변환하면 행동 트리가 실행 중인 행동을 올바르게 추적하지 못한다. - halt 메커니즘의 정합성: 행동 트리는
RUNNING상태인 노드만을 halt 대상으로 식별한다.RUNNING이SUCCESS나FAILURE로 변환되면, 실행 중인 행동이 halt되지 않아 리소스 누수가 발생한다. - tick 전파의 일관성:
RUNNING상태는 다음 tick에서 해당 노드를 다시 tick하여야 한다는 신호이다. 이를 변환하면 tick 전파 로직이 파괴된다.
4. 상태 변환 데코레이터의 합성
복수의 상태 변환 데코레이터를 중첩하면, 합성 변환 함수가 형성된다.
f_{\text{outer}} \circ f_{\text{inner}}(s) = f_{\text{outer}}(f_{\text{inner}}(s))
합성 예시
| 중첩 구조 | 합성 변환 | SUCCESS | FAILURE |
|---|---|---|---|
Inverter(Inverter(C)) | 항등 변환 | S→S | F→F |
ForceSuccess(Inverter(C)) | 항상 SUCCESS | S→S | F→S |
Inverter(ForceSuccess(C)) | 항상 FAILURE | S→F | F→F |
ForceFailure(ForceSuccess(C)) | 항상 FAILURE | S→F | F→F |
이중 Inverter는 항등 변환이므로, 불필요한 중첩이다. 코드 검토 시 이를 식별하여 제거하여야 한다.
XML 행동 트리에서의 활용
Sequence에서의 선택적 단계
<Sequence>
<Action ID="MoveToTarget"/>
<ForceSuccess>
<Action ID="OptionalScan"/> <!-- 실패해도 계속 -->
</ForceSuccess>
<Action ID="PerformTask"/>
</Sequence>
Fallback에서의 조건 반전
<Fallback>
<Sequence>
<Inverter>
<Condition ID="IsBatteryLow"/>
</Inverter>
<Action ID="ContinueMission"/>
</Sequence>
<Action ID="ReturnToBase"/>
</Fallback>
배터리가 충분하면(IsBatteryLow가 FAILURE → Inverter가 SUCCESS) 임무를 계속하고, 배터리가 부족하면 기지로 귀환한다.
설계 시 고려 사항
의미론적 명확성
상태 변환의 결과가 행동 트리의 문맥에서 직관적인지를 확인하여야 한다. 특히 ForceSuccess로 감싼 행동의 실패가 안전에 영향을 미치지 않는지를 신중히 판단하여야 한다.
과도한 변환의 회피
여러 변환을 중첩하면 결과의 예측이 어려워진다. 변환이 2단계를 초과하면 설계를 재검토하여 더 단순한 구조를 모색하여야 한다.
ForceSuccess의 안전 위험
ForceSuccess는 자식의 실패를 은폐하므로, 안전에 중요한 행동에 적용하면 위험하다. 실패가 허용되는 선택적 행동에만 적용하여야 한다.
참고 문헌
- Colledanchise, M., & Ogren, P. (2018). Behavior Trees in Robotics and AI: An Introduction. CRC Press.
- BehaviorTree.CPP 공식 문서. https://www.behaviortree.dev/
| 버전 | 날짜 | 변경 사항 |
|---|---|---|
| v0.1 | 2026-04-04 | 초안 작성 |