1294.80 Fallback에서의 자식 Halt 처리
1. Fallback에서 Halt가 발생하는 상황
Fallback 노드에서 자식의 Halt가 발생하는 상황은 Sequence와 대칭적 구조를 가진다. 주요 상황은 다음과 같다: (1) Fallback 자체가 부모로부터 Halt 요청을 받는 경우, (2) ReactiveFallback에서 앞쪽 자식의 SUCCESS에 의해 뒤쪽 RUNNING 자식이 중단되는 경우이다(Colledanchise & Ogren, 2018).
2. 부모로부터의 Halt 전파
2.1 Sequence 부모에 의한 Fallback Halt
<Sequence>
<Fallback name="FB">
<Action ID="Alt1"/> <!-- RUNNING -->
</Fallback>
<Action ID="NextStep"/>
</Sequence>
Sequence의 부모가 Halt를 호출하면, Sequence는 RUNNING 상태인 Fallback에 Halt를 전파하고, Fallback은 자신의 RUNNING 자식인 Alt1에 Halt를 전파한다.
부모 → Sequence.halt()
→ Fallback.halt()
→ Alt1.halt() (RUNNING → IDLE)
→ Fallback: IDLE
→ Sequence: IDLE
2.2 Halt의 재귀적 전파
<Fallback name="Outer">
<Fallback name="Inner">
<Action ID="DeepAction"/> <!-- RUNNING → Halt -->
</Fallback>
</Fallback>
Halt는 트리 구조를 따라 재귀적으로 전파된다. Outer의 Halt가 Inner로, Inner의 Halt가 DeepAction으로 전달된다.
3. ReactiveFallback에서의 Halt
3.1 상위 대안의 SUCCESS에 의한 Halt
ReactiveFallback에서는 매 Tick 첫 번째 자식부터 재평가한다. 앞쪽 자식이 SUCCESS를 반환하면, 뒤쪽에 RUNNING 상태인 자식이 Halt된다.
<ReactiveFallback>
<Sequence>
<Condition ID="IsPrimaryAvailable"/>
<Action ID="PrimaryAction"/>
</Sequence>
<Action ID="FallbackAction"/> <!-- RUNNING → Halt -->
</ReactiveFallback>
Tick 1: IsPrimaryAvailable→F → Sequence: FAILURE
FallbackAction→R → RUNNING (대안 수행 중)
Tick 2: IsPrimaryAvailable→F → Sequence: FAILURE
FallbackAction→R → RUNNING
Tick 3: IsPrimaryAvailable→S, PrimaryAction→R → RUNNING
FallbackAction→Halt (RUNNING → IDLE)
Tick 3에서 1순위 대안이 가용해지면, ReactiveFallback은 1순위 대안의 Sequence를 실행하고 FallbackAction을 Halt한다. 이는 “더 우선순위가 높은 대안이 가용해졌으므로, 현재 대안을 중단한다“는 의미이다.
3.2 Halt 후 재전환
Tick 3: IsPrimaryAvailable→S, PrimaryAction→R (1순위 실행)
FallbackAction→Halt
Tick 4: IsPrimaryAvailable→F → Sequence: FAILURE (1순위 다시 불가)
FallbackAction→R (대안 재시작, onStart() 호출)
1순위 대안이 다시 불가해지면 FallbackAction이 재시작된다. Halt된 액션은 IDLE 상태이므로 onStart()부터 새로 시작한다.
4. FallbackWithMemory에서의 Halt
4.1 부모 Halt 시 idx 초기화
<FallbackWithMemory>
<Action ID="Alt1"/> <!-- 이전 FAILURE, 건너뜀 -->
<Action ID="Alt2"/> <!-- RUNNING (idx=1) -->
<Action ID="Alt3"/>
</FallbackWithMemory>
FallbackWithMemory가 부모로부터 Halt를 받으면:
- RUNNING 상태인 Alt2에
halt()호출 (RUNNING → IDLE) current_child_idx가 0으로 초기화- 다음에 Tick되면 Alt1부터 다시 시작
부모 Halt → Alt2.halt() (RUNNING → IDLE)
→ idx = 0 (기억 초기화)
다음 Tick: Alt1부터 재시작
4.2 자식 FAILURE 시의 자연스러운 전환
FallbackWithMemory에서 현재 자식이 FAILURE를 반환하면, 다음 자식으로 자연스럽게 전환된다. 이 과정에서 Halt는 발생하지 않는다(FAILURE를 반환한 자식은 이미 IDLE 상태로 전이한 것이므로).
Tick 1: Alt1→R (idx=0)
Tick 2: Alt1→F, Alt2→R (idx=1, Alt1 자체적으로 IDLE로 전이)
5. Halt 처리의 구현
5.1 BehaviorTree.CPP에서의 Fallback Halt 구현
void FallbackNode::halt() {
// 현재 RUNNING인 자식을 Halt
if (current_child_idx_ < children_nodes_.size()) {
haltChild(current_child_idx_);
}
// 인덱스 초기화 (WithMemory 변형)
current_child_idx_ = 0;
// 자체 상태를 IDLE로 설정
setStatus(NodeStatus::IDLE);
}
5.2 ReactiveFallback에서의 선택적 Halt
// ReactiveFallback의 tick() 내부
for (size_t i = 0; i < children_count; i++) {
NodeStatus status = children_nodes_[i]->executeTick();
if (status == NodeStatus::SUCCESS) {
// i 이후의 RUNNING 자식을 Halt
haltChildren(i + 1);
return NodeStatus::SUCCESS;
}
// ...
}
ReactiveFallback은 SUCCESS를 반환한 자식 이후의 모든 RUNNING 자식을 Halt한다(Faconti, 2022).
6. Sequence와 Fallback의 Halt 대칭성
| 특성 | Sequence의 Halt | Fallback의 Halt |
|---|---|---|
| Halt 트리거 (Reactive) | 앞쪽 자식 FAILURE | 앞쪽 자식 SUCCESS |
| Halt 대상 | 뒤쪽 RUNNING 자식 | 뒤쪽 RUNNING 자식 |
| Halt의 의미 | “조건 위반, 작업 중단” | “상위 대안 가용, 전환” |
| 부모 Halt 시 | 모든 RUNNING 자식 Halt | 모든 RUNNING 자식 Halt |
7. 로봇 공학에서의 Fallback Halt 시나리오
7.1 복구 행동 중 정상 복귀
<ReactiveFallback>
<Sequence>
<Condition ID="IsPathClear"/>
<Action ID="FollowPath"/>
</Sequence>
<Action ID="RecoveryBehavior"/> <!-- RUNNING → Halt -->
</ReactiveFallback>
복구 행동 수행 중 경로가 다시 깨끗해지면, RecoveryBehavior가 Halt되고 FollowPath로 전환된다. onHalted()에서 복구 행동의 상태를 적절히 정리해야 한다.
7.2 비상 대응 중 상황 개선
<ReactiveFallback>
<Sequence>
<Condition ID="IsAllNormal"/>
<SubTree ID="NormalMission"/>
</Sequence>
<Action ID="EmergencyResponse"/> <!-- RUNNING → Halt -->
</ReactiveFallback>
비상 대응 중 시스템이 정상으로 복구되면, EmergencyResponse가 Halt되고 정상 임무로 전환된다.
8. Halt 처리의 설계 원칙
-
안전한 상태 전이: Halt 시 물리적 시스템을 안전한 상태로 전이시킨다. 특히 ReactiveFallback에서의 빈번한 Halt-재시작 사이클에서도 안전이 보장되어야 한다.
-
상태 정리의 완전성: Halt된 액션의 내부 상태가 다음 실행에 영향을 주지 않도록
onHalted()에서 완전히 정리한다. -
Halt 비용의 최소화: ReactiveFallback에서는 Halt가 빈번하게 발생할 수 있으므로,
onHalted()의 실행 비용을 최소화해야 한다. -
비동기 작업의 취소: 진행 중인 ROS2 액션 목표, 서비스 호출 등의 비동기 작업은
onHalted()에서 적절히 취소하여 자원 누수를 방지한다(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/