# ZenAI KTC Counter — Edge Deployment Guide Production deployment for **RK3588** (or compatible RKNN NPU) edge devices running a **zone-based sack feeder counter** (1…N named zones): | Component | Script | systemd unit | |-----------|--------|--------------| | RTSP counter (RKNN + ByteTrack + zones) | `counter_live_rknn.py` | `zenai-ktc-counter.service` | | Web dashboard (Flask) | `counter_dashboard.py` | `zenai-ktc-dashboard.service` | Both processes share a single `.env` file and read/write the same SQLite database and state JSON. **Counting model:** `ZONE_COUNT` rectangular zones (`zone_1`…`zone_N`). A sack is counted once after its centroid **dwells** inside a zone long enough (`ZONE_i_DWELL_SEC`). --- ## 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-ktc-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-ktc-counter/ # persistent runtime data (created automatically) ├── counter.db # SQLite daily records ├── current_counter.json # live counting-day state ├── snapshots/ # zone-entry/detect JPEGs (if enabled) └── crossings.csv # optional per-event CSV /opt/models/ # RKNN models (deploy separately) /dev/shm/zenai-ktc-counter/ # live JPEG frame for dashboard video (tmpfs) ``` --- ## 3. Install application ### 3.1 Copy code to the device ```bash sudo mkdir -p /opt/zenai-ktc-python sudo rsync -av --exclude venv --exclude .env --exclude __pycache__ \ ./ /opt/zenai-ktc-python/ # Or: sudo git clone /opt/zenai-ktc-python ``` ### 3.2 Create virtual environment and install dependencies ```bash cd /opt/zenai-ktc-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-ktc-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`) | | `ZONE_COUNT` / `ZONE_i_*` | Named zone rectangles, dwell, cooldown (tune per camera) | | `SECRET_KEY` | Random string for Flask sessions | Ensure `STATE_FILE` and `DB_PATH` both live under `/opt/zenai-ktc-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-ktc-counter /opt/models /dev/shm/zenai-ktc-counter ``` --- ## 4. Install systemd services ```bash cd /opt/zenai-ktc-python sudo cp zenai-ktc-counter.service zenai-ktc-dashboard.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable zenai-ktc-counter zenai-ktc-dashboard sudo systemctl start zenai-ktc-counter sudo systemctl start zenai-ktc-dashboard ``` The dashboard unit starts **after** the counter unit (`After=zenai-ktc-counter.service`). ### Verify ```bash systemctl status zenai-ktc-counter systemctl status zenai-ktc-dashboard journalctl -u zenai-ktc-counter -f ``` Open the dashboard in a browser: ```text http://:5000 ``` (Port is set by `DASHBOARD_PORT` in `.env`, default `5000`.) --- ## 5. Tuning counting zones Zones are axis-aligned rectangles. Default is a single zone: ```ini ZONE_COUNT=1 ZONE_1_NAME=feeder ZONE_1_X1_FRAC=0.10 ZONE_1_Y1_FRAC=0.10 ZONE_1_X2_FRAC=0.90 ZONE_1_Y2_FRAC=1.00 ZONE_1_COOLDOWN_SEC=15 ZONE_1_DWELL_SEC=2 ``` For pixel-perfect placement, set absolute coordinates instead (they override fractions): ```ini ZONE_1_X1=40 ZONE_1_Y1=120 ZONE_1_X2=420 ZONE_1_Y2=900 ``` Enable `DEBUG_TRACKING=true` temporarily for verbose inherit / dup / cooldown logs. Each successful count always prints a Frigate-style line to the journal: ```text Total karung di kandang_bawah_feeder: 5 ``` Set `ZONE_i_NAME` (and `OBJECT_LABEL`) in `.env` to match your site. --- ## 6. Operations ### Restart after config change ```bash sudo systemctl restart zenai-ktc-counter sudo systemctl restart zenai-ktc-dashboard ``` ### View logs ```bash journalctl -u zenai-ktc-counter -n 100 --no-pager journalctl -u zenai-ktc-dashboard -n 100 --no-pager ``` ### Stop services ```bash sudo systemctl stop zenai-ktc-dashboard zenai-ktc-counter ``` The counter handles `SIGTERM` gracefully — it finishes the current frame, persists state to SQLite, then exits. ### Update application code ```bash cd /opt/zenai-ktc-python sudo systemctl stop zenai-ktc-dashboard zenai-ktc-counter # rsync or git pull new code sudo ./venv/bin/pip install -r requirements.txt # if dependencies changed sudo systemctl start zenai-ktc-counter zenai-ktc-dashboard ``` --- ## 7. Troubleshooting | Symptom | Things to check | |---------|-----------------| | Counter won't start | `journalctl -u zenai-ktc-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 | | Wrong zone / missed counts | Tune `ZONE_i_*_FRAC`, `CONF`, ByteTrack thresholds; enable `DEBUG_TRACKING=true` | | Service keeps restarting | `journalctl -u zenai-ktc-counter -e`; often missing model, bad RTSP URL, or venv not created | ### Manual test (without systemd) ```bash cd /opt/zenai-ktc-python source venv/bin/activate python counter_live_rknn.py # terminal 1 python counter_dashboard.py # terminal 2 ``` --- ## 8. 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; } } ``` --- ## 9. 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-ktc-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.