- server-deploy.md: state MySQL 8.0.13+ / MariaDB 10.2+ is required (expression defaults), not "5.7+" — an import against 5.7 is a day-1 ERROR 1064 blocker. - pilot-runbook: add the air-gapped Pi path and mark pip/fetch as build-machine only; stop the shipped doc from telling a Pi operator the bundle "does not exist". - install_offline.sh: instruct the camera probe before enable (mirrors setup_online). - setup_online.sh: chmod 600 device.env on read; README documents delete + rotate. - systemd unit: order After/Wants=time-sync.target so occurred_at is real wall time.
9.3 KiB
파일럿 · 운영 런북
이 문서는 두 경로를 다룬다. 자신의 상황에 맞는 절에서 시작하라:
-
§0 폐쇄망 Pi 현장 설치·운영 — Hailo·Pi5 실장비를 오프라인 번들로 설치한 경우. systemd 유닛의
Documentation=이 이 문서를 가리키므로 현장 운영자는 §0만 보면 된다. -
§1~§6 노트북 웹캠 개발 파일럿 — 개발/데모 머신에서 CPU MoveNet로 엔드투엔드를 돌리는 최소 경로. 이 절들의
pip install·모델fetch명령은 개발/빌드 머신 전용이다 — 폐쇄망 Pi에서 실행하지 말 것. -
엣지:
python -m edge(또는move-detect-edge) — pose → 감지 → HMAC 서명 POST. -
서버: 기존
server/PHP 앱 — ingest + 대시보드 + 사고 상세 + 데스크 Web Audio 알림.
0. 폐쇄망 Pi 현장 설치·운영
실장비 Hailo·Pi5는 오프라인 전체 번들로 설치한다. 런타임 휠·pose 모델(HEF)·소스·systemd 유닛이
모두 번들에 동봉되어 있으므로 Pi에서 인터넷을 받지 않는다 (아래 §1의 pip install/fetch는 절대
실행 금지 — 빌드 머신 전용).
- 설치: 번들 절차는 deploy/README.md를 따른다. 빌드 머신에서
deploy/bundle/build_offline_bundle.sh(aarch64)로 번들을 만들어 Pi로 옮기고,sudo ./install_offline.sh가 SHA256SUMS 검증 → 오프라인 휠 설치 → HEF 배치 → 유닛 설치를 수행한다. (온라인 프로비저닝은sudo deploy/setup_online.sh— 카메라 프로브까지 자동.) - 멀티카메라:
/etc/move-detect/edge.env의CAMERAS=csi:0:cam-01,uvc:0:cam-02형식으로 1 Pi에 2~4대를 배선한다(형식backend:device:camera_id). - enable 전 카메라 프로브 (설치 스크립트가 출력하는 명령) — 잘못된 인덱스/미체결 리본이 만드는 "초록불인데 한 대 덜 감시" 상태를 현장에서 잡는다.
- 기동·확인:
sudo systemctl enable --now move-detect-edge→journalctl -u move-detect-edge -f. - 시크릿 위생: 프로비저닝에
deploy/device.env를 썼다면 설치 후 삭제·필요시 로테이션 (deploy/README.md "Secret handling").
시크릿·서버 URL·모델 경로 등 env 키는 §3 표와 동일하다. 감지 임계값은 서버 대시보드의 구역·임계값이 다음 config 폴링에 반영된다.
1. 빌드/개발 머신 준비 (모델 · onnxruntime) — 폐쇄망 Pi에서 실행 금지
폐쇄망 원칙: onnxruntime 휠과
.onnx모델은 빌드 머신에서 받아 번들한다. 타깃(폐쇄망 Pi)에서는 인터넷을 받지 않으며, 아래 두 명령을 Pi에서 실행하면 안 된다(§0 참조 — Pi는 이미 번들로 설치됨). 노트북 파일럿은 빌드=타깃이 같은 머신이라 아래를 그대로 실행하면 된다.
pip install onnxruntime # CPU 추론 런타임 (에어갭 타깃엔 오프라인 휠로 번들)
python -m edge.tools.fetch_movenet # MoveNet SinglePose Lightning ONNX -> models/movenet_lightning.onnx
fetch_movenet.py의 URL은 빌드 편의용이다. 실제 내려받은 모델의 입력 크기/자료형과 출력 레이아웃
[1,1,17,3](y,x,score)를 어댑터 기대값과 대조하라(다르면 MoveNetPoseAdapter(input_size=...) 조정;
COCO-17 remap 로직은 그대로). 어댑터 remap은 tests/test_movenet_pose.py(fake session)로 고정돼 있고,
실모델 경로는 tests/hardware/test_movenet_smoke.py가 모델이 번들됐을 때만 게이트로 검증한다.
2. 서버 로컬 기동
로컬 MySQL 8(스키마 CHECK 제약은 8.0.16+)과 PHP 8.2+ 필요. 상세 배포·전제조건(gd/argon2/HTTPS 등)은 server/docs/server-deploy.md 참조 — 여기선 로컬 최소 절차만.
- DB 생성 후 스키마 임포트:
mysql <db> < server/schema.sql(10개 테이블). server/config.php를server/config.example.php에서 복사, 로컬 DB DSN/계정 기입 (snapshot_dir는 쓰기 가능 경로로).- 관리자 계정 · 디바이스 시크릿 등록 — 앱에 셀프 가입 흐름이 없으므로 직접 넣는다.
CLI 스니펫과 SQL은 server-deploy.md §6(디바이스 시크릿) · §7(첫 관리자) 그대로 사용:
- 시크릿 생성:
php -r "echo bin2hex(random_bytes(32)), \"\n\";" INSERT INTO devices (device_id, name, secret, config_version) VALUES ('pi-01','노트북','<시크릿>',1);- 관리자 해시:
php -r "echo password_hash('바꿀-암구호', PASSWORD_ARGON2ID), \"\n\";" INSERT INTO users (username, password_hash, role, ...) VALUES ('admin','<해시>','admin',NULL,0,0);
- 시크릿 생성:
- 기동:
php -S localhost:8000 -t server/public- 세션 쿠키가
cookie_secure라 순수 HTTP에선 로그인 쿠키가 안 붙을 수 있다. 로컬은 대개localhost가 secure-context로 취급돼 동작하지만, 안 되면 server-deploy.md §8(HTTPS) 참조.
- 세션 쿠키가
3. 엣지 환경변수
edge/config.py(load_config) + edge/daemon.py(main)가 읽는 실제 키:
| env | 의미 | 예시 |
|---|---|---|
DEVICE_ID |
devices.device_id와 일치 |
pi-01 |
DEVICE_SECRET |
devices.secret와 일치(HMAC) |
<§2에서 생성한 값> |
CAFE24_URL |
서버 베이스 URL | http://localhost:8000 |
CAMERA_ID |
이벤트/구역의 camera_id | cam-01 |
MOVENET_ONNX |
모델 경로 (필수, 없으면 즉시 KeyError) | models/movenet_lightning.onnx |
UVC_DEVICE |
OpenCV 캡처 인덱스 | 0 |
TARGET_FPS |
프레임 간격 = 1/fps | 12 |
QUEUE_DB |
오프라인 큐 sqlite 경로 | queue.db |
감지 임계값(선택, 데모 단축용): FALL_PRONE_S(기본 3.0), FALL_NO_MOTION_S(10.0),
IMMOBILE_S(120.0), GHOST_REAPPEAR_S(10.0), ZONE_DEBOUNCE_S(0.75), COOLDOWN_S(30.0).
export DEVICE_ID=pi-01 DEVICE_SECRET='<시크릿>' CAFE24_URL=http://localhost:8000 \
CAMERA_ID=cam-01 MOVENET_ONNX=models/movenet_lightning.onnx UVC_DEVICE=0 TARGET_FPS=12
python -m edge # 또는 move-detect-edge
4. 데모 시나리오
- 낙상: 웹캠 앞에서 눕는다 →
FALL_PRONE_S(기본 3초) 후fall(high) 이벤트. - 위급 상승: 계속 안 움직이면
FALL_NO_MOTION_S(기본 10초) 후no_motion(critical) → 대시보드에서 데스크 비프 반복(첫 클릭으로 오디오 언락 필요). - 가시화: 목록의 이벤트 유형(링크) 클릭 →
/events/{id}/view상세 → 스냅샷 이미지 확인. - 응답: 상세/목록에서 확인 → 미확인 critical이 사라지면 비프 정지.
- 역루프(구역): 대시보드
구역 설정에서 폴리곤 저장 → 발끝 진입 →zone_*이벤트. 구역 변경은 엣지의 다음 config 폴링 때 적용된다 — 기본 cadencesync_every=150프레임 ≈ 12fps에서 약 12초. 저장 후 ~15초 기다렸다가 진입할 것. - 오프라인 큐: 네트워크(또는
php -S) 중단 상태에서 이벤트 발생 → 큐에 적재 → 복구 후flush_every=300프레임(≈25초)마다 flush로 밀린 이벤트 전송. - 하트비트: 30초 시간기반(
heartbeat_interval_s=30)으로POST /api/heartbeat(서명). 부하와 무관하게 주기가 일정해 서버의 오프라인 판정(120초, 4배 마진)이 정상 장치를 오프라인으로 깜빡이지 않는다. temp/throttle/queue_pending/config_version 텔레메트리를 함께 싣는다.
cadence(
sync_every/flush_every는 프레임 수,heartbeat_interval_s는 초)는daemon.main()기본값이며 env로 노출돼 있지 않다. 데모에서 더 빠른 주기가 필요하면run_loop(...)호출 인자를 임시 조정한다. 감지 타이머는 monotonic 시계로 돌아 NTP 시각 점프에 영향받지 않는다(occurred_at만 벽시계).
5. 검증(테스트)
pytest -q # 엣지 유닛(하드웨어 스모크는 모델 없으면 skip)
cd server && ./vendor/bin/phpunit --display-warnings --display-deprecations # 서버(0 warnings)
모델을 번들한 뒤 실추론 경로까지 확인하려면 models/movenet_lightning.onnx 존재 상태로 pytest -q —
tests/hardware/test_movenet_smoke.py가 skip 해제돼 실행된다.
6. 한계 (명시)
이 절은 §1~§5의 노트북 웹캠 파일럿에 대한 것이다. 폐쇄망 Pi 현장 설치(§0)는 아래 항목 다수를 이미 해소했다 — 실장비 Hailo 가속, 멀티카메라, 오프라인 전체 번들이 제공된다.
- 노트북 파일럿은 단일 카메라 · CPU pose(저 fps) — 폐쇄망 Pi(§0)는 Hailo 가속 + 멀티카메라
(
CAMERAS=다중 배선)를 사용한다. - 단일 프레임 스냅샷 — 前中後 링버퍼 다중 프레임은 P2(양끝 인프라는 존재, 배선 대기).
- 알림 1채널(데스크 Web Audio) — critical SMS/webhook·미확인 에스컬레이션 메일은 후속(P3).
- 폐쇄망 전체 번들은 이미 존재한다 —
deploy/bundle/에 오프라인 휠·HEF·소스·SHA256SUMS 검증 설치를 제공한다(§0). 노트북 파일럿만pip install/fetch로 편의 실행할 뿐이다. - 드릴 게이트 · F14 컴플라이언스 · 감사 해시체인 — 범위 밖(P1-C/P3).