EDGE AI · SCHOOL-ZONE RISK MONITOR · SYSTEM · DATA · INSIGHT · AWARENESS

엣지 AI 기반 통학 시간대
교통 위험 모니터링 시스템 구축 및 인식 개선 — 프로젝트 개요

문제: 등교 시간 교문 앞 도로의 과속 · 정차 구역 밖 정차 · 횡단 학생 근접은 체감과 민원만 있고 숫자가 없음. 해법: 창 안쪽 엣지 디바이스(Jetson Orin Nano)가 영상을 저장하지 않고 통행을 세어 지표 3개(K1~K3)를 만들고, 위험 이벤트 순간에만 시각-언어 모델(VLM)이 "그 정차가 무엇이었는가"를 한 줄 태그로 붙여 오탐을 줄임. 숫자는 안내판 · 규범 메시지 · 건의서로 돌아가 운전자 · 학부모의 행동과 지자체의 결정을 바꿈. 연구: 같은 이벤트에 YOLO 단독 · VLM 단독 · 하이브리드 판정을 나란히 기록해 8GB 엣지 보드에서 하이브리드가 무엇을 더 주고 무엇을 비용으로 치르는지(fps · 지연 · 메모리 · 전력) 실측함. 증명: 개입 전 2주와 개입 후를 같은 요일 · 시간대로 비교해 공개함.

WHY
문제 · 가설
과속 · 정차 · 횡단
CAM
측정 · 판정
YOLO 상시 · VLM 이벤트
KPI
지표 3 + 맥락 1
K1 · K2 · K3 · C1
DB
5분 집계 · 추론 기록
traffic.db
ACT
개입
안내판 · 규범 · 건의
FX
효과 · 검증
전후 비교 · 벤치 공개

이 문서의 모든 절은 이 여섯 마디 중 하나에 속함. 절 3 이후의 지도는 파이프라인 마디(CAM · DET · TRK · VLM · DB · ANL · MSG)로 바뀌어 다시 나타남. 이 문서는 docs/index.html로 저장소에 들어가 GitHub Pages의 "프로젝트개요"가 됨.

관측 대상: 교문 앞 도로 통행 (신원 · 학교 관련 여부 불문)범위 밖: 얼굴 · 번호판 · 학생 여부 · 사고 예측 · 야간주 하드웨어: Jetson Orin Nano Super 8GB + ELP AR0234 글로벌 셔터 USB (2.8-12mm CS)설치 지점: 본관 3층 중앙탑 창 · 교문 정면 축대체 트랙: Pi 5 8GB + AI HAT+ 2 + Camera Module 3원칙: 프레임 무저장 · 텍스트만 저장 · 사람 승인 후 발송개발: 로컬 PC 작업 폴더 + Claude Code + GitHub + 기기 SSH

0-1두 문서가 만나는 자리 — 문제 우선 위에 연구 트랙을 얹는다

계승하는 것 (교통카운터 매뉴얼)

"모델이 확실히 잴 수 있는 것만 지표로" · 프레임 무저장 · 가상선 · 호모그래피 · 5분 버킷 · QC · 수동 대조 GATE · 검토 큐 · 규범 메시지 · 전후 비교. 이 뼈대는 VLM이 없어도 그대로 돌아가야 하며, VLM이 죽어도 K1~K3는 나온다가 설계의 첫 조건.

추가하는 것 (연구 계획서)

위험 이벤트 순간에만 도는 Slow Loop(VLM) · YOLO / VLM / HYBRID 3중 기록 · 자원(FPS · 지연 · 메모리 · 전력) 실측 · 안전 지수 · 학술 벤치마크. 계획서의 "동시 적재 가능 · 5.2 GB · 30→24 fps · 오탐 60%↓ · Recall 95%"는 실측 전 가정이므로 이 개요에서는 전부 가설(R1~R3)과 측정 프로토콜로 바꿈.

무엇을 바꾸려는가

학교가 직접 제어할 장치는 없음. 바꿀 수 있는 것은 운전자 · 학부모의 행동(정보 · 규범 메시지)과 지자체의 결정(정확도가 붙은 건의서). 시스템의 출력은 알림과 리포트이고, 성공은 K1~K3의 변화 + 벤치마크 표 1장으로 정의함.

가설내용지표 · 검증
H1등교 시간대 통과 차량 상당수가 30 km/h를 초과한다K1
H2정차 구역 밖 정차가 시야를 가려 횡단 위험을 만든다K2
H3횡단과 차량 통과가 겹치는 순간이 특정 15분에 집중된다K3
R1VLM 태그를 붙이면 "정차 = 위험"으로 세던 오탐(승하차 · 대기 등 정상 정차)이 줄어든다C1 · 사람 라벨 대비 FPR
R2Slow Loop가 돌 때 Fast Loop fps 저하가 QC 임계(15 fps) 안에 머문다fps · tegrastats
R38GB 통합 메모리에서 YOLO + 2~3B VLM Q4 동시 상주가 스왑 없이 유지된다mem · swap · 온도
핵심 지표 3개 + 맥락 태그 1개 K1 등교 시간대 4륜 차량의 30 km/h 초과 비율 (%) · K2 정차 구역 밖 20초 이상 정차 건수 (건/등교일) · K3 횡단보도에 보행자가 있는 동안 4륜 차량이 2 m 이내로 접근한 건수 (건/등교일, 프록시) · C1 K2 이벤트의 정차 사유 태그 분포 (VLM, 고정 선택지 4개, GATE 2 통과 후에만 리포트) · C2 등교 시간 교문 통과(교내 진입 · 진출) 차량 수 (가림 없는 진입로에서, 오차 최소). 정의와 오차는 절 1.

0-2범위 — 무엇을 하고, 무엇을 주장하지 않는가

기대 효과 · 안전
K1 · K2 · K3 ↓
등교 시간 과속 · 위험 정차 · 횡단 중 근접이 줄어드는 것을 숫자로 확인
기대 효과 · 연구
벤치 표 1장
YOLO / VLM / HYBRID × (FPR · Recall · fps · ms · MB · W) — 같은 이벤트 키로
기대 효과 · 제도 · 교육
건의서 1건 · 발표 1회
정확도 부록이 붙은 데이터로 지자체와 대화 · 학생이 가설 → 검증 → 공개를 수행
함정 · 이 프로젝트가 주장할 수 없는 것카메라는 "지나가는 차 · 서 있는 차 · 걷는 사람"을 볼 뿐 그 차가 학교 관련인지, 걷는 사람이 학생인지, 어린이인지 모름. VLM이 붙어도 마찬가지 — VLM 태그는 "정차의 모양"(문 열림 · 사람 승하차 동작 · 정지 상태)에 대한 판정이지 신원 · 연령 · 소속에 대한 판정이 아님. 따라서 "학부모 차량의 n%", "어린이 n명", "학원 승합차" 같은 문장은 어떤 리포트에도 쓰지 않음. 계획서 예시 문장 "학원 승합차 어린이 승하차 지도 중"은 이 문서에서 "차량 정차 · 승하차 동작 관찰됨 (tag = boarding)"으로 바뀜.
!두 문서 사이의 쟁점 · 이 개요의 판단
착수 전 읽기 15분
WHY계획서는 모델에서 출발(YOLO + VLM을 8GB에 올려 비교)하고, 카운터 매뉴얼은 문제에서 출발(잴 수 있는 것만 지표로)함. 두 문서를 그대로 합치면 서로 어긋나는 여덟 곳이 있고, 설치 지점 사진을 본 뒤 기하 문제 하나(9번)가 더해짐. 아래 표는 각 쟁점에서 무엇이 확인된 사실이고 무엇이 가정인지 가르고, 이 개요가 택한 결정을 적음. 결정이 마음에 들지 않으면 이 표를 먼저 고친 뒤 뒤의 절을 고칠 것.
#쟁점계획서카운터 매뉴얼확인된 사실 · 이 개요의 결정
1Camera Module 3를 Jetson에RTSP 1080p30 카메라 가정Pi 5 + CM3 (picamera2 · 22핀)CM3(IMX708)는 Jetson 공식 지원 센서가 아님. 커뮤니티 드라이버(RidgeRun)가 있으나 JetPack 5.1.1 / 6.0 커널 패치 기준이고 단일 모드(4608×2592 @ 14 fps)만 제공 → 현재 보드의 JetPack 7.2.1(커널 6.8)에는 그대로 적용되지 않을 가능성이 큼. 결정: Jetson 트랙은 USB UVC 카메라(무드라이버 · 1080p30 · 오토포커스 끔) 또는 IMX477 / IMX219 CSI(네이티브 드라이버)로 시작하고, CM3는 Pi 트랙 전용. CM3를 Jetson에 반드시 써야 한다면 R1에서 1일 한도로 시도하고 실패 시 되돌림.
2JetPack 버전"JetPack 6 세팅, NanoLLM"보유 보드는 JetPack 7.2.1(L4T r39.2.1 · Ubuntu 24.04 · CUDA 13 · TensorRT 10.x · Python 3.12)이 이미 설치됨. JetPack 6 시대 자료(jetson-containers · NanoLLM · cu126 휠)는 그대로 쓰이지 않고, TensorRT 엔진은 보드에서 다시 빌드해야 함. 결정: 계획서의 1단계 "OS 세팅"을 "JetPack 7.2.1 위에서 툴체인 검증"으로 바꾸고 R1에 1주를 줌. VLM 런타임은 NanoLLM 대신 llama.cpp(CUDA 빌드)를 1순위, Ollama(arm64 CUDA)를 2순위로 실측 후 선택.
3수치의 출처5.2 GB 점유 · 30→24 fps · GPU 42→85% · FPR 60%↓ · Recall ≥ 95%"수동 대조 ≤ 10% 통과 뒤에만 보고"계획서 수치는 어느 것도 이 보드에서 잰 값이 아님(문헌 · 추정치). 8GB는 OS · 데스크톱 · CUDA 컨텍스트 · 카메라 버퍼와 공유되므로 "모델 파일 크기의 합"으로 판단할 수 없음. 결정: 모두 절 4의 실측 프로토콜로 대체. 목표치는 "Fast Loop ≥ 15 fps(QC 임계) · 스왑 0 · VLM 이벤트당 ≤ 4 s · 정상 정차 오탐 감소를 사람 라벨로 확인"으로 낮춰 잡음. 95% Recall은 목표가 아니라 측정 결과임.
4스몸비 · 승하차 지도 · 사각지대 궤적VLM이 정황 판정 (핵심 위험 트리거)"모델이 확실히 잴 수 있는 것만"2~3층 창에서 본 보행자의 "고개 숙임 · 스마트폰"은 원경 · 저해상(얼굴 판독 불가 거리)이라는 프라이버시 전제와 정면 충돌하고 측정 가능성 등급이 낮음. 결정: 스몸비 · 어린이 관련 판정은 제외. VLM의 역할은 K2 정차 이벤트의 사유 태그(C1) 하나로 좁힘 — 고정 선택지 boarding · waiting · delivery · other. 사각지대 궤적은 K3 기하(거리 · 시간)로만 다룸.
52안 · 원본 영상 녹화 후 순차 벤치마크RTSP → NVMe에 H.264 저장, 3회 재실행영상 · 이미지 파일 저장 금지 (디버그 포함)학교 창에서 공공 도로를 녹화해 보관하는 것은 GATE 0 자체를 통과하지 못할 가능성이 큼. 결정: 운영 카메라로는 어떤 모드에서도 녹화 없음. 순차 벤치마크가 필요하면 (a) 교직원 · 학생 자원자가 연출한 통제 세션(학교 안 도로 · 참여 동의 · 암호화 · 세션 후 30일 폐기)이나 (b) 공개 교통 영상 데이터셋으로만 함. 1안(섀도 로깅)이 기본이며, 이것만으로 3중 기록이 되므로 2안 없이도 논문 표는 채워짐(자원 경합이 있는 상태의 벤치라는 것을 명시).
6VLM 묘사 텍스트"한국어 정황 묘사"를 DB에 저장 · CQA 점수 평가"특정 차량 · 특정 사람 언급 없음"자유 문장은 색 · 차종 · 인상착의 · 연령 추정을 담기 쉬워 텍스트여도 식별 정보가 됨. 결정: VLM 출력은 JSON 고정 선택지만 저장(tag · confidence · door_open · person_near). 자유 묘사는 개발 중 화면 확인용으로만 쓰고 DB에 넣지 않음. 금칙어 필터(색상 · 차종 · 성별 · 연령 · 번호판 패턴)를 PARSE에 둠.
7일정에 게이트가 없음10주 · 1~5단계 직선12주 · GATE 0/1/2 · 실패 시 되돌아감VLM 판정을 사람 라벨과 대조하는 절차가 계획서에 없고(is_ground_truth_hazard만 NULLABLE로 존재), 그것 없이는 FPR · Recall을 계산할 수 없음. 결정: 게이트를 4개로(G0 협의 · G1 수동 대조 · G2 VLM 판정 검증 · G3 발송 승인) 늘리고 14주로 다시 짬. 사람 라벨은 창가 실시간 육안 기록으로 만듦(영상 없이도 가능한 유일한 방법).
8단일 테이블traffic_monitoring_events 한 장에 모델별 행10개 테이블 · 5분 버킷 · 이벤트 · QC · 검증 · 검토 큐한 이벤트에 3개 모델 결과와 사람 라벨이 붙으므로 "이벤트"와 "추론"과 "라벨"은 다른 표여야 조인이 됨. 결정: 카운터의 10개 표를 그대로 두고 inferences · labels · bench_runs 3개를 추가(절 5). 계획서의 단일 표는 VIEW로 제공해 분석 코드의 편의는 유지.
9설치 지점의 기하 (사진 확인 후 추가)RTSP 카메라 위치 미정2~3층 창 · 20~40° 사선 · 8~15 m 가상선 구간항공사진으로 보면 카메라(3층 중앙탑 창)는 교문 정면 축 위에 있고 도로는 교문 너머 좌우 방향. 사진 축척 추정으로 교문까지 30~40 m, 도로 중심까지 45~55 m로 진술(20 m)과 차이가 크며, 시선 각은 약 11°로 권장보다 얕음. 교문 기둥과 담장 가로수가 좌우 도로를 가려 도로는 교문 틈(11~15 m)으로만 보이고, 두 횡단보도는 축에서 비켜 가로수 뒤일 가능성. 결정: ① 거리를 지도 거리 재기로 실측한 뒤 렌즈 초점거리 확정(절 H-04) ② 가림 없는 교내 진입로를 K2와 교문 통과 차량 수(C2)의 주 관측 구간으로 승격 ③ K1 가상선은 교문 틈 안에 ④ K3는 R1 시험 캡처에서 횡단보도 가시성을 확인한 뒤 채택/보류를 결정.
TIP · 한 줄 요약"YOLO가 세고, VLM은 정차 사유만 태그하고, 사람이 라벨하고, 숫자는 5분 버킷에만 남는다." 이 한 줄에서 벗어나는 요구(스몸비 · 녹화 · 자유 묘사)는 프라이버시 담당자에게 가기 전에 여기서 걸러짐.
S시스템 큰그림 — 컴포넌트와 데이터 흐름
읽기 10분

상자 안은 코드명, 이름은 상자 바깥. 회색 점선 = 엣지 보드 한 대 안(Jetson 또는 Pi), 붉은 점선 = 영상이 존재하는 두 구간(프레임 · 크롭, 모두 메모리). 카운터 매뉴얼의 지도에 QUE · VLM · MON 세 상자와 inferences 층이 추가된 것이 전부이며, 초록 경로만으로도 K1~K3가 나옴.

EDGE DEVICE · Jetson Orin Nano Super 8GB (대체: Pi 5 8GB + AI HAT+ 2)프레임 존재 구간 · 메모리만 · 파일 없음VLM 크롭 · 메모리만 · 처리 후 폐기ROAD교문 앞 도로 · 관측 대상CAM15~30 fps카메라 · 창 안쪽DETTRT / .hef검출 · YOLO11TRKByteTrack · H행렬추적 · 가상선 · 이벤트ACC5분 집계 · QCDB버킷·이벤트·추론traffic.db (SQLite WAL)EXT기상청 · 학사일정QUEQueue(8)비동기 큐 · 이벤트 크롭VLMQ4 · JSON enum맥락 판정 · Qwen-VLMONtegrastats · 전력자원 계측ANLsystemd timer배치 분석 · M0~M4 · 벤치LLM숫자 검증기로컬 LLM · 리포트 초안WEB공개/관리 포트FastAPI + ReactMSGoutbox: approved발송 · 승인된 것만PPL운전자 · 학부모 · 지자체framebox · track_id통과 · 정차 · 근접이벤트 시 크롭tag · latency · memread집계 JSONoutbox draft사람 승인공개 카드승인된 알림초록 = 상시 실시간 경로(Fast Loop) · 보라 = 이벤트 시 비동기 경로(Slow Loop) · 붉은 점선 = 영상 프레임이 존재하는 유일한 구간 · 이미지 파일은 어디에도 없음

컴포넌트 설명표 (지도의 코드명 순)

코드이름역할Jetson 트랙Pi 트랙산출물 (저장되는 것)
CAM카메라 · 창 안쪽2~3층 창 안쪽 사선 구도, 1280×720 · 15~30 fps, 프레임은 메모리에서만USB UVC(V4L2) 또는 IMX477/219(nvarguscamerasrc)Camera Module 3 (picamera2)없음
DET검출YOLO11n/s · COCO 6종 → veh4 · two_wheel · person 묶음TensorRT FP16 .engine (ultralytics export, 보드에서 빌드)Hailo-10H .hef상자 + 클래스 (메모리)
TRK추적 · 가상선 · 이벤트ByteTrack · 호모그래피 · 가상선 A/B · dwell/conflict 판정 · 이벤트 시 크롭을 큐로공통 Python (trafficsvc/counter) — 백엔드 무관이벤트 · 5분 누적기
QUE비동기 큐이벤트 크롭 + 메타를 상한 8개까지 보관, 넘치면 오래된 것부터 버림(dropped 카운트)공통 (asyncio.Queue / 스레드)없음 (드롭 수만 QC에)
VLM맥락 판정크롭 1장 + 고정 프롬프트 → JSON 태그. 처리 후 크롭 폐기Qwen2.5-VL 3B / Qwen3-VL 2B GGUF Q4 · llama.cpp(CUDA) 서버Hailo GenAI VLM (지원 시) 또는 CPU llama.cpp (배치)inferences 행 (텍스트)
MON자원 계측1 Hz로 GPU · RAM · 스왑 · 전력 · 온도 · 콜백 fps를 읽어 5분 QC 행과 벤치 실행 행에 붙임tegrastats / jtopvcgencmd · hailortcli monitorqc_5min · bench_runs
ACC5분 집계 · QC버킷 flush, 표본 < 5면 null, calib_ver · qc 비트공통counts_5min · ped_5min · events · qc_5min
DBtraffic.db카운터 10개 표 + inferences · labels · bench_runs · VIEWSQLite WAL (단일 writer = trafficsvc)표 13개 (절 5)
EXT외부 데이터기상청 강수 · 기온, 학사일정API · 수기 캘린더 (timer)ext_wx · school_cal
ANL배치 분석 · 벤치M0~M4(카운터) + B1 혼동행렬 · B2 자원 곡선 · 안전지수systemd timer (야간)analysis · 주간 집계 JSON
LLM로컬 LLM집계 JSON → 리포트 문장 초안 · 숫자 검증기같은 llama.cpp 서버의 텍스트 모델 (야간)Hailo GenAI 또는 CPUoutbox draft
WEBFastAPI + React공개 카드 · 차트 · 벤치 뷰 · 검토 큐 · 라벨 입력 · PDF공개 포트 / 관리 포트 분리 (Mealboard 규칙)
MSG발송outbox status = approved 만 채널별 발송텔레그램 봇 · 안내판 텍스트 · PDF발송 로그
PPL이해관계자운전자 · 학부모 · 학생안전부 · 지자체 · 경찰 — 숫자를 받아 행동 · 결정을 바꾸는 사람피드백 (전후 비교 대상)
TIP · 백엔드 경계는 두 곳뿐Jetson과 Pi에서 달라지는 코드는 detect/(TensorRT vs Hailo)와 vlm/(llama.cpp vs Hailo GenAI) 두 패키지의 백엔드 클래스뿐. 카메라는 OpenCV/GStreamer/picamera2 소스를 capture/에서 같은 제너레이터 인터페이스로 감쌈. 나머지(추적 · 지표 · 집계 · DB · 웹 · 배치)는 한 벌.
P프로세스 맵 — 사람 게이트 4개와 되돌아가는 길
읽기 10분

문제 확인(1주 관찰)이 맨 앞에 오고, GATE 0 GATE 1 GATE 2 GATE 3 네 곳은 사람이 통과를 결정함. 카운터 매뉴얼의 게이트 3개에 GATE 2 · VLM 판정 검증이 끼어든 것이 차이. VLM 트랙(R5)은 GATE 1을 지난 뒤에만 시작 — 지표가 먼저, 연구가 그 위에.

OBS문제 확인 · 1주 관찰G0협의 · 승인 · 안내문R1런타임 · 카메라 · 캡처R2검출 · 추적 · 지표R35분 집계 · QC · 서비스G1수동 대조 ≤ 10%R5VLM Slow Loop · 벤치G2VLM 판정 검증R6대시보드 · 배치 · 알림G3발송 승인FX전후 비교 · 공개 · 건의GATE 1 통과 → baseline 2주 시작 · VLM 트랙 착수실패 → 구도 · 가상선 · 임계값 재보정 (calib_ver ↑)실패 → 트리거 · 프롬프트 · 모델 교체 (prompt_ver ↑)반려 → 초안 수정GATE = 사람이 통과를 결정. G0 카메라를 켜기 전 · G1 K1~K3가 리포트에 실리기 전 · G2 VLM 태그가 리포트에 실리기 전 · G3 발송 전.LLM · VLM은 어느 게이트도 스스로 열지 못함. R4(수동 대조 프로토콜)는 G1 안에 포함.
GATE 0 · 협의 · 승인
켜기 전
개인정보 담당 확인 · 교육청 지침 · 안내문 게시 · 구도 승인 · VLM 크롭 처리 방식과 통제 세션 여부를 설계서에 명시
GATE 1 · 수동 대조
오차 ≤ 10%
15분 × 10회 육안 카운트 vs 자동. 속도 ±3 km/h. 넘으면 구도 · 가상선 · 임계값 재보정 (calib_ver ↑)
GATE 2 · VLM 판정 검증
정밀도 ≥ 0.7
K2 이벤트 ≥ 60건에 대해 창가 실시간 육안 라벨 vs VLM 태그. 미달이면 트리거 · 프롬프트 · 모델 교체 (prompt_ver ↑). 통과 전 C1은 리포트 금지
GATE 3 · 검토 · 승인
발송 전
알림 · 리포트는 담당 교사가 검토 큐에서 승인해야 나감. LLM 초안은 승인 대상일 뿐
단계주차산출물완료 기준
문제 확인11주 관찰 로그 · 가설 H1~H3 · R1~R3 확정 · 카메라 결정(쟁점 1)문제 진술 1장에 담당 교사 서명0 · !
준비 · GATE 01~2승인 문서 · 안내문 · JetPack 7.2.1 툴체인 검증 · 창 조준GATE 0 통과 · 30분 무저장 실행 로그 · TensorRT 엔진 빌드 성공H · R1
구현 + 검증3~6검출 · 추적 · 가상선 · 속도 · 5분 집계 · 수동 대조표Fast Loop ≥ 15 fps · 속도 ±3 km/h · GATE 1 통과R2 · R3 · R4
연구 트랙6~9Slow Loop · 자원 실측표 · 육안 라벨 60건 · 혼동행렬GATE 2 통과 · 벤치 표 초안R5
운영 + 전달8~14baseline 2주 · 대시보드 · 배치 · 알림 · LLM 리포트 · 개입 2회주간 리포트 3회 · 전후 비교 1건 · 건의서 초안 · 벤치 표 공개R6 · R7 · FX
이식 (선택)12~14Pi 5 + AI HAT+ 2 백엔드 · 같은 DB · 같은 UI같은 통제 세션에서 두 보드 벤치 표 1장R8
H하드웨어 2트랙 · 런타임 · 세팅
준비 1~2주
WHY두 보드는 "같은 일을 다른 가속기로" 함. 본 프로젝트는 Jetson으로 구축하고, Pi 트랙은 백엔드 두 개만 갈아 끼워 같은 DB · 같은 UI로 돌리는 이식 실험. 그래서 처음부터 백엔드 경계(detect/ · vlm/ · capture/)를 코드에 그어 둠. 아래 표의 "실측"은 R1에서 채움.

01트랙 비교표

항목Jetson 트랙 (본 프로젝트)Pi 트랙 (이식)비고
보드Jetson Orin Nano Super 8GB 개발킷 · NVMe 부팅 · JetPack 7.2.1Raspberry Pi 5 8GB + AI HAT+ 2 (Hailo-10H, 8GB) · Trixie · hailo-h10-allPi는 운영 중인 Mealboard Pi와 별도 장비
메모리8GB LPDDR5 통합 — OS · CUDA 컨텍스트 · 카메라 버퍼 · 두 모델이 한 풀을 나눔Pi RAM 8GB + HAT 전용 8GB — 검출/LLM은 HAT 메모리, 파이썬은 Pi RAMJetson은 데스크톱을 끄고(multi-user.target) 실측 시작
검출YOLO11n/s → ONNX → TensorRT FP16 .engine (보드에서 빌드, JetPack 버전 종속)Model Zoo YOLOv8/11 .hef커스텀 학습은 마지막에
추적ByteTrack (supervision) — 공통track 단위 카운트
VLMQwen2.5-VL-3B 또는 Qwen3-VL-2B GGUF Q4 · llama.cpp CUDA 서버(llama-server --mmproj) · 대안 OllamaHailo GenAI VLM (지원 모델 한정) 또는 CPU llama.cpp 야간 배치실측 tok/s · 이벤트당 ms 로 결정
텍스트 LLMQwen2.5 1.5B/3B Q4 · 같은 llama.cpp (야간)Hailo GenAI 또는 Ollama CPU카운터 매뉴얼 절 4 계승
카메라ELP AR0234 2.3MP 컬러 글로벌 셔터 USB (UVC · MJPEG) + 2.8-12mm CS 가변 렌즈 — 실측 거리에 맞춰 초점거리 고정 후 링 나사 잠금. CM3 비권장Camera Module 3 (22핀 mini FPC)쟁점 1 · 9 · 절 H-04
전력 · 발열25 W 모드(MAXN SUPER) · 팬 상시 · tegrastats로 전력 읽기27 W PD · Active Cooler발열 throttling은 QC 비트
저장NVMe (DB · 모델 · 로그) — 영상 없음microSD 64GB A2find 결과 항상 0건
원격SSH (Tailscale 노드 추가) · Claude Code 원격 세션SSH (기존 rspi 방식 · 학교망 제약은 Mealboard CLAUDE.md 참고)절 7

02Jetson 런타임 세팅 (R1 · 문서 순서대로)

scripts/setup_jetson.sh · 핵심만 (sudo 는 사람이)
# 0) 현재 상태 확인 — 계획서의 "JetPack 6" 가정을 여기서 깸
cat /etc/nv_tegra_release            # R39 (rel 39.2.x) 이면 JetPack 7.2.x
apt list --installed 2>/dev/null | grep -E "nvidia-jetpack|tensorrt|cuda-toolkit"
python3 --version                    # 3.12
# 1) 전력 모드 · 데스크톱 끄기 · 계측 도구
sudo nvpmodel -q ; sudo nvpmodel -m 0 ; sudo jetson_clocks      # MAXN SUPER 여부는 -q 출력에서 확인
sudo systemctl set-default multi-user.target && sudo reboot     # GUI 메모리 반환 (필요 시 graphical 로 복귀)
sudo pip3 install -U jetson-stats && jtop                        # 메모리 · 전력 · 온도 한눈에
# 2) 프로젝트 venv (시스템 site-packages 상속 — CUDA · OpenCV 접근)
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/xparapx/<REPO>.git ~/traffic && cd ~/traffic
uv venv --system-site-packages && uv sync
# 3) PyTorch/TensorRT 휠 — JetPack 7.2 (CUDA 13) 용 aarch64 휠을 NVIDIA PyPI(jp7) 또는 검증된 릴리스에서. cu126 휠은 동작하지 않음
# 4) YOLO11 → TensorRT 엔진 (보드에서, 30분 내외)
uv run yolo export model=yolo11n.pt format=engine half=True imgsz=640 device=0   # → yolo11n.engine (models/ · git 제외)
# 5) llama.cpp CUDA 빌드 · VLM 서버 (모델 파일은 git 제외)
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp && cmake -B build -DGGML_CUDA=ON && cmake --build build -j
./build/bin/llama-server -m models/qwen2.5-vl-3b-q4_k_m.gguf --mmproj models/mmproj-qwen2.5-vl-3b-f16.gguf -ngl 99 -c 2048 --port 8090
# 6) 카메라 확인
v4l2-ctl --list-devices ; v4l2-ctl -d /dev/video0 --list-formats-ext        # ELP: MJPG 1920x1200 @30 확인
v4l2-ctl -d /dev/video0 -c exposure_auto=1 -c exposure_absolute=150               # 수동 노출 고정 (역광 대비)
gst-launch-1.0 v4l2src device=/dev/video0 ! image/jpeg,width=1920,height=1080,framerate=30/1 ! nvv4l2decoder mjpeg=1 ! nvvidconv ! video/x-raw,format=BGRx ! fakesink -v
gst-launch-1.0 nvarguscamerasrc ! nvvidconv ! fakesink                       # CSI (IMX477/219)
# 7) 원격
curl -fsSL https://tailscale.com/install.sh | sh && sudo tailscale up
함정 · JetPack 7.2.1에서 먼저 확인할 것① 예제 · 컨테이너 · 휠의 상당수가 JetPack 6(CUDA 12.6 · Python 3.10) 기준 — 설치 전에 대상 JetPack을 확인하고 안 맞으면 소스 빌드(llama.cpp는 문제 없음). ② TensorRT 엔진은 빌드한 보드 · 버전에서만 동작 — PC에서 만든 엔진을 복사하지 않음. ③ 통합 메모리라 "GPU 메모리 여유"가 곧 "시스템 메모리 여유"임 — free -htegrastats를 같이 봄. ④ 카메라 모듈 3는 절 !의 쟁점 1 — R1의 1일 한도 시도 후 되돌림. ⑤ 헤드리스로 바꾼 뒤 조준은 MJPEG 미리보기(카운터 매뉴얼 P1)로.
TIP · Pi 트랙 세팅카운터 매뉴얼 절 H의 setup.sh(Trixie · hailo-h10-all · hailortcli fw-control identify)를 그대로 씀. 이 저장소에서는 scripts/setup_pi.sh로 두고, 백엔드 선택은 data/config.json"backend": "tensorrt" | "hailo".

03백엔드 경계 — 코드에 그어 두는 두 줄

edge/trafficsvc/detect/__init__.py · 핵심만
from typing import Protocol, Iterator
import numpy as np

class Detection:  # 정규화 좌표 · 원 라벨 · 신뢰도 — 백엔드가 무엇이든 같은 모양
    __slots__ = ("label", "conf", "x1", "y1", "x2", "y2")

class Detector(Protocol):
    def infer(self, frame_bgr: np.ndarray) -> list[Detection]: ...
    def stats(self) -> dict: ...            # {"infer_ms": .., "backend": "tensorrt"}

def load(cfg: dict) -> Detector:
    if cfg["backend"] == "tensorrt":
        from .tensorrt_yolo import TensorRTYolo   # ultralytics YOLO("yolo11n.engine")
        return TensorRTYolo(cfg["engine"], conf=0.40, classes=[0, 1, 2, 3, 5, 7])
    if cfg["backend"] == "hailo":
        from .hailo_yolo import HailoYolo         # HailoRT .hef + 후처리
        return HailoYolo(cfg["hef"], conf=0.40)
    raise ValueError(cfg["backend"])

# vlm/__init__.py 도 같은 꼴: class Tagger(Protocol): def tag(self, crop_bgr, meta) -> dict
#   llama_cpp 백엔드 = HTTP(localhost:8090, OpenAI 호환) · hailo 백엔드 = GenAI SDK

04현장 기하 검토 — 3층 중앙탑 창에서 교문 · 도로까지 (사진 기반 추정 · 실측 전)

항공사진의 화살표 시작점(본관 3층 중앙탑 창)을 기준으로 시야 · 가림 · 거리 · 렌즈를 검토함. 아래 수치는 사진 축척 추정이며, R0에서 지도 거리 재기(네이버 · 카카오맵)와 창 높이 실측으로 바꿔 넣은 뒤 렌즈를 확정함.

평면 (위에서 본 그림 · 축척 약 1:250 · 수치는 사진 추정, 실측 전)본관 (3층) · 중앙탑CAM · 3층 중앙탑 창 · 높이 ≈ 10 m중앙 수목 (높이 6~8 m · 시야 간섭 후보)교내 진입로 (붉은 포장)담장 + 담장 안쪽 가로수 (은행나무 · 침엽수, 10 m급) — 좌우 도로를 가림교문 (기둥 ≈ 4~5 m)도로 (좌↔우 통행 · 2~3차로)횡단보도 (교문 왼쪽 · 가로수 뒤일 가능성)횡단보도 · 주차 차량 (교문 오른쪽)교문 틈으로 보이는 도로 폭 ≈ 11~15 m (가상선 8~12 m 겨우 들어감)① 카메라 축이 교문 정면 → 도로는 좌우 방향으로 지나감 = 가상선 배치에는 좋은 방향② 교문 기둥 · 담장 가로수 뒤의 도로는 보이지 않음 → 가상선 구간은 교문 틈 안에서만③ 교내 진입로(교문 ↔ 건물 사이)는 가림 없이 가깝게 보임 → K2(정차)와 교문 통과 차량 수의 최적 관측 구간④ 두 횡단보도 모두 카메라 축에서 15 m 이상 비켜 있고 가로수 뒤 → K3는 시험 캡처로 가시성 확인 후 채택/보류측면 (시선 높이 계산 · H = 10 m 가정)CAM 10 m수목 15 m교문 35 m도로 45~55 m시선 각 ≈ 11°기둥 위 시선: 기둥 뒤 도로는 63 m 이후만h(d) = H·(1 − d/D) · D=50 m: 교문(35 m)에서 h=3 m → 담장은 넘고 기둥은 못 넘음수목(15 m)에서 h=7 m → 수목이 7 m를 넘으면 도로가 가려짐 (잎 있는 계절 기준 확인)거리 D는 지도 거리 재기로 실측 — 사진 추정 45~55 m ≠ 진술 20 m (렌즈 결정의 전제)11°는 권장(20~40°)보다 얕음 → 밀집 시 겹침 ↑ · 바닥점 오차 ↑ → 수동 대조로 확인
항목사진 추정영향대응
카메라 → 교문 거리30~40 m (진술 20 m)렌즈 초점거리 · 픽셀당 크기가 2배 차이지도 거리 재기로 실측 후 렌즈 확정. 20 m면 5~6 mm, 45~55 m면 10~12 mm
카메라 → 도로 중심45~55 m시선 각 ≈ 11° (권장 20~40°보다 얕음) → 밀집 시 차량 겹침 · 바닥점 오차12 mm에서 1 px ≈ 1.2 cm, 보행자 ≈ 130 px로 검출은 충분. 겹침은 수동 대조(G1)에서 밀집 시간대 오차로 드러냄
교문 기둥 (4~5 m) · 담장 가로수 (10 m급)기둥 뒤 · 가로수 뒤 도로는 보이지 않음도로 가시 구간이 교문 틈 폭 11~15 m로 제한가상선 A·B(8~12 m)를 교문 틈 안에 배치. 구간이 8 m 미만이면 K1은 p85 대신 "통과 시간" 프록시로 강등
중앙 수목 (건물 앞 15 m · 높이 6~8 m)시선 높이 7 m와 겹칠 수 있음여름철 잎이 도로 하단을 가릴 가능성R1 시험 캡처에서 확인 · 가리면 창을 한 층 위(옥상 탑) 또는 좌우 창으로 검토
횡단보도 2곳축에서 좌 · 우 15 m 이상 · 가로수 뒤 가능성K3(횡단 중 근접)의 ROI를 못 잡을 수 있음시험 캡처에서 횡단보도 다각형이 그려지면 채택, 안 되면 K3 보류 + 문제 진술에 명시 (스코프 축소는 실패가 아님)
교내 진입로 (교문 ↔ 건물)가림 없음 · 10~35 m · 붉은 포장가장 잘 보이는 구간이며 학부모 차량 정차 · 진입이 실제로 일어나는 곳K2 정차 구역 다각형과 교문 통과 차량 수(C2, 진입/진출)의 주 관측 구간으로 승격. 이곳은 사유지라 안내문 게시가 곧 근거
태양 방향미상 (건물이 남향이면 카메라는 남향 → 등교 시간 역광)08:00 전후 자동 노출 흔들림 · 유리 반사R1 첫 날 07:40~08:30 시험 캡처로 확인 · 수동 노출 고정 · 후드 밀착 · 하늘을 화면 밖으로
렌즈 초점거리 — 실측 거리별 (AR0234 1/2.6", 가로 5.76 mm)
도로 중심까지권장 초점거리수평 화각 · 커버 폭1 px ≈보행자 (px)번호판 글자 (px)
20~25 m5~6 mm51~58° · 20~25 m1.1~1.3 cm130~1608~10 (판독 불가)
35~40 m8 mm40° · 26~29 m1.1~1.2 cm140~1509~10 (판독 불가)
45~55 m10~12 mm27~32° · 24~30 m1.2~1.4 cm120~1408~9 (판독 불가)

거리가 멀수록 초점거리를 올려도 번호판 글자가 10 px를 넘지 않으므로 "판독 불가" 원칙은 어느 행에서도 유지됨. 2.8-12 mm 가변 렌즈로 세 행을 모두 덮고, 확정 후 링 나사를 잠그고 마커 선을 그음(12 mm 이상은 이 프로젝트에서 쓰지 않음).

함정 · 이 창의 세 가지 위험① 교문 틈 폭이 8 m 미만이면 속도(K1)의 표본이 급감 — 그때는 K1을 보류하고 K2 · C2 중심으로 문제 진술을 고쳐 씀. ② 11°의 얕은 각은 승합차 · 버스가 뒤 차량을 완전히 가리므로 밀집 시간대 과소계수가 커짐 — G1 보정계수(M0)를 밀집 · 한산으로 나눠 구함. ③ 교내 진입로를 관측 구간으로 쓰면 "학교 안"이지만 촬영 대상은 여전히 불특정 차량 · 보행자이므로 안내문과 무저장 원칙은 동일하게 적용.
K기초 지식 · 용어 정리
읽기 20분

카운터 매뉴얼 절 K(사선 구도 · 프레임 미저장 · track_id · 가상선 · 호모그래피 · p85 · 5분 버킷 · QC · ORB · 기준 프로파일 · 규범 메시지 · 검토 큐 · systemd)는 그대로 전제. 여기는 이 프로젝트에서 새로 나오는 말만.

통합 메모리Jetson

CPU와 GPU가 같은 8GB를 씀. "VRAM 5.2 GB"라는 표현은 Jetson에서 성립하지 않고, OS · 버퍼 · 모델이 한 풀에서 경합함. 스왑이 생기면 fps가 급락.

TensorRT 엔진.engine

ONNX 모델을 특정 GPU · JetPack 버전에 맞게 컴파일한 파일. FP16이면 정확도 손실 거의 없이 2배 안팎 빠름. 다른 보드 · 버전에서는 못 씀.

Fast Loop · Slow Loop구조

Fast = 전 프레임에 도는 검출 · 추적 · 지표(동기). Slow = 이벤트당 한 번 도는 VLM(비동기). 둘은 큐 하나로만 만나고, Slow가 죽어도 Fast는 돔.

비동기 큐 · 상한QUE

이벤트가 VLM 처리보다 빨리 오면 쌓임. 상한(8)을 두고 넘치면 오래된 것부터 버리며 dropped를 QC에 기록 — 무한히 쌓여 메모리를 먹는 것을 막음.

VLM시각-언어 모델

이미지 + 텍스트 프롬프트 → 텍스트. 여기서는 "묘사"가 아니라 고정 선택지 분류기로만 씀. 2~3B · Q4 양자화면 Jetson에서 이벤트당 수 초.

Q4 · GGUF · mmprojllama.cpp

가중치를 4비트로 줄인 형식(GGUF). VLM은 언어 모델 GGUF + 비전 인코더(mmproj) 두 파일. -ngl 99로 전 레이어를 GPU에.

섀도 로깅1안

실시간 관제를 유지한 채 백그라운드에서 다른 모델의 판정을 "그림자처럼" 함께 기록. 같은 이벤트 키에 YOLO · VLM · HYBRID 3행이 붙음.

자원 경합 · 벤치 오염MON

VLM이 GPU를 쓰는 순간 YOLO의 fps가 떨어짐. 그 상태에서 잰 fps는 "경합 있는 벤치"이며, 그렇게 표기하면 논문에 쓸 수 있음 — 숨기면 오염.

그라운드 트루스 · 라벨labels

사람이 판정한 정답. 영상을 저장하지 않으므로 이벤트 발생 순간 창가에서 육안으로 기록하는 것이 이 프로젝트의 유일한 정답 생성법.

FPR · Recall · 정밀도G2

FPR = 정상 정차를 위험으로 부른 비율. Recall = 진짜 위험을 놓치지 않은 비율. 정밀도 = 위험이라 부른 것 중 진짜 비율. 셋 다 라벨이 있어야 계산됨.

안전 지수Safety Score

K1~K3를 baseline 대비 정규화해 100점으로 합친 표시용 값. 지표가 아니라 커뮤니케이션 장치이며, 리포트에는 항상 K1~K3 원값과 함께.

tegrastats · jtop계측

Jetson의 RAM · GPU 사용률 · 전력(mW) · 온도를 1초마다 찍는 도구. 벤치 표의 MB · W 열은 여기서 옴.

1측정 가능성 · 지표 · 연구 질문 — 잴 수 있는 것만 지표로, 나머지는 실험 변수로
설계 1주
CAM
카메라
DET
검출
TRK
가상선
VLM
태그
DB
5분 집계
MSG
알림
WHY · 등급이 곧 역할카운터 매뉴얼의 등급표를 그대로 쓰되, VLM이 추가할 수 있다고 계획서가 주장한 변수들을 같은 잣대로 평가함. 높음 · 중상 → 핵심 지표(K), 중 → 맥락 또는 연구 변수(C · R, 게이트 통과 후 리포트), 낮음 · 불가 → 제외. VLM이 붙는다고 등급이 올라가지 않음 — 등급은 사람 라벨 대조(G1 · G2)에서 실측으로 갱신.

01후보 변수 측정 가능성 등급 (카운터 표 + VLM 후보)

후보 변수모델이 재는 방법주요 오차 원인등급채택
4륜 통과 수 · 방향 · 속도veh4 track이 가상선 A·B 통과 · 시간차 × 실거리겹침 · 가림 · 15 fps 타이밍높음 · 중상핵심 (K1)
정차 (≥ 20 s · 구역 밖)같은 track 20 s 이동 < 1 m · 다각형 안구역 경계 · track 단절높음핵심 (K2) · Slow Loop 트리거
횡단 중 차량 근접crosswalk 안 person 존재 중 veh4 최근접 < 2 m ≥ 1 s바닥점 · 원근 · 군집핵심 (K3) · 프록시 명시 + 육안 대조
정차 사유 태그 (boarding · waiting · delivery · other)K2 이벤트 크롭 1장 → VLM 고정 선택지원경 · 문 열림 가림 · 모델 편향 · 프롬프트 민감성중 (실측 전)연구 변수 C1 · G2 통과 후만 리포트
정차 중 문 열림 · 사람 접근 (보조 신호)VLM boolean 2개 (door_open · person_near)위와 같음C1 보조 · 결합 규칙 입력
스마트폰 보행 (스몸비)보행자 크롭 → VLM원경 · 저해상 전제와 충돌, 자세 판독 불가낮음제외 (쟁점 4)
어린이 · 학생 · 학부모 여부식별 · 연령 추정 = 설계상 하지 않음불가제외
통학버스 사각지대 궤적bus 라벨 정차 중 person track이 차량 전방 2 m 진입버스 ↔ 트럭 혼동 · 가림K3의 변형으로만 기록 (zone = bus_front) · 리포트 보류
교문 통과 차량 수 · 방향 (진입 / 진출)교내 진입로에 가상선 1개 — veh4 track 통과 · 순서거의 없음 (가림 없는 구간)높음맥락 변수 C2 · "등교 시간 교내 진입 n대"로 K2와 함께 리포트
추론 지연 · 메모리 · 전력 · fpsMON 1 Hz + 이벤트별 타이머계측 오버헤드 · 온도높음연구 변수 R (벤치 표)

02정의 (등교 시간대 = 등교 시각 − 40분 ~ 등교 시각)

K1 = n_over30 / n_speed (veh4, 등교 시간대, 주 단위) · 보조 n_over33 / n_speed
K2 = Σ dwell(veh4, duration ≥ 20 s, zone = no_stop) / 등교일 수
K3 = Σ conflict(person ∈ crosswalk, min dist(veh4) < 2 m, ≥ 1 s) / 등교일 수 (프록시)
C2 = Σ 교내 진입로 가상선 통과(veh4, 방향별) / 등교일 수 (가림 없는 구간 · 오차 최소)
C1 = K2 이벤트별 tag 분포 {boarding, waiting, delivery, other, unknown} · G2 통과 후 · "정상 정차로 재분류된 비율"을 함께 표기
R = 모델별 (fps_fast, latency_ms, mem_mb, power_w, FPR, Recall, precision) — 같은 event_id 기준, 라벨 있는 이벤트만
학교 기여분 = 지표(등교 시간대) − 지표(같은 요일 10:00~11:00)
TIP · 연구 질문의 정직한 형태"하이브리드가 오탐을 60% 줄인다"가 아니라 "K2로 잡힌 정차 중 사람이 정상(boarding · waiting)이라 판정한 비율이 얼마이고, VLM 태그가 그 비율을 얼마나 맞히며, 그 대가로 fps · 메모리 · 전력이 얼마나 드는가". 답이 "별로 못 맞힌다"여도 벤치 표는 완성되고 논문이 됨.
2프라이버시 · 협의 · 안내문 GATE 0
준비 1~2주
WHY카운터 매뉴얼 절 2를 전제로, VLM이 들어오면서 새로 생기는 세 지점 — 크롭(프레임의 일부가 잠시 존재), 텍스트(묘사가 식별 정보가 될 수 있음), 녹화 요구(벤치마크용) — 를 여기서 닫음. 이 세 가지를 설계서에 명시한 뒤에만 담당자 확인을 받음.

01하는 것 · 하지 않는 것 (VLM 추가분)

하는 것

  • K2 이벤트 순간 차량 상자 주변 크롭 1장(448 px, 여백 20%)을 메모리에서 VLM에 전달, 응답 즉시 del
  • VLM 출력은 고정 선택지 JSON만 저장: tag · conf · door_open · person_near · latency_ms
  • 사람 라벨은 창가 육안 기록 (시각 · 태그 · 라벨러) — 텍스트만
  • 벤치마크는 1안 섀도 로깅(운영 중 자동) + 필요 시 통제 세션 · 공개 데이터셋
  • 모델 · 프롬프트 버전을 모든 추론 행에 기록 (재현성)

하지 않는 것

  • 운영 카메라의 영상 · 프레임 · 크롭 파일 저장 (어느 모드 · 디버그 포함)
  • VLM 자유 묘사 문장 저장 · 색상 · 차종 · 성별 · 연령 · 인상착의 · 번호판 질의
  • 보행자 얼굴 · 자세 · 스마트폰 판정 (스몸비)
  • 클라우드 API로 이미지 전송 (모든 추론은 보드 안)
  • VLM 태그의 자동 발송 · 자동 리포트 (G2 · G3 전에는 화면에도 안 나감)

02통제 세션 규칙 (2안이 필요할 때만)

항목규칙
장소 · 시간학교 부지 안 도로 또는 주차장, 수업 외 시간, 안내 표지 설치
참여자교직원 · 자원 학생(보호자 동의) · 교직원 차량. 외부 통행이 섞이지 않는 구간
내용정상 정차 시나리오(승하차 · 대기 · 배송 흉내) × n회, 위험 시나리오(정차 구역 밖 장기 정차 · 횡단 중 근접)를 안전 속도로 연출
저장별도 NVMe 디렉터리, LUKS 암호화, data/sessions/<date>/ — 운영 파이프라인이 읽지 못하는 경로. 세션 종료 30일 후 삭제 로그 남김
용도run.py --source file --mode YOLO_ONLY | VLM_ONLY | HYBRID 순차 벤치 (자원 경합 0). 라벨은 시나리오 대본이 곧 정답
  • 학교 개인정보 보호 담당자에게 설계서(절 1 · 2 · 3) 제출 · 확인 — VLM 크롭 처리 · 텍스트 저장 범위 · 통제 세션 여부 명시
  • 교육청 영상정보처리기기 · 개인정보 지침 해당 여부 확인 (문의 기록)
  • 학교장 · 학생안전부 승인 · 학부모회 사전 설명 (VLM은 "정차 사유 태그"로만 설명)
  • 안내문 게시 (창 안쪽 · 교문) 후 카메라 전원 ON
  • CLAUDE.md 프라이버시 조항 v1: "이미지 파일 쓰기 코드 금지 · VLM 출력은 enum JSON만 · 금칙어 필터 통과 전 DB 기록 금지"
[안내] 이 창에는 교문 앞 통행량을 세는 장치가 설치되어 있습니다. - 영상이나 사진을 저장하지 않으며, 보행자·자전거·차량의 수와 속도, 정차 통계만 기록합니다. - 정차 차량은 "승하차·대기·배송·기타"로만 분류하며, 사람이나 차량을 식별하지 않습니다. - 번호판이나 얼굴을 식별할 수 없는 거리와 해상도로, 낮 시간에만 작동합니다. - 결과는 통학 안전 개선을 위해 학교·학부모회·지자체에 통계로 공유됩니다. 문의: 공주고 인공지능정보부 (내선 000)
함정 · 코드에서 새는 지점 (VLM 추가분)① llama.cpp · Ollama 서버의 요청 로그가 이미지 base64를 디스크에 남길 수 있음 → --log-disable 또는 로그 경로 tmpfs, 설치 직후 확인. ② 개발 중 "VLM이 뭘 보는지" 확인하려고 크롭을 cv2.imwrite하는 습관 → 화면 표시(imshow)만, 그것도 개발 플래그 뒤에. ③ 프롬프트에 "묘사하라"가 들어가면 모델은 색 · 차종을 씀 → 선택지만 묻고 JSON 외 출력은 버림.
3Fast / Slow Loop 하이브리드 파이프라인
설계 1주 · 구현 3주
CAM
카메라
DET
검출
TRK
가상선
VLM
태그
DB
추론 기록
MSG
알림
WHY계획서의 그림을 "이벤트가 언제 · 무엇을 · 어디로" 수준으로 내림. 핵심은 세 가지 — Slow Loop는 K2 정차 이벤트에서만 깨어남(K1 · K3는 기하로 끝), 큐는 상한이 있고, 같은 event_id에 세 판정이 붙음.
FAST LOOP · 상시 · 15~30 fps · 스레드 1 · 전 프레임SLOW LOOP · 이벤트당 1회 · 1~4 s · 스레드 2 · 크롭 1장CAM1280×720캡처PRE640 · FP16리사이즈 · 정규화YOLO6종 → 3묶음검출 · TensorRTTRKByteTrack · track_idGEOpx → m호모그래피 · 가상선EVTdwell·conflict이벤트 판정ACCflush → DB5분 버킷 · QCK1 속도 · K2 정차 · K3 근접 은 여기서 끝남 — VLM 없이도 지표는 나옴 (카운터 문서 P2~P4 계승)QUEmax 8 · drop-old비동기 큐 · 상한 8CROP여백 20%이벤트 크롭 · 448pxVLMllama.cpp CUDAQwen-VL 2~3B · Q4PARSEenum 외→unknownJSON 검증 · 금칙어HYBYOLO ∧ VLM결합 규칙 · risk_levelINF3 model_typeinferences 3행 기록put_nowait(crop, meta) — 큐가 차면 버리고 dropped++MONtegrastats 1 Hz자원 계측크롭은 처리 후 즉시 del · VLM 출력은 텍스트 태그만 저장 · 묘사 문장에 색·차종·인상착의·연령 언급 금지(프롬프트 + 금칙어 필터)큐가 비어 있을 때 VLM 프로세스는 idle — GPU 경합은 이벤트 순간에만 생김. 그 순간의 fps 저하가 절 4의 실측 대상실선 초록 = 프레임 단위 동기 경로 · 점선 보라 = 이벤트 단위 비동기 경로 · 두 스레드는 큐 하나로만 만남

01트리거 · 크롭 · 프롬프트 · 결합 규칙

단계규칙비고
트리거K2 dwell 이벤트가 20 s 조건을 만족하는 순간 1회 (track당 1회). conflict(K3)는 트리거하지 않음이벤트 빈도 ≈ 하루 10~30건 → VLM 부하 작음
크롭차량 상자 + 여백 20%, 긴 변 448 px로 축소, BGR→RGB, 메모리 버퍼얼굴 · 번호판이 판독되는 해상도가 아님을 G0 시험 캡처로 확인
프롬프트고정 템플릿 v1 + 선택지 4개 + JSON 스키마. 온도 0. max_tokens 64prompt_ver를 추론 행에 기록
파싱JSON 파싱 실패 · enum 외 값 · 금칙어 포함 → tag=unknown, 원문 폐기unknown 비율도 벤치 지표
결합 (HYBRID)YOLO_ONLY: dwell(no_stop) = DANGER · VLM_ONLY: tag 만으로 · HYBRID: dwell ∧ (tag ∈ {other, unknown} ∨ person_near ∧ zone = no_stop) = DANGER, tag ∈ {boarding, waiting, delivery} ∧ duration < 60 s = WARNING, 그 외 SAFE규칙은 hybrid_rules.py 한 파일 · 버전 기록
기록inferences에 3행 (model_type × 같은 event_id), 각 행에 latency · mem · power · model_ver · prompt_ver절 5
[system] You classify a single road-side vehicle stop. Answer ONLY with JSON matching the schema. Never describe people, colors, brands, plates, age or gender.[schema] {"tag": "boarding|waiting|delivery|other", "door_open": true|false, "person_near": true|false, "conf": 0.0-1.0} [user] The vehicle in the image has been stopped for {{duration_s}} s in a {{zone}} zone next to a school gate. Which tag fits best?
edge/trafficsvc/slowloop.py · 핵심만
import asyncio, time, json, re, numpy as np
from .vlm import load as load_tagger            # llama_cpp | hailo 백엔드
ENUM = {"boarding", "waiting", "delivery", "other"}
BAN  = re.compile(r"(색|흰|검정|빨간|파란|승용|SUV|승합|트럭|남성|여성|아이|어린이|노인|\d{2,3}[가-힣]\d{4})")

class SlowLoop:
    def __init__(s, cfg, db, mon):
        s.q = asyncio.Queue(maxsize=cfg.get("queue_max", 8)); s.tagger = load_tagger(cfg); s.db, s.mon = db, mon; s.dropped = 0
    def submit(s, event_id, crop_rgb, meta):     # Fast Loop 스레드에서 호출 · 절대 블록하지 않음
        try: s.q.put_nowait((event_id, crop_rgb, meta))
        except asyncio.QueueFull:
            s.q.get_nowait(); s.q.put_nowait((event_id, crop_rgb, meta)); s.dropped += 1
    async def run(s):
        while True:
            event_id, crop, meta = await s.q.get(); t0 = time.time(); m0 = s.mon.snapshot()
            try:    out = s.tagger.tag(crop, meta)              # dict 또는 예외
            except Exception as e: out = {"tag": "unknown", "err": type(e).__name__}
            finally: del crop                                   # 크롭은 여기서 끝
            tag = out.get("tag") if out.get("tag") in ENUM and not BAN.search(json.dumps(out, ensure_ascii=False)) else "unknown"
            row = {"event_id": event_id, "model_type": "VLM_ONLY", "tag": tag, "door_open": bool(out.get("door_open")),
                   "person_near": bool(out.get("person_near")), "conf": out.get("conf"),
                   "latency_ms": int((time.time() - t0) * 1000), **s.mon.delta(m0)}   # mem_mb · power_w · gpu_pct
            s.db.insert_inference(row); s.db.insert_inference(hybrid(meta, row))     # HYBRID 행 · YOLO_ONLY 행은 Fast Loop이 이미 씀
함정submit이 블록되면 Fast Loop fps가 떨어짐 — put_nowait만. ② VLM 서버 워밍업(첫 요청 10 s+)은 서비스 시작 시 더미 요청으로 미리. ③ llama.cpp 서버는 동시 요청 1로 고정(--parallel 1) — KV 캐시 중복 할당 방지. ④ 온도 throttling이 오면 latency가 2~3배 뛰고 그것이 곧 R2 · R3의 데이터임 — 숨기지 말고 QC 비트로.
4운용 대안 1 · 2 · 3 — 자원 실측 프로토콜
실측 1주
WHY계획서의 세 대안은 "메모리를 감당하는가"라는 질문에서 나왔고, 그 답은 표가 아니라 이 보드에서 잰 곡선임. 여기서는 세 대안을 무엇으로 결정할지(측정 항목 · 합격선)만 정하고, 표의 빈칸은 R5에서 채움.

01대안 비교 · 이 개요의 결정

대안동작얻는 것비용 · 위험결정
1안 섀도 로깅 (실시간 비동기)YOLO 상시 + VLM 상주, 이벤트 시 큐 → 같은 event_id에 3행운영 · 알림 · 연구 데이터가 한 번의 가동으로이벤트 순간 GPU 경합 → fps 순간 저하 (실측 대상)기본. Fast ≥ 15 fps · 스왑 0 이면 채택
2안 순차 벤치 (파일 재실행)같은 파일에 --mode 3회경합 0의 순수 벤치운영 카메라 녹화 불가 (쟁점 5)통제 세션 · 공개 데이터셋에서만
3안 동적 스위칭피크 시간대에만 VLM 로드/언로드비피크 전력 · 발열 감소로드 지연 3~10 s · 복잡도 · 첫 이벤트 놓침1안이 메모리 · 발열에서 실패할 때의 대체안. 구현은 systemctl start vlm@peak 타이머 수준으로 단순하게

02메모리 예산 — 가정을 적고 실측으로 지움

항목가정 (착수 전)실측 (R1 · R5에서 채움)측정 방법
OS + 서비스 (헤드리스)~1.0 GB__ GBfree -h 부팅 직후
CUDA 컨텍스트 + TensorRT YOLO11n FP16 + 카메라 버퍼~1.0 GB__ GBFast Loop만 10분 · tegrastats RAM 열
VLM 2~3B Q4 + mmproj + KV(2048)~2.5~3.5 GB__ GBllama-server 기동 후 · 첫 요청 후
Python 런타임 (추적 · 웹 · DB)~0.5 GB__ GB서비스별 RSS
합계 · 여유~5~6 GB · 여유 2 GB__ GB · 스왑 __zramctl · swap used 가 0을 벗어나면 R3 실패

03실측 프로토콜 (R5 · 통제 세션 또는 등교 3일)

B0 · 기준선

Fast Loop만 30분. fps(콜백) · RAM · GPU% · 전력 · 온도 1 Hz. → bench_runs(mode='YOLO_ONLY')

B1 · 상주만

VLM 서버를 띄우되 요청 없음, 30분. 상주 비용(메모리 · 유휴 전력)을 분리.

B2 · 경합

1안 그대로 등교 3일. 이벤트 순간 ±10 s 창의 fps 최저값 · 회복 시간 · VLM latency 분포(p50 · p95). → 계획서의 "30→24 fps" 자리에 실측값.

B3 · 스트레스

통제 세션에서 이벤트를 1분에 4회로 몰아 큐 드롭 · 온도 throttling 발생점을 찾음. 이 값이 3안으로 갈지 결정.

B4 · 순차 (선택)

통제 세션 파일로 --mode 3회. 경합 0 조건의 latency · 정확도. B2와 나란히 표에.

edge/trafficsvc/mon.py · 핵심만
import subprocess, re, time, threading
PAT = re.compile(r"RAM (\d+)/(\d+)MB.*?SWAP (\d+)/(\d+)MB.*?GR3D_FREQ (\d+)%.*?(?:VDD_IN|VIN_SYS_5V0) (\d+)mW.*?(?:gpu|GPU)@([\d.]+)C")
class Mon:                                   # tegrastats 1 Hz 를 파싱해 최신값 유지 (Pi 백엔드는 vcgencmd)
    def __init__(s): s.v = {}; threading.Thread(target=s._loop, daemon=True).start()
    def _loop(s):
        p = subprocess.Popen(["tegrastats", "--interval", "1000"], stdout=subprocess.PIPE, text=True)
        for line in p.stdout:
            m = PAT.search(line)
            if m: s.v = dict(ts=time.time(), ram_mb=int(m[1]), swap_mb=int(m[3]), gpu_pct=int(m[5]), power_w=int(m[6]) / 1000, temp_c=float(m[7]))
    def snapshot(s): return dict(s.v)
    def delta(s, m0): now = s.v; return dict(mem_mb=now.get("ram_mb"), power_w=now.get("power_w"), gpu_pct=now.get("gpu_pct"), swap_mb=now.get("swap_mb"))
# 정규식은 보드의 실제 tegrastats 한 줄로 R1 에서 맞춤 — 필드명이 JetPack 버전마다 다름
AI · 계획서 수치의 위치"GPU 42 → 85%", "30 → 24 fps", "5.2 GB"는 이 표의 가정 열에 옮겨 적고 실측 열이 채워지면 지움. 논문 · 보고서에는 실측 열만 실림. 가정이 맞았는지 자체가 학생 발표의 한 장면이 됨.
5DB 스키마 · 수집 페이로드 — 카운터 10표 + 연구 3표
설계 2일
WHY카운터의 counts_5min · ped_5min · events · qc_5min · calib · ext_wx · school_cal · validation · analysis · outbox는 그대로. 연구를 위해 "한 이벤트에 여러 판정과 라벨이 붙는" 구조 세 장을 더함. 계획서의 단일 표는 VIEW로 남겨 분석 코드가 한 번에 읽게 함.
edge/trafficsvc/schema.sql · 추가분 (카운터 절 1 스키마 뒤에)
-- events 확장: Slow Loop 대상인지 · 최종 risk 는 HYBRID 행에서 가져오되 표시용으로 캐시
ALTER TABLE events ADD COLUMN event_id TEXT;            -- UUID v4 · 3표 조인 키 (id 는 로컬 정수 유지)
ALTER TABLE events ADD COLUMN risk_level TEXT;          -- SAFE | WARNING | DANGER (HYBRID 기준 캐시)
ALTER TABLE qc_5min ADD COLUMN vlm_dropped INTEGER;     -- 큐 드롭 수
ALTER TABLE qc_5min ADD COLUMN power_w REAL;            -- 5분 평균 전력 (MON)
ALTER TABLE qc_5min ADD COLUMN mem_mb INTEGER;          -- 5분 최대 RAM
ALTER TABLE qc_5min ADD COLUMN throttle INTEGER;        -- 온도 throttling 비트

-- 판정: 같은 event_id 에 model_type 별 1행 (섀도 로깅)
CREATE TABLE inferences(
  id INTEGER PRIMARY KEY, event_id TEXT NOT NULL, ts TEXT NOT NULL,
  model_type TEXT NOT NULL,            -- YOLO_ONLY | VLM_ONLY | HYBRID
  risk_level TEXT NOT NULL,            -- SAFE | WARNING | DANGER
  tag TEXT, door_open INTEGER, person_near INTEGER, conf REAL,   -- VLM 출력 (enum 만 · 자유 문장 없음)
  latency_ms INTEGER, mem_mb INTEGER, power_w REAL, gpu_pct INTEGER, swap_mb INTEGER,
  model_ver TEXT, prompt_ver TEXT, rules_ver TEXT, backend TEXT, -- 재현성 (yolo11n-fp16-trt10 · qwen2.5vl-3b-q4 · p1 · h1 · tensorrt|hailo)
  UNIQUE(event_id, model_type));

-- 사람 라벨: 창가 실시간 육안 기록 · 통제 세션 대본 — 이미지 없이 만드는 유일한 정답
CREATE TABLE labels(
  id INTEGER PRIMARY KEY, event_id TEXT NOT NULL, ts TEXT NOT NULL,
  hazard INTEGER NOT NULL,             -- 1 = 실제 위험 정차 (구역 밖 · 시야 가림) · 0 = 정상
  tag TEXT,                            -- boarding | waiting | delivery | other (사람 판정)
  labeler TEXT NOT NULL, source TEXT NOT NULL,   -- live | session
  note TEXT);

-- 벤치 실행: B0~B4 각 실행의 요약 (초 단위 시계열은 qc_5min · inferences 에 이미 있음)
CREATE TABLE bench_runs(
  id INTEGER PRIMARY KEY, started TEXT, ended TEXT, board TEXT, mode TEXT,   -- jetson|pi · YOLO_ONLY|SHADOW|SEQ_*
  fps_med REAL, fps_p05 REAL, lat_p50_ms INTEGER, lat_p95_ms INTEGER, mem_max_mb INTEGER,
  swap_max_mb INTEGER, power_avg_w REAL, temp_max_c REAL, n_events INTEGER, n_dropped INTEGER, note TEXT);

-- 계획서 호환 뷰: 단일 표처럼 읽기
CREATE VIEW traffic_monitoring_events AS
  SELECT i.event_id, i.ts AS timestamp, i.model_type, i.risk_level, i.tag AS context_tag,
         i.latency_ms AS inference_latency_ms, i.mem_mb AS gpu_mem_used_mb, i.power_w AS power_draw_w,
         l.hazard AS is_ground_truth_hazard, l.tag AS gt_tag, e.zone, e.duration_s
  FROM inferences i JOIN events e ON e.event_id = i.event_id LEFT JOIN labels l ON l.event_id = i.event_id;
CREATE INDEX ix_inf_event ON inferences(event_id); CREATE INDEX ix_lab_event ON labels(event_id);

01수집 페이로드 — 누가 어느 표에 쓰는가

쓰는 쪽주기비고
Fast Loop (ACC)counts_5min · ped_5min · qc_5min5분표본 < 5 → null · calib_ver · qc 비트 · MON 요약
Fast Loop (EVT)events (+ event_id) · inferences(YOLO_ONLY)이벤트dwell 판정 즉시 YOLO_ONLY 행 기록 → 큐 submit
Slow Loopinferences(VLM_ONLY · HYBRID) · events.risk_level이벤트 + 1~4 s실패 시 tag=unknown 행이라도 남김 (결측 ≠ 미실행)
라벨 화면 (관리 포트)labels이벤트 순간 · 세션라벨러는 태그 알림을 받고 60 s 안에 입력 · 늦으면 note='late'
배치 (ANL)analysis · bench_runs · outbox(draft)매일 22:00 · 주간M0~M4 + B1 혼동행렬 + 안전지수
사람 (검토)outbox(approved) · validation승인은 관리 포트에서만
TIP · 규약 네 줄① 저장은 UTC, 표시만 KST. ② 표본이 없으면 null. ③ 모든 추론 행에 model_ver · prompt_ver · rules_ver · backend. ④ 라벨이 없는 이벤트는 FPR · Recall 계산에서 빠지고, "라벨 있는 이벤트 수"를 벤치 표에 함께 적음.
6분석 · 소셜 임팩트 · UI 설계 — 숫자가 사람에게 닿는 자리
설계 1주 · 구현 3주
CAM
카메라
DET
검출
TRK
가상선
DB
집계
ANL
분석 · 벤치
MSG
알림 · UI
WHY · 제어 대신 알림교문 앞 도로에는 켤 장치가 없음. 그래서 분석의 목적은 "누구에게, 언제, 어떤 숫자를, 어떤 말로 보여 주면 행동이 바뀌는가"이고, 연구 트랙의 목적은 "그 숫자가 얼마나 믿을 만한가를 표 1장으로". 모델은 작고 설명 가능해야 하며(학생이 발표에서 설명), 출력은 전부 검토 큐를 거침.

01인사이트 → 알림 매핑 (카운터 표 + 추가 3행)

인사이트입력모델 · 방법알림 형태 · 수신자기대하는 인식 · 행동 변화
K1 30 km/h 초과 비율counts_5min(veh4) · school_cal주 단위 비율 + M2 추세 · 전후 검정안내판 · 주간 메시지 · 건의서운전자 감속 · 단속 건의
K2 정차 구역 밖 정차events(dwell, no_stop)일 단위 건수 + M3 군집(핫스팟)안내판 · 학부모 규범 메시지하차는 드롭존에서 · 정차 시간 단축
K3 횡단 중 차량 근접events(conflict) · cross_n일 단위 건수 + M4 위험 시간대주간 리포트 · 건의서안전요원 배치 시간 · 보행 신호 검토
C1 정차 사유 분포 (G2 후)inferences(HYBRID) · labels태그 비율 + "정상 정차로 재분류된 비율"주간 리포트 (학부모회) · 드롭존 설계 근거"정차의 70%가 승하차 → 드롭존 위치 · 길이 조정"처럼 비난 없는 시설 제안
안전 지수 (표시용)K1~K3 · baseline100 − Σ w·clip((K − K_base)/K_base) · w = (0.4, 0.3, 0.3) · 주 단위정문 모니터 · PWA 카드한눈에 보는 추세. 원값 K1~K3 없이 단독 표기 금지
벤치 표 (연구)inferences · labels · bench_runsB1 혼동행렬(모델별 FPR · Recall · 정밀도) · B2 자원 곡선(fps · ms · MB · W)관리 포트 벤치 뷰 · 보고서 부록"하이브리드가 무엇을 주고 무엇을 치르는가"를 한 장으로
오늘의 이상 · 학교 기여분 · 개입 효과 · 내일 예보카운터 매뉴얼 절 3의 매핑 그대로 (M1 · M2 · M5)

02UI 설계 — 화면 · 포트 · 데이터 원천

Plant_Growth_Monitoring_Demo의 web/(React · Vite · Tailwind v4 · ECharts · PWA · 목 데이터 모드)를 셸로 가져오고, 페이지만 바꿈. 공개 포트는 analysis만 읽고, 이미지 · 라이브 프리뷰는 어느 포트에도 없음.

페이지포트내용읽는 표
오늘 /공개아침 카드: 오늘 K1 · K2 · K3 잠정값 · 안전 지수 게이지 · 현재 5분 통행 · QC 통과율 · "자동 카운트 · 수동 대조 오차 ±n%"analysis · counts_5min · qc_5min
프로파일 /profile공개요일 기준 프로파일 + 오늘 곡선 · 이상 마커analysis(M1)
속도 /speed공개주간 p85 · 초과율 · 추세 · 개입 표시선analysis(M2)
정차 /dwell공개도로 평면 정차 히트맵(군집 3) · G2 통과 후 사유 태그 도넛analysis(M3) · inferences
벤치 /bench관리모델별 혼동행렬 · fps / latency / mem / power 시계열 · 이벤트 ±10 s fps 창 · B0~B4 요약 표 · 보드(jetson · pi) 토글bench_runs · inferences · qc_5min · labels
라벨 /label관리 (모바일)이벤트 알림 → 60 s 카운트다운 → hazard · tag 4버튼 · 라벨러 이름. 이미지 없음, 이벤트 시각 · 구역 · 지속시간만 표시events → labels
검토 큐 /outbox관리초안 목록 · 수정 · 승인 · 반려 · 검증기 결과 · 모델 버전outbox
시스템 /system관리서비스 4개 상태 · tegrastats 현재값 · 디스크 · DB 행 수 · config.json 편집 · "이미지 파일 0건" 검사 버튼MON · config
월간 PDF /report/monthly.pdf관리K1~K3 · 정확도 부록 · (G2 후) C1 · 벤치 표 1장Playwright 렌더
와이어 · 오늘 (공개 · 모바일 1열)
┌──────────────────────────────┐
│ 교문 앞 통행 · 오늘 09/15(화)  │
│ 안전 지수  ▮▮▮▮▮▮▮░░░ 72 (▲3) │
│ K1 초과율 24% │ K2 6건 │ K3 3건 │
│ 지금 5분: 차 14 · 이륜 3 · 보행 22│
│ QC 통과 91% · 수동 대조 ±8%     │
│ [지난주 리포트] [프로파일] [정차]│
└──────────────────────────────┘
와이어 · 라벨 (관리 · 모바일)
┌──────────────────────────────┐
│ 이벤트 08:21:14 · no_stop · 27 s │
│ 남은 시간 ●●●●○○ 41 s           │
│ 위험한 정차였나요?  [예] [아니오] │
│ 사유  [승하차][대기][배송][기타]  │
│ 라벨러: 김○○   [저장]            │
│ (이미지 없음 · 창가에서 보고 입력)│
└──────────────────────────────┘

03인식 개선 루프 · 알림 규칙

baseline 2주

GATE 1 통과 직후. 알림 없이 측정만. 이 기간의 프로파일 · p85 · 정차 건수가 비교 기준. Slow Loop는 돌지만 결과는 화면에 안 나감.

개입 1 · 정보 제공

PWA 카드 · 교문 안내판에 지난주 K1 · K2 게시. 2주.

개입 2 · 규범 메시지

"지난주 정차의 78%가 드롭존 안이었습니다"처럼 다수의 행동을 숫자로. G2를 통과했으면 C1로 "정차의 대부분은 승하차 → 드롭존 안내"까지. 2주.

전후 비교 공개 · 건의

같은 요일 · 시간대 짝지어 순열 검정. 효과 없음도 공개. 지자체 · 경찰에 월간 PDF(정확도 부록 + 벤치 표).

규칙 · 모든 알림에 적용 (카운터 6개 + 2개)K1~K3 · C1과 맥락 변수만 말함 · 개인 · 특정 차량 언급 없음 · 규범 메시지 · 숫자마다 근거(기간 · 표본 · 정확도) · 사람 승인 후 발송 · 같은 종류 하루 1회 · VLM 태그는 G2 통과 전 어떤 화면 · 문안에도 나오지 않음 · "어린이 · 학생 · 학부모"를 주어로 쓰지 않음.
[교문 앞 통행 · 이번 주] 10/12~10/16 등교 시간(08:00~08:40) · 통과 차량 298대 중 79%가 30 km/h 이하로 지났습니다 (지난주 74%). ← K1 · 정차 구역 밖 20초 이상 정차: 하루 평균 7건 → 5건. 정차의 71%는 승하차였습니다. ← K2 · C1(검증 완료) · 보행자가 건너는 동안 차량이 2 m 안으로 접근한 경우: 하루 2건. ← K3(근접 프록시) 다음 주 목표: 정차 구역 밖 정차 4건 이하. (자동 카운트 · 수동 대조 오차 ±8% · 정차 사유 태그 정밀도 0.76)
7개발 환경 · 저장소 · CLAUDE.md — 로컬 PC가 메인, 기기는 pull만
준비 2일
WHYPlant_Growth_Monitoring_Demo에서 확정한 운영 구조(로컬 작업 폴더 메인 · git push → 기기 git pull --ff-only · deploy.ps1 한 번 · 기기 작업트리 항상 clean)를 그대로 씀. 기기가 둘(Jetson · Pi)이라도 저장소는 하나, 다른 것은 config.jsonbackend와 설치 스크립트뿐. Claude Code는 로컬 세션이 주가 되고, 기기 위 확인은 SSH 원격 세션에서.

01운영 구조

자리역할도구 · 규칙
로컬 PC 작업 폴더 (집 · 학교 · 노트북)모든 편집 · 커밋 · 테스트(TRAFFIC_FAKE_HW=1) · 웹 빌드Claude Code + GitHub. 다른 PC는 같은 저장소 clone 또는 Claude Code 원격제어. 기기에서 직접 편집 금지
GitHub xparapx/<REPO>단일 진실 · main 하나 · GitHub Pages(docs/) · 웹 릴리스 tarball모델 가중치 · DB · .env · 세션 데이터는 .gitignore
Jetson (~/traffic)서비스 4개 실행 · 엔진 빌드(보드 종속) · 실측scripts/deploy.ps1 -Target jetson → push · pull · install.sh --update · 헬스체크. sudo · 발송은 사람
Pi (~/traffic, 이식 단계)같은 서비스 · Hailo 백엔드deploy.ps1 -Target pi. 학교망 제약(Tailscale 차단)은 Mealboard CLAUDE.md의 Cloudflare 경로 참고
Claude Code 원격 세션 (SSH)기기 위 확인 · 로그 · doctor · 실측 스크립트 실행편집은 하지 않음 — 고칠 것이 보이면 로컬에서 고쳐 push

02저장소 구조 (Plant_Growth_Monitoring_Demo 포맷)

<REPO>/                       # 예: SchoolZone_Edge_Traffic_Demo
  edge/trafficsvc/            # 패키지 (Plant 의 hub/plantsvc 대응): settings · schema · config · db
    capture/                  #   uvc.py · csi_argus.py · picam.py — 같은 제너레이터 인터페이스 (프레임 파일 쓰기 없음)
    detect/                   #   __init__.py(Protocol) · tensorrt_yolo.py · hailo_yolo.py
    counter/                  #   tracker.py(ByteTrack) · geo.py(호모그래피·가상선) · counter.py · acc.py · drift.py(ORB)
    vlm/                      #   __init__.py(Protocol) · llama_cpp.py · hailo_genai.py · prompts/p1.txt · hybrid_rules.py
    slowloop.py · fastloop.py · mon.py · qc.py · api.py(공개/관리) · cli.py(serve·doctor·seed·bench·session) · doctor.py
  analysis/                   # run_daily.py · run_weekly.py · m0_calib.py … m4_conflict.py · b1_confusion.py · b2_resource.py · safety_score.py
  llm/                        # weekly_draft.py · validate.py · prompts/weekly_ko.txt
  notify/                     # dispatch.py (approved 만)
  web/                        # React + Vite + TS + Tailwind v4 + ECharts (src/features/{today,profile,speed,dwell,bench,label,outbox,system})
  deploy/systemd/             # traffic-fast.service.tmpl · traffic-vlm.service.tmpl · traffic-web.service.tmpl · traffic-analysis.timer.tmpl
  scripts/                    # setup_jetson.sh · setup_pi.sh · install.sh · deploy.ps1 · bench.sh · session.sh · rollback.sh
  models/                     # export_trt.sh · fetch_gguf.sh · README (가중치 · 엔진은 git 제외)
  tests/                      # pytest — 합성 궤적 · 큐 드롭 · 검증기 · dispatch(draft 무시) · 스키마 (하드웨어 없이)
  docs/                       # index.html(이 문서) · manual.html(구축 매뉴얼 · 카운터 P1~P7 계승) · WORKLOG.md · GATES.md
  data/                       # traffic.db · config.json · calib/ · sessions/ (전부 git 제외 · 이미지 없음)
  CLAUDE.md  README.md  pyproject.toml  .gitignore

03CLAUDE.md 필수 조항 (첫 커밋에)

프라이버시 (버전 관리)
  • 이미지 · 영상 파일을 쓰는 코드(imwrite · VideoWriter · save) 금지. 디버그는 --show 플래그 뒤 화면 표시만
  • VLM 출력은 enum JSON만 DB에. 자유 문장 · 색 · 차종 · 성별 · 연령 · 번호판 필드 없음
  • 통제 세션 파일은 data/sessions/에만, 운영 파이프라인이 읽지 못함
  • 리포트 · 알림의 주어에 "어린이 · 학생 · 학부모" 금지
운영 · 완료 기준 · 함정
  • 기기 작업트리 clean · main 하나 · deploy.ps1만으로 배포
  • 완료 기준: 절 8 각 단계의 ①~④를 그대로 복사
  • 확인된 함정: JetPack 7.2.1 휠 · CM3 드라이버 · 엔진 이식 불가 · tegrastats 필드명 · llama-server 로그 · PowerShell 5.1 && 없음
  • 검증: uv run pytest -q · cd web && npm run lint && npm run build · TRAFFIC_FAKE_HW=1 uv run trafficsvc serve
TIP · 더미 모드Plant의 PLANT_FAKE_HW처럼 TRAFFIC_FAKE_HW=1이면 합성 궤적 생성기가 Fast Loop를 대신하고 VLM은 규칙 기반 가짜 태거가 됨. 노드 · 카메라 없이 전 화면과 배치가 돌고, 화면에 DUMMY DATA 배지. 학교 PC에서 UI를 다듬을 때 기기가 필요 없음.
8구축 단계 R0 ~ R8 — 완료 기준과 Claude Code 위임 프롬프트
14주 · 단계별 완료 기준

각 단계는 카운터 매뉴얼의 P1~P7에 대응하고(괄호), 상세 실습 절차는 docs/manual.html로 옮김. 여기는 무엇을 · 언제 · 어디까지와 위임 프롬프트의 컨텍스트 블록만. 프롬프트의 [컨텍스트]에 있는 것 이상을 Claude Code에 요구하지 않음 — 그것이 곧 CLAUDE.md의 현재 상태.

R0문제 확인 · GATE 0 · 저장소 생성 (카운터 절 0 · 2) · 1~2주차

산출물

1주 관찰 로그 · 문제 진술 1장(H1~H3 · R1~R3 · K1~K3 · C1 · C2 · 목표치) · 거리 · 창 높이 실측 → 렌즈 초점거리 확정(절 H-04) · 카메라 · 렌즈 주문(ELP AR0234 + 2.8-12mm) · 승인 문서 · 안내문 · 저장소(구조 · CLAUDE.md · pyproject · 더미 모드 · 스키마 · 테스트 뼈대)

완료 기준

① 담당 교사 서명 ② GATE 0 문서 보관 ③ TRAFFIC_FAKE_HW=1로 서비스 기동 · 8 페이지 렌더 ④ pytest 통과 · GitHub Pages에 이 문서 게시

[컨텍스트] - 저장소 새로 생성. 구조는 xparapx/Plant_Growth_Monitoring_Demo 포맷(edge/trafficsvc · analysis · llm · notify · web · deploy/systemd · scripts · tests · docs · data). 이 문서(docs/index.html)의 절 5 스키마와 절 7 구조를 그대로 따름 - 첫 커밋 범위: pyproject(uv) · trafficsvc 패키지 뼈대(settings · schema.sql · db · cli serve/doctor/seed) · detect/vlm/capture 의 Protocol 과 fake 백엔드 · TRAFFIC_FAKE_HW=1 합성 궤적 생성기 · FastAPI 공개/관리 포트 · web/ 는 Plant 의 셸 복사 후 페이지 8개 자리만 - 금지: 이미지 파일 쓰기 코드 일체 · VLM 자유 문장 저장 필드 · 실제 하드웨어 의존 import 를 모듈 최상위에 두는 것 - 완료 기준: uv run pytest 통과(스키마 생성 · 더미 이벤트 → inferences 3행 · dispatch 가 draft 를 건드리지 않음), TRAFFIC_FAKE_HW=1 uv run trafficsvc serve 로 8 페이지 200 응답위 구조로 첫 커밋을 만들어 줘. CLAUDE.md 에는 이 문서의 절 7-03 조항을 그대로 넣고, 각 파일 상단에 읽는 표 · 쓰는 표를 주석으로 명시해.

R1Jetson 런타임 · 카메라 · 미저장 캡처 (H · P1) · 2주차

산출물

JetPack 7.2.1 툴체인 검증 로그 · yolo11n.engine 빌드 · llama-server 기동 · ELP UVC(MJPEG → nvv4l2decoder) 캡처 확인 · 창 조준(MJPEG 미리보기) · 07:40~08:30 역광 시험 캡처 · 교문 틈 도로 폭 · 횡단보도 가시성 · 중앙 수목 가림 확인 → K1 · K3 채택/보류 판정 · ORB 기준점 · B0 · B1 기준선 · 메모리 예산 실측 열

완료 기준

① 30분 캡처 루프 fps ≥ 15 · 이미지 파일 0건 ② 엔진 단독 추론 ≤ 15 ms/frame (640) ③ VLM 서버 더미 요청 응답 · 상주 메모리 기록 ④ 절 4-02 표의 실측 열 채움 · 스왑 0

함정이 단계의 시험 캡처 결과로 K1 · K3의 채택 여부가 갈리므로(절 H-04), 캡처 화면 위에 가상선 A·B 후보 · 횡단보도 다각형 · 정차 구역 다각형을 그려 담당 교사와 함께 판정하고 그 화면(저장 없이 캡처 보드 촬영이 아니라 도면으로 옮겨 그린 것)을 GATE 0 문서에 넣음. CM3를 Jetson에 붙이는 시도는 1일로 제한 — 커널 패치 · 디바이스 트리 작업은 JetPack 7.2.1에서 검증된 자료가 없으므로 실패해도 프로젝트 손실이 아님. 실패 기록을 CLAUDE.md "확인된 함정"에 남기고 UVC로 진행.

R2검출 · 추적 · 가상선 · 속도 · 정차 · 근접 (P2 · P3) · 3~4주차

산출물

tensorrt_yolo.py(ultralytics engine 로드 · 6종 · conf 0.40 · 묶음) · ByteTrack · 호모그래피 4점 실측 · counter.py(카운터 매뉴얼 P3 이식) · 이벤트에 event_id · YOLO_ONLY 추론 행

완료 기준

① 콜백 fps ≥ 15 · track_id 유지(10대) ② 4점 재투영 < 0.3 m · 자전거 속도 ±3 km/h ③ 합성 궤적 pytest(직진 · 정차 30 s · 횡단 중 근접) ④ dwell 판정 시 inferences(YOLO_ONLY) 1행 · 파일 저장 없음

[컨텍스트] - 보드: Jetson Orin Nano Super 8GB · JetPack 7.2.1 · yolo11n.engine(FP16, 640) 빌드됨 · 카메라 소스 capture/uvc.py 동작(1280×720 @ 30) - 목표: detect/tensorrt_yolo.py — ultralytics YOLO(engine) 로 6종(person·bicycle·car·motorcycle·bus·truck) conf ≥ 0.40, GROUP 으로 veh4/two_wheel/person, 정규화 상자 반환. counter/ 는 카운터 매뉴얼 P3 의 Counter(가상선·방향·속도·dwell ≥20 s·conflict <2 m)를 이식하되 dwell 확정 순간 events.event_id(uuid4) 생성 → inferences(YOLO_ONLY, risk=DANGER if zone=no_stop) 기록 → slowloop.submit(crop, meta) 호출(크롭은 여백 20%·긴 변 448) - 금지: 프레임·크롭 저장, 화면 표시는 --show 뒤에만 - 완료 기준: 15 fps 이상 로그 · pytest 합성 궤적 3종 통과 · submit 이 블록되지 않음(put_nowait)위 조건으로 detect/tensorrt_yolo.py · counter/counter.py · fastloop.py 와 pytest 를 작성하고 실행 · 확인 명령을 알려줘.

R35분 집계 · QC · MON · 서비스 등록 (P4) · 4~5주차

산출물

acc.py(카운터 P4) + mon.py(tegrastats 파서) → qc_5min에 power · mem · throttle · vlm_dropped · systemd 유닛 4개 템플릿 · install.sh · deploy.ps1

완료 기준

① 등교일 하루 주간 버킷 132개 · qc=0 ≥ 80% ② 표본 부족 버킷 null ③ 재부팅 후 4 서비스 자동 복구 ④ deploy.ps1 한 번으로 push → pull → update → 헬스체크

R4수동 대조 GATE 1 (P5) · 5~6주차

프로토콜

카운터 P5 그대로: 15분 × 10회, 학생 2인 클리커 + 1인 근접 육안 기록. 추가: 같은 시간에 /label 화면을 처음 써 보며 정차 이벤트가 오면 hazard · tag를 입력(G2용 라벨 축적 시작).

완료 기준 · GATE 1

veh4 오차 ≤ 10% · 속도 ±3 km/h · conflict 정밀도 ≥ 0.7 · 카운터 간 일치 ≥ 95% · 정확도 표 작성. 통과 시점부터 baseline 2주 시작, VLM 트랙(R5) 착수.

R5Slow Loop · 자원 실측 · 라벨 60건 GATE 2 (신규) · 6~9주차

산출물

vlm/llama_cpp.py(OpenAI 호환 HTTP · 이미지 base64 · 온도 0 · JSON 스키마) · slowloop.py · hybrid_rules.py · 프롬프트 p1 · B2 · B3 실측(등교 3일 + 통제 세션 1회) · /label 실운영 · b1_confusion.py · b2_resource.py · 벤치 뷰

완료 기준 · GATE 2

① 이벤트당 latency p95 ≤ 4 s · 이벤트 순간 fps 최저 ≥ 15 · 스왑 0 ② 라벨 ≥ 60건 · unknown ≤ 15% ③ HYBRID 정밀도 ≥ 0.7 · YOLO_ONLY 대비 FPR 감소를 수치로(감소 폭은 결과) ④ 절 4 표 완성. 미달이면 p2 프롬프트 · 모델 교체(Qwen3-VL 2B ↔ Qwen2.5-VL 3B) 후 재시도, 2회 실패 시 C1은 "연구 결과: 미달"로 보고하고 운영 리포트에서 제외

[컨텍스트] - R2·R3 완료 · fastloop 이 dwell 확정 시 slowloop.submit(event_id, crop_rgb, meta{zone,duration_s,ts}) 호출 · llama-server 가 localhost:8090 (qwen2.5-vl-3b q4 + mmproj, --parallel 1) 에서 OpenAI 호환 /v1/chat/completions 제공 · mon.Mon 이 tegrastats 최신값 제공 - 요구: (1) vlm/llama_cpp.py — 크롭을 JPEG 메모리 인코딩 → base64 → 프롬프트 p1(prompts/p1.txt) + 이미지 · 온도 0 · max_tokens 64 · 응답 JSON 파싱, enum 외/파싱 실패/금칙어 → tag=unknown (2) slowloop.py — asyncio.Queue(8) · drop-old · 처리 후 del crop · inferences(VLM_ONLY, HYBRID) 기록 · latency/mem/power 포함 (3) vlm/hybrid_rules.py — 이 문서 절 3-01 결합 규칙 · rules_ver='h1' (4) analysis/b1_confusion.py — labels 와 조인해 model_type 별 FPR·Recall·정밀도 (5) 벤치 페이지 API - 금지: 크롭·프레임 파일 쓰기 · 자유 묘사 저장 · 서버 요청 로그에 이미지 남기기(--log-disable 확인) - 완료 기준: pytest — 큐 상한 초과 시 오래된 것부터 드롭 · 금칙어 포함 응답 → unknown · 라벨 없는 이벤트는 혼동행렬에서 제외위 조건으로 구현하고, 등교 3일 B2 실측을 돌리는 scripts/bench.sh 와 결과를 bench_runs 에 넣는 코드를 함께 작성해 줘.

R6대시보드 · 배치 · 알림 · 검토 큐 (P6) · 8~11주차

산출물

공개 페이지 4 + 관리 페이지 4 · run_daily.py(M0~M4 + 안전지수) · dispatch.py · 개입 1 · 2 문안 · 월간 PDF(정확도 부록 + 벤치 표)

완료 기준

① 공개 카드 QR · QC 통과율 표시 ② 매일 배치가 analysis · outbox(draft) 채움 ③ draft 상태 발송 0건(테스트) ④ 주간 리포트 1회 실제 발송 · C1은 G2 통과 시에만 노출

R7로컬 LLM 리포트 · 전후 비교 · 건의 GATE 3 (P7) · 11~14주차

산출물

주간 집계 JSON → llama.cpp 텍스트 모델 → 검증기 → outbox · 톤 3안 · 전후 비교(순열 검정) · 월간 건의 PDF · 학생 발표 자료 · 벤치 표 공개

완료 기준

① 초안 2분 내 · 검증기 통과 · 숫자 변조 초안 rejected ② 주간 리포트 3회 발송(승인 로그) ③ 개입 2회 + 전후 비교 1건 공개(효과 없음 포함) ④ 건의서 1건 제출 또는 제출 준비

R8Pi 5 + AI HAT+ 2 이식 (선택 · 12~14주차)

산출물

capture/picam.py(CM3) · detect/hailo_yolo.py · vlm/hailo_genai.py 또는 CPU 배치 · setup_pi.sh · 같은 통제 세션 파일로 B4 재실행

완료 기준

① 같은 DB 스키마 · 같은 UI에 board='pi' 벤치 행 ② 두 보드 벤치 표 1장(fps · ms · MB · W · FPR · Recall) ③ 이식에 바뀐 파일이 capture/ · detect/ · vlm/ · scripts/ 밖에 없음을 git diff --stat으로 확인

일정 · 역할 · 완료 기준
14주 · 2026-09-14 ~ 12-18

14주 일정 (주차 격자 · 1주차 = 9/14 주)

작업주차
R0 문제 확인 · GATE 0 · 저장소 · 카메라 결정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R1 JetPack 7.2.1 런타임 · 카메라 · 캡처 · B0/B1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R2 검출 · 추적 · 가상선 · 속도 · 이벤트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R3 5분 집계 · QC · MON · 서비스 · deploy.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R4 수동 대조 · GATE 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
baseline 2주 (측정만 · Slow Loop 섀도)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R5 Slow Loop · 자원 실측 · 라벨 60건 · GATE 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R6 대시보드 · 배치 · 알림 · 개입 1 · 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R7 로컬 LLM 리포트 · GATE 3 · 전후 비교 · 건의 · 발표
1
2
3
4
5
6
7
8
9
10
11
12
13
14
R8 Pi 5 + AI HAT+ 2 이식 (선택)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
TIP · 일정의 두 갈래GATE 1이 6주차에 통과하지 않으면 R5는 밀리지만 baseline · R6 · R7은 VLM 없이 그대로 진행함 — 운영 트랙과 연구 트랙이 독립이라는 것이 이 일정의 보험. 12월 셋째 주 이후는 기말 · 방학이므로 14주 안에 못 끝난 R8은 겨울방학 과제.

역할 (팀 5명 기준 · 겸임 가능)

현장 · 하드웨어설치 · 실측

창 거치, 4점 실측, 속도 검증 주행, 통제 세션 연출, 청소 · 이동 점검 로그

파이프라인R1 · R2 · R3

런타임 검증, 검출 백엔드, Counter, 누적기, 서비스. Claude Code 위임 프롬프트 작성 · 검수

연구 · 벤치R5

프롬프트 · 결합 규칙 · 실측 프로토콜 · 혼동행렬 · 자원 곡선 · 벤치 표. 라벨 캠페인 조직

데이터 · 분석R4 · M0~M4

수동 대조 설계, 정확도 표, 기준 프로파일, 전후 검정, 안전지수

리포트 · 커뮤니케이션R6 · R7

알림 문안, 규범 메시지 3안, UI 문구, 학부모회 · 지자체 대응, 발표

교사 (게이트 담당)G0 · G1 · G2 · G3

협의 · 승인, 검증 판정, VLM 판정 검증 판정, 발송 승인. sudo 와 발송은 교사가

프로젝트 완료 기준 (전부 체크되면 종료)

  • 문제 진술 1장(H1~H3 · R1~R3 · K1~K3 · C1 · 목표치)에 담당 교사 서명
  • GATE 0 문서(확인 · 승인 · 안내문 사진 · VLM 처리 방식 명시) 보관
  • 이미지 · 영상 파일 0건 — 저장소 · Jetson · Pi 전체 검색 결과 첨부 (통제 세션 디렉터리는 별도 삭제 로그)
  • 등교일 20일 이상 5분 버킷, qc=0 ≥ 80%
  • GATE 1 정확도 표 — 차량류 오차 ≤ 10%
  • GATE 2 결과 — 라벨 ≥ 60건, 모델별 혼동행렬, 자원 실측표(절 4) 완성 (통과 여부와 무관하게 공개)
  • K1 · K2 · K3 baseline 2주 + 개입 2회 + 전후 비교 결과 공개 (효과 없음 포함)
  • 주간 리포트 3회 발송(승인 로그) · 로컬 LLM 실측표
  • 월간 건의 리포트 PDF 1건(정확도 부록 + 벤치 표) 제출 또는 제출 준비
  • 벤치 표 1장: YOLO / VLM / HYBRID × (FPR · Recall · 정밀도 · fps · ms · MB · W) — 계획서의 가정 열과 실측 열 나란히
  • 학생 발표: "우리 데이터로 무엇이 바뀌었고, 하이브리드는 무엇을 주고 무엇을 치렀는가"
치트시트 · 참고

명령 (Jetson)

cat /etc/nv_tegra_release                 # JetPack 7.2.x = R39
jtop / tegrastats --interval 1000         # RAM · GPU · 전력 · 온도
sudo nvpmodel -q ; sudo jetson_clocks     # 전력 모드 확인 · 클럭 고정
v4l2-ctl --list-devices                   # 카메라
systemctl status traffic-fast traffic-vlm traffic-web ; journalctl -u traffic-fast -f
systemctl list-timers | grep traffic      # 배치
curl -s localhost:8090/health             # llama-server
sqlite3 data/traffic.db "SELECT model_type, COUNT(*) FROM inferences GROUP BY 1;"
sqlite3 data/traffic.db "SELECT * FROM bench_runs ORDER BY id DESC LIMIT 5;"
find ~/traffic -name "*.jpg" -o -name "*.png" -o -name "*.mp4" -o -name "*.h264"   # 항상 0건 (data/sessions 제외)
tailscale status

참고 자료

Jetson Orin Nano · JetPack 7.2.1공식

Jetson ISO 설치 · L4T r39.2.x · Ubuntu 24.04 · CUDA 13. docs.nvidia.com/jetson/orin-nano-devkit · 포럼 "JetPack 7.2 Getting Started" 스레드

Camera Module 3 on Jetson쟁점 1

RidgeRun IMX708 커뮤니티 드라이버(JetPack 5.1.1 / 6.0 패치 · 단일 모드 4608×2592@14). developer.ridgerun.com · 7.2.1 적용 여부 미확인 → 1일 한도

ELP AR0234 글로벌 셔터 USB · CS 렌즈카메라

UVC · MJPEG 1920×1200/1080 @30 · 수동 노출 · 2.8-12mm 가변 렌즈 확정 후 잠금. Jetson: v4l2src ! image/jpeg ! nvv4l2decoder mjpeg=1 ! nvvidconv

Ultralytics YOLO11 · TensorRT export검출

yolo export format=engine half=True · Jetson 가이드 · 엔진은 보드에서 빌드

ByteTrack · supervision추적

github.com/roboflow/supervision · 카운터 매뉴얼 P2 경로 B

llama.cpp · Qwen-VL GGUFVLM · LLM

github.com/ggml-org/llama.cpp · llama-server --mmproj · OpenAI 호환 API · --log-disable

Ollama (arm64 · CUDA)대안

qwen2.5vl · qwen3-vl 태그 · JetPack 7 지원 여부를 설치 전 확인

Raspberry Pi AI HAT+ 2 · hailo-rpi5-examplesPi 트랙

Hailo-10H · Trixie · hailo-h10-all · GenAI 예제. 카운터 매뉴얼 절 H · P2

Telraam선례

창문 안쪽 시민과학 교통 카운터 · 영상 미저장 · EU WeCount. telraam.net

지난 프로젝트계승 코드

Plant_Growth_Monitoring_Demo(저장소 구조 · uv · deploy.ps1 · 더미 모드 · React 셸), Mealboard(FastAPI · PWA · Funnel · Playwright PDF · 포트 분리), multinode_aq(5분 버킷 · analysis · CLAUDE.md 관행), 스쿨존 교통카운터 매뉴얼(P1~P7 · 게이트 · 규범 메시지)

글꼴 IBM Plex Sans KR / Mono · 색 토큰은 대시보드 hub/web/app.css와 동일(라이트 기본, 다크 토글) · 세 지도는 archify 규약(상자 안 코드명 · 이름은 바깥 · 큰 글꼴)으로 작성한 인라인 SVG · 이 파일은 외부 의존성 없이 단독 열림(글꼴만 온라인) · 원본: 스쿨존_교통카운터.html(v0.2) + 스쿨존_교통_위험_연구_계획서.html · 확인 기준일 2026-09-14