1293.100 저지연 제어 요구와 Tick 설계

1. 저지연 제어의 필요성

저지연 제어(low-latency control)란, 센서 입력으로부터 액추에이터 명령 출력까지의 전체 지연(end-to-end latency)을 최소화하는 제어 방식이다. 로봇 공학에서 특정 응용은 밀리초 이하의 지연을 요구한다. 산업용 로봇 팔의 충돌 회피, 드론의 자세 안정화, 고속 이동 로봇의 장애물 회피 등에서 제어 지연은 직접적으로 안전과 성능에 영향을 미친다(Colledanchise & Ogren, 2018).

행동 트리의 Tick 메커니즘은 이 지연 경로의 일부를 구성하므로, Tick의 설계가 전체 시스템의 지연 특성을 결정하는 요인 중 하나이다.

2. 종단 간 지연의 구성 요소

센서 입력에서 액추에이터 출력까지의 종단 간 지연은 다음 요소의 합이다.

L_{total} = L_{sensor} + L_{comm} + L_{tick\_wait} + L_{tick\_exec} + L_{output}

  • L_{sensor}: 센서 측정과 데이터 전달 지연
  • L_{comm}: ROS2 통신 지연 (직렬화, 전송, 역직렬화)
  • L_{tick\_wait}: 다음 Tick까지의 대기 시간 (최대 Tick 주기)
  • L_{tick\_exec}: Tick 실행 시간
  • L_{output}: 출력 명령의 전달과 액추에이터 응답 시간

이 중 L_{tick\_wait}는 Tick 주기에 의해 결정되는 고정 지연이며, 주기적 Tick 모델에서 평균적으로 Tick 주기의 절반이다.

평균 대기 지연 = T_period / 2

Tick 주기 10ms (100 Hz) → 평균 대기 5ms
Tick 주기 100ms (10 Hz) → 평균 대기 50ms

저지연을 위한 Tick 주기 설정

최대 허용 지연으로부터의 역산

시스템의 최대 허용 종단 간 지연이 L_{max}로 주어지면, Tick 주기는 다음을 만족해야 한다.

T_{period} \leq L_{max} - L_{sensor} - L_{comm} - L_{tick\_exec} - L_{output}

예를 들어, 최대 허용 지연이 20ms이고 다른 지연 요소의 합이 10ms이면, Tick 주기는 10ms (100 Hz) 이하로 설정해야 한다.

2.1 고빈도 Tick의 비용

Tick 주기를 줄이면 지연은 감소하지만, CPU 사용량이 증가한다. Tick 실행 시간이 T_{exec}이면, CPU 점유율은 다음과 같다.

U_{cpu} = \frac{T_{exec}}{T_{period}}

Tick 주기가 지나치게 짧으면 Tick 실행 시간이 주기를 초과하여 데드라인 위반이 발생한다.

저지연 Tick 설계 기법

이벤트 기반 즉각 Tick

센서 콜백에서 직접 Tick을 트리거하면, L_{tick\_wait}를 제거할 수 있다. 이벤트 발생 후 대기 없이 즉시 Tick이 실행되므로, 주기적 Tick 대비 평균 T_{period}/2의 지연을 절감한다.

void sensorCallback(const sensor_msgs::msg::LaserScan::SharedPtr msg) {
    // 블랙보드 갱신
    blackboard_->set("latest_scan", *msg);
    
    // 즉각 Tick 실행
    std::lock_guard<std::mutex> lock(tick_mutex_);
    tree_.tickOnce();
    
    // 결과에 따른 즉각 명령 출력
    publishCommand();
}

이 패턴에서는 센서 콜백 스레드에서 직접 Tick이 실행되므로, Tick 실행 시간이 콜백 처리 시간에 추가된다. Tick 실행이 다음 센서 데이터 수신을 블로킹하지 않도록 주의해야 한다.

경량 Tick 경로

저지연이 요구되는 안전 관련 의사 결정은 별도의 경량 트리에서 처리하고, 복잡한 임무 관리는 별도의 트리에서 낮은 빈도로 처리하는 이중 트리 구조를 적용한다.

// 고빈도 안전 트리 (100 Hz)
BT::Tree safety_tree = factory.createTreeFromFile("safety_tree.xml");
// 저빈도 임무 트리 (10 Hz)
BT::Tree mission_tree = factory.createTreeFromFile("mission_tree.xml");

// 안전 트리는 매 센서 콜백에서 실행
void sensorCallback(const auto& msg) {
    auto safety_status = safety_tree.tickOnce();
    if (safety_status == BT::NodeStatus::FAILURE) {
        executeEmergencyStop();
    }
}

// 임무 트리는 주기적으로 실행
void missionTimerCallback() {
    mission_tree.tickOnce();
}

Tick 실행 시간의 최소화

저지연 환경에서는 Tick 실행 시간 자체가 지연의 주요 구성 요소이므로, Tick 내에서 수행되는 연산을 최소화해야 한다.

  • 불필요한 블랙보드 접근 제거
  • 동적 메모리 할당 제거
  • 로깅을 비동기로 분리
  • 조건 노드의 연산 비용 절감
  • 트리의 깊이와 노드 수 최소화

직접 하드웨어 접근

ROS2의 DDS 통신 계층을 경유하지 않고, 공유 메모리나 직접 하드웨어 인터페이스를 통해 센서 데이터를 읽고 명령을 출력하면, 통신 지연(L_{comm})을 최소화할 수 있다.

// 공유 메모리를 통한 센서 데이터 접근 (개념적)
class DirectSensorAccess {
public:
    double readBatteryLevel() {
        return shared_memory_->battery_level.load(
            std::memory_order_acquire);
    }

    bool readEmergencyButton() {
        return shared_memory_->emergency_pressed.load(
            std::memory_order_acquire);
    }

private:
    SharedSensorData* shared_memory_;
};

지연 분석과 측정

종단 간 지연 측정

센서 데이터의 타임스탬프와 액추에이터 명령의 발행 시각을 비교하여 종단 간 지연을 측정한다.

void measureLatency(const rclcpp::Time& sensor_stamp) {
    auto now = node_->get_clock()->now();
    auto latency_ms = (now - sensor_stamp).seconds() * 1000.0;
    
    latency_stats_.addSample(latency_ms);
    
    if (latency_ms > max_allowed_latency_ms_) {
        RCLCPP_WARN(logger_, 
            "End-to-end latency %.1fms exceeds limit %.1fms",
            latency_ms, max_allowed_latency_ms_);
    }
}

지연 요소별 분해

종단 간 지연을 각 구성 요소별로 분해하여, 병목 지점을 식별한다.

struct LatencyBreakdown {
    double sensor_to_callback_ms;   // 센서 → 콜백
    double callback_to_tick_ms;     // 콜백 → Tick 시작
    double tick_execution_ms;       // Tick 실행
    double tick_to_output_ms;       // Tick 완료 → 명령 출력
    double total_ms;
};

저지연 설계의 트레이드오프

기법지연 감소비용
고빈도 TickL_{tick\_wait} 감소CPU 사용 증가
이벤트 기반 TickL_{tick\_wait} 제거구현 복잡도
경량 트리L_{tick\_exec} 감소기능 분리 필요
직접 하드웨어 접근L_{comm} 감소이식성 저하
실시간 OS전체 지터 감소시스템 복잡도

저지연 제어와 행동 트리의 모듈성·확장성 사이에는 본질적인 트레이드오프가 존재한다. 극도의 저지연이 요구되는 저수준 반응 루프는 행동 트리 외부에서 처리하고, 행동 트리는 고수준 의사 결정에 집중하는 계층적 설계가 일반적으로 권장된다(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/