실행 제어 데코레이터 (Execution Control Decorators)
1. 개요
실행 제어 데코레이터는 자식 노드의 실행 횟수, 재시도 정책, 반복 패턴 등을 제어하는 데코레이터 노드의 범주이다. 반환 상태 변환 데코레이터가 자식의 결과만을 수정하는 반면, 실행 제어 데코레이터는 자식이 언제, 몇 번 실행되는지를 결정한다. 이 유형의 데코레이터는 내부 상태(카운터, 플래그)를 유지하며, 상태 유지 데코레이터(stateful decorator)에 해당한다.
2. 주요 실행 제어 데코레이터
2.1 RetryNode (재시도)
자식이 FAILURE를 반환하면 지정된 최대 횟수까지 재시도한다.
| 자식 반환 | RetryNode 동작 | 결과 |
|---|---|---|
| SUCCESS | 재시도 종료 | SUCCESS 반환 |
| FAILURE (횟수 미초과) | 카운터 증가, 다음 tick에서 재시도 | RUNNING 반환 |
| FAILURE (횟수 초과) | 재시도 포기 | FAILURE 반환 |
| RUNNING | 자식 실행 계속 | RUNNING 반환 |
<RetryNode num_attempts="3">
<Action ID="ConnectToSensor"/>
</RetryNode>
센서 연결에 실패하면 최대 3회 재시도한다.
2.2 RepeatNode (반복)
자식이 SUCCESS를 반환하면 지정된 횟수만큼 반복 실행한다.
| 자식 반환 | RepeatNode 동작 | 결과 |
|---|---|---|
| SUCCESS (횟수 미도달) | 카운터 증가, 다음 tick에서 재실행 | RUNNING 반환 |
| SUCCESS (횟수 도달) | 반복 완료 | SUCCESS 반환 |
| FAILURE | 반복 중단 | FAILURE 반환 |
| RUNNING | 자식 실행 계속 | RUNNING 반환 |
<Repeat num_cycles="5">
<Action ID="PatrolWaypoint"/>
</Repeat>
num_cycles를 -1로 설정하면 무한 반복이 된다.
2.3 RunOnce (일회 실행)
자식을 최초 한 번만 실행하고, 이후 tick에서는 이전 결과를 캐싱하여 반환한다.
| 상태 | 동작 |
|---|---|
| 최초 tick | 자식을 tick하고 결과를 캐싱 |
| 이후 tick | 캐싱된 결과를 즉시 반환 (자식을 tick하지 않음) |
<RunOnce>
<Action ID="InitializeHardware"/>
</RunOnce>
시스템 초기화와 같이 한 번만 수행하여야 하는 행동에 적합하다.
2.4 KeepRunningUntilFailure (실패까지 계속)
자식이 FAILURE를 반환할 때까지 RUNNING을 반환하여 실행을 지속한다.
| 자식 반환 | 데코레이터 결과 |
|---|---|
| SUCCESS | RUNNING (다음 tick에서 자식 재실행) |
| FAILURE | FAILURE |
| RUNNING | RUNNING |
<KeepRunningUntilFailure>
<Action ID="MonitorSensorData"/>
</KeepRunningUntilFailure>
센서 감시와 같이 지속적으로 실행되어야 하는 행동에 적합하다.
3. 내부 상태 관리
3.1 카운터 기반 데코레이터
RetryNode와 RepeatNode는 내부 카운터를 유지하여 실행 횟수를 추적한다.
class RetryNode : public BT::DecoratorNode
{
BT::NodeStatus tick() override
{
setStatus(BT::NodeStatus::RUNNING);
BT::NodeStatus child_status = child_node_->executeTick();
if (child_status == BT::NodeStatus::SUCCESS)
{
try_count_ = 0;
return BT::NodeStatus::SUCCESS;
}
if (child_status == BT::NodeStatus::FAILURE)
{
++try_count_;
if (try_count_ >= max_attempts_)
{
try_count_ = 0;
return BT::NodeStatus::FAILURE;
}
return BT::NodeStatus::RUNNING;
}
return child_status; // RUNNING
}
void halt() override
{
try_count_ = 0;
DecoratorNode::halt();
}
private:
int try_count_ = 0;
int max_attempts_ = 1;
};
3.2 halt() 시 상태 초기화
실행 제어 데코레이터가 halt될 때, 내부 카운터와 플래그를 반드시 초기화하여야 한다. 초기화하지 않으면 다음 실행에서 이전 상태가 잔존하여 예기치 않은 동작이 발생한다.
4. 실행 제어 데코레이터의 조합
<Timeout msec="60000">
<RetryNode num_attempts="5">
<Action ID="DownloadMap"/>
</RetryNode>
</Timeout>
전체 60초 이내에서 지도 다운로드를 최대 5회 재시도한다. Timeout은 시간 제어 데코레이터이며, RetryNode는 실행 제어 데코레이터이다. 두 유형의 조합으로 시간과 횟수 모두 제한된 재시도 정책을 구현한다.
5. 설계 시 고려 사항
5.1 무한 반복 방지
RepeatNode에 -1을 설정하거나, KeepRunningUntilFailure를 사용하면 무한 실행이 발생한다. 반드시 Timeout 또는 외부 조건에 의한 종료 메커니즘과 함께 사용하여야 한다.
5.2 재시도 간격
RetryNode는 기본적으로 다음 tick에서 즉시 재시도한다. 재시도 사이에 지연이 필요한 경우 Delay 데코레이터를 내부에 배치한다.
<RetryNode num_attempts="3">
<Delay delay_msec="1000">
<Action ID="ConnectToServer"/>
</Delay>
</RetryNode>
5.3 RUNNING 반환의 의미
RetryNode와 RepeatNode가 RUNNING을 반환할 때, 이는 자식이 실행 중인 것이 아니라 데코레이터가 다음 시도/반복을 준비 중임을 나타내는 경우가 있다. 이 구분은 halt 처리에 중요하다.
6. 참고 문헌
- 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 | 초안 작성 |