1293.76 Tick 단위 노드 방문 로그

1. 노드 방문 로그의 정의

Tick 단위 노드 방문 로그란, 단일 Tick 실행 중에 tick(), onStart(), onRunning() 등의 메서드가 호출된 모든 노드를 시간 순서대로 기록한 것이다. 이 로그는 Tick 전파 경로를 완전히 추적하여, 어떤 노드가 방문되었고 어떤 노드가 건너뛰어졌는지를 명시적으로 보여준다. 노드 방문 로그는 트리의 실행 경로 분석, 조기 종료 검증, 불필요한 노드 방문 식별에 활용된다(Faconti, 2022).

2. 방문 로그의 구성 요소

각 방문 로그 엔트리는 다음의 정보를 포함한다.

필드설명예시
tick_idTick 순번142
visit_orderTick 내 방문 순서3
node_name노드 이름NavigateToGoal
node_type노드 유형StatefulActionNode
method_called호출된 메서드onRunning
depth트리 내 깊이2
parent_name부모 노드 이름MainSequence

3. 방문 로그 수집 방법

3.1 TreeObserver 활용

BehaviorTree.CPP v4의 TreeObserver 인터페이스를 활용하여 노드 방문을 감지한다.

class VisitLogger {
public:
    void onTickStart(int tick_id) {
        current_tick_ = tick_id;
        visit_order_ = 0;
        current_visits_.clear();
    }

    void onNodeVisited(const BT::TreeNode& node, 
                       const std::string& method) {
        VisitEntry entry;
        entry.tick_id = current_tick_;
        entry.visit_order = visit_order_++;
        entry.node_name = node.name();
        entry.node_type = node.registrationName();
        entry.method = method;
        entry.timestamp = std::chrono::steady_clock::now();
        
        current_visits_.push_back(entry);
    }

    void onTickEnd() {
        all_visits_.insert(all_visits_.end(), 
                          current_visits_.begin(), 
                          current_visits_.end());
    }

    void printTickVisits(int tick_id) const {
        for (const auto& v : current_visits_) {
            printf("  [%d] %s.%s (%s)\n",
                   v.visit_order,
                   v.node_name.c_str(),
                   v.method.c_str(),
                   v.node_type.c_str());
        }
    }

private:
    int current_tick_{0};
    int visit_order_{0};
    std::vector<VisitEntry> current_visits_;
    std::vector<VisitEntry> all_visits_;
};

3.2 StatusChangeLogger 기반 수집

상태 변화 로거를 활용하면 실제로 상태가 변경된 노드만을 기록할 수 있다. 그러나 상태가 변경되지 않고 동일 상태를 유지하는 재진입(예: RUNNING → RUNNING)도 기록하려면 추가적인 처리가 필요하다.

4. 방문 로그의 출력 형식

4.1 들여쓰기 기반 계층 출력

트리의 깊이에 따라 들여쓰기를 적용하여 계층 구조를 시각적으로 표현한다.

Tick #142:
  Sequence (MainSequence)
    Condition (IsBatteryAbove) → tick()
    Condition (IsLocalized) → tick()
    StatefulAction (NavigateToGoal) → onRunning()
    [이후 자식 미방문: NavigateToGoal이 RUNNING 반환]

4.2 타임라인 형식 출력

각 노드의 방문 시간을 절대 또는 상대 시간으로 표시한다.

Tick #142 (start: 0.000ms):
  +0.002ms  Sequence.tick()
  +0.005ms  IsBatteryAbove.tick() → SUCCESS
  +0.012ms  IsLocalized.tick() → SUCCESS
  +0.018ms  NavigateToGoal.onRunning() → RUNNING
  Total: 0.025ms

5. 방문 패턴 분석

노드 방문 로그를 Tick 간에 비교하면, 트리의 실행 패턴을 분석할 수 있다.

5.1 안정적 실행 패턴

동일한 노드 집합이 반복적으로 방문되는 패턴이다. 비동기 노드가 RUNNING을 유지하는 동안 나타난다.

Tick 140: [Seq, Cond_A, Cond_B, Action_1(RUNNING)]
Tick 141: [Seq, Cond_A, Cond_B, Action_1(RUNNING)]
Tick 142: [Seq, Cond_A, Cond_B, Action_1(SUCCESS), Action_2(RUNNING)]

5.2 전환 패턴

조건 변화에 의해 실행 경로가 변경되는 패턴이다.

Tick 150: [Seq, Cond_A(SUCCESS), Action_1(RUNNING)]
Tick 151: [Seq, Cond_A(FAILURE)]  ← 조건 변화, 경로 단축

6. 방문 통계

Tick 단위 방문 로그를 집계하면 다음의 통계를 산출할 수 있다.

\text{visit\_rate}(n) = \frac{N_{visited}(n)}{N_{total\_ticks}} \times 100\%

여기서 N_{visited}(n)은 노드 n이 방문된 Tick 수이다. 방문률이 높은 노드는 성능 최적화의 우선 대상이다.

7. 비방문 노드의 분석

각 Tick에서 방문되지 않은 노드의 식별도 중요하다. 비방문의 원인은 조기 종료, Memory 모드에 의한 건너뛰기, Fallback에서의 이전 대안 성공 등이다. 비방문 노드의 패턴 분석을 통해 트리 설계의 효율성을 평가할 수 있다.


참고 문헌

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