8.3 KiB
ZenAI KPC Counter — Edge Deployment Guide
Production deployment for RK3588 (or compatible RKNN NPU) edge devices running:
| Component | Script | systemd unit |
|---|---|---|
| Status webhook (IN/OUT/OFF gate) | status_webhook.py |
zenai-kpc-status-webhook.service |
| RTSP counter (RKNN + ByteTrack) | counter_live_rknn.py |
zenai-kpc-counter.service |
| Web dashboard (Flask) | counter_dashboard.py |
zenai-kpc-dashboard.service |
All three processes share a single .env file. The counter and dashboard read/write the same SQLite database and state JSON. The webhook writes status_webhook_state.json, which the counter polls when STATUS_WEBHOOK_ENABLED=true.
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
sudo apt update
sudo apt install -y python3 python3-venv python3-pip ffmpeg libgl1 openssl
ffmpeg is required for low-latency RTSP capture via OpenCV and for session MP4 recording (RECORD_VIDEO=true). libgl1 is often needed for opencv-python on headless systems.
RKNN model
Export or copy your .rknn model to the device, e.g.:
/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:
/opt/zenai-kpc-python/ # application code (this repo)
├── counter_live_rknn.py
├── counter_dashboard.py
├── counter_store.py
├── status_webhook.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
sudo mkdir -p /opt/zenai-kpc-python
sudo rsync -av --exclude venv --exclude .env --exclude __pycache__ \
./ /opt/zenai-kpc-python/
# Or: sudo git clone <repo-url> /opt/zenai-kpc-python
3.2 Create virtual environment and install dependencies
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-lite2is platform-specific. Install on the target ARM device, not on a Windows dev machine.
3.3 Create runtime config
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)
sudo mkdir -p /opt/zenai-kpc-counter /opt/models /dev/shm/zenai-kpc-counter
4. Install systemd services
cd /opt/zenai-kpc-python
sudo cp zenai-kpc-status-webhook.service zenai-kpc-counter.service zenai-kpc-dashboard.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable zenai-kpc-status-webhook zenai-kpc-counter zenai-kpc-dashboard
sudo systemctl start zenai-kpc-status-webhook
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). Enable the webhook so it comes up on boot with the other two.
Verify
systemctl status zenai-kpc-status-webhook
systemctl status zenai-kpc-counter
systemctl status zenai-kpc-dashboard
journalctl -u zenai-kpc-status-webhook -f
Open the dashboard in a browser:
http://<device-ip>:5000
(Port is set by DASHBOARD_PORT in .env, default 5000.)
Status webhook endpoints (fixed ports):
http://<device-ip>:8002/?lokasi=IN|OUT&status=ON|OFF
https://<device-ip>:8443/?lokasi=IN|OUT&status=ON|OFF
http://<device-ip>:8002/status
5. Operations
Restart after config change
sudo systemctl restart zenai-kpc-status-webhook
sudo systemctl restart zenai-kpc-counter
sudo systemctl restart zenai-kpc-dashboard
View logs
journalctl -u zenai-kpc-status-webhook -n 100 --no-pager
journalctl -u zenai-kpc-counter -n 100 --no-pager
journalctl -u zenai-kpc-dashboard -n 100 --no-pager
Stop services
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter zenai-kpc-status-webhook
The counter handles SIGTERM gracefully — it finishes the current frame, persists state to SQLite, then exits.
Update application code
cd /opt/zenai-kpc-python
sudo systemctl stop zenai-kpc-dashboard zenai-kpc-counter zenai-kpc-status-webhook
# rsync or git pull new code
sudo ./venv/bin/pip install -r requirements.txt # if dependencies changed
sudo systemctl start zenai-kpc-status-webhook 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 |
| Status webhook won't start | journalctl -u zenai-kpc-status-webhook; ports 8002/8443 free; openssl installed if certs are missing |
| No RTSP frames | Ping camera; test with ffplay <SOURCE>; check OPENCV_FFMPEG_CAPTURE_OPTIONS |
| Dashboard shows 0 count | STATE_FILE in .env must match between counter and dashboard; check file exists |
| Counting stays OFF | Check STATUS_WEBHOOK_ENABLED and GET http://<device-ip>:8002/status; STATUS_WEBHOOK_FILE must match the webhook state file |
| 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)
cd /opt/zenai-kpc-python
source venv/bin/activate
python status_webhook.py # terminal 1
python counter_live_rknn.py # terminal 2
python counter_dashboard.py # terminal 3
7. Optional: reverse proxy
For HTTPS or port 80 access, put nginx in front of the dashboard:
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_KEYfrom the default before exposing the dashboard on a network. - Services currently run as
rootfor simplicity on edge devices. For hardened deployments, create a dedicated user, chown/opt/zenai-kpc-counter, and update theUser=/Group=lines in the service files. - Do not commit
.env— it may contain RTSP credentials. - Set
FLASK_DEBUG=falsein production.