감사에서 확정된 잔여 항목을 일괄 처리. 이전 커밋이 증상(스테일 번들)을 고쳤다면 이번에는 그것이 조용히 지나가게 만든 구조를 고친다. 보안 - CSP 도입. 인라인 2블록에 per-request nonce, unsafe-inline 없음. 빌드가 없다는 제약이 nonce를 막지는 않는다. 보안 헤더를 .htaccess 단독에서 PHP로 옮겨 개발/납품이 같아진다. - /logout에 CSRF. SameSite=Lax가 막아주지만, 사고 대응 중 운영자를 콘솔에서 튕겨내는 건 이 제품에선 실제 가용성 공격이라 예외로 둘 이유가 없다. - 잠긴 계정 423 → 401. 아무나 이름을 추측해 5회 실패로 잠근 뒤 423을 계정 실재 확인으로 쓸 수 있었다. 잠금 사실은 감사 로그에 남고, 익명 호출자는 못 읽는다. 엣지 - 큐 flush에 회당 상한. 정전 복구 직후 2000건을 비우는 동안 감지가 멈추던 구간을 없앤다. - 타이머 env 전수 검증. COOLDOWN_S=nan은 dedup을 무력화해 매 틱 발보시켰다. - T2 프레임 스킵(INFER_EVERY). 스킵 틱은 프레임을 읽어 버퍼만 비우고 링버퍼에 넣지 않는다 — 추론이 없으면 얼굴 박스도 없어 그대로 넣으면 증거 스냅샷에 미블러 얼굴이 실린다. T3(모션 게이트)는 의도적으로 미구현: 게이트가 틀리면 '움직이지 않는 쓰러진 작업자'에게 추론이 안 돈다. 실영상 없이 임계값을 정할 수 없다. - systemd 워치독(sd_notify) + 메모리 상한. Restart=always는 죽은 프로세스만 잡고 '살아 있는데 멈춘' 상태는 못 잡는다. UVC 해상도·버퍼 고정(링버퍼 RSS가 웹캠 기본값에 좌우). 테스트 — 감사의 최대 갭 - PDO 통합 테스트가 pdo_sqlite 부재로 통째로 스킵되고 CI도 없어 사실상 한 번도 안 돌았다. `-d extension=pdo_sqlite`로 되살리고 리포트·히트맵·익스포트 집계 SQL을 최초로 실행한다. - 얼굴 블러 테스트가 out is not None만 봤다 — blur()가 무동작이어도 통과. PIPA 대응의 유일한 실행 가능한 증거였는데 아무것도 증명하지 못했다. 디테일 파괴를 단언하도록 교체. - index.php 라우트 테이블 대조(프런트 호출 15개 교차검증), dist↔소스 패리티 테스트, CI 신설(스킵이 하나라도 있으면 실패). - ArraySessionStore.regenerate()가 데이터를 지워 실제 session_regenerate_id(true)와 달랐다. 더블이 실구현과 어긋난 자리 하나를 실제로 잡았다. 프런트 - 이벤트 목록 시그니처 재렌더 스킵 + 포커스 복원. aria-live 영역을 5초마다 통째로 다시 만들어 스크린리더가 200행을 재낭독하고 키보드 포커스가 body로 날아갔다. 같은 패턴이 device-tree.js에는 이미 있었다. - 폴링 중첩 가드(느린 공유호스팅에서 응답 역전), 콜드스타트 실패 표시. 납품 - VERSION 신설, 두 산출물에 version/git_commit/built_at 스탬프. 스테일 번들의 SHA256SUMS는 자기정합적이라 통과한다 — provenance 없이는 하류에서 탐지할 방법이 없다. - deploy/README·env.example을 Hailo 단독 번들 실제와 일치(템플릿이 없는 ONNX를 가리켰다). - 이벤트 보존 정책은 기본 0(영구). 산업안전 기록을 시스템이 임의로 지우지 않는다. 검증: pytest 251 / PHPUnit 162, 스킵 0. CSP nonce 결합은 실제 렌더 경로로 확인. 브라우저 실렌더는 MySQL이 필요해 이 환경에서 미수행.
157 lines
6.8 KiB
Markdown
157 lines
6.8 KiB
Markdown
# Hardware Bring-up & Benchmark Procedure (Raspberry Pi 5 + Hailo-8L)
|
||
|
||
This document describes the validation procedure for the edge detection core on actual hardware (Pi 5 + Hailo accelerator). Development PC testing is covered in the main test suite; this procedure applies to the Pi deployment phase.
|
||
|
||
## 1. PCIe Gen3 Activation
|
||
|
||
Enable PCIe Gen3 for Hailo-8L optimal throughput:
|
||
|
||
1. SSH to Pi 5 and edit `/boot/firmware/config.txt`:
|
||
```bash
|
||
sudo nano /boot/firmware/config.txt
|
||
```
|
||
|
||
2. Add or update the line:
|
||
```
|
||
dtparam=pciex1_gen=3
|
||
```
|
||
|
||
3. Reboot:
|
||
```bash
|
||
sudo reboot
|
||
```
|
||
|
||
4. Verify PCIe Gen3 negotiation:
|
||
```bash
|
||
lspci -vv | grep -i speed
|
||
```
|
||
Expected output: `LnkSta: Speed 8GT/s` (Gen3) or higher, not 2.5GT/s (Gen1).
|
||
|
||
---
|
||
|
||
## 2. HEF Benchmark: Single, 2-Stream, 4-Stream
|
||
|
||
Benchmark `yolov8s_pose.hef` (fall detection backbone) under different load scenarios:
|
||
|
||
1. **Single-stream baseline:**
|
||
- Load HEF via HailoRT VDevice, run 1 stream continuously (640×640 RGB input).
|
||
- Measure: FPS, CPU usage, Hailo die temperature.
|
||
- Target: ≥ 20 FPS @ <60°C (baseline headroom).
|
||
|
||
2. **2-stream round-robin:**
|
||
- Process 2 video streams (CSI + USB UVC) round-robin into Hailo.
|
||
- Measure: Combined FPS per stream, CPU, temperature.
|
||
- Target: ≥ 10 FPS per stream (full fall detection at 2 streams).
|
||
|
||
3. **4-stream round-robin (stress test):**
|
||
- Process 4 USB UVC streams into Hailo round-robin.
|
||
- Measure: FPS per stream, CPU, temperature, memory.
|
||
- Target: ≥ 7–8 FPS per stream (performance envelope; may require USB controller separation).
|
||
|
||
**Benchmark tooling:**
|
||
- Use `hailo-benchmark` or custom Python loop with HailoRT + OpenCV frame capture.
|
||
- Log FPS, CPU (top/mpstat), temperature (vcgencmd measure_temp).
|
||
- Store results in `docs/benchmarks/YYYY-MM-DD-pi5-hef-results.txt`.
|
||
|
||
---
|
||
|
||
## 3. FPS 3-Tier Validation
|
||
|
||
Measure frame processing under three operational modes:
|
||
|
||
| Tier | Mode | Description | Target FPS | Notes |
|
||
|------|------|-------------|-----------|-------|
|
||
| **T1: Full** | Detection on all frames | Hailo inference on every frame; maximum load. | ≥ 12 fps (crucial: fall detection responsiveness) | For 1–2 primary cameras (fall risk zones). Limits concurrent streams. |
|
||
| **T2: Detection** | Inference every 2–3 frames | Trade-off: skip frames to reduce Hailo load. | ≥ 8 fps | For additional context cameras; acceptable for non-critical zones. |
|
||
| **T3: Motion** | Hailo only on motion keyframes | Inference triggered by motion (v4l2 MJPEG keyframe or frame diff). | ≥ 5 fps avg | For background monitoring; many cameras, low overhead. |
|
||
|
||
1. Tier selection is `INFER_EVERY` in `/etc/move-detect/edge.env` (per device):
|
||
`1` = T1 (every tick, the default), `2`–`3` = T2. On a skipped tick the frame is still read
|
||
and discarded — the camera buffer stays current — but no pose runs and nothing enters the
|
||
evidence ring, so no unblurred frame can reach a snapshot.
|
||
**T3 (motion-gated inference) is deliberately NOT implemented.** A motion gate that misjudges
|
||
would stop inferring on a *motionless fallen worker* — the exact case this product exists to
|
||
catch — and its threshold cannot be tuned without real footage. Decide it during bring-up
|
||
with measured video, not in advance.
|
||
2. Run each tier with realistic camera setup (e.g., T1 on main fall cam, T2 on perimeter).
|
||
3. Log FPS, E2E latency (frame capture → alert), CPU, Hailo temp for 30 min per tier.
|
||
4. Confirm T1 ≥ 12 fps for the target scene (well-lit, medium activity).
|
||
|
||
---
|
||
|
||
## 4. Multi-Camera Stability (Long-run Test)
|
||
|
||
Validate sustained operation and resource cleanup:
|
||
|
||
1. **Setup:**
|
||
- 1 CSI camera (picamera2 via PiCameraSource) + 2–4 USB UVC cameras (UvcSource).
|
||
- Route USB cameras to 2 separate USB3 controllers (Pi 5 has 2 controllers; check with `lsusb -t`).
|
||
|
||
2. **Test duration:** 4–8 hours continuous frame capture and optional Hailo inference.
|
||
|
||
3. **Monitor:**
|
||
- FPS stability (no drift or sudden drops).
|
||
- Memory usage (RSS, no unbounded growth; confirm video buffer cleanup).
|
||
- CPU distribution (no single core maxed; picamera2 process isolation working).
|
||
- Hailo temperature (sustained < 75°C).
|
||
- Error logs (no hang or exception cascades).
|
||
|
||
4. **Pass criterion:** No crashes, memory leaks, or FPS degradation after 4 hours.
|
||
|
||
---
|
||
|
||
## 5. Reflect Results in Thresholds & Camera Roles
|
||
|
||
After benchmark completion, update configuration:
|
||
|
||
Every knob below lives in `/etc/move-detect/edge.env` (read by `edge/config.py::load_config`),
|
||
**not** in a code file — no redeploy is needed to retune a site. All are validated at startup:
|
||
a non-finite or non-positive value refuses to boot rather than silently disabling a detector.
|
||
The full list is in `docs/pilot-runbook.md` §3.
|
||
|
||
1. **Detector thresholds:**
|
||
- `MOTION_THR` (default 0.25) — per-second stillness threshold shared by fall no-motion and
|
||
immobility. This is the primary re-tuning knob on real video: it is frame-rate normalised,
|
||
so a device running at a different measured fps needs it re-checked.
|
||
- `FALL_PRONE_S` (3.0) / `FALL_NO_MOTION_S` (10.0) — how long prone before a fall confirms,
|
||
and before it escalates to the 위급 no-motion event. Raise if real footage over-triggers.
|
||
- `IMMOBILE_S` (120.0), `GHOST_REAPPEAR_S` (10.0), `ZONE_DEBOUNCE_S` (0.75), `COOLDOWN_S` (30.0).
|
||
|
||
2. **Per-camera load:**
|
||
- `INFER_EVERY` (1) — 1 for the fall-critical camera, 2–3 for context cameras that missed
|
||
the T1 target. Set per device; each camera on a device shares it today.
|
||
- `TARGET_FPS` (12) — loop pacing. It only slows the loop down; it cannot buy throughput.
|
||
|
||
3. **Benchmark report:**
|
||
- Document actual Pi hardware, kernel version, HailoRT version, Hailo FW version.
|
||
- Archive FPS/latency/temp results.
|
||
- Flag any anomalies (thermal throttling, USB contention, CPU saturation).
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
**PCIe speed not Gen3:**
|
||
- Confirm cable seating and power to Hailo.
|
||
- Check firmware update: `sudo rpi-update` (experimental; use with caution).
|
||
|
||
**HEF FPS < target:**
|
||
- Verify HailoRT version matches Hailo FW.
|
||
- Check for CPU threads contention (picamera2 process + Python inference + OS).
|
||
- Monitor Hailo die temperature; throttle if > 85°C.
|
||
|
||
**Memory leak in USB capture:**
|
||
- Ensure OpenCV's `VideoCapture.release()` is called on cleanup.
|
||
- Check `v4l2-ctl -D` for buffer count and memory mapping.
|
||
|
||
**USB controller contention:**
|
||
- Separate high-bandwidth cameras (USB3 MJPEG) across 2 controllers.
|
||
- Use `lsusb -t` and `/sys/bus/usb/devices/` to map.
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- [hailo-rpi5-examples](https://github.com/hailo-ai/hailo-rpi5-examples) — Hailo + Pi 5 reference (MIT).
|
||
- [picamera2 docs](https://datasheets.raspberrypi.com/camera/picamera2-manual.pdf).
|
||
- [Raspberry Pi 5 datasheet](https://datasheets.raspberrypi.com/rpi5/raspberry-pi-5-datasheet.pdf).
|