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>
310 lines
13 KiB
Markdown
310 lines
13 KiB
Markdown
# Fahrzeug- und Objekt-Erkennungssystem
|
||
|
||

|
||
|
||
[](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 (~5–10 €), 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/)
|