1293.72 Tick 실행 시간 초과의 검출
1. Tick 실행 시간 초과의 정의
Tick 실행 시간 초과(Tick overrun)란, 단일 Tick의 실행 시간이 설정된 Tick 주기를 초과하는 상황을 의미한다. Tick 주기가 T_{period}이고 Tick 실행 시간이 T_{tick}일 때, T_{tick} > T_{period}이면 Tick 오버런이 발생한다. 오버런이 발생하면 다음 Tick이 지연되어 시스템 응답성이 저하되고, 조건 재평가 빈도가 감소하며, 제어 루프의 안정성이 위협받는다(Faconti, 2022).
2. 오버런 검출 메커니즘
2.1 단순 시간 비교
Tick 실행 후 소요 시간을 Tick 예산과 비교하는 기본적 검출 방법이다.
void tickLoop() {
const auto tick_budget = std::chrono::milliseconds(100); // 10 Hz
rclcpp::Rate rate(10);
while (rclcpp::ok()) {
executor_.spin_some();
auto start = std::chrono::steady_clock::now();
tree_.tickOnce();
auto elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > tick_budget) {
auto overrun = std::chrono::duration_cast<
std::chrono::milliseconds>(elapsed - tick_budget);
RCLCPP_WARN(logger_,
"Tick overrun: %ld ms over budget", overrun.count());
}
rate.sleep();
}
}
2.2 연속 오버런 검출
단발성 오버런은 일시적 부하에 의해 발생할 수 있으나, 연속적 오버런은 구조적 문제를 나타낸다. 연속 오버런 카운터를 통해 심각도를 판정한다.
class OverrunDetector {
public:
void check(std::chrono::microseconds tick_time) {
if (tick_time > budget_) {
consecutive_overruns_++;
total_overruns_++;
if (consecutive_overruns_ >= critical_threshold_) {
RCLCPP_ERROR(logger_,
"Critical: %d consecutive tick overruns",
consecutive_overruns_);
onCriticalOverrun();
}
} else {
consecutive_overruns_ = 0;
}
}
private:
std::chrono::microseconds budget_;
int consecutive_overruns_{0};
int total_overruns_{0};
int critical_threshold_{5};
};
2.3 비율 기반 검출
일정 시간 창(window) 내에서 오버런 비율이 임계값을 초과하면 경고를 발생시킨다.
\text{overrun\_rate} = \frac{N_{overrun}}{N_{total}} \times 100\%
class OverrunRateMonitor {
public:
void recordTick(bool is_overrun) {
history_.push_back(is_overrun);
if (history_.size() > window_size_) {
history_.pop_front();
}
int overruns = std::count(history_.begin(), history_.end(), true);
double rate = static_cast<double>(overruns) / history_.size();
if (rate > rate_threshold_) {
RCLCPP_WARN(logger_,
"Overrun rate: %.1f%% exceeds threshold %.1f%%",
rate * 100.0, rate_threshold_ * 100.0);
}
}
private:
std::deque<bool> history_;
size_t window_size_{100};
double rate_threshold_{0.05}; // 5%
};
3. 오버런의 원인 분류
| 원인 분류 | 설명 | 검출 방법 |
|---|---|---|
| 동기 노드 과중 계산 | SyncActionNode에서 복잡한 계산 수행 | 노드별 프로파일링 |
| 차단 호출 | 동기 서비스 호출, 차단 I/O | 호출 스택 분석 |
| 블랙보드 대용량 복사 | 점군, 이미지 등의 데이터 복사 | 블랙보드 접근 프로파일링 |
| 락 경합 | 멀티스레드 환경에서 뮤텍스 대기 | 락 대기 시간 측정 |
| 트리 깊이/폭 과대 | 방문 노드 수 과다 | 방문 노드 카운트 |
| GC/메모리 할당 | 동적 메모리 할당에 의한 지연 | 할당 횟수 모니터링 |
4. 노드별 기여도 분석
오버런 발생 시 어떤 노드가 주요 원인인지 식별하기 위해, 노드별 실행 시간의 기여도를 분석한다.
void analyzeOverrun(const std::map<std::string,
std::chrono::microseconds>& node_times,
std::chrono::microseconds total_time) {
RCLCPP_WARN(logger_, "=== Tick Overrun Analysis ===");
RCLCPP_WARN(logger_, "Total: %ld us", total_time.count());
for (const auto& [name, time] : node_times) {
double contribution = 100.0 * time.count() / total_time.count();
if (contribution > 10.0) { // 10% 이상 기여
RCLCPP_WARN(logger_, " %s: %ld us (%.1f%%)",
name.c_str(), time.count(), contribution);
}
}
}
5. 경고 수준의 단계적 정의
오버런의 심각도에 따라 경고 수준을 단계적으로 정의한다.
| 수준 | 조건 | 대응 |
|---|---|---|
| 정보(INFO) | T_{tick} > 0.8 \times T_{period} | 모니터링 로그 기록 |
| 경고(WARN) | T_{tick} > T_{period} | 오버런 로그, 원인 분석 |
| 오류(ERROR) | 연속 5회 이상 오버런 | 진단 메시지 발행, 운영자 통보 |
| 치명(FATAL) | T_{tick} > 2 \times T_{period} | 안전 모드 전환 고려 |
6. 오버런 검출과 실시간 시스템
실시간 시스템에서 Tick 오버런은 데드라인 위반(deadline miss)에 해당한다. 오버런 검출은 데드라인 준수 여부를 런타임에 확인하는 메커니즘으로, 실시간 요구 사항이 있는 로봇 시스템에서 필수적이다.
\Pr(T_{tick} > T_{period}) < \epsilon \quad \text{(실시간 요구 사항)}
여기서 \epsilon은 허용 가능한 데드라인 위반 확률이다. 경성 실시간(hard real-time) 시스템에서는 \epsilon = 0이 요구되며, 연성 실시간(soft real-time) 시스템에서는 낮은 확률의 위반이 허용된다.
참고 문헌
- 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/