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…
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.
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.
En 2013-2014, tous les samedis de 10h00 à 12h00, à Pen ar Créac'h
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
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 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
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); }
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.
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); }
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 }
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) ; }
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); }
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); }