Voiture RC augmentée
Voiture RC pilotée en WiFi depuis un PC via une application desktop PyQt6, avec retour vidéo temps réel depuis une Pi Camera embarquée sur un Raspberry Pi Zero W. Commandes UDP basse latence, stream H264, système d'éclairage automatique (freins, recul, phares), et simulateur 3D intégré pour tester sans matériel.

# Voiture RC pilotée par ordinateur avec retour vidéo en temps réel
Le concept
Transformer une voiture radiocommandée classique en véhicule pilotable depuis un ordinateur, avec un retour vidéo embarqué en temps réel. Le récepteur radio d'origine est remplacé par un Raspberry Pi Zero W qui reçoit les commandes en WiFi et renvoie le flux vidéo d'une caméra montée sur le toit.
Le résultat : une expérience de pilotage immersive en vue FPV (First Person View), pilotée au clavier depuis une application desktop dediée.
Architecture
Le système repose sur deux logiciels qui communiquent en WiFi :
Un troisième programme, `simulator.py`, remplace le Pi pour tester le système complet sans aucun matériel physique.
```
PC (controller.py) Pi Zero W (server.py)
+---------------------------+ +---------------------------+
| Application PyQt6 | UDP 5000 | Ecoute commandes |
| - Flux vidéo | -----------> | - Servo direction (GPIO 12)
| - Jauges vitesse/dir | | - Servo camera (GPIO 13) |
| - Boussole camera | TCP 5001 | - ESC moteur (GPIO 18) |
| - Indicateurs status | <----------- | - LEDs éclairage |
| - Logs | H264 vidéo | - Pi Camera V1 (CSI) |
+---------------------------+ +---------------------------+
```
Matériel embarqué
| Composant | Rôle | Connexion Pi |
|-----------|------|-------------|
| Raspberry Pi Zero W | Cerveau embarque | - |
| Pi Camera V1 (OV5647) | Camera avec encodage GPU | Port CSI (nappe adaptateur Zero) |
| Servo direction | Tourner les roues | GPIO 12 (PWM0) |
| Servo camera | Panoramique 180 deg | GPIO 13 (PWM1) |
| ESC 320A brushed | Contrôle moteur | GPIO 18 |
| LED blanche avant | Feux de position | GPIO 24 |
| LED rouge arrière (PWM) | Position (20%) / Freinage (100%) | GPIO 25 |
| LED blanche arrière | Feux de recul | GPIO 8 |
| Batterie LiPo 2S-3S | Alimentation | via ESC (BEC) |
Chaque LED est cablées : `GPIO -> Résistance 220 Ohm -> LED -> GND`.
Les servos et l'ESC sont alimentes par le BEC de l'ESC, pas par le Pi. Seuls les fils signal vont aux GPIO.
Protocole de communication
Commandes (PC vers Pi) — UDP port 5000
Format JSON envoyé a 30 Hz :
```json
{"type": "direction", "value": 0.5}
{"type": "vitesse", "value": -0.3}
{"type": "camera", "value": 0.0}
{"type": "phares", "value": 1}
```
Les valeurs vont de -1 a +1 :
UDP a été choisi pour sa faible latence. La perte occasionnelle d'un paquet est acceptable car chaque tick envoie l'état complet (pattern heartbeat). Le Pi coupe automatiquement le moteur si aucune commande n'est reçue depuis 500 ms (failsafe).
Vidéo (Pi vers PC) — TCP port 5001
Stream H264 encode matériellement par le GPU du Pi via `rpicam-vid`, décode cote PC par ffmpeg en mode basse latence. Résolution 640x480 a 30 fps, bitrate 2 Mbps, profil baseline.
Choix techniques et défis
Architecture multi-thread
Le controller utilise trois threads indépendants :
1. Keyboard input : intercepteur global PyQt6 qui capture les touches avant les widgets
2. UDP sender : envoie l'état complet a 30 Hz (direction, vitesse, caméra)
3. Video receiver : décodage du flux H264 via ffmpeg subprocess
Cote serveur, deux threads :
1. UDP listener : reçoit et met a jour l'état
2. Output loop : applique l'état aux GPIO a 50 Hz, découple de la réception UDP
Marche arrière — Double-tap ESC
L'ESC brushed nécessite une séquence spécifique pour reculer : freinage, retour au neutre, puis signal de recul. Une machine a états transparente gère cette séquence automatiquement :
```
forward -> braking (signal négatif, 150ms)
-> neutral_wait (neutre, 150ms)
-> reverse (signal négatif = recul)
```
Du point de vue du pilote, appuyer sur la flèche bas suffit.
Optimisation de la latence
Le Pi Zero W est un monocoeur ARM a 1 GHz. Chaque opération GPIO compte. Plusieurs optimisations ont été mises en place :
Eclairage automatique
Le système d'éclairage combine automatismes et commande manuelle :
Calibration direction
Deux paramètres compensent les imperfections mécaniques :
Application desktop (controller.py)
L'interface PyQt6 se compose de deux écrans :
Ecran de configuration
Saisie de l'adresse du Pi (mDNS ou IP), ports UDP et vidéo, et mode simulateur. La résolution DNS se fait en arrière-plan pour ne pas bloquer l'interface.
Ecran cockpit
Contrôles clavier
| Touche | Action |
|--------|--------|
| Flèche haut/bas | Accélérer / freiner / reculer |
| Flèche gauche/droite | Tourner |
| Z | Caméra vers l'avant |
| Q | Caméra vers la gauche |
| S | Caméra vers l'arrière |
| D | Caméra vers la droite |
| L | Allumer/éteindre les phares |
| Espace | Arrêt d'urgence |
Simulateur
Le simulateur remplace entièrement le Pi pour le développement et les tests. Il génère une scène 3D simplifiée avec :
Le flux vidéo est envoyé en RGB brut sur TCP (pas de H264), et le controller bascule automatiquement sur le bon décodeur en mode simulateur.
Dépendances
Pi Zero W (Raspbian Trixie)
```
gpiozero
pigpio
```
Service pigpiod requis : `sudo systemctl enable pigpiod && sudo systemctl start pigpiod`
PC
```
PyQt6
opencv-python
numpy
ffmpeg (binaire systeme)
```
Démarrage
```bash
# Sur le Pi Zero W
python3 server.py
# Sur le PC
python3 controller.py
# Ou pour tester sans matériel
python3 simulator.py # dans un terminal
python3 controller.py # dans un autre (cocher "Mode simulateur")
```
Structure du projet
```
rc/
server.py # Serveur Pi Zero W (commandes + PWM + video + éclairage)
controller.py # Application desktop PyQt6 (cockpit + commandes)
simulator.py # Simulateur 3D (remplacement du Pi pour les tests)
requirements-pi.txt # Dépendances Python Pi
requirements-pc.txt # Dépendances Python PC
projet-rc-car.md # Cahier des charges initial
```
3 vues