1293.37 데코레이터 노드에서의 Tick 전파
1. 데코레이터 노드의 정의
데코레이터 노드(Decorator Node)는 행동 트리(Behavior Tree)에서 정확히 하나의 자식 노드를 가지며, 자식의 Tick 전파 방식이나 반환 상태를 변환하는 단항(unary) 제어 노드이다. Sequence, Fallback, Parallel 등의 제어 노드가 복수의 자식에 대한 Tick 전파 정책을 정의하는 것과 달리, 데코레이터 노드는 단일 자식에 대한 Tick의 전달 여부, 횟수, 또는 반환 상태의 변환을 결정한다(Colledanchise & Ogren, 2018).
2. 데코레이터의 Tick 전파 기본 구조
데코레이터 노드가 부모로부터 Tick을 수신하면, 내부 로직에 따라 자식에 Tick을 전파할지 여부를 결정한다. 기본적인 Tick 전파 구조는 다음과 같다.
function Decorator.tick():
if shouldTickChild():
child_status = child.tick()
return transformStatus(child_status)
else:
return cachedOrDefaultStatus()
이 구조에서 shouldTickChild()는 자식에 Tick을 전파할지 여부를 결정하는 함수이며, transformStatus()는 자식의 반환 상태를 변환하는 함수이다. 데코레이터의 종류에 따라 이 두 함수의 구현이 달라진다.
3. Tick 전파 유형에 따른 분류
데코레이터 노드는 Tick 전파 방식에 따라 다음과 같이 분류된다.
3.1 투과형 데코레이터 (Pass-through Decorator)
자식에 Tick을 항상 전파하고, 반환 상태만 변환하는 유형이다. 자식의 실행 자체에는 개입하지 않는다.
function PassThroughDecorator.tick():
child_status = child.tick() // 항상 자식에 Tick 전파
return transformStatus(child_status)
대표적인 예로 Inverter, ForceSuccess, ForceFailure가 있다.
3.2 필터형 데코레이터 (Filter Decorator)
특정 조건에 따라 자식에 Tick을 전파하거나 차단하는 유형이다. 자식이 Tick을 수신하지 못하는 경우가 존재한다.
function FilterDecorator.tick():
if condition_met:
child_status = child.tick()
return child_status
else:
return FAILURE // 또는 SKIPPED
대표적인 예로 Precondition, IfThenElse가 있다.
3.3 반복형 데코레이터 (Repeat Decorator)
자식에 Tick을 반복적으로 전파하거나, 복수의 Tick에 걸쳐 반복 실행을 관리하는 유형이다.
function RepeatDecorator.tick():
if repeat_count < max_repeats:
child_status = child.tick()
if child_status == SUCCESS:
repeat_count++
if repeat_count >= max_repeats:
return SUCCESS
child.halt()
return RUNNING
return child_status
return SUCCESS
대표적인 예로 Repeat, Retry가 있다.
4. 주요 데코레이터의 Tick 전파 상세
4.1 Inverter
Inverter는 자식에 Tick을 항상 전파하고, 자식의 반환 상태를 반전시킨다.
function Inverter.tick():
child_status = child.tick()
if child_status == SUCCESS:
return FAILURE
elif child_status == FAILURE:
return SUCCESS
else: // RUNNING
return RUNNING
RUNNING 상태는 반전되지 않는다. 이는 RUNNING이 작업의 진행 중임을 나타내는 메타 상태이므로, 의미론적으로 반전의 대상이 아니기 때문이다.
4.2 Repeat
Repeat 데코레이터는 자식을 지정된 횟수만큼 반복 실행한다. 자식이 SUCCESS를 반환할 때마다 반복 카운터를 증가시키고, 자식을 리셋하여 다음 반복을 준비한다.
function Repeat.tick():
while repeat_count < num_cycles:
child_status = child.tick()
if child_status == SUCCESS:
repeat_count++
child.halt() // 자식 리셋
elif child_status == RUNNING:
return RUNNING
elif child_status == FAILURE:
repeat_count = 0
return FAILURE
repeat_count = 0
return SUCCESS
단일 Tick 내에서 자식이 동기적으로 SUCCESS를 반환하면 다음 반복이 같은 Tick 내에서 시작될 수 있다. 자식이 비동기적으로 RUNNING을 반환하면, 다음 Tick에서 해당 반복이 계속된다.
4.3 Retry
Retry 데코레이터는 자식이 FAILURE를 반환할 때 지정된 횟수만큼 재시도한다.
function Retry.tick():
while retry_count < max_retries:
child_status = child.tick()
if child_status == SUCCESS:
retry_count = 0
return SUCCESS
elif child_status == RUNNING:
return RUNNING
elif child_status == FAILURE:
retry_count++
child.halt() // 자식 리셋
retry_count = 0
return FAILURE
4.4 Timeout
Timeout 데코레이터는 자식에 Tick을 전파하되, 자식이 지정된 시간 내에 완료되지 않으면 자식에 Halt를 호출하고 FAILURE를 반환한다.
function Timeout.tick():
if child.status() == IDLE:
start_time = current_time()
if current_time() - start_time > timeout_duration:
child.halt()
return FAILURE
return child.tick()
5. 데코레이터의 Halt 처리
데코레이터 노드가 부모로부터 Halt를 수신하면, 자식 노드에도 Halt를 전파해야 한다. 이는 데코레이터가 자식의 생명 주기를 관리하는 책임을 가지기 때문이다.
void Decorator::halt() {
halt_child(); // 자식에 Halt 전파
resetStatus(); // 자신의 상태 리셋
// 데코레이터별 내부 상태 리셋
repeat_count_ = 0; // Repeat의 경우
retry_count_ = 0; // Retry의 경우
}
데코레이터의 내부 상태(반복 카운터, 타이머 등)도 Halt 시 리셋되어야 한다. 이를 통해 다음 Tick에서 데코레이터가 초기 상태에서 새로 시작될 수 있다.
6. 데코레이터 체인에서의 Tick 전파
복수의 데코레이터를 연쇄적으로 배치하여 데코레이터 체인(decorator chain)을 구성할 수 있다. Tick은 외부 데코레이터에서 내부 데코레이터로 순차적으로 전파되며, 각 데코레이터가 자신의 변환 로직을 적용한다.
Retry (max=3)
└── Timeout (5초)
└── NavigateToGoal
이 체인에서 Tick 전파 흐름은 다음과 같다.
Tick N:
Retry.tick()
→ Timeout.tick()
→ NavigateToGoal.tick() → RUNNING
→ Timeout 반환: RUNNING
→ Retry 반환: RUNNING
Tick N+k (5초 초과):
Retry.tick()
→ Timeout.tick()
→ 시간 초과 감지
→ NavigateToGoal.halt()
→ Timeout 반환: FAILURE
→ Retry: retry_count++ (재시도)
→ Timeout.tick()
→ NavigateToGoal.tick() → RUNNING (재시작)
→ Timeout 반환: RUNNING
→ Retry 반환: RUNNING
7. 데코레이터와 제어 노드의 Tick 전파 비교
| 특성 | 데코레이터 노드 | 제어 노드 |
|---|---|---|
| 자식 수 | 정확히 1개 | 1개 이상 |
| Tick 전파 대상 | 단일 자식 | 복수 자식 중 선택 |
| 반환 상태 결정 | 자식 상태의 변환 | 자식 상태의 집계 |
| 주요 역할 | 행동 수정/제약 | 실행 흐름 제어 |
| 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/