Retour aux projets
IoT

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 augmentée
ArduinoIOT

# 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 :

  • `server.py` tourne sur le Pi Zero W embarque dans la voiture. Il reçoit les commandes, pilote les servos et le moteur, gère l'éclairage, et diffuse le flux vidéo de la caméra.
  • `controller.py` est l'application desktop PyQt6 sur le PC. Elle affiche le flux vidéo, les instruments de bord, et transmet les commandes clavier en temps réel.
  • 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 :

  • direction : -1 = gauche max, 0 = centre, +1 = droite max
  • vitesse : -1 = recul max, 0 = arrêt, +1 = avant max
  • camera : -1 = arrière gauche, 0 = avant, +1 = arrière droite
  • phares : 0 = éteints, 1 = allumés
  • 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 :

  • Cache GPIO : les valeurs ne sont écrites sur les pins que lorsqu'elles changent réellement
  • Boucle de sortie découplée : les sorties sont appliquées a fréquence fixe (50 Hz), indépendamment du débit de réception UDP
  • Envoi sélectif : les phares ne sont transmis en UDP que lors d'un changement d'état, pas à chaque tick
  • PWM matériel : via pigpio pour des signaux stables sans jitter logiciel
  • Eclairage automatique

    Le système d'éclairage combine automatismes et commande manuelle :

  • Feux de freinage : s'allument automatiquement à pleine intensité lors d'un freinage
  • Feux de recul : s'allument automatiquement en marche arrière
  • Feux de position : activés/desactivés manuellement avec la touche L (avant en on/off, arrière en PWM a 20%)
  • Calibration direction

    Deux paramètres compensent les imperfections mécaniques :

  • `DIRECTION_INVERT` : inverse le sens du servo si le câblage est à l'envers
  • `DIRECTION_TRIM` : offset de centrage pour compenser un désalignement au repos (-0.18 dans notre cas)
  • 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

  • Vidéo centrale : flux temps réel de la camera embarquée
  • Jauge de vitesse : barre verticale, vert (avant) / rouge (recul)
  • Boussole de direction : barre horizontale avec indicateur triangulaire
  • Indicateur camera : vue du dessus avec cône de vision rotatif
  • Pastilles de statut : UDP, vidéo, phares (vert/rouge)
  • Logs : console en temps réel des évènements
  • Arrêt d'urgence : bouton et touche espace
  • 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 :

  • Sol quadrillé avec perspective
  • Pylones rouges comme repères visuels
  • Ciel dégradé et ligne d'horizon
  • Physique de déplacement basique
  • HUD affichant les valeurs de commande en temps réel
  • 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