1293.21 Tick 실행 중 노드 상태 변화 추적

1. 노드 상태의 정의

행동 트리(Behavior Tree)에서 각 노드는 Tick을 수신할 때마다 자신의 현재 상태(NodeStatus)를 결정하여 반환한다. BehaviorTree.CPP 프레임워크에서 노드가 취할 수 있는 상태는 다음과 같이 정의된다(Faconti, 2022).

상태의미
IDLE노드가 아직 Tick되지 않았거나 Halt된 상태
RUNNING노드의 작업이 진행 중이며 아직 완료되지 않은 상태
SUCCESS노드의 작업이 성공적으로 완료된 상태
FAILURE노드의 작업이 실패한 상태

노드 상태 변화 추적이란, Tick 실행 과정에서 각 노드의 상태가 어떤 값에서 어떤 값으로 전이(transition)하는지를 체계적으로 기록하고 분석하는 것을 의미한다. 이는 행동 트리의 동작을 이해하고, 의도한 대로 동작하는지를 검증하며, 오류를 진단하는 데 필수적인 기법이다.

2. 상태 전이 모델

행동 트리 노드의 상태 전이는 다음과 같은 유한 상태 전이 모델로 기술된다.

         tick()              tick()
IDLE ──────────► RUNNING ──────────► SUCCESS
  ▲                │   │                │
  │                │   │    tick()       │
  │    halt()      │   └──────────► FAILURE
  │                │                    │
  └────────────────┘    halt()          │
  └─────────────────────────────────────┘

주요 상태 전이는 다음과 같다.

  • IDLE → RUNNING: 노드가 처음으로 Tick되어 작업을 시작하는 전이
  • IDLE → SUCCESS: 동기 노드가 처음 Tick에서 즉시 성공하는 전이
  • IDLE → FAILURE: 동기 노드가 처음 Tick에서 즉시 실패하는 전이
  • RUNNING → RUNNING: 비동기 노드가 작업을 계속 수행 중인 상태 유지
  • RUNNING → SUCCESS: 비동기 노드의 작업이 성공적으로 완료된 전이
  • RUNNING → FAILURE: 비동기 노드의 작업이 실패한 전이
  • RUNNING → IDLE: Halt에 의해 실행이 중단된 전이
  • SUCCESS/FAILURE → IDLE: Halt 또는 트리 리셋에 의한 초기화

3. TreeObserver를 통한 상태 변화 추적

BehaviorTree.CPP는 TreeObserver 인터페이스를 제공하여 노드의 상태 변화를 관찰할 수 있다. TreeObserver를 구현한 객체를 트리에 등록하면, 각 노드의 상태가 변경될 때마다 콜백이 호출된다.

class StatusChangeLogger : public BT::StatusChangeLogger {
    void callback(
        BT::Duration timestamp,
        const BT::TreeNode& node,
        BT::NodeStatus prev_status,
        BT::NodeStatus new_status) override 
    {
        std::cout << "[" << timestamp.count() << "us] "
                  << node.name() << ": "
                  << BT::toStr(prev_status) << " -> "
                  << BT::toStr(new_status) << std::endl;
    }
};

이 관찰자를 트리에 등록하면 모든 노드의 상태 전이가 자동으로 기록된다.

BT::Tree tree = factory.createTreeFromFile("tree.xml");
StatusChangeLogger logger(tree.rootNode());

4. 상태 변화 추적의 계층적 구조

행동 트리에서 노드 상태의 변화는 트리의 계층 구조를 따라 전파되므로, 상태 변화 추적도 계층적 관점에서 분석해야 한다.

4.1 리프 노드 수준의 추적

리프 노드(액션 노드, 조건 노드)의 상태 변화는 가장 기본적인 추적 단위이다. 리프 노드의 상태 전이는 실제 작업의 수행 결과나 환경 조건의 평가 결과를 직접적으로 반영한다.

조건 노드의 경우, 매 Tick마다 IDLE → SUCCESS 또는 IDLE → FAILURE 전이가 발생한다. 조건 노드는 상태를 유지하지 않으므로 RUNNING 상태가 존재하지 않으며, 매 Tick 후 IDLE로 복귀한다.

비동기 액션 노드의 경우, 첫 Tick에서 IDLE → RUNNING 전이가 발생하고, 후속 Tick에서 RUNNING → RUNNING 유지 또는 RUNNING → SUCCESS/RUNNING → FAILURE 전이가 발생한다.

4.2 제어 노드 수준의 추적

제어 노드의 상태는 자식 노드들의 상태에 의해 결정되므로, 제어 노드의 상태 전이는 자식 노드의 상태 전이에 대한 집약(aggregation)이다. 예를 들어, Sequence 노드의 SUCCESS 전이는 모든 자식 노드가 SUCCESS를 반환한 결과이며, Fallback 노드의 SUCCESS 전이는 하나 이상의 자식 노드가 SUCCESS를 반환한 결과이다.

제어 노드 수준의 추적에서는 자식 노드의 상태 전이가 부모 노드의 상태 결정에 어떻게 기여하는지를 기록하는 것이 중요하다.

4.3 트리 수준의 추적

트리 수준에서의 상태 변화 추적은 루트 노드의 최종 반환 상태의 변화를 Tick 단위로 기록한다. 이는 행동 트리 전체의 거시적 동작 패턴을 파악하는 데 활용된다.

Tick 1: ROOT → RUNNING
Tick 2: ROOT → RUNNING
Tick 3: ROOT → SUCCESS
Tick 4: ROOT → RUNNING (새로운 임무 시작)
...

5. 상태 변화 이벤트의 구조화된 기록

상태 변화 이벤트를 구조화된 형식으로 기록하면 사후 분석의 효율성이 높아진다. 각 이벤트에 포함되어야 하는 필드는 다음과 같다.

필드설명예시
tick_id현재 Tick의 일련 번호42
timestamp이벤트 발생 시각1680123456.789
node_uid노드의 고유 식별자7
node_name노드의 이름“CheckBattery”
node_type노드의 유형ConditionNode
prev_status이전 상태IDLE
new_status새로운 상태SUCCESS

이러한 구조화된 기록은 JSON, CSV, 또는 프로토콜 버퍼(Protocol Buffers) 형식으로 저장할 수 있으며, 이후 필터링, 집계, 시각화 등의 분석에 활용된다.

6. 상태 변화 빈도 분석

각 노드의 상태 변화 빈도를 분석하면 행동 트리의 동적 특성을 정량적으로 파악할 수 있다. 특정 노드에서 상태 변화가 과도하게 빈번하게 발생하면 해당 노드의 조건이 불안정하거나 환경이 빈번하게 변동하고 있음을 나타낸다.

상태 변화 빈도는 다음과 같이 정의된다.

f_{transition}(n) = \frac{|\{e \mid e.node = n \wedge e.prev \neq e.new\}|}{N_{ticks}}

여기서 n은 대상 노드, N_{ticks}는 관찰 기간의 총 Tick 수이다. f_{transition}이 1에 가까우면 해당 노드가 매 Tick마다 상태가 변경되고 있음을 의미하며, 0에 가까우면 상태가 안정적으로 유지되고 있음을 의미한다.

7. 상태 변화 패턴의 이상 탐지

정상적인 행동 트리 동작에서 기대되지 않는 상태 변화 패턴을 탐지함으로써 설계 오류나 환경 이상을 조기에 발견할 수 있다. 대표적인 이상 패턴은 다음과 같다.

7.1 진동 패턴 (Oscillation)

특정 노드의 상태가 SUCCESSFAILURE 사이에서 빈번하게 진동하는 패턴이다. 이는 조건의 경계값 근처에서 센서 잡음에 의해 판단이 반복적으로 뒤바뀌는 상황을 나타낼 수 있다. 히스테리시스를 적용하여 완화할 수 있다.

7.2 장기 RUNNING 패턴

비동기 노드가 예상보다 오랜 기간 동안 RUNNING 상태를 유지하는 패턴이다. 이는 비동기 작업의 교착 상태(deadlock), 외부 시스템의 응답 부재, 또는 완료 조건의 설계 오류를 나타낼 수 있다.

7.3 예기치 않은 IDLE 전이

명시적인 Halt 호출 없이 노드가 IDLE 상태로 전이하는 패턴이다. 이는 프레임워크의 내부 오류나 메모리 손상 등 심각한 문제를 나타낼 수 있으므로, 즉시 조사가 필요하다.

8. BehaviorTree.CPP의 내장 로거 활용

BehaviorTree.CPP는 상태 변화 추적을 위한 내장 로거를 제공한다. StdCoutLogger는 콘솔에 상태 변화를 출력하고, FileLogger2는 바이너리 형식의 로그 파일을 생성하여 Groot2 시각화 도구에서 재생할 수 있다(Faconti, 2022).

BT::Tree tree = factory.createTreeFromFile("tree.xml");

// 콘솔 출력 로거
BT::StdCoutLogger console_logger(tree);

// 파일 로거 (Groot2 호환)
BT::FileLogger2 file_logger(tree, "bt_trace.btlog");

FileLogger2가 생성하는 로그 파일은 Groot2에서 로드하여 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/