HoloDeck_Robot_System/README.md

643 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

# HoloDeck — AprilTag-Robotersteuerung für den Unterricht
**Pädagogische Hochschule Weingarten · FrankeLab**
Ein overhead-kamerabasiertes System zur autonomen Navigation physischer Differentialantriebs-Roboter in Klassenzimmern. Schülerinnen und Schüler programmieren die Roboter über eine grafische Blockly-Oberfläche.
---
## Inhaltsverzeichnis
1. [Systemübersicht](#1-systemübersicht)
2. [Hardware-Komponenten](#2-hardware-komponenten)
3. [Verdrahtung & Schaltplan](#3-verdrahtung--schaltplan)
4. [Netzwerk & Infrastruktur](#4-netzwerk--infrastruktur)
5. [AprilTag-Layout](#5-apriltag-layout)
6. [Software-Architektur](#6-software-architektur)
7. [Entwicklungsgeschichte (v005v011)](#7-entwicklungsgeschichte-v005v011)
8. [Deployment (Server)](#8-deployment-server)
9. [Firmware flashen (ESP32)](#9-firmware-flashen-esp32)
10. [Konfiguration (config.yaml)](#10-konfiguration-configyaml)
11. [Kommunikationsprotokoll](#11-kommunikationsprotokoll)
12. [Regler-Tuning](#12-regler-tuning)
13. [Bekannte Probleme & Workarounds](#13-bekannte-probleme--workarounds)
14. [Roadmap](#14-roadmap)
---
## 1. Systemübersicht
```
┌─────────────────────────────────────────────────────────────┐
│ Overhead-Kamera (HuskyLens 2) │
│ RTSP → 192.168.0.30:8554/live │
└────────────────────┬────────────────────────────────────────┘
│ RTSP (UDP, 1280×720)
┌─────────────────────────────────────────────────────────────┐
│ Ubuntu-Server 192.168.0.250 │
│ /opt/robot-tracker-v010/ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ tracker │ │ collision │ │ navlogger │ │
│ │ AprilTag │→ │ Potential │→ │ CSV-Logging │ │
│ │ Detektor │ │ -feld PID │ │ │ │
│ └──────────┘ └───────────┘ └──────────────┘ │
│ FastAPI / uvicorn Port 80 │
│ TCP-Server Port 8765 │
└────────────────┬────────────────────────────────────────────┘
│ TCP (JSON, newline-delimited)
┌─────────────────────────────────────────────────────────────┐
│ Roboter (Freenove ESP32-S3 + MicroPython v1.23) │
│ WiFi FrankeLab → 192.168.0.250:8765 │
│ ┌──────────┐ ┌────────────┐ ┌────────────┐ │
│ │ PID-Loop │ │ Encoder │ │ Motoren │ │
│ │ 20 Hz │← │ IRQ 50ms │ │ L9110S │ │
│ └──────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## 2. Hardware-Komponenten
### Server / Kamera
| Komponente | Details |
|---|---|
| Server | Ubuntu 24.04, statisch 192.168.0.250 |
| Kamera | HuskyLens 2 (RTSP-Modus) |
| Kamera-Mount | Overhead-Befestigung zentral über der Spielfläche |
### Roboter (ein Exemplar, erweiterbar)
| Komponente | Bezeichnung / Bestellnummer |
|---|---|
| Chassis | DollaTek 2WD Roboter-Chassis-Kit |
| Mikrocontroller | Freenove ESP32-S3 WROOM Board Lite |
| Breakout-Board | Freenove ESP32-S3 Breakout Board (Schraubklemmen) |
| Motortreiber | L9110S-Modul (Schraubklemmen, 2-Kanal) |
| Encoder-Sensoren | Optokoppler-Geschwindigkeitssensor (74HC14D-basiert, 3,35V) |
| Powerbank | Intenso XS5000 (USB-A + USB-C Ausgang) |
| AprilTag (Roboter) | Tag36h11, ID 91, auf Aufkleber am Roboter |
> **Hinweis Motortereiber-Geschichte:** Zunächst wurde ein handgelöteter TB6612FNG auf Lochraster verwendet. Dieser wurde durch das deutlich handhabbarere L9110S-Schraubklemmen-Modul ersetzt.
> **WARNUNG:** Ein früheres AZDelivery-ESP32-Board wurde wahrscheinlich durch gleichzeitigen USB+Akku-Betrieb beschädigt. **Niemals** gleichzeitig USB und Powerbank anlegen wenn GPIO-Pins des Motortreibers auf HIGH liegen!
---
## 3. Verdrahtung & Schaltplan
### Stromversorgung — ZWEI GETRENNTE RAILS (kritisch!)
Das System verwendet **zwei physisch getrennte Stromversorgungskreise**, weil WiFi-Peak (200400 mA) + zwei Motoren den 5V-Rail eines einzigen USB-Anschlusses überlasten:
```
┌─────────────────────┐ ┌──────────────────────────────────┐
│ Intenso XS5000 │ │ Intenso XS5000 │
│ USB-C Ausgang │ │ USB-A Ausgang │
│ 5V → ESP32 (VIN) │ │ Kabel aufschneiden: │
│ │ │ Roter Draht → VM (Motortreiber) │
└─────────────────────┘ └──────────────────────────────────┘
│ │
│ USB-C Kabel │ Modifiziertes USB-A-Kabel
▼ ▼
ESP32-S3 (USB-C) VM-Pin L9110S
Logikversorgung Motorversorgung
GND verbunden: ESP32 GND ←——→ L9110S GND (gemeinsame Masse!)
```
**Wichtig:** Das schwarze (GND) Kabel des USB-A-Kabels wird ebenfalls mit dem ESP32-GND verbunden. Die Datenleitungen (grün/weiß) bleiben unbenutzt / abgeklebt.
---
### GPIO-Belegung ESP32-S3
| GPIO | Funktion | Verbindung |
|---|---|---|
| GPIO 17 | AIN1 (Motor A Richtung 1) | L9110S Pin IA1 |
| GPIO 18 | AIN2 (Motor A Richtung 2) | L9110S Pin IA2 |
| GPIO 8 | PWMA (Motor A Geschwindigkeit) | L9110S Pin — via Jumper oder direkt |
| GPIO 9 | BIN1 (Motor B Richtung 1) | L9110S Pin IB1 |
| GPIO 10 | BIN2 (Motor B Richtung 2) | L9110S Pin IB2 |
| GPIO 11 | PWMB (Motor B Geschwindigkeit) | L9110S Pin — |
| GPIO 6 | Encoder Links (Interrupt) | Optokoppler OUT |
| GPIO 7 | Encoder Rechts (Interrupt) | Optokoppler OUT |
| 3V3 | Encoder VCC | Optokoppler VCC |
| GND | Gemeinsame Masse | L9110S GND, Encoder GND |
> **Achtung GPIO 12:** GPIO 12 ist am ESP32(-S3 nicht betroffen, aber klassischer ESP32) ein Strapping-Pin und verursacht Boot-Probleme. Wird hier nicht verwendet.
---
### L9110S Motor-Treiber Anschlussschema
```
ESP32-S3 L9110S Modul
───────── ────────────
GPIO 17 ──────────────────→ IA1 OA1 ──→ Motor A (links) Klemme 1
GPIO 18 ──────────────────→ IA2 OA2 ──→ Motor A (links) Klemme 2
GPIO 8 ──────── (PWM) ──→ IA1 oder Jumper (je nach Modul)
GPIO 9 ──────────────────→ IB1 OB1 ──→ Motor B (rechts) Klemme 1
GPIO 10 ──────────────────→ IB2 OB2 ──→ Motor B (rechts) Klemme 2
GPIO 11 ──────── (PWM) ──→ IB1 oder Jumper
3V3 ──────────────────→ VCC (Logik)
GND ──────────────────→ GND
USB-A rot ────────────────→ VM (Motorversorgung 5V)
```
> **Hinweis:** Das L9110S-Modul vereint Richtungssteuerung und PWM auf zwei Eingangspins pro Kanal (IA1/IA2). Die Drehrichtung ergibt sich aus dem Muster HIGH/LOW bzw. PWM/GND. Konkrete Zuordnung in `main.py → set_motor()`.
---
### Encoder-Sensor Anschluss
```
Optokoppler-Modul (74HC14D) ESP32-S3
─────────────────────────── ─────────
VCC ──────────────────────→ 3V3
GND ──────────────────────→ GND
OUT (linker Sensor) ──────→ GPIO 6
OUT (rechter Sensor) ──────→ GPIO 7
Lochscheibe: 20 Löcher/Umdrehung → 20 Pulse/Rev
```
---
### Befestigung
- Alle Elektronikmodule mit **Heißkleber** auf dem Chassis fixiert (sicher, rückbaubar)
- Encoder-Sensoren justiert auf Lochscheibe der Motorachsen
- AprilTag (ID 91) flach auf der Oberseite des Roboters aufgeklebt, muss von oben sichtbar sein
---
## 4. Netzwerk & Infrastruktur
| Komponente | IP-Adresse | Port |
|---|---|---|
| Ubuntu-Server | 192.168.0.250 (statisch) | — |
| FastAPI/Webinterface | 192.168.0.250 | 80 |
| TCP Robot-Server | 192.168.0.250 | 8765 |
| HuskyLens 2 (RTSP) | 192.168.0.30 | 8554 |
| Roboter (ESP32) | DHCP | — |
**WLAN:** `FrankeLab` (Passwort in `main.py` und `WIFI_PSW` Variable)
**Python-Umgebung Server:**
```bash
/opt/vision-venv/ # Virtuelle Umgebung
/opt/robot-tracker-v010/ # Aktueller Produktionsstand
```
---
## 5. AprilTag-Layout
Alle Tags aus der Familie **tag36h11**.
### Kalibrierungs-Tags (Referenzpunkte)
Auf einem A4-Blatt, fixiert im Sichtfeld der Kamera:
| Tag-ID | Position (mm) |
|---|---|
| 0 | (0.0, 0.0) — Ursprung |
| 1 | (100.5, 0.0) |
| 2 | (0.0, 181.0) |
| 3 | (100.5, 181.0) |
### Roboter-Tags
| Tag-ID | Bedeutung |
|---|---|
| 91 | Roboter 1 |
### Hindernis-Tags
| Tag-IDs |
|---|
| 11, 12, 21, 22, 31, 32, 41, 42, 51, 52, 61, 62, 71, 72, 81, 82, 92 |
Hindernisse werden als AprilTags auf dem Boden oder an Objekten platziert. Der Server berechnet automatisch Abstoßungskräfte (Potentialfeld).
---
## 6. Software-Architektur
### Server-Stack
```
app.py FastAPI-App, 20-Hz-Steuerungsloop, TCP-Server, WebSocket-Broadcast
tracker.py AprilTag-Detektion via pupil-apriltags, EMA-Filter, Dead Reckoning
collision.py Potentialfeld-Navigation: Zielanziehung + Hindernisabstoßung
navlogger.py CSV-Logging aller Navigationsdaten → logs/nav_YYYYMMDD_HHMMSS.csv
calibration.py Homographie-Kalibrierung (Kamerabild → Weltkoordinaten mm)
config.yaml Alle Parameter externalisiert
static/ Browser-Frontend (Live-Kamerabild + AprilTag-Overlay)
```
### ESP32-Firmware
```
main.py Komplette Roboter-Firmware (MicroPython)
├── WiFi-Verbindung mit Auto-Reconnect
├── TCP-Client → Server 192.168.0.250:8765
├── Encoder-Interrupts (GPIO 6, 7)
├── PID-Loop 20 Hz (asyncio)
├── Telemetrie-Loop 10 Hz (asyncio)
└── Feedforward-Architektur (v011, aktuell deaktiviert = Option B)
```
---
## 7. Entwicklungsgeschichte v005v011
### v005 — Stabile Tracking-Basis (Server only)
- **Kamera-I/O** auf dedizierten Background-Thread via `asyncio.Queue(maxsize=1)`
- **ffmpeg-Watchdog** mit Auto-Reconnect bei Kameraausfall
- **config.yaml** — alle Parameter externalisiert, kein Hardcoding mehr
- **Velocity-Vektor-Visualisierung** im Browser-Frontend
- Bestätigt: Merklich flüssiger als v004
### v006 — Grundlegende Navigation
- Erste TCP-Kommunikation mit ESP32
- Einfache Richtungs-Befehle (links/rechts/vorwärts)
- Kein Encoder-Feedback
### v007 — Homographie-Kalibrierung
- `calibration.py` eingeführt: 4 Referenz-Tags → Kamerabild-zu-Weltkoordinaten-Mapping
- Kamerakoordinaten erstmals in echte mm umgerechnet
- A4-Kalibrierblatt mit Tags 03 als Referenz
### v008 — Erste Hindernisvermeidung
- `collision.py` eingeführt: Potentialfeld-Ansatz
- Zielanziehung + Hindernisabstoßung
- Noch instabil: Spin-Probleme, zu aggressiv
### v008_log — Navigation-Logging
- `navlogger.py` hinzugefügt
- CSV-Logging aller Navigationsdaten für spätere Analyse
- Log-Dateien in `logs/nav_DATUM_UHRZEIT.csv`
### v009 — Encoder-PID + Erste stabile Navigation
**Meilenstein: Erster Roboter fährt autonom zum Ziel**
- **Encoder-Interrupts** auf GPIO 6/7
- **PID-Regler** für Radgeschwindigkeit: `KP=1.2, KI=0.3, KD=0.05`
- **Kommunikationsprotokoll** auf mm/s umgestellt (server→robot: `{"speed_l": N, "speed_r": N}`)
- **TCP statt WebSocket** für Robot-Kommunikation (simpler, stabiler)
- **Dead Reckoning**: Bei Tag-Verlust schätzt ESP32 Position aus Encoder-Daten
- Hardware-Parameter in config.yaml: `mm_per_pulse=10.525`, `axle_width_mm=121.0`
- **PWM-Minimum 150** damit Motoren sicher anlaufen
### v009_fix → v009_spin_fix → v009_goal_fix
Iterative Fixes basierend auf Log-Analyse:
- Spin-Schwelle angepasst
- Ziel-Erreicht-Erkennung verbessert
- Repulsionskraft von 5000 → 1500 (war zu aggressiv)
### v010 — Stabilisierte Navigation (aktueller Produktionsstand)
**Serverseitige Verbesserungen:**
| Parameter | Alt | Neu | Grund |
|---|---|---|---|
| `FORCE_ALPHA` | 0.15 | 0.35 | Zu träge → Endlos-Spin |
| `SPIN_THRESHOLD` | 120° | 90° | 120° zu hoch, Roboter überschoss |
| `SPIN_STOP` | — | 35° | Sauberes Spin-Ende |
| `SPIN_SLOWDOWN` | — | 60° | Sanftes Abbremsen |
| `HEADING_DEADBAND` | — | 6° | Unter 6° kein Kurskorrektur |
| `repulsion_strength` | 5000 | 1500 | Sanftere Abstoßung |
| Controller-Frequenz | 10 Hz | 20 Hz | Doppelte Reaktionsgeschwindigkeit |
| `reset_robot()` | — | ✓ | Roboter stoppt bei Verbindungsabbruch |
**Bug behoben:** `FORCE_ALPHA=0.15` war zu träge — der geglättete Kraftvektor folgte der echten Richtung so langsam, dass der Roboter beim Drehen immer wieder die Stopp-Schwelle überschoss und neu anfing zu drehen → Endlos-Spin.
### v011 — Feedforward-Architektur (aktuell auf ESP32)
**Firmware-Verbesserungen:**
Neue Feedforward-Architektur: `u = kS·sign(v) + kV·|v| + PID(error)`
- **kS (Haftreibungskompensation):** Fester PWM-Wert um Haftreibung zu überwinden
- **kV (Kennlinien-Linearisierung):** PWM proportional zur Sollgeschwindigkeit
- **PID regelt nur noch den kleinen Restfehler** statt alles alleine
- **Gleitendes Messfenster** `SPEED_WINDOW=3` (150ms) für Istgeschwindigkeit — bei 20 Pulsen/Rev ist 50ms zu grob (quantisiertes Rauschen)
- **Anti-Windup:** Integral einfrieren bei PWM-Sättigung
- **Option B (aktuell aktiv):** `KS=0, KV=0` → reiner PID wie v009, aber mit Messfenster und Anti-Windup
**Bugs gefunden und behoben in v011:**
*Bug 1: Motoren drehten nicht*
- Ursache: In v011 wird `current_pwm = ff + pid_out · sign` **direkt gesetzt** statt aufaddiert
- Mit `KS=KV=0` gibt feedforward() 0 zurück → PWM bleibt auf der ersten Stufe, zu klein für Motoranlauf
- Fix: Wenn `KS=KV=0` → aufaddieren wie v009 (`current_pwm += pid_out · sign`) + PWM_MIN erzwingen
```python
if KS_L == 0.0 and KV_L == 0.0:
current_pwm_l += pid_out_l * sign_l # aufaddieren
if sign_l > 0 and current_pwm_l < PWM_MIN:
current_pwm_l = float(PWM_MIN) # Mindest-PWM
else:
current_pwm_l = ff_l + pid_out_l * sign_l # Feedforward-Modus
```
---
## 8. Deployment (Server)
### Voraussetzungen
```bash
# Python-Umgebung
python3 -m venv /opt/vision-venv
source /opt/vision-venv/bin/activate
pip install fastapi uvicorn pupil-apriltags opencv-python-headless numpy pyyaml
```
### Aktuellen Stand deployen
```bash
# Dateien kopieren
scp -r v010/* root@192.168.0.250:/opt/robot-tracker-v010/
# Server starten
ssh root@192.168.0.250
cd /opt/robot-tracker-v010
/opt/vision-venv/bin/python -m uvicorn app:app --host 0.0.0.0 --port 80
```
### Schnell neu starten
```bash
ssh root@192.168.0.250 "pkill -f uvicorn; sleep 1; \
cd /opt/robot-tracker-v010 && \
/opt/vision-venv/bin/python -m uvicorn app:app --host 0.0.0.0 --port 80"
```
### Nur collision.py neu laden (kein Neustart nötig bei Code-Änderungen)
```bash
scp collision.py root@192.168.0.250:/opt/robot-tracker-v010/
# dann Server neu starten
```
### Logs einsehen
```bash
ssh root@192.168.0.250
ls -lt /opt/robot-tracker-v010/logs/
cat /opt/robot-tracker-v010/logs/nav_20260401_192534.csv | head -5
```
### Log-Format
```
timestamp, elapsed_s, robot_x_mm, robot_y_mm, robot_theta,
goal_x_mm, goal_y_mm, dist_to_goal_mm, angle_error_deg,
cmd_speed_l, cmd_speed_r, cmd_status,
num_obstacles, nearest_obstacle_id, nearest_obstacle_dist_mm,
all_obstacle_dists
```
`cmd_status` kann sein: `straight`, `curving`, `spinning`
---
## 9. Firmware flashen (ESP32)
### Voraussetzungen
```bash
# esptool aus Freenove-Toolchain
~/Downloads/Freenove_ESP32_S3_WROOM_Board_Lite-main/Python/Python_Firmware/
# mpremote für REPL-Zugriff
pip install mpremote
```
### Gerät erkennen
Der ESP32-S3 erscheint unter Ubuntu als `/dev/ttyACM0` (native USB, **nicht** `/dev/ttyUSB*`).
```bash
ls /dev/ttyACM*
```
### MicroPython flashen (nur wenn nötig)
```bash
# Flash löschen
python esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash
# MicroPython flashen (v1.23.0)
python esptool.py --chip esp32s3 --port /dev/ttyACM0 write_flash \
-z 0x0 ESP32_GENERIC_S3-SPIRAM_OCT-20240602-v1.23.0.bin
```
### Firmware-Datei übertragen
```bash
# main.py auf ESP32 kopieren
mpremote connect /dev/ttyACM0 fs cp main.py :main.py
# Neustart
mpremote connect /dev/ttyACM0 reset
```
### REPL für Debugging
```bash
mpremote connect /dev/ttyACM0
# Dann Ctrl+C → interaktive Python-Konsole
```
---
## 10. Konfiguration (config.yaml)
```yaml
camera:
rtsp_url: "rtsp://192.168.0.30:8554/live"
width: 1280
height: 720
rtsp_transport: "udp"
reconnect_delay: 3.0
server:
host: "0.0.0.0"
port: 80
broadcast_hz: 30
tracker:
tag_family: "tag36h11"
detector_threads: 2
quad_decimate: 1.0
ema_alpha_pos: 0.35 # Positions-Glättung (0=träge, 1=roh)
ema_alpha_ang: 0.35 # Winkel-Glättung
ema_alpha_vel: 0.25 # Geschwindigkeits-Glättung
hold_seconds: 0.80 # Tag-Verlust Toleranz
predict_seconds: 0.25 # Prediction-Horizont
robots:
tcp_port: 8765
robot_tag_ids: [91]
goal_radius: 80 # mm — Ziel gilt als erreicht
speed_base: 160 # mm/s — Fahrgeschwindigkeit
speed_turn: 90 # mm/s — Spin-Geschwindigkeit
speed_min: 50
speed_max: 350
angle_threshold: 120 # Grad — nur noch für Spin-Trigger
calibration:
reference_tags:
0: [0.0, 0.0 ]
1: [100.5, 0.0 ]
2: [0.0, 181.0]
3: [100.5, 181.0]
obstacles:
safe_distance: 150 # mm — Abstoßungsbereich
repulsion_strength: 1500 # Stärke der Abstoßung
obstacle_tag_ids: [11,12,21,22,31,32,41,42,51,52,61,62,71,72,81,82,92]
robot_hardware:
wheel_diameter_mm: 67.0
wheel_circumference_mm: 210.5
encoder_pulses_per_rev: 20
mm_per_pulse: 10.525 # 210.5 / 20
axle_width_mm: 121.0 # Radmitte links → Radmitte rechts
pulses_per_360deg: 36.1 # π × axle_width / mm_per_pulse
```
---
## 11. Kommunikationsprotokoll
### Server → Roboter (TCP, newline-delimited JSON)
```json
{"speed_l": 160.0, "speed_r": 160.0}
```
Beide Werte in **mm/s**. Negativ = rückwärts. `0.0` = Stop.
### Roboter → Server (alle 100 ms)
```json
{
"robot_id": 91,
"enc_l": 1234,
"enc_r": 1235,
"speed_l": 158.3,
"speed_r": 161.1,
"pwm_l": 420,
"pwm_r": 435
}
```
| Feld | Bedeutung |
|---|---|
| `enc_l/r` | Absoluter Encoder-Zähler (monoton steigend) |
| `speed_l/r` | Istgeschwindigkeit in mm/s (aus gleitendem Fenster) |
| `pwm_l/r` | Aktueller PWM-Wert (0950) |
---
## 12. Regler-Tuning
### Serverseitig (collision.py)
| Parameter | Aktuell | Wirkung |
|---|---|---|
| `SPIN_THRESHOLD` | 90° | Ab diesem Winkel dreht Roboter auf der Stelle |
| `SPIN_STOP` | 35° | Unterhalb: Spin beendet, Kurvenfahrt beginnt |
| `SPIN_SLOWDOWN` | 60° | Spin wird sanfter |
| `FORCE_ALPHA` | 0.35 | EMA-Glättung des Kraftvektors (0=träge, 1=roh) |
| `HEADING_DEADBAND` | 6° | Winkeltoleranz für Geradeausfahrt |
| `repulsion_strength` | 1500 | Abstoßungsstärke von Hindernissen |
| `goal_radius` | 80 mm | Ziel gilt als erreicht |
### ESP32-seitig (main.py) — Option A: Feedforward aktivieren
```
Schritt 1 — kS bestimmen (Haftreibung):
→ speed_base sehr klein setzen (z.B. 20 mm/s)
→ KS_L/R erhöhen bis Räder gerade anfangen zu drehen
→ Typisch: 130170
Schritt 2 — kV bestimmen (Kennlinie):
→ speed_base = 160 mm/s, KS bereits gesetzt
→ Log beobachten: pwm_l/pwm_r bei Geradeausfahrt ablesen
→ kV = (PWM_gemessen - kS) / 160
→ Typisch: 2.03.5
Schritt 3 — PID reduzieren:
→ KP von 1.2 auf ~0.4 reduzieren
→ KI von 0.3 auf ~0.08 reduzieren
→ KD bleibt ~0.02
Schritt 4 — Links/Rechts-Asymmetrie:
→ kS_L ≠ kS_R fein abgleichen bis Geradeausfahrt ohne Drift
```
### ESP32-seitig — Option B (aktuell): Reiner PID
```python
KS_L = KS_R = 0.0 # Feedforward aus
KV_L = KV_R = 0.0
KP = 1.2
KI = 0.3
KD = 0.05
PWM_MIN = 150 # Erzwungenes Mindest-PWM
```
---
## 13. Bekannte Probleme & Workarounds
| Problem | Ursache | Workaround / Fix |
|---|---|---|
| Endlos-Spin | `FORCE_ALPHA` zu klein, Kraftvektor folgt zu langsam | `FORCE_ALPHA=0.35`, `SPIN_THRESHOLD=90°` |
| Motoren laufen nicht an | PWM_MIN fehlt im direkten Feedforward-Pfad | `PWM_MIN=150` erzwingen wenn `KS=0` |
| ESP32 bootet nicht | GPIO 12 belegt (Strapping-Pin) | GPIO 12 nicht verwenden |
| ESP32 beschädigt | Gleichzeitig USB + Powerbank + Motoren | Stets separate Stromkreise! |
| Kamera-Freeze | ffmpeg verliert RTSP-Stream | Watchdog mit Auto-Reconnect in v005+ |
| Tag nicht erkannt | Schlechte Lichtverhältnisse | Gleichmäßige Beleuchtung, möglichst kein direktes Sonnenlicht |
| Ziel nie erreicht | `goal_radius` zu klein, Kamera-Jitter | `goal_radius` auf 100120 mm erhöhen |
| Ziel wird weggedrückt | Hindernis-Repulsion nahe am Ziel | `repulsion_strength` bei kleiner `dist_to_goal` abschwächen (TODO) |
---
## 14. Roadmap
### Kurzfristig (Tuning)
- [ ] Feedforward-Koeffizienten `kS`, `kV` experimentell bestimmen und aktivieren
- [ ] `goal_radius` auf 100 mm erhöhen
- [ ] Repulsion nahe am Ziel abschwächen
- [ ] `axle_width_mm` und `pulses_per_360deg` exakt nachmessen
### Mittelfristig (Architektur)
- [ ] v/ω-Regler statt direktem Innen-/Außenrad-Schema (`collision.py`)
- [ ] Weiches Dead-Reckoning/Vision-Fusion (kein harter Sprung bei Tag-Wiedersehen)
- [ ] Mehrere Roboter gleichzeitig (IDs 91, 92, ...)
- [ ] Seeed Grove I2C TB6612 für einfachere Multi-Roboter-Verdrahtung
### Langfristig (Unterrichts-Interface)
- [ ] **Blockly-Interface** für Schülerinnen und Schüler
- [ ] Ziel per Klick im Browser setzen (bereits teilweise vorhanden)
- [ ] Mehrere Ziele als Route definieren
- [ ] Begegnungs-/Kooperationsszenarien
---
## Lizenz
Entwickelt für Unterrichtszwecke an der Pädagogischen Hochschule Weingarten.
Internes Projekt — nicht zur öffentlichen Weiterverbreitung bestimmt.
---
*Zuletzt aktualisiert: April 2026 — v011 Firmware, v010 Server*