임베디드용 Reconciler 패턴
쿠버네티스에서 착안한 상태 수렴 패턴으로 여러 서브시스템의 목표 상태 유지와 안전 인터락 구현
배경
소독 로봇에서 UVC 램프, 팬, 플라즈마 발생기, LED 등 여러 서브시스템은 일시적 장애, 통신 오류, 물리적 제약이 있어도 목표 상태를 유지해야 합니다. 각 서브시스템마다 타이밍 요구사항과 안전 의존성이 다릅니다. 기존 상태 머신 방식은 서브시스템 간 의존성이 생기면 금방 복잡해집니다.
핵심 문제
재시도 스케줄과 안전 제약이 다른 여러 독립 서브시스템의 상태 수렴을 어떻게 관리할 수 있을까요? 모든 것을 합친 거대한 컨트롤러 없이.
핵심 아이디어
쿠버네티스가 오케스트레이션에서 유사한 문제를 해결합니다: 현재 상태 관찰 → 목표와의 차이 계산 → 조정 액션 실행. 이 “조정 루프” 패턴이 임베디드에도 잘 맞습니다. 각 서브시스템이 독립적으로 목표를 향해 수렴하는 자체 Reconciler를 가집니다.
접근 방식
std::function 콜백을 쓰는 제네릭 Reconciler 템플릿이 핵심입니다:
ReconcileResult Reconcile() {
if (!check_diff_()) {
retry_counter_ = reload_value_; // 수렴됨; 다음 차이에 대비
return kNoDiff;
}
if (--retry_counter_ <= 0) {
action_();
retry_counter_ = reload_value_;
return kTriedAction;
}
return kWaitingForNextAction;
}
- check_diff: 현재 상태가 목표와 다르면 true 반환
- action: 조정이 필요할 때 실행할 함수
Reconciler는 카운터 기반 재시도 조절로 100 Hz 메인 루프에서 돌아갑니다. 1000ms 재시도 간격이면 reload 값은 100 틱입니다 ((retry_interval_ms / 1000) * loop_frequency). check_diff가 클리어되면(수렴) 카운터가 리셋돼서 다음 차이 발생 시 바로 액션할 수 있습니다.
네 개의 독립 Reconciler가 서브시스템을 관리합니다:
- UVC Reconciler - 안전 인터락: 로봇이 기울지 않아야 함
- 팬 Reconciler - 독립 작동
- 플라즈마 Reconciler - 안전 인터락: 팬이 켜져 있어야 함
- LED Reconciler - 무선 충전 펌웨어 엣지 케이스 처리용 60초마다 강제 업데이트
각 Reconciler는 세 상태 중 하나를 반환합니다: kNoDiff, kTriedAction, kWaitingForNextAction.
안전 인터락은 check_diff 조건에 직접 통합됩니다. 예를 들어 UVC Reconciler의 diff 체크에 기울기 센서 상태를 포함했습니다. 로봇이 기울면 UVC 목표와 무관하게 false를 반환해서 UV 노출을 방지합니다.
전체 패턴이 헤더 123줄 + 구현 59줄에 들어갑니다.
장애 처리
- 연쇄 방지: 플라즈마 Reconciler가 check_diff에서 팬 상태를 확인합니다. 팬이 멈추면 플라즈마는 무한 대기하지 않고 kNoDiff를 반환합니다.
- 정체 감지: Reconciler가 kWaitingForNextAction에 오래 머물면, 세 상태 반환으로 외부에서 이상을 감지하고 로깅할 수 있습니다.
- 메모리 사용:
std::function클로저가 보통 8~16바이트(콜백 포인터 + 캡처 상태 포인터)라서 RAM 제한 임베디드에 적합합니다.
트레이드오프
| 결정 | 이유 | 대가 |
|---|---|---|
가상 메서드 대신 std::function | 유연함, 람다 지원, 테스트 쉬움 | vtable 대비 약간의 오버헤드 |
| 타이머 대신 카운터 기반 조절 | 타이머 의존 없음, 결정적, 메인 루프 통합 | 루프 주파수에 결합 |
| check_diff에 안전 인터락 | 안전 로직 중앙화, 액션 전 위험 상태 차단 | check_diff 로직 복잡해질 수 있음 |
| 서브시스템별 독립 Reconciler | 분리됨, 독립 재시도 스케줄, 유지보수 쉬움 | 관리할 인스턴스 늘어남 |
| diff 발생 시 즉시 액션 | 일시적 장애에서 빠른 복구 | 상태가 진동하면 액션 몰림 가능 |
| LED 주기적 강제 업데이트 | 무선 충전 펌웨어 엣지 케이스 대응 | 정상 시 불필요한 명령 |
| check_diff로 연쇄 방지 | 의존 서브시스템이 graceful하게 실패 | 조건 로직 복잡도 증가 |
결과
실제 배포:
- Bear Robotics 소독 로봇 전 기종에 배포
- UVC, 팬, 플라즈마, LED 네 서브시스템이 패턴 사용
안전 검증:
- UVC 인터락 확인: 기울기 센서 활성 시 check_diff가 false 반환, 운송 중 UV 노출 방지
- 플라즈마-팬 의존성: 팬이 안정적이지 않으면 플라즈마 활성화 불가
개발자 경험:
- 새 서브시스템 추가: check_diff 조건 + action 콜백 정의하고 Reconciler 인스턴스화
- 코드베이스 전체에 흩어진 재시도 로직 제거
- 세 상태 반환으로 Reconciler 내부 수정 없이 외부 모니터링 가능
핵심 교훈
- 쿠버네티스 패턴이 임베디드에도 적용됨: 조정 루프가 컨테이너뿐 아니라 100 Hz에서도 동작
- 조건에 안전 포함: check_diff에 인터락을 넣어서 안전 로직 중앙화, 액션 전 위험 상태 차단
- 독립성으로 분리: 각 서브시스템이 자체 재시도 스케줄로 독립 조정, 거대 컨트롤러 없음
- 재사용 가능 템플릿: 새 서브시스템은 콜백 두 개만 제공하면 됨; 재시도 조절과 상태 추적은 공짜