Files
vehicle-counter/README.md
Joachim Hummel 144582f866 README: Abschnitt zum Dauerbetrieb mit supervisord (24/7)
Beispiel-Config + reread/update/restart/status/Logs, mit Hinweis auf
directory= (.env/counting_line) und Restart-Bedarf nach Code-Updates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 18:09:38 +02:00

310 lines
13 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fahrzeug- und Objekt-Erkennungssystem
![Vehicle Counter Smart Traffic Monitoring mit ESP32-CAM](pictures/vehicle-counter.png)
[![Video Demo](https://img.youtube.com/vi/j94-OHGflT8/maxresdefault.jpg)](https://www.youtube.com/watch?v=j94-OHGflT8)
Eine webbasierte Anwendung zur Echtzeit-Objekterkennung und -Verfolgung mittels YOLOv11. Die Anwendung unterstützt sowohl Live-Webcam-Streams als auch die Verarbeitung hochgeladener Videodateien.
## Funktionen
- **Webcam-Erkennung**: Echtzeit-Objekterkennung über die Webcam
- **Video-Upload**: Hochladen und Verarbeiten von Videodateien mit Objekterkennung
- **Objekt-Tracking**: Persistente Verfolgung von Objekten mit eindeutigen IDs über Frames hinweg
- **Visuelle Markierungen**: Bounding Boxes und Labels für erkannte Objekte
- **Browser-basiert**: Einfacher Zugriff über den Webbrowser
## Voraussetzungen
- Python 3.12 oder höher
- **NVIDIA-Grafikkarte mit CUDA-Unterstützung** für die Echtzeit-Erkennung erforderlich (siehe [Hardware & CUDA](#hardware--cuda))
- **[ESP32-CAM](https://mygopage.de/s/xkKwJF)** als Live-Quelle, die einen MJPEG-Stream im Netzwerk bereitstellt (siehe [Kamera / Livestream (ESP32-CAM)](#kamera--livestream-esp32-cam))
- **MQTT-Broker** (*optional*) zum Empfang der Zähl-Events (z. B. [Mosquitto](https://mosquitto.org/)); per `MQTT_ENABLED=false` abschaltbar (siehe [MQTT / Zähl-Events](#mqtt--zähl-events))
- Die YOLO-Modelle werden beim ersten Start automatisch von Ultralytics geladen (Internetverbindung erforderlich)
> ⚠️ **Wichtig:** Für die flüssige Live-Erkennung wird eine **NVIDIA-GPU mit CUDA** benötigt. Ohne CUDA läuft die Inferenz auf der CPU und ist für Echtzeit-Streams zu langsam.
## Installation
1. Repository klonen oder herunterladen
2. Erforderliche Python-Pakete installieren:
```bash
pip3 install flask opencv-python numpy ultralytics requests paho-mqtt python-dotenv
```
3. **CUDA-fähiges PyTorch installieren** (für GPU-Beschleunigung). Die passende
Variante richtet sich nach der installierten CUDA-Version, z. B. für CUDA 12.x:
```bash
pip3 install torch --index-url https://download.pytorch.org/whl/cu121
```
Prüfen, ob die GPU erkannt wird:
```bash
python3 -c "import torch; print(torch.cuda.is_available())"
```
Gibt der Befehl `True` aus, wird die NVIDIA-GPU genutzt und FP16-Inferenz
automatisch aktiviert.
4. Die YOLO-Modelle (`yolo11s.pt` für Video-Upload, `yolo11n.pt` für die Webcam)
müssen **nicht** manuell beschafft werden Ultralytics lädt sie beim ersten
Start automatisch herunter (Internetverbindung erforderlich) und legt sie im
Hauptverzeichnis ab.
## Verwendung
### Anwendung starten
```bash
python3 app.py
```
Die Anwendung ist dann unter `http://localhost:8080` erreichbar.
### Dauerbetrieb mit supervisord (24/7)
Für den Dauerbetrieb (automatischer Start, Neustart bei Absturz) eignet sich
[supervisord](http://supervisord.org/). Beispiel-Konfiguration unter
`/etc/supervisor/conf.d/vehicle-counter.conf`:
```ini
[program:vehicle-counter]
directory=/pfad/zum/vehicle-counter
command=/pfad/zur/python app.py
user=DEIN_USER
autostart=true
autorestart=true
startsecs=5
stopwaitsecs=10
stdout_logfile=/var/log/vehicle-counter.log
stderr_logfile=/var/log/vehicle-counter-error.log
stdout_logfile_maxbytes=20MB
stderr_logfile_backups=5
environment=PYTHONUNBUFFERED="1"
```
> ⚠️ **`directory=` muss auf das Projektverzeichnis zeigen!** Sonst findet die App
> die `.env` (Kamera/MQTT) und `counting_line.json` nicht und fällt auf Defaults
> zurück.
Aktivieren und steuern:
```bash
sudo supervisorctl reread # neue Config einlesen
sudo supervisorctl update # Programm hinzufügen/starten
sudo supervisorctl restart vehicle-counter # nach Code-Updates neu starten
sudo supervisorctl status vehicle-counter # Status prüfen
tail -f /var/log/vehicle-counter.log # Logs ansehen
```
Nach einem `git pull` mit Code-Änderungen ist ein `restart` nötig; reine
Template-/Frontend-Änderungen werden dank `TEMPLATES_AUTO_RELOAD` auch ohne
Neustart übernommen.
### Konfiguration (.env)
Kamera-, MQTT- und Inferenz-Einstellungen werden über Umgebungsvariablen
gesteuert. Am einfachsten kopierst du die mitgelieferte Vorlage und passt die
Werte an die `.env` wird beim Start **automatisch geladen** (`python-dotenv`):
```bash
cp .env.example .env
# anschließend .env im Editor öffnen und die Werte anpassen
```
Die wichtigsten Einstellungen:
```bash
export CAMERA_URL="http://CAMERA-IP:81/stream" # MJPEG-Stream der Kamera
export MQTT_ENABLED="true" # "false" = ohne Broker starten
export MQTT_HOST="127.0.0.1" # Adresse des MQTT-Brokers
export MQTT_PORT=1883
export MQTT_TOPIC="vehiclecounter/meine-kamera" # Basis-Topic der Events
export CAMERA_ID="meine-kamera" # Kennung in den Events
```
Eine vollständige, kommentierte Liste aller Variablen steht in
[`.env.example`](.env.example).
> 💡 **Ohne MQTT-Broker?** Setze `MQTT_ENABLED="false"` dann startet die App
> ganz normal, zählt und streamt, sendet aber keine MQTT-Events. Ideal zum
> Ausprobieren ohne Broker-Setup. (Auch mit `true` startet die App übrigens,
> wenn der Broker gerade nicht erreichbar ist sie verbindet sich dann später.)
> ⚠️ Beim Betrieb über einen Prozess-Manager (z. B. **supervisord**) muss das
> Arbeitsverzeichnis auf das Projekt zeigen (`directory=/pfad/zum/projekt`),
> sonst wird die `.env` nicht gefunden und es greifen die Defaults.
> Die `.env` ist per `.gitignore` ausgeschlossen und gehört nicht ins Repo.
### Webcam-Erkennung
1. Öffnen Sie `http://localhost:8080` im Browser
2. Klicken Sie auf "Start Webcam Detection"
3. Die Webcam wird aktiviert und Objekte werden in Echtzeit erkannt und markiert
4. Jedes Objekt erhält eine Track-ID und Klassenbeschriftung
### Video-Upload
1. Öffnen Sie `http://localhost:8080` im Browser
2. Wählen Sie eine Videodatei über das Upload-Formular aus
3. Klicken Sie auf "Upload Video"
4. Das Video wird verarbeitet und mit Objekterkennungen angezeigt
## Projektstruktur
```
.
├── app.py # Haupt-Flask-Anwendung
├── yolo11s.pt # YOLOv11-Modell (wird autom. geladen, nicht versioniert)
├── yolo11n.pt # YOLOv11-Nano-Modell für Webcam (autom. geladen)
├── templates/ # HTML-Templates
│ ├── index.html # Startseite
│ ├── webcam.html # Webcam-Anzeige
│ └── play_video.html # Video-Wiedergabe
├── uploads/ # Hochgeladene Videos (automatisch erstellt)
└── highway1.mp4 # Beispielvideo
```
## Technische Details
### Kamera / Livestream (ESP32-CAM)
In diesem Beispiel dient eine **ESP32-CAM** als Live-Quelle. Sie stellt einen
**MJPEG-Stream** im Netzwerk bereit, den die Anwendung abgreift, mit YOLO
auswertet und an die Browser-Viewer weiterverteilt.
**📷 Hardware-Empfehlung (Outdoor-Setup):**
| Komponente | Zweck |
| ---------- | ----- |
| [ESP32-CAM](https://mygopage.de/s/xkKwJF) | Die Kamera selbst günstig (~510 €), bei Diebstahl/Defekt kein großer Verlust |
| [Regenwasserdichte Box](https://mygopage.de/s/brkpeY) | Wetterschutz für den Außeneinsatz Kamera **und** Powerbank passen hinein |
| [Powerbank](https://mygopage.de/s/2S7hhr) | Günstige Stromversorgung, macht die Kamera netzunabhängig |
> *Affiliate-Links beim Kauf darüber unterstützt du das Projekt ohne Mehrkosten für dich.*
- Die Stream-Adresse wird über die Umgebungsvariable `CAMERA_URL` gesetzt.
Typisch für die ESP32-CAM sind Port `81` und der Pfad `/stream` `CAMERA-IP`
durch die IP-Adresse der eigenen Kamera ersetzen:
```bash
export CAMERA_URL="http://CAMERA-IP:81/stream"
```
- Es wird **eine einzige Verbindung** zur ESP32-CAM aufgebaut und das Bild an
beliebig viele Zuschauer verteilt (Fan-out). So wird der begrenzte
Stream-Slot der ESP32-CAM nicht durch jeden Browser blockiert.
- Mit `GRABBER_ALWAYS_ON=1` läuft der Grabber rund um die Uhr und zählt auch
dann, wenn niemand zuschaut. Bei `0` (Standard) verbindet er sich nur, solange
ein Browser den Stream betrachtet das gibt den ESP32-Slot wieder frei.
> 💡 Statt einer ESP32-CAM kann über `CAMERA_URL` auch jede andere MJPEG-/HTTP-
> Stream-Quelle eingebunden werden.
### MQTT / Zähl-Events
Jede erkannte Linienüberquerung wird als **MQTT-Event** veröffentlicht. Dafür
wird ein **MQTT-Broker** benötigt (z. B. [Mosquitto](https://mosquitto.org/)).
So lassen sich die Zähldaten z. B. über **n8n** weiterverarbeiten und in einer
Datenbank wie **NocoDB** ablegen.
- Die Events werden auf dem Topic `{MQTT_TOPIC}/crossing` mit **QoS 1**
(nicht retained) publiziert.
- Zusätzlich gibt es ein **Status-/Availability-Topic** `{MQTT_TOPIC}/status`
(retained): Beim Verbinden sendet die App `online` (Birth-Message), bei einem
Verbindungsabbruch publiziert der Broker automatisch `offline` (Last Will /
LWT). So lässt sich jederzeit erkennen, ob der Counter läuft.
- Das Payload ist JSON, z. B.:
```json
{
"event": "crossing",
"camera": "cam1",
"source": "webcam",
"type": "car",
"track_id": 42,
"ts": "2026-06-01T12:34:56+02:00"
}
```
- Konfiguriert wird der Broker über Umgebungsvariablen:
| Variable | Standard | Beschreibung |
| -------------- | ---------------------- | ------------------------------------ |
| `MQTT_ENABLED` | `true` | MQTT komplett ein-/ausschalten |
| `MQTT_HOST` | `127.0.0.1` | Adresse des MQTT-Brokers |
| `MQTT_PORT` | `1883` | Broker-Port |
| `MQTT_USER` | | Benutzername (optional) |
| `MQTT_PASS` | | Passwort (optional) |
| `MQTT_TOPIC` | `vehiclecounter/cam1` | Basis-Topic der Events |
| `CAMERA_ID` | `cam1` | Kamera-Kennung im Payload |
> Die Verbindung zum Broker erfolgt asynchron. Ist der Broker nicht
> erreichbar, startet die Anwendung trotzdem es werden dann lediglich keine
> Events übertragen.
### Hardware & CUDA
Die Objekterkennung mit YOLOv11 ist rechenintensiv. Für die Echtzeit-Verarbeitung
von Live-Streams wird daher eine **NVIDIA-Grafikkarte mit CUDA** benötigt.
- **Mit NVIDIA-GPU (CUDA):** Die Inferenz läuft auf der Grafikkarte. Erkennt die
Anwendung eine CUDA-fähige GPU, wird automatisch **FP16-Inferenz** aktiviert
(per `YOLO_HALF` überschreibbar) das halbiert den Speicherbedarf und erhöht
die Bildrate spürbar.
- **Ohne GPU (nur CPU):** Die Anwendung startet zwar, die Inferenz ist für
Live-Streams jedoch zu langsam. Ein zusätzliches **Motion-Gate** sorgt dafür,
dass YOLO nur bei tatsächlicher Bewegung im Bild ausgeführt wird, was die Last
reduziert ersetzt eine GPU aber nicht.
Voraussetzungen für die GPU-Nutzung:
- NVIDIA-Treiber + passende CUDA-Runtime
- CUDA-fähiges PyTorch (siehe [Installation](#installation))
### Verwendete Technologien
- **Flask**: Web-Framework für Routing und Template-Rendering
- **OpenCV**: Videobearbeitung und Frame-Manipulation
- **Ultralytics YOLO**: YOLOv11-Modell für Objekterkennung und Tracking
- **NumPy**: Array-Operationen für Bilddaten
### Verarbeitungs-Pipeline
1. **Frame-Erfassung**: Webcam oder Videodatei als Quelle
2. **Frame-Skipping**: Verarbeitung jedes 2. Frames zur Leistungsoptimierung
3. **Größenanpassung**: Alle Frames werden auf 1020x600 Pixel skaliert
4. **YOLO-Tracking**: Objekterkennung mit persistenten Track-IDs
5. **Annotation**: Zeichnen von Bounding Boxes und Labels
6. **Streaming**: Übertragung als MJPEG-Stream an den Browser
### Objekterkennung
- Erkennt verschiedene Objektklassen (abhängig vom YOLO-Modell)
- Vergibt eindeutige Track-IDs für jedes Objekt
- Beschriftung im Format: `{Track-ID} - {Klassenname}`
- Grüne Bounding Boxes um erkannte Objekte
- Magenta-farbene Textbeschriftungen
## Leistungsoptimierung
- **Frame-Skipping**: Nur jeder 2. Frame wird verarbeitet, um CPU-Last zu reduzieren
- **Feste Auflösung**: Einheitliche Größe von 1020x600 Pixel für alle Frames
- **Effizientes Streaming**: JPEG-Kompression für Frame-Übertragung
## Einschränkungen
- Keine Validierung der Upload-Dateigröße
- Keine automatische Bereinigung hochgeladener Dateien
- Feste Frame-Dimensionen (1020x600)
- Keine Fehlerbehandlung bei Webcam-Zugriffsproblemen
## Hinweise
- Bei der ersten Verwendung kann das Laden des YOLO-Modells einige Sekunden dauern
- Die Erkennungsgenauigkeit hängt vom verwendeten YOLO-Modell ab
- Frame-Skipping kann bei sehr schnell bewegten Objekten zu Erkennungslücken führen
- Hochgeladene Videos werden im Ordner `uploads/` gespeichert und müssen manuell gelöscht werden
## Lizenz
Projekt basiert auf Ressourcen von [Pyresearch](https://pyresearch.org/)