Programmation

La programmation des PAMIs va consister à donner des ordres afin de respecter les règles des maths de la CDR. C’est à dire une immobilité totale pendant 90 secondes puis 10 secondes d’action. Pendant ces 10 secondes d’action, le robot doit rejoindre une zone de dépot et si possible toucher une fleur. Ces deux conditions réunies, le robot gagne 10 points : 5 points pour la zone et 5 points pour le contact avec une plante

L’IDE Arduino a été le choix car c’est un IDE simple à configurer et très intuitif, permettant de coder en C/C++. Presque Plug & Play, qui permet de commencer le code rapidement sans perdre de temps. Pour rappel, le code des PAMIs a réellement commencé a être développé la veille de la coupe. Il fallait donc doubler d’éfficacité pour rendre les robots fonctionnels pour le premier match.

Par où commencer le code ?

La première chose à faire est de définir l’ensemble des GPIO utilisés et de configurer leur mode de fonctionnement. Il est important d’attribuer les GPIO en correlation avec le PCB utilisé, certains ne peuvent pas être exploités car ils ne sont pas reliés au PCB. Mais certains GPIO ont des spécificités que d’autres n’ont pas, comme la fonction ADC de l’ESP32 qui permet de lire des tensions analogiques. Il faut donc veiller à utiliser les bons GPIO dans le code et bien les relier au PCB.

La seconde chose à faire et d’essayer chaque élement du robot indépendemment des autres. Cela permet un code simple à mettre en place et permet la vérification du bon fonctionnement de chacun.

Servomoteurs

Après avoir calibré les servomoteurs, il faut les essayer avec un code permettant de tester les deux directions et faire varier la vitesse. La librairie ESP32Servo.h est utilisée, c’est l’équivalent de la librairie Servo.h utilisée avec une carte Arduino. Cela permet de les contrôler facilement.

//Mise à l'arrêt
GAUCHE.write(90);
DROITE.write(90);

Pour vérifier le neutre des servomoteurs. Si le calibrage est réussi, ils sont à l’arrêt. Puis avec le code suivant, permet au robot d’avancer.

#include <Arduino.h>
#include <ESP32Servo.h>

//Définition GPIO
#define SERVOG 12
#define SERVOD 14

//Déclaration variables de classe
Servo GAUCHE;
Servo DROITE;

void setup(){
    //Mode de fonctionnement GPIO
    pinMode(SERVOG, OUTPUT);
    pinMode(SERVOD, OUTPUT);

    //Configuration du Servo gauche
    GAUCHE.setPeriodHertz(50); // Fréquence PWM pour le GAUCHE
    GAUCHE.attach(SERVOG, 500, 2400); // Largeur minimale et maximale de l'impulsion (en µs) pour aller de 0° à 180°

    //Configuration du Servo droite
    DROITE.setPeriodHertz(50); // Fréquence PWM pour le GAUCHE
    DROITE.attach(SERVOD, 500, 2400); // Largeur minimale et maximale de l'impulsion (en µs) pour aller de 0° à 180°
}

void loop(){
    //Mise à l'arrêt
    GAUCHE.write(90);
    DROITE.write(90);

    //Attendre 1 seconde
    delay(1000);

    //Vers l'avant
    GAUCHE.write(180);
    DROITE.write(0);
}

Plus la valeur se rapproche de 90 plus la vitesse est réduit. La valeur 0 traduit un sens de rotation et 180 le sens de rotation opposé. En fonction du placement des servomoteurs, le sens de rotation de l’un doit être inversé par rapport à l’autre pour permettre d’avancer.

Capteur à ulrasons

Le capteur à ultrasons permet de calculer la distance du robot de l’obstacle. Il faut réaliser un code permettant la meilleure des précisions. La seule particularité est de brancher la partie Echo du capteur sur un pin de type ADC de l’ESP32. Et essayer le code suivant pour vérifier le fonctionnement :

#include <Arduino.h>

//Capteurs Ultrasons :
#define CAPF_Echo 15
#define CAPF_Trig 16

//Variables de calcul de distance
long duration, distance; 

float sensor_face(){
    // Envoie de l'onde 
    digitalWrite(CAPF_Trig, LOW); 
    delayMicroseconds(2); 
    digitalWrite(CAPF_Trig, HIGH); 
    delayMicroseconds(10); 
    digitalWrite(CAPF_Trig, LOW); 
    // Réception de l'écho 
    duration = pulseIn(CAPF_Echo, HIGH); 
    // Calcul de la distance 
    distance = (duration/2) / 29.1; 
    return distance;
}

void setup(){
    //Fréquence flux de données
    Serial.begin(115200);

    //Récupération signal sonore
    pinMode(CAPF_Echo, INPUT);
    //Envoie signal sonore
    pinMode(CAPF_Trig, OUTPUT);
}

void loop(){
    float distance = sensor_face();
    Serial.println(distance);
}

La distance séparant le capteur de l’objet devrait s’afficher dans le Serial Monitor.

Tirette et bouton d’équipe

La tirette magnétique permet le lancement du robot en tirant dessus, elle se base sur un fonctionnement simple. C’est un interrupteur qui se ferme à l’approche d’un aimant et reste en postion ouverte sinon. A partir de ce principe, il suffit de lire la tension de l’une des broches de la tirette relié à un pin ADC de l’ESP32 configuré avec une résistance de tirage, tandis que l’autre est reliée à la masse.

Le bouton d’équipe sert à chosir l’équipe, soit bleu soit jaune. Il suit exactement le même principe que la tirette à la différence qu’il faut appuyer dessus pour faire basculer son état.

La résistance de tirage à un rôle important dans ce montage, elle consiste à forcer un état haut si l’interrupteur est ouvert, mais l’interrupteur peut provoquer l’état bas en se fermant. Sans celle-ci, la tention mesurée est inconnue et va varier, l’entrée est dite “flottante”. Pour lever cette indetermination, il faut ajouter cette resistance. Sa valeur standard est de 10kΩ mais inutile de l’ajouter dans le montage. Certains microcontrolleurs possèdent déja des résistances internes et vont grâce à l’odre donné dans le code, les implémenter et les placer en tirage. L’ESP32 possède déjà des composants internes et les résistances internes en font partie.

PAMI

Voici comment définir le fonctionnement d’un GPIO avec une résistance de tirage :

pinMode(X, INPUT_PULLUP);
int val = digitalRead(X);

La variable “val” est un entier, elle vaut “1” quand l’interrupteur est ouvert et “0” quand l’interrupteur est fermé. A partir de cette variable, il est possible d’ajouter des conditions et vérifier l’état du robot à un instant donné.

Timer et Delay

AU début d’un match, les PAMIs restent immobiles pendant 90 secondes puis se déplace pour faire leurs actions pendant 10 secondes. Le robot par trop tôt, il n’est pas comptabilisé, s’il s’arrète trop tard aussi. Il faut un moyen précis de mesurer le temps écoulé pendnant le match. Pour celà, 2 moyens vont être mis en place, un délais avec la fonction “delay()” et la mise en place d’un timer intégré à l’ESP32 plus complexe.

La fonction “delay()”, est une fonction permettant de mettre en pose le code pour une certaine durée. Sa base temporelle est la milliseconde. C’est un moyen très éfficace de mettre une pause dans le code pendant une durée très précise. La particularité de cette fonction est qu’elle est bloquante, c’est à dire que aucun code peut être éxecuté pendant cette durée. Cela peut poser problème quand la batterie se met en veille pour inactivité. Voici comment mettre une pause de 90 secondes :

delay(90*1000); //90 sec * 1000ms

Un timer est un compteur électronique capable de compter le temps qui s’écoule de manière très précise. En fait, c’est un registre à l’intérieur d’un microcontrôleur qui s’incrémente chaque fois qu’il reçoit une impulsion d’un signal d’horloge générée en interne par le microcontrôleur. Pour en apprendre plus sur ce concept complexe, une documentation complète du constructeur existe. Il ya 4 timers indépendants sur cet ESP32, seul le timer 0 sera utilisé. Voici comment s’en servir avec un timer comptant jusqu’à 10 secondes:

#include <Arduino.h>

//Timer 0 PAMI (10sec)
volatile int Interruption0; // For counting interrupt
hw_timer_t *timer0 = NULL; // H/W timer defining (Pointer to the Structure)
portMUX_TYPE timerMux0 = portMUX_INITIALIZER_UNLOCKED; // Timer

//Fonction d'interruption timer 0
void IRAM_ATTR onTimer0(){ // Defining Inerrupt function with IRAM_ATTR for faster access
    portENTER_CRITICAL_ISR(&timerMux0);
    Interruption0++;
    portEXIT_CRITICAL_ISR(&timerMux0);
}

void setup(){
    //Mise en place du Timer 0
    timer0 = timerBegin(0, 80, true);             // timer 0, 80Mhz base, UP counting
    timerAttachInterrupt(timer0, &onTimer0, true); // Attach interrupt
    timerAlarmWrite(timer0, 1000000*10, true);     // Match value = 1000000 for 1 sec and * 10 sec, true for auto reload
    timerAlarmEnable(timer0);                     // Enable Timer with interrupt (Alarm Enable)
}
void loop(){
    //Detection interruption Timer 0
    if (Interruption0 > 0){
        //Arreter PAMI ici
        portENTER_CRITICAL(&timerMux0);
        Interruption0--;
        portEXIT_CRITICAL(&timerMux0);
        timerAlarmDisable(timer0); // Disable Timer with interrupt (Alarm Disable)
        exit(EXIT_SUCCESS);
  }
}

La structure du code n’est pas simple et intuitive mais très éfficace pour ce qui est de sa précision. De plus, la fonction d’interruption permet de ne pas bloquer le code et permettre des actions pendant ce temps. Ce timer était au moins nécessaire pour les 10 dernières secondes de match.

Voilà l’essentiel du code afin de programmer des PAMIs ayant un objectif précis. A partir de ces morceaux de code, il est possible d’orchestrer un match entier. C’est donc pour cette raison qu’une stratégie doit être mise en place en amont.

Statégie

Pour ce qu’il s’agit de la stratégie, il fallait être en phase avec notre robot qui lui met les plantes dans les jardinières, il fallait donc au moins 2 PAMIs qui vont aux jardinières et au moins une qui se retrouve dans une zone de dépose. Mettre 2 PAMIs sur une jardinières est un pari trop risqué, il a été préférable de perdre 5 points à l’avance mais de s’assurer une régularité.

Le robot le moins fonctionnel fait l’action la plus facile, c’est à dire qu’il sera PAMIs 1 et ira tout droit dans une zone de dépose. Les deux autres font dans deux jardinières différentes en suivant une trajectoire avec un angle particulié. Cette stratégie peut remporter 25 points quand tous les robots vont dans leurs zones et que deux PAMIs soient en contact avec des plantes.

Voici le déroulement de la stratégie sur les 10 dernières secondes de match :

Là RObz faut bricoler un truc avec la strat

En somme, le premier match n’a pas été concluant, l’équipe bleu était imposée, c’était les trajectoires les mieux réussies dans le code. Le PAMIs le moins fonctionnel qui doit aller tout droit est le seul à marquer des points, les autres se sont arrêtés en cours de route à cause du capteur. Par la suite, celui-ci a été desactivé pour éviter ce problème. Le 2ème match était aussi peu concluant, car l’équipe jaune était imposée, sur cette équipe l’un des robots allant aux jardinières ne pouvait pas tourner dans le bon sens dû à une raison obscure. Puis, le second robot allant aux jardinières a prit une trajectoire plus courte à cause de son pneu qui s’est déplacé. Mais le robot allant tout droit à bien fonctionner.

Néanmoins, il existe des essais en vidéo réalisés la veille où les robots fonctionnaient parfaitement, voici ce que cela devait donner :

Vidéo YT d’un perfect :