====== Objectif de l'activité ======
Construction d'un robot autonome au comportement simple :
* il avance tout droit au démarrage
* s'il détecte un obstacle devant lui il s'arrête
* il fait une mesure de distance à droite, puis une à gauche
* il tourne et part tout droit dans la direction où il a mesuré la plus grande distance
* ... et on recommence...
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
{{ :lespetitshackers:01fadepotarserial.png?nolink&500 |}}
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
{{ :lespetitshackers:02fadeblinkpotarserial.png?nolink&500 |}}
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 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 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 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);
}