1293.33 ReactiveSequence 노드에서의 Tick 전파

1. ReactiveSequence의 정의

ReactiveSequence는 행동 트리(Behavior Tree)에서 WithoutMemory 속성을 가지는 Sequence 노드의 변형이다. 표준 Sequence(WithMemory)가 이전에 SUCCESS를 반환한 자식을 건너뛰고 RUNNING 중인 자식부터 재개하는 것과 달리, ReactiveSequence는 매 Tick마다 첫 번째 자식부터 모든 자식을 재평가한다. 이를 통해 조건 변화에 즉각적으로 반응하는 행동 패턴을 구현한다(Colledanchise & Ogren, 2018).

2. Tick 전파 알고리즘

function ReactiveSequence.tick():
    for each child in children (왼쪽에서 오른쪽):
        child_status = child.tick()
        
        if child_status == RUNNING:
            // 이후 자식 중 RUNNING인 것이 있으면 Halt 불필요
            // (현재 자식이 RUNNING이므로 이후 자식은 Tick하지 않음)
            haltChildrenAfter(current_index + 1)
            return RUNNING
        
        if child_status == FAILURE:
            haltRunningChildren()
            return FAILURE
    
    return SUCCESS

2.1 핵심 동작

  1. 매 Tick마다 첫 번째 자식부터 시작: 이전 Tick의 상태와 무관하게 항상 인덱스 0부터 자식을 Tick한다.
  2. 자식이 FAILURE 반환 시: 즉시 FAILURE를 반환하고, RUNNING 중인 자식에 Halt를 호출한다.
  3. 자식이 RUNNING 반환 시: 해당 자식 이후의 자식은 Tick하지 않고 RUNNING을 반환한다.
  4. 모든 자식이 SUCCESS: 모든 자식이 SUCCESS를 반환하면 SUCCESS를 반환한다.

3. 구체적 실행 흐름 예시

다음과 같은 ReactiveSequence 구조를 가정한다.

ReactiveSequence
├── IsBatteryOk (조건)
├── IsPathClear (조건)
└── NavigateToGoal (비동기 액션)

3.1 정상 실행 흐름

TickIsBatteryOkIsPathClearNavigateToGoalReactiveSequence
1SUCCESSSUCCESSRUNNINGRUNNING
2SUCCESSSUCCESSRUNNINGRUNNING
3SUCCESSSUCCESSSUCCESSSUCCESS

매 Tick마다 두 조건이 재평가되고, 두 조건이 모두 SUCCESS인 경우에만 NavigateToGoal이 Tick된다.

3.2 조건 변화에 의한 중단

TickIsBatteryOkIsPathClearNavigateToGoalReactiveSequence
1SUCCESSSUCCESSRUNNINGRUNNING
2SUCCESSSUCCESSRUNNINGRUNNING
3SUCCESSFAILUREHaltFAILURE

Tick 3에서 IsPathClear가 FAILURE를 반환하면, ReactiveSequence는 즉시 FAILURE를 반환하고 NavigateToGoal에 Halt를 호출하여 네비게이션을 중단한다.

3.3 조건 복구 후 재시작

TickIsBatteryOkIsPathClearNavigateToGoalReactiveSequence
3SUCCESSFAILUREHaltFAILURE
4SUCCESSSUCCESSRUNNING (재시작)RUNNING

Tick 4에서 IsPathClear가 다시 SUCCESS를 반환하면, NavigateToGoal이 IDLE 상태에서 새로 Tick되어 작업을 처음부터 재시작한다.

4. 조건 노드와 액션 노드의 배치 규칙

ReactiveSequence에서 자식 노드의 배치 순서는 중요한 설계 결정이다. 일반적으로 다음 규칙을 따른다.

4.1 조건 노드를 앞에 배치

조건 노드(ConditionNode)를 ReactiveSequence의 앞쪽에 배치하여, 매 Tick마다 전제 조건을 먼저 검증한 후 액션 노드를 실행한다.

<ReactiveSequence>
    <Condition_1/>  <!-- 전제 조건 1 -->
    <Condition_2/>  <!-- 전제 조건 2 -->
    <Action/>       <!-- 조건이 유지되는 동안만 실행 -->
</ReactiveSequence>

4.2 액션 노드는 하나만 배치

ReactiveSequence의 마지막 자식으로 하나의 비동기 액션 노드만 배치하는 것이 권장된다. 복수의 비동기 액션을 배치하면, 첫 번째 액션이 RUNNING을 반환할 때 두 번째 액션은 Tick되지 않으며, 첫 번째 액션이 SUCCESS를 반환한 후에야 두 번째 액션이 시작된다. 그러나 다음 Tick에서 조건이 재평가될 때 첫 번째 액션도 재Tick되어 의도치 않은 재실행이 발생할 수 있다.

5. ReactiveSequence와 SequenceWithMemory의 비교

특성ReactiveSequenceSequenceWithMemory
조건 재평가매 Tick최초 1회만
조건 변화 감지즉시불가
액션 재시작 가능성조건 변화 시 재시작없음
Tick당 실행 노드 수조건 + 현재 액션현재 액션만
전형적 용도조건 감시 + 액션단계적 작업 수행

6. ReactiveSequence의 안전성 보장

ReactiveSequence의 가장 중요한 특성은 전제 조건의 지속적 감시를 통한 안전성 보장이다. 로봇공학에서 다음과 같은 안전 관련 패턴에 널리 사용된다.

<ReactiveSequence>
    <IsEmergencyStopNotPressed/>
    <IsBatteryAbove threshold="5"/>
    <IsWithinOperationalBoundary/>
    <ExecuteMission/>
</ReactiveSequence>

이 패턴에서 비상 정지 버튼, 배터리 잔량, 운용 경계 등의 안전 조건이 매 Tick마다 검증되며, 하나라도 위반되면 임무 실행이 즉시 중단된다.

7. Tick 전파의 비용 분석

ReactiveSequence에서 매 Tick마다 모든 조건 노드를 재평가하는 비용은 조건 노드의 수와 개별 평가 비용에 비례한다.

T_{reactive} = \sum_{i=0}^{k-1} T_{condition_i} + T_{action}

여기서 k는 조건 노드의 수, T_{condition_i}는 각 조건 노드의 평가 시간, T_{action}은 액션 노드의 onRunning() 실행 시간이다. 조건 평가가 경량 연산(블랙보드 값 비교, 센서 데이터 임계값 확인 등)인 경우 추가 비용은 미미하지만, 고비용 연산(기하학적 계산, 확률적 추론 등)인 경우에는 Tick 실행 시간에 유의미한 영향을 미칠 수 있다.


참고 문헌

  • 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/