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() 메서드를 받으면:

  1. onHalted() 콜백이 호출되어 노드 내부의 정리 작업이 수행된다.
  2. 노드의 상태가 IDLE로 전이한다.
  3. 다음에 이 노드가 Tick되면 onStart()부터 새로 시작한다.

4.2 onHalted() 콜백의 역할

class NavigateAction : public BT::StatefulActionNode {
    void onHalted() override {
        // 네비게이션 목표 취소
        navigation_client_->cancelGoal();
        // 로봇 정지
        publishZeroVelocity();
    }
};

onHalted()에서는 진행 중인 비동기 작업의 취소, 하드웨어의 안전 상태 전이, 자원 해제 등이 수행된다.

5. Sequence 변형별 Halt 동작 비교

상황Sequence (기본)SequenceWithMemoryReactiveSequence
부모로부터 Halt모든 자식 Halt모든 자식 Halt, idx=0모든 자식 Halt
자식 FAILUREHalt 불필요 (동기)현재 자식만 (다른 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 처리의 설계 원칙

  1. onHalted()의 비블로킹 구현: onHalted() 메서드는 블로킹 없이 즉시 반환해야 한다. 장시간 소요되는 정리 작업은 비동기적으로 요청하고, 완료 대기는 하지 않는다.

  2. 안전 상태 전이: Halt 시 물리적 시스템(모터, 액추에이터)을 안전한 상태로 전이시켜야 한다. “안전한 상태“의 정의는 로봇 유형에 따라 다르다: 지상 로봇은 정지, 드론은 호버링, 매니퓰레이터는 현재 자세 유지가 일반적이다.

  3. 자원 해제: 점유한 통신 채널, 액션 클라이언트 목표, 뮤텍스 등을 적절히 해제하여, 다음 실행 시 자원 충돌이 발생하지 않도록 한다.

  4. Halt 후 재시작 대비: Halt 후 노드는 IDLE 상태가 되며, 다음 Tick에서 onStart()부터 새로 시작한다. 이전 실행의 잔여 상태가 다음 실행에 영향을 주지 않도록 onHalted()에서 내부 상태를 초기화해야 한다.

  5. 재귀적 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/