본 문서는 제안단계 기술검토 자료입니다. 기재된 성능수치는 공개 사양 및 공개 벤치마크에 근거한 산정치이며, 하드웨어(SBC 기종 · 카메라 사양) 확정 후 실측을 통해 검증 · 보정하는 것을 전제로 합니다.
요구사양은 채널당 15FPS 이상, 최대 4채널입니다. 초당 60프레임의 추론 처리량이 필요하며, 본 항목의 달성 가능 여부가 시스템 설계의 기준점이 되므로 산정근거를 우선 제시합니다.
| 가속기 연산성능 | 26 TOPS (INT8) |
|---|---|
| 적용 검토모델 | YOLOv8n / YOLOv8s (640×640, INT8 양자화) |
| YOLOv8n 단일스트림 처리량 | 약 400 FPS |
| YOLOv8s 단일스트림 처리량 | 약 180 FPS |
| 요구 처리량 (4CH × 15FPS) | 60 FPS |
| YOLOv8s 기준 여유율 | 약 3.0 배 |
추론연산 자체는 병목요인이 아니며, 정확도가 높은 YOLOv8s 적용 시에도 요구치 대비 3배의 여유가 확보됩니다. 실제 병목은 아래 영상 디코딩 및 전처리 구간에서 발생합니다.
1080p RTSP 4채널을 CPU 디코딩할 경우 저전력 SBC의 CPU 자원을 대부분 소모하게 됩니다. 따라서 아래 세 가지 사항을 설계 초기단계에 확정하여야 합니다.
# GStreamer 파이프라인 (채널별) rtspsrc location=rtsp://cam{n}/sub latency=120 protocols=tcp ! rtph264depay ! h264parse ! v4l2h264dec capture-io-mode=dmabuf # VPU 디코딩 ! videoscale ! video/x-raw,width=640,height=640 ! hailonet hef-path=/opt/models/active.hef # 가속기 추론 ! hailofilter so-path=libyolo_post.so # NMS 후처리 ! hailotracker # 객체추적 ! appsink
검출박스의 하단 중심점(접지점)을 기준으로 감지구역 다각형 내부 여부를 판정합니다. 박스 중심점을 기준할 경우 원근에 의한 오판정이 증가하므로 접지점 기준이 타당합니다. 현장설정 화면에서 감지구역을 직접 지정하여 판정동작을 확인할 수 있습니다.
# 오검지 억제 : 연속프레임 조건 + 재발생 억제시간 class RoiJudge: def update(self, track_id, point, roi, frame_no): inside = point_in_polygon(point, roi.points) st = self.state.setdefault(track_id, {"streak": 0, "last": -999}) st["streak"] = st["streak"] + 1 if inside else 0 # 3프레임 연속 침범 + 직전 이벤트 후 5초(75프레임) 경과 if st["streak"] >= 3 and frame_no - st["last"] > 75: st["last"] = frame_no return True return False
장비 재기동 없이 추론모델을 교체하는 기능입니다. 신규 모델을 선로딩하여 대기시킨 후 참조를 전환하는 방식이며, 기존 모델을 먼저 해제할 경우 전환구간의 프레임이 유실됩니다.
# 이중슬롯 원자적 전환 async def hot_swap(new_hef_path): # 1) 신규모델 별도슬롯 적재 (기존 추론 계속 수행) standby = await hailo.load_network(new_hef_path) # 2) 워밍업 — 초기 추론지연 제거 await standby.infer(dummy_frame) # 3) 참조 전환 (프레임 유실 없음) old, ACTIVE.model = ACTIVE.model, standby # 4) 진행중 추론 완료대기 후 해제 await old.drain(timeout=2.0) old.release()
전환구간 프레임 유실이 발생하지 않습니다. 신규모델 적재단계에서 실패할 경우 해당 단계에서 중단되므로 운용중인 모델은 영향을 받지 않습니다.
현장 무인장비 특성상 배포실패는 곧 현장출동을 의미하므로, A/B 슬롯 구조를 적용하여 실패 시 직전 정상버전으로 자동 복구되도록 구성합니다.
┌ 슬롯 A (운용중) ─────────────┐ ┌ 슬롯 B (대기) ───────────────┐ │ 운용SW v1.4.2 / 모델 v2.0 │ │ 운용SW v1.5.0 / 모델 v2.1 │ └──────────────────────────────┘ └──────────────────────────────┘ ▲ │ └────────── 자동복구 (검증실패) ◀──────┘ 배포절차 1. 다운로드 : SHA256 + 서명 검증 (실패 시 즉시 폐기) 2. 비활성 슬롯 기록 : 운용중 슬롯 무손상 3. 부트 플래그 try-once 설정 후 재기동 4. 신규슬롯 기동 : 120초 이내 자가검증 통과 필요 - 카메라 채널 수신 정상 - 추론결과 유효성 정상 - 관제서버 상태보고 도달 5-a. 통과 : confirmed 확정, 슬롯 승격 5-b. 실패 · 무응답 : 감시기가 직전 슬롯으로 자동 기동
신규버전이 자가검증을 통과하지 못하면 확정되지 않고 복구되는 구조이므로, 장비가 사용불능 상태(brick)에 도달하지 않습니다.
교착상태(Deadlock) 감지는 프로세스 생존확인만으로는 불충분합니다. 프로세스가 기동상태를 유지하면서 처리가 정지한 경우를 검출해야 하므로, 처리 진행도를 감시대상으로 합니다.
WatchdogSec 기반 프로세스 상태보고 감시.
미보고 시 서비스 재기동[Service] Type=notify WatchdogSec=30 Restart=always RestartSec=5 StartLimitBurst=5 # 응용 주기보고 sd_notify(0, "WATCHDOG=1")
현장 LTE 회선은 단절이 상시 발생하므로, 현장 로컬 데이터베이스를 1차 저장소로 하고 클라우드 전송은 비동기로 처리합니다. 이벤트는 로컬 기록시점에 이미 보전되며 전송은 후행합니다.
# 로컬 우선기록 후 후행전송 def on_event(ev): db.insert(ev, synced=0) # 1차 기록 (이 시점에 보전 완료) queue.put_nowait(ev.id) async def uploader(): while True: for ev in db.select_unsynced(order="occurred_at ASC", limit=50): try: await s3.put(ev.snapshot); await s3.put(ev.clip) await api.post_event(ev) db.mark_synced(ev.id) backoff.reset() except NetworkError: await asyncio.sleep(backoff.next()) # 1→2→4…→60초 break await asyncio.sleep(1)
통합관제 화면에서 통신단절 발생 시 미전송 적재 알림이 표시되며, 복구 시 재전송 표기가 부여된 이벤트가 순차 수신되는 것을 확인할 수 있습니다.
┌──────────── 관제센터 (AWS) ─────────────┐
[현장장비 × N] │ │
│ │ ALB → 응용서버(API) → 데이터베이스 │
├ 이벤트 (mTLS) ────┼─→ 메시지브로커 → 수신처리 → DB │
├ 미디어 업로드 ────┼─→ 오브젝트스토리지 → CDN │
├ 상태보고 ─────────┼─→ 상태관리 → 감시 → 알림 │
└ 배포조회 ─────────┼─← 배포저장소 + 배포이력 DB │
└──────────────────────────────────────────┘
미구현 : 실물 가속기 추론, 저장소 실업로드, 신호제어기 연동
아래 항목은 일정 및 비용 산정의 전제조건으로, 미확정 시 위험도가 높은 순으로 정리하였습니다.
본 시험구성의 모든 화면은 실제 API · 데이터베이스 · 실시간 통신 위에서 동작합니다. 구성은 현장장비 소프트웨어 / 관제 서버 / 관제 화면으로 분리되어 있으며, 실물 하드웨어 연동 시 모의동작 모듈만 실제 추론 수신부로 교체하는 구조로 설계하였습니다.