Table des matières
- Présentation générale
- Architecture logicielle
- Gestion des actionneurs
- Communication & entrées/sorties
- Stratégie et séquencement
- Tâches FreeRTOS
1. Présentation générale
Le PAMI (Petit Actionneur Mobile Indépendant) est un robot entièrement autonome conçu pour la Coupe de France de Robotique. Il n’est relié à aucun robot principal pendant le match : il démarre seul, se déplace et réalise ses actions sans intervention extérieure.
Il s’active automatiquement 85 secondes avant la fin du match, déclenché par le retrait d’une tirette (corde de démarrage). L’arrêt d’urgence matériel est géré par un BAU (Bouton d’Arrêt d’Urgence) câblé en dehors du logiciel.
1.1 Carte de contrôle
| Paramètre | Valeur |
|---|---|
| Microcontrôleur | Seeed XIAO ESP32-S3 (DIP) |
| Cœur | Xtensa LX7 dual-core (240 MHz) |
| Environnement | Espressif IDF via couche Arduino (PlatformIO) |
| Langage | C++ |
| Tâches RTOS | FreeRTOS — 2 tâches (setup/loop + detection) |
| Alimentation | +5 V via diode Schottky D1 |
2. Architecture logicielle
Le code est organisé en un fichier principal main.cpp accompagné d’un fichier de définitions des broches Def_PAMI.h.
2.1 Bibliothèques tierces
| Bibliothèque | Rôle |
|---|---|
AccelStepper | Contrôle moteurs pas-à-pas avec profil d’accélération |
ESP32Servo | Pilotage du servo SG90 via PWM |
Ultrasonic | Lecture du capteur HC-SR04 |
Wire | Bus I2C pour l’expander GPIO PCF8574 |
2.2 Découpage en modules fonctionnels
| Fonction | Rôle |
|---|---|
configStepper() | Initialise vitesse et accélération des deux steppers |
modif_config_stepper() | Reconfigure dynamiquement les steppers (lent pour les rotations) |
waitingTirette() | Boucle bloquante jusqu’au retrait de la corde de démarrage |
runStepper() | Appel périodique AccelStepper::run() pour les deux moteurs |
goTo() | Déplacement vers position absolue avec surveillance obstacle |
goToRelative() | Déplacement relatif avec surveillance obstacle et mise à jour odométrie |
move_co_abs() | Navigation 2D vers une cible en coordonnées absolues (mm) |
esquive() | Contournement d’obstacle en U (4 mouvements) |
detection() (tâche) | Lecture sonar sur Core 1 toutes les 50 ms |
strategy() | Séquence d’actions selon la couleur d’équipe |
boucle_actionneur() | Animation du servo SG90 (0 → 70 → 0°) |
2.3 Flux d’exécution global
Au démarrage (setup()), la séquence est :
1. Initialisation périphériques (Serial, broches, servo, I2C)
2. Lecture switch d'équipe via PCF8574 (I2C 0x20, bit 0)
3. Configuration steppers (configStepper)
4. Création mutex FreeRTOS
5. Lancement tâche detection → Core 1
6. Attente retrait tirette (waitingTirette)
7. Activation drivers moteurs (pinEnable → LOW)
8. Appel move_co_abs(CO_POINT_ABS) → navigation vers la cible
La fonction
loop()est volontairement vide. Toute la logique est danssetup()et dans la tâche FreeRTOS.
3. Gestion des actionneurs
3.1 Moteurs pas-à-pas
Le PAMI utilise deux moteurs pas-à-pas en configuration DRIVER (signaux STEP + DIR). Les broches sont définies dans Def_PAMI.h.
| Paramètre | Moteur Gauche (stepperG) | Moteur Droit (stepperD) |
|---|---|---|
| Broche STEP | pinStep1 (D1 / GPIO2) | pinStep2 (D3 / GPIO4) |
| Broche DIR | pinDir1 (D0 / GPIO1) | pinDir2 (D2 / GPIO3) |
| Vitesse max | 8 000 pas/s | 8 000 pas/s |
| Accélération | 3 000 pas/s² | 3 000 pas/s² |
| Vitesse rotation lente | 2 000 pas/s | 2 000 pas/s |
Note : Le moteur droit est inversé en logiciel (signe négatif dans
moveSteppers_absetmoveSteppers_Relative) pour compenser le montage mécanique symétrique.
3.2 Conversion pas ↔ distance
| Constante | Valeur | Description |
|---|---|---|
diametre_roue | 75 mm | Diamètre roue motrice |
step_to_mm | 650 / 5205 ≈ 0,125 | Ratio pas → mm (calibré empiriquement) |
step_90 | −590 pas | Nombre de pas pour une rotation de 90° |
| Facteur avance | ×8 | Conversion mm → pas dans move_co_abs (empirique) |
3.3 Servo SG90
- Broche : D4 (GPIO5 / I2C_SDA)
- Fréquence PWM : 50 Hz
- Usage :
boucle_actionneur()— balayage 0 → 70° → 0° par pas de 1° toutes les 5 ms
4. Communication & entrées/sorties
4.1 Schéma de câblage
(Voir fichier image : schema_elec.png)
4.2 Tableau des broches
| Broche ESP32 | Signal | Périphérique | Description |
|---|---|---|---|
| D0 / GPIO1 | DIR G | Stepper Gauche | Direction moteur gauche |
| D1 / GPIO2 | STEP G | Stepper Gauche | Impulsion pas moteur gauche |
| D2 / GPIO3 | DIR D | Stepper Droit | Direction moteur droit |
| D3 / GPIO4 | STEP D | Stepper Droit | Impulsion pas moteur droit |
| D4 / GPIO5 | SDA / Servo | I2C + SG90 | Bus I2C SDA + PWM servo |
| D5 / GPIO9 | SCL | I2C (PCF8574) | Bus I2C SCL — expander GPIO |
| D6 / UOTXD | RX-LIDAR | Lidar (optionnel) | UART réception lidar |
| D7 / UORXD | TX-LIDAR | Lidar (optionnel) | UART émission lidar |
| D8 / GPIO7 | trig | HC-SR04 | Trigger ultrason |
| D9 / GPIO8 | echo | HC-SR04 | Echo ultrason |
| D10 / GPIO9 | TIRETTE | Switch J1 | Détection retrait corde (INPUT_PULLUP) |
| D9 / GPIO8 | EN | Drivers steppers | Enable moteurs (LOW = actif) |
| D10 / GPIO10 | Lidar_PWM | Lidar | Signal PWM lidar |
4.3 I2C — Expander PCF8574
- Adresse :
0x20 - Usage : lecture unique au démarrage pour détecter la couleur d’équipe
- Protocole : bit 0 du registre d’entrée →
0= équipe 1,1= équipe 2
Wire.begin(D4, D5);
Wire.beginTransmission(0x20);
Wire.write(0xFF); // tous pins en entrée
Wire.endTransmission();
Wire.requestFrom(0x20, 1);
byte etat = Wire.read();
team = ((etat & 0b00000001) == 0) ? 1 : 2;
4.4 Capteur ultrason HC-SR04
- Bibliothèque :
Ultrasonic - Tâche : Core 1, priorité 1, lecture toutes les 50 ms
- Seuil de détection : distance ≤ 10 cm →
obstacle = true - Partage inter-core : variable
volatile bool obstacle+ mutexobstacleMutex
⚠️ Bug connu : la protection mutex dans
detection()est actuellement commentée. La variableobstacleest donc accédée sans verrou depuis les deux cores → risque de comportement indéterminé.
5. Stratégie et séquencement
5.1 Démarrage — Tirette
waitingTirette() bloque jusqu’au cycle insertion + retrait de la corde, avec debounce de 200 ms :
while ( digitalRead(pinTirette)); // attend l'insertion (pin → LOW)
delay(200);
while (!digitalRead(pinTirette)); // attend le retrait (pin → HIGH)
delay(200);
5.2 Système de coordonnées
| Variable | Description |
|---|---|
CO_PAMI_ABS[3] | Position courante : {x (mm), y (mm), angle (°)} |
CO_POINT_ABS[2] | Point d’arrivée cible : {x=1200, y=650} en mm |
step_90 = −590 | Pas pour une rotation de 90° (à recalibrer si mécanique change) |
| Angles | 0°=avant · 90°=droite · 180°=arrière · 270°=gauche |
5.3 Navigation absolue — move_co_abs()
Déplace le PAMI de sa position courante vers la cible en deux phases :
Phase 1 — Axe X :
→ Calculer l'angle à corriger
→ Rotation lente (modif_config_stepper)
→ Avancer sur X (goToRelative)
→ Mettre à jour CO_PAMI_ABS[0]
Phase 2 — Axe Y :
→ Calculer l'angle à corriger
→ Rotation lente
→ Avancer sur Y (goToRelative)
→ Mettre à jour CO_PAMI_ABS[1]
5.4 Détection et évitement d’obstacles
Lors d’un déplacement (goToRelative), si un obstacle est détecté :
- Mémoriser les pas restants
- Mettre à jour
CO_PAMI_ABSavec les pas déjà effectués stopMotors()- Attendre 250 ms
- Si l’obstacle a disparu → reprendre le déplacement
- Si l’obstacle persiste → appeler
esquive()(actuellement commenté)
Manœuvre d’esquive en U (esquive()) :
1. Rotation gauche 90°
2. Avance ~150 mm
3. Rotation droite 90°
4. Avance ~150 mm (passage devant l'obstacle)
5. Rotation droite 90°
6. Avance ~150 mm
7. Rotation gauche 90° (retour à l'angle initial)
8. Reprendre move_co_abs() depuis la nouvelle position
5.5 Stratégie par équipe — strategy()
| Équipe | Action |
|---|---|
| Équipe 1 | Déplacement relatif (−500, +500 pas) — repositionnement final |
| Équipe 2 | Déplacement relatif (+500, −500 pas) + boucle_actionneur() (servo SG90) |
6. Tâches FreeRTOS
| Tâche | Core | Priorité | Description |
|---|---|---|---|
setup() / loop() | Core 0 | — | Navigation, stratégie, séquencement principal |
detection() | Core 1 | 1 | Lecture sonar, mise à jour du flag obstacle |
La séparation sur deux cores garantit que la lecture ultrason (toutes les 50 ms) ne bloque jamais le calcul des pas moteurs, qui doit être appelé très fréquemment pour ne pas perdre de pas (runStepper() doit tourner en boucle serrée).