# ZenAI KPC Counter — Edge Deployment Guide Production deployment for **RK3588** (or compatible RKNN NPU) edge devices running: | Component | Script | systemd unit | |-----------|--------|--------------| | RTSP counter (RKNN + ByteTrack) | `counter_live_rknn.py` | `zenai-kpc-counter.service` | | Web dashboard (Flask) | `counter_dashboard.py` | `zenai-kpc-dashboard.service` | Both processes share a single `.env` file and read/write the same SQLite database and state JSON. --- ## 1. Prerequisites ### Hardware & OS - RK3588 board (or Jetson/RK device with RKNN Lite runtime) - Linux with systemd - Network access to the RTSP camera stream ### System packages ```bash sudo apt update sudo apt install -y python3 python3-venv python3-pip ffmpeg libgl1 ``` `ffmpeg` is required for low-latency RTSP capture via OpenCV. `libgl1` is often needed for `opencv-python` on headless systems. ### RKNN model Export or copy your `.rknn` model to the device, e.g.: ```text /opt/models/your_model.rknn ``` Set `MODEL_PATH` in `.env` to match. The model class count must match `NUM_CLASSES`, and `OBJECT_CLASS_ID` must point at the class you count. --- ## 2. Directory layout Default paths used by the service files and `env.example`: ```text /opt/zenai-kpc-python/ # application code (this repo) ├── counter_live_rknn.py ├── counter_dashboard.py ├── counter_store.py ├── templates/ ├── venv/ # Python virtual environment (created during install) ├── .env # runtime config (not in git) ├── env.example # template — copy to .env └── DEPLOY.md /opt/zenai-kpc-counter/ # persistent runtime data (created automatically) ├── counter.db # SQLite daily records ├── current_counter.json # live counting-day state ├── snapshots/ # crossing/detect JPEGs (if enabled) └── crossings.csv # optional per-event CSV /opt/models/ # RKNN models (deploy separately) /dev/shm/zenai-kpc-counter/ # live JPEG frame for dashboard video (tmpfs) ``` --- ## 3. Install application ### 3.1 Copy code to the device ```bash sudo mkdir -p /opt/zenai-kpc-python sudo rsync -av --exclude venv --exclude .env --exclude __pycache__ \ ./ /opt/zenai-kpc-python/ # Or: sudo git clone /opt/zenai-kpc-python ``` ### 3.2 Create virtual environment and install dependencies ```bash cd /opt/zenai-kpc-python sudo python3 -m venv venv sudo ./venv/bin/pip install --upgrade pip sudo ./venv/bin/pip install -r requirements.txt ``` > `rknn-toolkit-lite2` is platform-specific. Install on the target ARM device, not on a Windows dev machine. ### 3.3 Create runtime config ```bash cd /opt/zenai-kpc-python sudo cp env.example .env sudo nano .env ``` **Minimum values to edit before starting:** | Variable | Description | |----------|-------------| | `SOURCE` | RTSP URL or local video file path | | `MODEL_PATH` | Path to your `.rknn` model on device | | `NUM_CLASSES` | Must match the exported model | | `OBJECT_CLASS_ID` | Class index of the object being counted | | `CLASS_OBJECT` / `OBJECT_LABEL` | Labels stored in DB (e.g. `karung`) | | `LINE_Y1_FRAC` / `LINE_Y2_FRAC` | Counting line positions (tune per camera) | | `SECRET_KEY` | Random string for Flask sessions | Ensure `STATE_FILE` and `DB_PATH` both live under `/opt/zenai-kpc-counter/` so data survives reboots (avoid `/tmp` in production). ### 3.4 Create data directories (optional — app creates most paths automatically) ```bash sudo mkdir -p /opt/zenai-kpc-counter /opt/models /dev/shm/zenai-kpc-counter ``` --- ## 4. Install systemd services ```bash cd /opt/zenai-kpc-python sudo cp zenai-kpc-counter.service zenai-kpc-dashboard.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable zenai-kpc-counter zenai-kpc-dashboard sudo systemctl start zenai-kpc-counter sudo systemctl start zenai-kpc-dashboard ``` The dashboard unit starts **after** the counter unit (`After=zenai-kpc-counter.service`). ### Verify ```bash systemctl status zenai-kpc-counter systemctl status zenai-kpc-dashboard journalctl -u zenai-kpc-counter -f ``` Open the dashboard in a browser: ```text http://:5000 ``` (Port is set by `DASHBOARD_PORT` in `.env`, default `5000`.) --- ## 5. Operations ### Restart after config change ```bash sudo systemctl restart zenai-kpc-counter sudo systemctl restart zenai-kpc-dashboard ``` ### View logs ```bash journalctl -u zenai-kpc-counter -n 100 --no-pager journalctl -u zenai-kpc-dashboard -n 100 --no-pager ``` ### Stop services ```bash sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter ``` The counter handles `SIGTERM` gracefully — it finishes the current frame, persists state to SQLite, then exits. ### Update application code ```bash cd /opt/zenai-kpc-python sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter # rsync or git pull new code sudo ./venv/bin/pip install -r requirements.txt # if dependencies changed sudo systemctl start zenai-kpc-counter zenai-kpc-dashboard ``` --- ## 6. Troubleshooting | Symptom | Things to check | |---------|-----------------| | Counter won't start | `journalctl -u zenai-kpc-counter`; verify `MODEL_PATH` exists; RKNN drivers installed | | No RTSP frames | Ping camera; test with `ffplay `; check `OPENCV_FFMPEG_CAPTURE_OPTIONS` | | Dashboard shows 0 count | `STATE_FILE` in `.env` must match between counter and dashboard; check file exists | | Live video blank | `LIVE_STREAM_ENABLED=true`; path matches `LIVE_STREAM_FRAME_PATH` in both processes; if using nginx, set `proxy_read_timeout` (see §7) | | Wrong counts | Tune `LINE_Y1_FRAC`/`LINE_Y2_FRAC`, `CONF`, ByteTrack thresholds; enable `DEBUG_TRACKING=true` temporarily | | Service keeps restarting | `journalctl -u zenai-kpc-counter -e`; often missing model, bad RTSP URL, or venv not created | ### Manual test (without systemd) ```bash cd /opt/zenai-kpc-python source venv/bin/activate python counter_live_rknn.py # terminal 1 python counter_dashboard.py # terminal 2 ``` --- ## 7. Optional: reverse proxy For HTTPS or port 80 access, put nginx in front of the dashboard: ```nginx server { listen 80; server_name counter.example.com; location / { proxy_pass http://127.0.0.1:5000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_buffering off; # required for /api/live-video MJPEG stream proxy_read_timeout 3600s; # keep long-lived MJPEG connections open proxy_send_timeout 3600s; } } ``` The live JPEG at `LIVE_STREAM_FRAME_PATH` can also be served statically by nginx if you prefer not to use the Flask MJPEG endpoint. --- ## 8. Security notes - Change `SECRET_KEY` from the default before exposing the dashboard on a network. - Services currently run as `root` for simplicity on edge devices. For hardened deployments, create a dedicated user, chown `/opt/zenai-kpc-counter`, and update the `User=` / `Group=` lines in the service files. - Do not commit `.env` — it may contain RTSP credentials. - Set `FLASK_DEBUG=false` in production.