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를 받으면:

  1. RUNNING 상태인 Alt2에 halt() 호출 (RUNNING → IDLE)
  2. current_child_idx가 0으로 초기화
  3. 다음에 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의 HaltFallback의 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 처리의 설계 원칙

  1. 안전한 상태 전이: Halt 시 물리적 시스템을 안전한 상태로 전이시킨다. 특히 ReactiveFallback에서의 빈번한 Halt-재시작 사이클에서도 안전이 보장되어야 한다.

  2. 상태 정리의 완전성: Halt된 액션의 내부 상태가 다음 실행에 영향을 주지 않도록 onHalted()에서 완전히 정리한다.

  3. Halt 비용의 최소화: ReactiveFallback에서는 Halt가 빈번하게 발생할 수 있으므로, onHalted()의 실행 비용을 최소화해야 한다.

  4. 비동기 작업의 취소: 진행 중인 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/