1294.77 Sequence에서의 자식 Halt 처리
1. Halt의 개념과 역할
Halt는 행동 트리에서 RUNNING 상태의 노드를 강제로 중단시키는 메커니즘이다. Sequence 노드에서 Halt가 발생하는 상황은 크게 두 가지이다: (1) Sequence 자체가 부모로부터 Halt 요청을 받는 경우, (2) ReactiveSequence에서 앞쪽 조건 노드의 FAILURE에 의해 뒤쪽 RUNNING 자식이 중단되는 경우이다. 두 경우 모두 RUNNING 상태의 자식 노드에 halt() 메서드가 호출되어, 해당 노드가 IDLE 상태로 전이한다(Colledanchise & Ogren, 2018).
2. Halt가 발생하는 상황
2.1 상황 1: 부모로부터의 Halt 전파
Sequence의 부모 노드가 Halt를 호출하면, Sequence는 자신의 모든 RUNNING 상태 자식에게 Halt를 전파한다.
<Fallback>
<Sequence name="Seq_A"> <!-- 부모가 이 Sequence를 Halt -->
<Action ID="ActionA"/> <!-- RUNNING → Halt -->
</Sequence>
<Action ID="AlternativeAction"/>
</Fallback>
Tick K: Seq_A가 Fallback의 부모로부터 Halt 요청을 받음
→ ActionA.halt() 호출 (RUNNING → IDLE)
→ Seq_A 자체도 IDLE 상태로 전이
2.2 상황 2: ReactiveSequence에서의 조건 FAILURE
<ReactiveSequence>
<Condition ID="IsSafe"/> <!-- Tick K: FAILURE -->
<Action ID="LongRunningTask"/> <!-- Tick K-1: RUNNING → Halt -->
</ReactiveSequence>
Tick K-1: IsSafe→S, LongRunningTask→R → RUNNING
Tick K: IsSafe→F → FAILURE
LongRunningTask.halt() 호출 (RUNNING → IDLE)
ReactiveSequence에서 앞쪽 자식의 FAILURE가 뒤쪽 RUNNING 자식의 Halt를 유발한다.
2.3 상황 3: SequenceWithMemory에서의 자식 FAILURE
<SequenceWithMemory>
<Action ID="Step1"/> <!-- SUCCESS (이전 Tick) -->
<Action ID="Step2"/> <!-- FAILURE (현재 Tick) -->
<Action ID="Step3"/>
</SequenceWithMemory>
SequenceWithMemory에서 자식이 FAILURE를 반환하면, 해당 자식 이외에 RUNNING 상태인 자식은 없으므로(SequenceWithMemory는 한 번에 하나의 자식만 RUNNING), 추가적인 Halt가 필요하지 않다. 단, Sequence 자체가 FAILURE를 반환한 후 부모로부터 Halt를 받으면, current_child_idx가 0으로 초기화된다.
3. Halt 처리의 구현 메커니즘
3.1 haltChild() 메서드
BehaviorTree.CPP에서 Sequence 노드가 자식을 Halt하는 과정은 다음과 같다(Faconti, 2022):
void ControlNode::haltChild(size_t index) {
auto child = children_nodes_[index];
if (child->status() == NodeStatus::RUNNING) {
child->halt();
}
child->setStatus(NodeStatus::IDLE);
}
halt() 메서드는 노드의 상태가 RUNNING인 경우에만 호출된다. 호출 후 노드의 상태는 IDLE로 설정된다.
3.2 haltChildren() 메서드
Sequence가 부모로부터 Halt를 받으면, 모든 자식을 순회하며 Halt한다:
void ControlNode::haltChildren() {
for (size_t i = 0; i < children_nodes_.size(); i++) {
haltChild(i);
}
}
3.3 ReactiveSequence에서의 선택적 Halt
ReactiveSequence는 FAILURE가 발생한 자식 이후의 RUNNING 자식만 Halt한다:
// ReactiveSequence의 tick() 내부
for (size_t i = 0; i < children_count; i++) {
NodeStatus status = children_nodes_[i]->executeTick();
if (status == NodeStatus::FAILURE) {
// i 이후의 RUNNING 자식을 Halt
haltChildren(i + 1);
return NodeStatus::FAILURE;
}
// ...
}
4. Halt 시 자식 노드의 상태 전이
4.1 상태 전이 다이어그램
RUNNING → halt() 호출 → onHalted() 실행 → IDLE
RUNNING 상태의 노드가 halt() 메서드를 받으면:
onHalted()콜백이 호출되어 노드 내부의 정리 작업이 수행된다.- 노드의 상태가 IDLE로 전이한다.
- 다음에 이 노드가 Tick되면
onStart()부터 새로 시작한다.
4.2 onHalted() 콜백의 역할
class NavigateAction : public BT::StatefulActionNode {
void onHalted() override {
// 네비게이션 목표 취소
navigation_client_->cancelGoal();
// 로봇 정지
publishZeroVelocity();
}
};
onHalted()에서는 진행 중인 비동기 작업의 취소, 하드웨어의 안전 상태 전이, 자원 해제 등이 수행된다.
5. Sequence 변형별 Halt 동작 비교
| 상황 | Sequence (기본) | SequenceWithMemory | ReactiveSequence |
|---|---|---|---|
| 부모로부터 Halt | 모든 자식 Halt | 모든 자식 Halt, idx=0 | 모든 자식 Halt |
| 자식 FAILURE | Halt 불필요 (동기) | 현재 자식만 (다른 RUNNING 없음) | 후속 RUNNING 자식 Halt |
| Halt 후 재시작 | idx=0부터 | idx=0부터 | idx=0부터 |
6. 로봇 공학에서의 Halt 시나리오
6.1 네비게이션 중 Halt
<ReactiveSequence>
<Condition ID="IsBatteryOK"/>
<SequenceWithMemory>
<Action ID="ComputePath"/>
<Action ID="FollowPath"/> <!-- RUNNING 중 Halt -->
</SequenceWithMemory>
</ReactiveSequence>
배터리 조건이 위반되면 FollowPath가 Halt된다. onHalted()에서 속도 명령을 0으로 설정하여 로봇을 정지시키고, 네비게이션 목표를 취소해야 한다.
6.2 매니퓰레이터 조작 중 Halt
<ReactiveSequence>
<Condition ID="IsHumanDistanceSafe"/>
<Action ID="MoveArm"/> <!-- RUNNING 중 Halt -->
</ReactiveSequence>
사람이 접근하면 MoveArm이 Halt된다. onHalted()에서 관절 제어 명령을 즉시 중단하고, 현재 위치에서 팔을 정지시켜야 한다. 관성에 의한 오버슈트를 방지하기 위해 능동적 제동이 필요할 수 있다.
6.3 드론 비행 중 Halt
<ReactiveSequence>
<Condition ID="IsGeofenceValid"/>
<Action ID="FlyToWaypoint"/> <!-- RUNNING 중 Halt -->
</ReactiveSequence>
지오펜스 위반 시 FlyToWaypoint가 Halt된다. 드론의 onHalted()에서는 모터를 즉시 정지시키면 추락하므로, 호버링 모드로 전환하거나 안전 비행 모드를 활성화해야 한다.
7. Halt 처리의 설계 원칙
-
onHalted()의 비블로킹 구현:
onHalted()메서드는 블로킹 없이 즉시 반환해야 한다. 장시간 소요되는 정리 작업은 비동기적으로 요청하고, 완료 대기는 하지 않는다. -
안전 상태 전이: Halt 시 물리적 시스템(모터, 액추에이터)을 안전한 상태로 전이시켜야 한다. “안전한 상태“의 정의는 로봇 유형에 따라 다르다: 지상 로봇은 정지, 드론은 호버링, 매니퓰레이터는 현재 자세 유지가 일반적이다.
-
자원 해제: 점유한 통신 채널, 액션 클라이언트 목표, 뮤텍스 등을 적절히 해제하여, 다음 실행 시 자원 충돌이 발생하지 않도록 한다.
-
Halt 후 재시작 대비: Halt 후 노드는 IDLE 상태가 되며, 다음 Tick에서
onStart()부터 새로 시작한다. 이전 실행의 잔여 상태가 다음 실행에 영향을 주지 않도록onHalted()에서 내부 상태를 초기화해야 한다. -
재귀적 Halt: Sequence의 자식이 서브트리인 경우, Halt는 서브트리 내부로 재귀적으로 전파된다. 서브트리 내의 모든 RUNNING 노드가 Halt되어야 한다(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/