1294.21 ReactiveSequence의 처음부터 재실행 동작
1. 처음부터 재실행의 의미
ReactiveSequence의 “처음부터 재실행(restart from the beginning)” 동작이란, 매 Tick에서 자식 배열의 인덱스 0부터 순차적으로 executeTick()을 호출하는 것을 의미한다. 이전 Tick에서 특정 자식이 RUNNING을 반환했더라도, 다음 Tick에서는 그 자식보다 앞에 위치한 모든 자식을 다시 평가한다. 이는 SequenceWithMemory의 재진입(resume) 동작과 대비되는 핵심 차이점이다(Colledanchise & Ogren, 2018).
2. 재실행 동작의 상세 과정
2.1 Tick별 실행 흐름
<ReactiveSequence>
<Condition ID="IsSafe"/> <!-- 자식 0 -->
<Condition ID="HasGoal"/> <!-- 자식 1 -->
<Action ID="Navigate"/> <!-- 자식 2 -->
</ReactiveSequence>
Tick 1:
children[0]: IsSafe → SUCCESS (평가됨)
children[1]: HasGoal → SUCCESS (평가됨)
children[2]: Navigate → RUNNING (평가됨)
ReactiveSequence → RUNNING
Tick 2: (처음부터 재실행)
children[0]: IsSafe → SUCCESS (다시 평가됨)
children[1]: HasGoal → SUCCESS (다시 평가됨)
children[2]: Navigate → RUNNING (다시 평가됨)
ReactiveSequence → RUNNING
Tick 3: (처음부터 재실행)
children[0]: IsSafe → FAILURE (변화 감지!)
children[1]: HasGoal → (미평가 — 조기 종료)
children[2]: Navigate → Halt 호출
ReactiveSequence → FAILURE
2.2 SequenceWithMemory와의 비교
동일한 트리에서 SequenceWithMemory의 동작:
Tick 1:
children[0]: IsSafe → SUCCESS
children[1]: HasGoal → SUCCESS
children[2]: Navigate → RUNNING (current_index = 2)
Sequence → RUNNING
Tick 2: (인덱스 2부터 재개)
children[0]: (건너뜀)
children[1]: (건너뜀)
children[2]: Navigate → RUNNING
Sequence → RUNNING
Tick 3: (인덱스 2부터 재개)
children[0]: (건너뜀 — IsSafe가 FAILURE로 변했지만 감지 못함)
children[1]: (건너뜀)
children[2]: Navigate → RUNNING
Sequence → RUNNING ← 안전 조건 위반을 감지하지 못함
3. RUNNING 자식에 대한 재실행의 영향
ReactiveSequence에서 RUNNING 상태인 자식은 매 Tick마다 다시 executeTick()이 호출된다. StatefulActionNode의 경우, 노드가 이미 RUNNING 상태이면 executeTick()은 onRunning()을 호출하여 작업 완료 여부를 확인한다. 즉, RUNNING 자식에 대한 “재실행“은 실제로는 “상태 확인“에 해당하며, 작업이 처음부터 다시 시작되는 것은 아니다.
// StatefulActionNode의 executeTick() 내부 동작
NodeStatus executeTick() {
if (status() == NodeStatus::IDLE) {
return onStart(); // 최초 실행
} else if (status() == NodeStatus::RUNNING) {
return onRunning(); // 상태 확인 (재실행이 아님)
}
}
그러나 SyncActionNode(동기 액션)가 ReactiveSequence의 앞쪽에 배치되어 매 Tick마다 재실행되는 경우, tick() 함수가 매번 처음부터 실행되므로 부수 효과가 발생할 수 있다.
4. Halt 후 재시작의 동작
ReactiveSequence에서 조건 실패로 인해 RUNNING 자식이 Halt된 후, 다음 Tick에서 조건이 다시 SUCCESS를 반환하면 해당 자식이 IDLE 상태에서 다시 시작된다.
Tick N: IsSafe→S, Navigate→R → RUNNING
Tick N+1: IsSafe→F → FAILURE (Navigate Halt → IDLE)
Tick N+2: IsSafe→S, Navigate→R → RUNNING (Navigate가 onStart()부터 재시작)
이 동작은 조건이 일시적으로 충족되지 않았다가 다시 충족되면 작업을 처음부터 재시작하는 행동 패턴을 자연스럽게 구현한다.
5. 처음부터 재실행 동작의 설계 의도
ReactiveSequence의 처음부터 재실행 동작은 행동 트리의 반응성(reactivity)을 극대화하기 위한 설계이다. 로봇이 작업을 수행하는 동안에도 환경 조건을 지속적으로 감시하여, 조건 위반 시 즉각적으로 반응하는 능력을 제공한다. 이는 특히 안전 관련 조건의 실시간 감시에 필수적이다(Faconti, 2022).
참고 문헌
- 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/