1293.73 Tick 실행 시간 예산 (Budget)의 설정
1. Tick 실행 시간 예산의 정의
Tick 실행 시간 예산(tick time budget)이란, 단일 Tick의 실행에 할당된 최대 허용 시간이다. Tick 실행 시간이 이 예산을 초과하면 Tick 오버런이 발생하여, 후속 Tick의 지연과 시스템 응답성 저하를 초래한다. 예산은 Tick 주기와 동일하거나 그보다 작게 설정되며, ROS2 콜백 처리와 sleep 시간을 포함한 전체 루프 주기를 고려하여 결정된다(Faconti, 2022).
2. 예산 산출의 기본 원리
전체 Tick 루프의 시간 구성은 다음과 같다.
T_{period} = T_{spin} + T_{tick} + T_{sleep} + T_{overhead}
여기서 T_{spin}은 spinSome()에 의한 콜백 처리 시간, T_{tick}은 Tick 실행 시간, T_{sleep}은 대기 시간, T_{overhead}는 루프 관리 오버헤드이다.
Tick 예산은 다음과 같이 산출된다.
T_{budget} = T_{period} - T_{spin}^{max} - T_{overhead}
Tick 주기가 100ms(10Hz)이고, spinSome()의 최대 실행 시간이 10ms, 오버헤드가 2ms이면, Tick 예산은 88ms이다.
3. 예산 설정 기준
3.1 Tick 주기 기반 설정
가장 기본적인 설정 방법으로, Tick 주기의 일정 비율을 예산으로 설정한다.
| Tick 주기 | 보수적 예산 (50%) | 표준 예산 (70%) | 적극적 예산 (90%) |
|---|---|---|---|
| 10ms (100Hz) | 5ms | 7ms | 9ms |
| 50ms (20Hz) | 25ms | 35ms | 45ms |
| 100ms (10Hz) | 50ms | 70ms | 90ms |
보수적 설정은 예상치 못한 지연에 대한 여유를 확보하며, 적극적 설정은 트리의 처리 능력을 최대화하되 오버런 위험이 증가한다.
3.2 노드 구성 기반 설정
트리의 노드 구성을 분석하여 최악의 경우 실행 시간을 추정하고, 이를 기반으로 예산을 설정한다.
T_{budget} \geq T_{tick}^{worst} = \sum_{i \in \text{worst\_path}} T_{node_i}^{max}
최악 경로(worst-case path)는 단일 Tick에서 방문 가능한 노드들 중 실행 시간이 가장 긴 경로이다.
4. 예산 분배 전략
4.1 균등 분배
모든 노드에 동일한 실행 시간을 할당하는 방식이다.
T_{node\_budget} = \frac{T_{budget}}{N_{max\_visited}}
여기서 N_{max\_visited}는 단일 Tick에서 방문 가능한 최대 노드 수이다.
4.2 비례 분배
노드의 역할과 복잡도에 따라 차등 예산을 할당하는 방식이다.
예산 분배 예시 (총 예산: 80ms):
조건 노드 (5개): 각 1ms → 5ms (6.25%)
동기 액션 노드 (3개): 각 5ms → 15ms (18.75%)
비동기 액션 상태 확인 (4개): 각 0.5ms → 2ms (2.5%)
제어 노드 오버헤드: 3ms (3.75%)
여유: 55ms (68.75%)
비동기 노드의 onRunning()은 상태 확인만 수행하므로 매우 적은 예산을 할당하고, 동기 액션 노드에 더 많은 예산을 할당한다.
5. 동적 예산 조정
런타임에 측정된 Tick 실행 시간에 기반하여 예산을 동적으로 조정하는 방식이다.
class AdaptiveBudget {
public:
void update(std::chrono::microseconds actual_time) {
// 지수 이동 평균으로 예측
predicted_time_ = alpha_ * actual_time
+ (1.0 - alpha_) * predicted_time_;
// 예측 시간의 1.5배를 예산으로 설정
budget_ = std::chrono::microseconds(
static_cast<int64_t>(predicted_time_.count() * 1.5));
// 최소/최대 범위 제한
budget_ = std::clamp(budget_, min_budget_, max_budget_);
}
std::chrono::microseconds getBudget() const { return budget_; }
private:
double alpha_{0.1}; // 평활 계수
std::chrono::microseconds predicted_time_{0};
std::chrono::microseconds budget_;
std::chrono::microseconds min_budget_{1000}; // 1ms
std::chrono::microseconds max_budget_{90000}; // 90ms
};
6. 예산 초과 시 대응 전략
예산 초과가 검출되면 다음의 단계적 대응이 가능하다.
- 로깅: 초과 시간과 원인 노드를 기록하여 사후 분석에 활용한다.
- 경고 발행: ROS2 진단 메시지로 운영자에게 통보한다.
- Tick 주기 조정: 오버런이 반복되면 Tick 주기를 동적으로 늘린다.
- 노드 비활성화: 비필수 노드의 Tick을 건너뛰어 부하를 줄인다.
- 안전 모드 전환: 심각한 오버런 시 안전 모드로 전환한다.
7. 예산 설정의 검증
설정된 예산의 적절성은 실제 운영 환경에서의 측정을 통해 검증해야 한다. 다양한 시나리오(정상 운행, 복구 동작, 최대 부하 등)에서 Tick 실행 시간을 측정하고, 99 백분위수가 예산 이내인지 확인한다.
T_{tick}^{p99} \leq T_{budget} \quad \text{(검증 기준)}
시뮬레이션 환경에서의 측정도 유효하지만, 실제 하드웨어와 네트워크 환경에서의 측정이 최종적으로 필요하다. 실제 환경에서는 CPU 부하, 메모리 압력, 네트워크 지연 등의 변동 요인이 존재하기 때문이다.
참고 문헌
- 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/