move_detect/deploy
유창욱 a2d2d24793 build: name the two delivery folders and stop archiving by default
Delivery had two ambiguously-named outputs and always produced an
archive, which made it unclear what actually gets uploaded/copied.

- dist/1_cafe24_upload/  -> FTP-upload contents to the Cafe24 web host
- dist/2_pi_install/     -> copy folder to the Pi, run ./install_offline.sh

Archives are now opt-in: MAKE_ZIP=1 (Cafe24) / MAKE_TARBALL=1 (Pi).
Docs and tests updated to the new paths.

Tests: 165 pass, 2 skipped.
2026-07-20 12:58:44 +09:00
..
bundle build: name the two delivery folders and stop archiving by default 2026-07-20 12:58:44 +09:00
systemd refactor: rename cloud/ to server/ (cafe24 is web hosting, not cloud) 2026-07-20 11:30:52 +09:00
README.md build: name the two delivery folders and stop archiving by default 2026-07-20 12:58:44 +09:00
setup_online.sh refactor: rename cloud/ to server/ (cafe24 is web hosting, not cloud) 2026-07-20 11:30:52 +09:00

Edge deployment (Raspberry Pi 5, air-gapped / 폐쇄망)

Offline-installable bundle + systemd unit for the move-detect edge daemon. The bundle is fully self-contained (all wheels + pose model + source): the Pi performs zero network fetches at install or run time. The only runtime network use is the server POST / config-poll / heartbeat, and even that tolerates being offline (events buffer in the local sqlite queue and flush on reconnect).

Scope: this covers the CPU MoveNet + USB (UVC) webcam path — the one the daemon wires today (edge/daemon.py:main) and the one verified end-to-end. Hailo-8 acceleration (edge/adapters/hailo_pose.py) and the CSI camera (edge/adapters/picamera_source.py) are separate device-validated paths — see docs/hardware-bringup.md.

Files

File Runs on Purpose
bundle/build_offline_bundle.sh build machine (has internet) downloads wheels + model, stages source, emits one tarball
bundle/install_offline.sh the Pi (no internet) venv + --no-index install, systemd unit, service account
systemd/move-detect-edge.service the Pi the daemon unit (auto-restart, device access, hardening)
systemd/move-detect-edge.env.example the Pi env template → /etc/move-detect/edge.env

Quick start — Pi with internet (one command)

If the Pi itself has internet, skip the offline bundle entirely. Copy this repo onto the Pi (git clone or scp), then:

sudo deploy/setup_online.sh

It installs the system packages (python3-picamera2, and hailo-all if present), builds the venv, pip-installs the runtime deps, fetches the MoveNet model, writes /etc/move-detect/edge.env (prompting for DEVICE_ID / DEVICE_SECRET / CAFE24_URL / CAMERAS, or take them from env vars), installs the systemd unit, and starts the service. It begins on CPU MoveNet; drop a yolov8s_pose.hef in and pass HAILO_HEF=... (or edit edge.env) to switch to Hailo. The rest of this doc is the air-gapped path.


1. Build the bundle (on a machine WITH internet)

Most reliable: build on an aarch64 host (a Pi 5, or an arm64 CI runner) so the downloaded wheels match the target exactly:

deploy/bundle/build_offline_bundle.sh            # native aarch64 wheels

From an x86 box you can attempt a cross build (best-effort — onnxruntime/opencv wheel tags may not resolve; if it fails, build natively):

deploy/bundle/build_offline_bundle.sh --cross    # target aarch64 / cp311

Output: the folder dist/2_pi_install/ (+ MANIFEST.txt listing the bundled wheels). Copy the WHOLE FOLDER across the air gap as-is — no archive step. Set MAKE_TARBALL=1 if you would rather transfer one dist/2_pi_install-aarch64-cp311-hailo8.tar.gz file.

2. Transfer + install (on the air-gapped Pi)

Copy dist/2_pi_install/ across the gap (USB, one-way transfer, etc.), then:

# tarball variant only (MAKE_TARBALL=1): tar xzf 2_pi_install-aarch64-cp311-hailo8.tar.gz
cd 2_pi_install
sudo ./install_offline.sh

The installer: creates the movedetect service account (in video/render/ plugdev/hailo groups), lays down source + model under /opt/move-detect, builds a venv and installs the bundled wheels with --no-index, verifies the package imports, seeds /etc/move-detect/edge.env, and installs the systemd unit.

3. Configure + start

sudo nano /etc/move-detect/edge.env      # DEVICE_ID, DEVICE_SECRET, CAFE24_URL, ...
sudo systemctl enable --now move-detect-edge
journalctl -u move-detect-edge -f

DEVICE_ID/DEVICE_SECRET must match a devices row in the server (see server/docs/server-deploy.md §6). MOVENET_ONNX should point at the bundled model (/opt/move-detect/models/movenet_lightning.onnx).

4. Verify the air gap (do this before trusting a build)

  • On a host with networking disabled, install_offline.sh completes — the --no-index installs prove nothing is fetched. If pip tries to reach PyPI, a wheel is missing from the bundle: rebuild (step 1) natively.
  • Step [4/6] of the installer prints edge import OK (onnxruntime + cv2 + numpy + requests all import from the venv).
  • systemctl status move-detect-edge is active (running); the log shows frames processed and (if the server is reachable) heartbeats.
  • Pull the network cable → events keep queueing (QUEUE_DB grows) → restore → the queue flushes. (End-to-end behavior verified in the edge e2e harness.)

Hailo-8 HAT + CSI camera path (optional, faster)

The default bundle runs CPU MoveNet + USB webcam. To use the Hailo-8 accelerator and/or a CSI camera, add these SYSTEM packages on the Pi (they are apt packages, not pip wheels — the venv sees them because it is created with --system-site-packages):

sudo apt install -y hailo-all python3-picamera2 python3-opencv
  • hailo-all — HailoRT runtime + driver + the Python hailo_platform bindings.
  • python3-picamera2 — CSI camera stack (libcamera).

Then place the pose model and switch backends in /etc/move-detect/edge.env:

POSE_BACKEND=hailo
HAILO_HEF=/opt/move-detect/models/yolov8s_pose.hef   # copy the compiled HEF here
CAMERA_BACKEND=csi
CSI_DEVICE=0

Validate on device per docs/hardware-bringup.md before trusting output (the Hailo decode is unit-locked, but the HailoRT inference + picamera2 color order are hardware- validated). USB webcam stays available anytime via CAMERA_BACKEND=uvc.

Multiple cameras at once. Set CAMERAS=backend:device:camera_id,... to run several cameras in one daemon — each gets its own detection pipeline and camera_id, they share the Hailo device (round-robin) and the offline queue, and server zones are routed to the matching camera. Example — CSI + USB together:

CAMERAS=csi:0:cam-01,uvc:0:cam-02

Each camera_id must match the camera_id on that camera's zones in the server.

Air gap: hailo-all / python3-picamera2 are the ONE piece this bundle can't carry (Hailo's own driver + apt tree). Pre-install them on the Pi image, or transfer their .debs separately, before running install_offline.sh. The pose HEF is likewise supplied out-of-band (compiled from the Hailo Model Zoo).

Not bundled (by design)

  • MySQL / the server app — a separate host; see server/docs/server-deploy.md.
  • HailoRT + yolov8s_pose.hef + picamera2 — Hailo's runtime/driver, the compiled HEF, and the CSI camera stack are Pi system packages installed out-of-band (above); the default CPU MoveNet + USB path needs none of them.