Table des matières

Objectif de l'activité

Construction d'un robot autonome au comportement simple :

Pour Les Petits Hackers, il s'agit d'apprendre, en construisant et en programmant ce robot, les principes de base des circuits électriques, du fonctionnement des micro-contrôleurs, de bricoler avec des composants électroniques, des capteurs, des fers à souder, des moteurs, des leds, des pistolets à colle… Et évidemment d'apprendre aussi des rudiments de programmation : variables, boucles, boucles conditionnelles, fonctions…

Public concerné

Un peu tout le monde… le public de l'atelier Petits Robots est constitué de jeunes âgés de 10-11 ans à 14 ans, des collégiens donc.

Pour cette année 2013-2014, les inscriptions sont closes ; l'atelier compte neuf Petits Hackers, donc neuf robots en construction.

Nous sommes entre 3 et 4 animateurs ; notre expérience montre qu'un tel taux d'encadrement, un adulte pour 3 jeunes, est nécessaire au bon déroulement de l'activité.

Nous essayons de faire en sorte que l'atelier fonctionne en mode collaboratif entre les jeunes : ceux qui vont plus vite sur une tâche aident les autres à la finir. Ce principe fonctionne assez bien.

Matériel utilisé

Une plateforme avec un chassis à deux roues motrices et une roue traînante, un Arduino Uno, shield moteur, capteur ultrason, servo-moteur, leds… Le reste en fonction des évolutions que les jeunes veulent expérimenter pour perfectionner le fonctionnement de leur robot. On parle de capteurs infra-rouge, de buzzers, etc.

Lieu et horaires

En 2013-2014, tous les samedis de 10h00 à 12h00, à Pen ar Créac'h

Premiers pas : un grand classique

blink, bien sûr !

On regarde un Arduino Uno, on essaie d'expliquer simplement de quoi il s'agit. On s'en sert pour alimenter une breadboard, on met une led, puis deux, on fait attention et on met des résistances en série où il faut, une pour chaque led, on modifie le programme pour en mettre encore une autre, on modifie les ordres de clignotement, on fait des chenillards à trois, quatre, cinq leds, on se dit qu'un sapin pour décembre ça serait sympa…

On en profite pour expliquer le principe des résistances, à quoi ça sert (diminuer la force du courant), le principe de la différence de potentiel entre “GND” et le 'pin' d'alimentation et le principe de l'intensité du courant

Les noms de variables c'est du no-limit à cet âge là. Dès la première séance les leds s'appellent pabel, laide, moche… LOL

Que faire avec un potentiomètre ?

Explication du principe du potentiomètre : une résistance variable. Donc on a maintenant une résistance qui doit nous permettre de faire varier l'intensité lumineuse des leds.

On apprend à lire les caractéristiques, à les rechercher éventuellement sur internet. En une à deux séances, on en vient à proposer un montage qui va permettre de lire la valeur de la résistance sur un terminal série au fur et à mesure que l'on manipule le potentiomètre.

Les Petits Hackers sont amenés à réfléchir par eux même à la réalisation du circuit avec le matériel : ils font donc des essais, et les adultes les guident mais ne font pas à leur place. C'est plus long (2 à 3 séances pour les deux circuits qui suivent), mais ça leur permet de mieux comprendre… et on a tout notre temps… Souvent on se retrouve à dessiner les possibilités au tableau et ils essaient ensuite de le faire en vrai.

Les schémas ci-dessous sont donc le résultat des recherches des enfants, pas un modèle qui leur serait distribué en début de séance ; c'est plutôt un guide pour les adultes

* Premier circuit

Explication de la manière dont on manipule une entrée analogique. Faire varier l'intensité de la lumière émise par la led et lire la valeur de la résistance sur le terminal

Circuit 01fadepotarserial

Le code Arduino correspondant :

  
  AnalogInLPH
  Demonstration de la lecture d'un signal analogique sur A0 reliee a un 
  potentiometre qui fait varier l'intensite lumineuse d'une led. 
  On peut lire la valeur sur un terminal serie (0 - 1023)
  
  Le circuit:
  * patte centrale du potentiomètre sur A0
  * une patte latérale sur GND
  * l'autre patte latérale sur +5V
  * LED anode (long) reliée à la patte centrale du potentiomètre 
    avec une résistance en serie
  * LED cathode (court) reliee au GND
  
  voir le circuit  01FadePotarSerial.fzz
  
  
 int sensorPin = A0;    // pin du potentiometre
 int sensorValue = 0;  // la variable pour stocker la valeur (0 - 1023)
 
 void setup() {
   Serial.begin(9600); // ouverture de la liaison serie
 }
 
 void loop() {
   // lecture de la valeur a la sortie du potentiometre:
   sensorValue = analogRead(sensorPin); 
 
   // On peut faire facilement la conversion de la valeur 
   // analogique (0 - 1023)
   // en valeur de tension de courant (0 - 5V) :
   //float voltage = sensorValue * (5.0 / 1023.0);
   
   
   // impression du résultat sur le terminal serie
   Serial.print("sensor = " );                       
   Serial.println(sensorValue);
   //Serial.print("tension = " );                       
   //Serial.print(voltage);
   //Serial.println("V ");

   // on attend un peu pour la suivante
   delay(200); 
  
  }

* Deuxième Circuit

Cette fois on va réutiliser la valeur lue en sortie du potentiomètre pour faire clignoter la led avec un pas de temps variable. On veut montrer aux Petits Hackers comment on peut réutiliser avec l'Arduino une valeur générée par un composant quelconque pour modifier le comportement d'autres composants dans le circuit.

D'après notre expérience, ça marche mieux si on commence les séances par l'examen du code ; on annonce l'objectif, ce que l'on veut que le montage fasse, puis on dissèque le code pour comprendre comment faire et enfin on passe au dessin du circuit, puis au montage

Circuit 02fadeblinkpotarserial

Le code Arduino correspondant :

  
   AnalogFadeBlinkLPH
   Demonstration de la lecture d'un signal analogique sur A0 reliee a un 
   potentiometre qui fait varier l'intensite lumineuse d'une led. 
   On peut lire la valeur sur un terminal serie (0 - 1023) ou (0 - 5V)
   
   La valeur du sensor sert aussi a faire clignoter une LED reliee au 
   pin 13 et l'intervalle du clignotement depend de la valeur lue sur A0
   
   Le circuit:
   * patte centrale du potentiomètre sur A0
   * une patte latérale sur GND
   * l'autre patte latérale sur +5V
   * LED1 anode (long) reliée à la patte centrale du potentiomètre
   * LED1 cathode (court) relièe au GND
   * LED2 anode sur le pin 13
   * LED2 cathode sur le GND
   
   voir le circuit  02FadeBlinkPotarSerial.fzz
   
   
  
  
  int sensorPin = A0;    // pin du potentiometre
  int sensorValue = 0;  // la variable pour stocker la valeur (0 - 1023)
  int ledPin = 13; // pin de la LED à faire clignoter
  float voltage = 0;
  
  void setup() {
    Serial.begin(9600); // ouverture de la liaison serie
    pinMode(ledPin, OUTPUT);
  }
  
  void loop() {
    // lecture de la valeur a la sortie du potentiometre:
    sensorValue = analogRead(sensorPin); 
    
    // allume ledPin :
    digitalWrite(ledPin, HIGH);  
    // on attend <sensorValue> millisecondes :
    delay(sensorValue);          
    // éteind ledPin :        
    digitalWrite(ledPin, LOW);   
    
    // On peut faire facilement la conversion de la valeur 
    // analogique (0 - 1023)
    // en valeur de tension de courant (0 - 5V) :
    voltage = sensorValue * (5.0 / 1023.0);
    
    // impression du résultat sur le terminal serie
    Serial.print("sensor = " );                       
    Serial.println(sensorValue);
    Serial.print("tension = " );                       
    Serial.print(voltage);
    Serial.println("V ");
    
    // on attend le temps d'un clignotement pour la suivante
    delay(sensorValue);
  
  }

Mesurer des distances avec le son

Maintenant que l'on sait comment gérer des entrées et des sorties et que l'on sait comment récupérer des valeurs sur un terminal série, on peut commencer à jouer avec un émetteur/récepteur à ultrasons (“pinger”). On va utiliser un grand standard, le HC-SR04 que l'on trouve facilement et qui n'est pas cher.

On va mesurer la distance entre le pinger et un obstacle placé face à lui, et on va faire afficher cette distance sur l'écran de l'ordinateur connecté à l'arduino, dans un terminal série.

Le pinger émet un signal ultrason inaudible puis il en reçoit l'écho lorsque le son rebondit sur un objet quelconque. Tout ça va très vite, à la vitesse du son dans l'air c'est à dire à 343.5 m/s à 20°C. Donc le son met 29,1 microsecondes à parcourir 1 cm (29µs/cm)

Pour mesurer à quelle distance se trouve l'objet du capteur il faut bien sûr d'abord mesurer le temps écoulé entre l'émission du ping et la réception de l'écho ; appelons ce temps “dt” que l'on exprime en microsecondes (µs). Il faut maintenant considérer que le temps écoulé dt est celui qu'a mis le ping pour atteindre l'objet ET revenir sur notre capteur : dt est donc le temps nécessaire à un aller-retour ! Donc le temps nécessaire à notre mesure est la moitié de dt.

Et donc la distance sera (dt/2)*29,1

On a fait un code basé sur celui de http://trollmaker.com/article3/arduino-and-hc-sr04-ultrasonic-sensor

  /*
   HC-SR04 Pinger et deux leds (rouge et verte)
   pour indiquer si on est au-dessus ou au dessous
   d'une distance limite que l'on fixe.
   Ne pas oublier les résistances sur les leds
   */
  
  #define trigPin 13
  #define echoPin 12
  #define led 11
  #define led2 10
  
  void setup() {
    Serial.begin (9600);
    pinMode(trigPin, OUTPUT);
    digitalWrite(trigPin, LOW);
    
    pinMode(echoPin, INPUT);
    
    pinMode(led, OUTPUT);
    pinMode(led2, OUTPUT);
  }
  
  void loop() {
    long duree, distance;
    
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10); 
    digitalWrite(trigPin, LOW);
    
    duree = pulseIn(echoPin, HIGH);
    
    /* Le son va à la vitesse de 340m/s ; il lui faut donc 29 microsecondes
    pour parcourir 1cm. Notre ping va jusqu'à l'obstacle, puis revient jusqu'au
    capteur, il parcourt donc la distance deux fois : il faut donc diviser
    la durée par deux
    */
    distance = (duree / 2) / 29.1;
    
    if (distance < 7) {  // On va allumer ou éteindre les leds
      digitalWrite(led,HIGH); // la rouge s'allume, 
      digitalWrite(led2,LOW); // et la verte s'éteint
  }
    else {
      digitalWrite(led,LOW); // la rouge s'éteint
      digitalWrite(led2,HIGH); // la verte s'allume
    }
    if (distance >= 200 || distance <= 0){ // attention aux limites du capteur
      Serial.println("Out of range");
    }
    else {
      Serial.print(distance);
      Serial.println(" cm");
    }
    delay(100); // on attend un peu 
  }

le code de la séance

On met ici un peu en vrac les codes à télécharger en séance
Un code de découverte des fonctions

On part de ça et on en construit d'autres pour faire danser le robot

  // Construction de fonctions simples avec les moteurs et 
  // mise en mouvement en utilisant les fonctions
  
  int DIR2 = 4;  // Direction rotation Moteur 2
  int SP2 = 5;   // Vitesse Moteur 2
  
  int SP1 = 6;   // Vitesse Moteur 1
  int DIR1 = 7;  // Direction rotation Moteur 1
  
  // Les pin du HCSR04
  #define trigPin 11
  #define echoPin 12
  
  // Des pin pour des leds
  #define led 13
  
  
  void setup() {
  
    // Tous les pin moteur sont des sorties
    pinMode(4, OUTPUT);  
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    
    // Le pin d'émission du capteur Ultrason
    pinMode(trigPin, OUTPUT);
    digitalWrite(trigPin, LOW);
    
    // Le pin de reception du capteur ultrason
    pinMode(echoPin, INPUT);
    
    // Config des pin led
    pinMode(led, OUTPUT);
  
  }
  
  int vitesse = 200 ;
  
  // Les fonctions de mouvement
  void AVANCE(int vitesse) {
    digitalWrite(DIR1,LOW); // Moteur 1 Gauche : HIGH Arrière & LOW Avant
    analogWrite(SP1,vitesse); // Moteur 1 à fond
    
    digitalWrite(DIR2,LOW); // Moteur 2 Gauche : HIGH Arrière & LOW Avant
    analogWrite(SP2,vitesse); // Moteur 2 à fond
  }
  
  void loop(){
    AVANCE(vitesse) ;
    
  }
Des fonctions sans delay()

On fait marcher le robot en utilisant les fonctions dans la void loop() avec le capteur ultrason et on n'utilise plus la fonction delay()

  // Fonctionnement du robot avec fonctions
    
   // Constantes des moteurs
   #define DIR2 4  // Direction rotation Moteur 2
   #define SP2  5   // Vitesse Moteur 2
   #define SP1  6   // Vitesse Moteur 1
   #define DIR1 7  // Direction rotation Moteur 1
   
   // Constantes pin du capteur US HCSR04
   #define trigPin 11
   #define echoPin 12
    
   // Declaration du servomoteur
   #include <Servo.h> 
   Servo flo;  
   int pos = 0;     
   
   // Des pin pour des leds
   #define led 13
    
   //la valeur de la vitesse
   #define speed1 100 // vitesse moteur droit
   #define speed2 100 // vitesse moteur gauche
   
   //variables pour la detection des obstacles
   const int obstacle = 7;
   long distG = 0;
   long distD = 0;
   long repere = 0;
   
   
   
   //gestion des intervalles de temps
   long currentMillisMove = 0;
   long currentMillisG = 0;
   long currentMillisD = 0;
   long previousMillis = 0; // 
   long interval = 1000; // 1 seconde
    
  void setup() {
    
     // Tous les pin moteur sont des sorties
     pinMode(4, OUTPUT);  
     pinMode(5, OUTPUT);
     pinMode(6, OUTPUT);
     pinMode(7, OUTPUT);
      
     // Le pin d'émission du capteur Ultrason
     pinMode(trigPin, OUTPUT);
     digitalWrite(trigPin, LOW);
     Serial.begin (9600);
      
     // Le pin de reception du capteur ultrason
     pinMode(echoPin, INPUT);
      
     // Config des pin led
     pinMode(led, OUTPUT);
     
     // Config du servo sur pin 9
     flo.attach(9);  
  }
    
    
   // Les fonctions de mouvement
   //fonction pour avancer
   void AVANCE(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
   // fonction pour reculer
   void RECULE(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
    
   // fonction pour tourner sur place
   void SUR_PLACE_G(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
   // fonction pour tourner sur place
   void SUR_PLACE_D(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
     
     digitalWrite(DIR2,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
    
   // fonction pour détecter
   /* Le son va à la vitesse de 340m/s ; il lui faut donc 29 microsecondes
    pour parcourir 1cm. Notre ping va jusqu'à l'obstacle, puis revient jusqu'au
    capteur, il parcourt donc la distance deux fois : il faut donc diviser
    la durée par deux. Soit 58 ms / cm.
   */ 
   int DETECTE () {
     int duree ;
     digitalWrite(trigPin, HIGH);
     delayMicroseconds(10); 
     digitalWrite(trigPin, LOW);
     duree = pulseIn(echoPin, HIGH);
     return(duree / 58) ;
   }
   // fonction pour tourner la tete
   void TETE (int p) {
   flo.write(p); 
   }
  
  void loop(){
   
    TETE (67);
    //Si on voit un obstacle à moins de dist.obst cm on s'arrete
    repere = DETECTE();
    if (repere < obstacle) {
      
      currentMillisMove = millis();
      while(millis() < currentMillisMove + interval) AVANCE (0,0);
      
      // Puis on recule 
      currentMillisMove = millis();
      while(millis() < currentMillisMove + (interval/2)) RECULE (100, 100);
      
      // on tourne la tete a gauche
      currentMillisG = millis();
      while(millis() < currentMillisG + interval)  {
        TETE(180);
        distG = DETECTE();
      }
      
      // on tourne la tete a droite
      currentMillisD = millis();
      while(millis() < currentMillisD + interval)  {
        TETE(0);
        distD = DETECTE();      
      }
      
      //
      SUR_PLACE_D(100,100);
      delay(500);
    }
  
    AVANCE(speed1, speed2); 
  
  }
Et le code final

Normalement, moyennant des réglages sur les prises moteurs et les vitesses, ce code ci fonctionne

  // Fonctionnement du robot avec fonctions
    
   // Constantes des moteurs
   #define DIR2 4  // Direction rotation Moteur 2
   #define SP2  5   // Vitesse Moteur 2
   #define SP1  6   // Vitesse Moteur 1
   #define DIR1 7  // Direction rotation Moteur 1
   
   // Constantes pin du capteur US HCSR04
   #define trigPin 11
   #define echoPin 12
    
   // Declaration du servomoteur
   #include <Servo.h> 
   Servo flo;  
   int pos = 0;     
   
   // Des pin pour des leds
   #define led 13
    
   //la valeur de la vitesse
   #define speed1 100 // vitesse moteur droit
   #define speed2 100 // vitesse moteur gauche
   
   //variables pour la detection des obstacles
   const int obstacle = 7;
   //long distG = 0;
   //long distD = 0;
   long repere = 0;
   int k = 0;
   
   
   
   //gestion des intervalles de temps
   long currentMillisMove = 0;
   long currentMillisG = 0;
   long currentMillisD = 0;
   long previousMillis = 0; 
   long currentMillisV = 0;
   long interval = 1000; // 1 seconde
    
  void setup() {
    
     // Tous les pin moteur sont des sorties
     pinMode(4, OUTPUT);  
     pinMode(5, OUTPUT);
     pinMode(6, OUTPUT);
     pinMode(7, OUTPUT);
      
     // Le pin d'émission du capteur Ultrason
     pinMode(trigPin, OUTPUT);
     digitalWrite(trigPin, LOW);
     Serial.begin (9600);
      
     // Le pin de reception du capteur ultrason
     pinMode(echoPin, INPUT);
      
     // Config des pin led
     pinMode(led, OUTPUT);
     
     // Config du servo sur pin 9
     flo.attach(9);  
  }
    
    
   // Les fonctions de mouvement
   //fonction pour avancer
   void AVANCE(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
   // fonction pour reculer
   void RECULE(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
    
   // fonction pour tourner sur place
   void SUR_PLACE_G(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
      
     digitalWrite(DIR2,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
   // fonction pour tourner sur place
   void SUR_PLACE_D(int lavitesse1,int lavitesse2) {
     digitalWrite(DIR1,LOW); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP1,lavitesse1); // Moteur 1 à fond
     
     digitalWrite(DIR2,HIGH); // Moteur 2 Gauche : HIGH Avant & LOW Arrière
     analogWrite(SP2,lavitesse2); // Moteur 2 à fond
   }
    
   // fonction pour détecter
   /* Le son va à la vitesse de 340m/s ; il lui faut donc 29 microsecondes
    pour parcourir 1cm. Notre ping va jusqu'à l'obstacle, puis revient jusqu'au
    capteur, il parcourt donc la distance deux fois : il faut donc diviser
    la durée par deux. Soit 58 ms / cm.
   */ 
   int DETECTE () {
     int duree ;
     digitalWrite(trigPin, HIGH);
     delayMicroseconds(10); 
     digitalWrite(trigPin, LOW);
     duree = pulseIn(echoPin, HIGH);
     return(duree / 58) ;
   }
   // fonction pour tourner la tete
   void TETE (int p) {
   flo.write(p); 
   }
  
  void loop(){
   
    TETE (67);
    //Si on voit un obstacle à moins de dist.obst cm on s'arrete
    repere = DETECTE();
    
    if (repere < obstacle) {
      
      // stoppe pendant interval
      currentMillisMove = millis();
      while(millis() < currentMillisMove + interval) AVANCE (0,0);
      
      // Puis on recule pendant interval/2
      currentMillisMove = millis();
      while(millis() < currentMillisMove + (interval/2)) RECULE (100, 100);
  
      // stoppe pendant interval
      currentMillisMove = millis();
      while(millis() < currentMillisMove + 2*interval) AVANCE (0,0);   
      
      // on tourne la tete a gauche
      //currentMillisG = millis();
      //while(millis() < currentMillisG + interval/2)  {
        TETE(180);
        int distG = DETECTE();
        delay(600);
        //Serial.print("distG");Serial.println(distG);
      //}
      
      // on tourne la tete a droite
      //currentMillisD = millis();
      //while(millis() < currentMillisD + interval/2)  {
        TETE(0);
        int distD = DETECTE(); 
        delay(600);
        //Serial.print("distD");Serial.println(distD);    
      //}
      
      // on remet la tete droite
      TETE(67);
      
      int dist = distD - distG;
      if (dist >= 0) { 
      k=1;
    }
      else{
      k=2;
    }
      //Serial.print("k=");Serial.println(k);
      
      switch (k) {
      case 1:
        SUR_PLACE_D(100,100);
        delay(700);
        break;
      case 2:
        SUR_PLACE_G(100,100);
        delay(700);
        break;
      //default: 
        // if nothing else matches, do the default
        // default is optional
    }
  
      
  //    if (distD >= distG){
  //      currentMillisV =millis();
  //      while(millis() > currentMillisV + interval/3) {
  //        SUR_PLACE_D(100,100);
  //      }
  //    }
  //    else {
  //      currentMillisV =millis();
  //      while(millis() > currentMillisV + interval/3) {
  //        SUR_PLACE_G(100,100);
  //      }
  //    }
  
    }
  
    AVANCE(100, 100); 
  
  }