Porteur(s) du projet: Maxime Farin (contact : maxime.farin1@gmail.com)
L'objectif de ce projet d'électronique est de créer un robot détecteur de présence qui se déplace dans une pièce et évite les obstacles lorsqu'il en rencontre. Les “yeux” du robot sont un capteur à ultrasons (par exemple HC-SR04). Ce capteur est connecté à une carte Arduino. Lorsque le capteur envoie l'information à l'Arduino qu'il y a un obstacle devant lui, l'Arduino doit envoyer une instruction aux moteurs qui contrôlent les roues pour éviter l'obstacle.
Si ce projet est un succès, une variante de ce projet serait un robot qui évite les obstacles et détecte et suit les personnes à l'aide d'un capteur infrarouge.
Ce projet est en partie inspiré de ce tutoriel.
Date de début du projet : 24 janvier 2018.
Durée du projet : plusieurs mois (?)
L'idée est d'utiliser une carte Arduino pour lire les données reçues par un, ou plusieurs, capteur(s) à ultrasons. Lorsque le capteur détecte un obstacle devant lui, l'Arduino doit envoyer des instructions aux moteurs qui pilotent les roues du robot pour l'éviter. Le chassis du robot serait en partie constitué de LEGO Technic et l'électronique (Arduino, capteur à ultrasons, moteurs,…) serait fixé au chassis à l'aide de pièces imprimées en 3D qui s'adaptent aux LEGOs.
- 1 Arduino Uno ou similaire
- 1 ou plusieurs capteurs à ultrasons HC-SR04
- Des moteurs DC pour piloter les roues du robot
- Un driver de moteurs DC (par exemple, Dual TB6612FNG (1A))
- Un petit servo-moteur pour faire pivoter le capteur à ultrasons (la tête du robot)
- Une pile de 9V et des connecteurs pour alimenter les moteurs DC.
- des câbles jumper, résistances, LED, une breadboard pour tester le circuit électronique.
- des LEGO Technic ou du carton rigide pour construire le chassis du robot.
- Une imprimante 3D pour imprimer des pièces qui permettront d'adapter les moteurs DC à des LEGO Technic.
Le fichier que nous allons imprimer est disponible sur Thingverse
On va commencer par tester le capteur à ultrasons. On écrit un petit programme pour allumer une LED lorsque le capteur voit un obstacle à moins de 2 mètres.
Pour comprendre comment mesurer une distance à l'aide du capteur HC-SR04, vous pouvez suivre ce petit tutoriel
Schéma du montage :
<note>La LED doit s'allumer lorsque le capteur “voit” un obstacle à moins de 2 mètre (ou n'importe quelle distance entre 10 cm et 4 m fixée dans le programme).</note>
Code Arduino :
/* * Code d'exemple pour un capteur à ultrasons HC-SR04. */ /* Constantes pour les broches */ const byte TRIGGER_PIN = 2; // Broche TRIGGER const byte ECHO_PIN = 3; // Broche ECHO const byte LED_PIN = 12; // Broche LED /* Constantes pour le timeout: le temps à partir duquel le capteur considère qu'il n'y a aucun obstacle à portée (temps de parcours d'environ 25 ms soit 8m aller-retour. Le capteur n'est pas sensible au delà de 4 m. */ const unsigned long MEASURE_TIMEOUT = 50000UL; // 25ms = ~8m a 340m/s /* Vitesse du son dans l'air en mm/microsecondes */ const float SOUND_SPEED = 340.0 / 1000; /** Fonction setup() */ void setup() { /* Initialise le port serie */ Serial.begin(115200); /* Initialise les broches */ pinMode(TRIGGER_PIN, OUTPUT); digitalWrite(TRIGGER_PIN, LOW); // La broche TRIGGER doit etre LOW au repos pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); pinMode(ECHO_PIN, INPUT); } /** Fonction loop() */ void loop() { /* 1. Lance une mesure de distance en envoyant une impulsion HIGH de 10 microsec sur la broche TRIGGER */ digitalWrite(TRIGGER_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER_PIN, LOW); /* 2. Mesure le temps entre l'envoi de l'impulsion ultrasonique et son echo (s'il existe) */ long measure = pulseIn(ECHO_PIN, HIGH, MEASURE_TIMEOUT); /* 3. Calcule la distance à partir du temps mesuré */ float distance_mm = measure / 2.0 * SOUND_SPEED; if(distance_mm/1000 < 2){ // CHANGER ICI LA DISTANCE DE DETECTION D'OBSTACLES digitalWrite(LED_PIN, HIGH); // si un obstacle est à moins de 2m, allume la LED } // Affiche les résultats en m Serial.print(F("Distance: ")); Serial.print(distance_mm / 1000.0, 2); Serial.println(F("m)")); /* Délai d'attente pour éviter d'afficher trop de résultats à la seconde */ delay(200); digitalWrite(LED_PIN, LOW); }
A l'aide d'une carte électronique Dual TB6612FNG (1A) (motor driver), on peut piloter deux moteurs DC avec la carte Arduino et leur envoyer des instructions de vitesse et de sens de rotation
La puce en question, dispo par exemple sur SparksFun :
Schéma du montage :
Les ports du motor driver Dual TB6612FNG :
- Les ports GND doivent être connectés au GND de l'Arduino - Le port VCC est connecté au port 5V de l'Arduino pour alimenter la puce. - Pour piloter les moteurs, on utilise une pile de 9V, le + de la pile est connecté à VM et le - au GND de l'Arduino. - Les deux connecteurs des moteurs DC doivent branchés sur A01 et A02 pour le moteur A et B01 et B02 pour le moteur B. - Les ports de gauche sont les ports qui permettent de contrôler la vitesse et le sens de rotation des moteurs. Ils sont connectés à des ports digitaux en sortie de l'Arduino. Notez que les ports PWMA et PWMB contrôlent la vitesse des moteurs en envoyant une valeur entre 0 et 255 (0 pour une vitesse nulle et 255 pour une vitesse maximale). Ces ports de sortie doivent donc être des ports PWM de l'Arduino (avec un ~). Les ports AIN1, AIN2, BIN1 et BIN2 contrôlent le sens de rotation des moteurs (voir le code ci-dessous).
Code Arduino :
//Moteur A connecté entre les pins A01 et A02 du Motor Driver TB6612FNG //Moteur B connecté entre les pins B01 et B02 du Motor Driver TB6612FNG int STBY = 10; // standby Pin (si HIGH alors on peut faire tourner les moteurs, si LOW, les moteurs s'arrêtent) //Motor A int PWMA = 5; //Pin qui contrôle la vitesse du moteur A (nombre entre 0 et 255 donc on doit le mettre sur un port PWM) // AIN1 et AIN2 sont les pins de sens de rotation du moteur A // Le moteur A tourne dans le sens horaire si le pin AIN1 est LOW et le pin AIN2 est HIGH // Inversement, si AIN1 est HIGH et AIN2 est LOW, il tourne dans le sens antihoraire // (si les deux sont LOW ou les deux sont HIGH, il ne tourne pas) int AIN1 = 9; //Sens de rotation int AIN2 = 8; //Sens //Motor B int PWMB = 3; //Pin qui contrôle la vitesse du moteur B // Pins de sens de rotation du moteur B int BIN1 = 11; //Sens int BIN2 = 12; //Sens void setup() { // On définit tous les ports comme sorties pour piloter les moteurs pinMode(STBY, OUTPUT); // Commandes Moteur A pinMode(PWMA, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); // Commandes Moteur B pinMode(PWMB, OUTPUT); pinMode(BIN1, OUTPUT); pinMode(BIN2, OUTPUT); } void loop() { move(1, 200, 1); //motor 1, full speed, left move(2, 200, 1); //motor 2, full speed, left delay(5000); //on les laisse tourner 5 seconde stop(); // on stoppe les moteurs delay(250); //hold for 250ms until move again move(1, 128, 0); //motor 1, half speed, right move(2, 128, 0); //motor 2, half speed, right delay(1000); stop(); delay(250); } void move(int motor, int speed, int direction) { // Function move(): Move specific motor at speed and direction // // Inputs: // motor: 0 for B and 1 for A // speed: number between 0 and 255: 0 is off, and 255 is full speed // direction: 0 clockwise, 1 counter-clockwise // DEMARRAGE digitalWrite(STBY, HIGH); //On met ce Pin sur HIGH avant de démarrer les moteurs // SENS DE ROTATION // Sens Horaire // Le moteur A tourne dans le sens horaire si le pin AIN1 est LOW et le pin AIN2 est HIGH (si les deux sont LOW ou les deux sont HIGH, il ne tourne pas) boolean inPin1 = LOW; boolean inPin2 = HIGH; // Sens Anti-Horaire if (direction == 1) { // Si on change la direction dans le sens antihoraire inPin1 = HIGH; inPin2 = LOW; } // ENVOI DES INSTRUCTIONS DE SENS DE ROTATION ET DE VITESSE AUX MOTEURS if (motor == 1) { // MOTEUR A digitalWrite(AIN1, inPin1); digitalWrite(AIN2, inPin2); analogWrite(PWMA, speed); } else { // if motor == 0 (MOTEUR B) digitalWrite(BIN1, inPin1); digitalWrite(BIN2, inPin2); analogWrite(PWMB, speed); } } void stop() { //Function stop(): Appeler cette fonction dans le programme pour stopper les moteurs digitalWrite(STBY, LOW); }
Maintenant que l'on sait comment lire les données de distance du capteur à ultrasons et comment piloter les moteurs, nous pouvons maintenant combiner les scripts précédents pour donner les instructions aux moteurs du robot. Il faut aussi modifier le montage pour incorporer le capteur à ultrasons et les moteurs.
Schéma du montage :
- Lorsqu'il ne voit pas d'obstacles devant lui, c'est-à-dire si le capteur à ultrasons retourne une distance supérieure à 20 cm (par exemple), le robot avance tout droit. Les instructions à lui donner sont donc :
<note>
move(1, 100, 1); //motor 1, full speed, left move(0, 100, 1); //motor 2, full speed, left
</note>
- Lorsque le robot voir un obstacle, on le fait s'arrêter avec l'instruction stop, puis reculer pendant 3s en faisant tourner les moteurs dans le sens opposé :
<note>
move(1, 50, 0); //motor 1, recule move(0, 50, 0); //motor 2, recule delay(3000); // recule pendant 2s
</note>
Enfin, on fait tourner le robot en faisant tourner ses deux roues dans un sens opposé pendant 1.5s, avant de s'arrêter et de repartir tout droit. Pour chaque manoeuvre, on calcule un nombre aléatoire entre 0 et 100. Si le nombre est inférieur à 50, on tourne à gauche sinon on tourne à droite.
<note>
randNumber = random(100.0); // On fait tourner le robot dans une direction aléatoire if (randNumber < 50.0) { move(1, 100, 1); //motor 1, tourne à gauche move(0, 100, 0); //motor 2, tourne } else { move(1, 100, 0); //motor 1, tourne à droite move(0, 100, 1); //motor 2, tourne } delay(1500); // tourne pendant 1s stop(); // Stopper les moteurs
</note>
Voilà le code complet ci-dessous :
Code Arduino :
/* Code pour piloter des moteurs quand le capteur à ultrasons détecte un obstacle */ /* Robot éviteur d'obstacles */ //Moteur A connecté entre les pins A01 et A02 du Motor Driver TB6612FNG //Moteur B connecté entre les pins B01 et B02 du Motor Driver TB6612FNG int STBY = 10; // standby Pin (si HIGH alors on peut faire tourner les moteurs, si LOW, les moteurs s'arrêtent) //Motor A int PWMA = 5; //Pin qui contrôle la vitesse du moteur A (nombre entre 0 et 255 donc on doit le mettre sur un port PWM) // AIN1 et AIN2 sont les pins de sens de rotation du moteur A // Le moteur A tourne dans le sens horaire si le pin AIN1 est LOW et le pin AIN2 est HIGH // Inversement, si AIN1 est HIGH et AIN2 est LOW, il tourne dans le sens antihoraire // (si les deux sont LOW ou les deux sont HIGH, il ne tourne pas) int AIN1 = 9; //Sens de rotation int AIN2 = 8; //Sens //Motor B int PWMB = 6; //Pin qui contrôle la vitesse du moteur B // Pins de sens de rotation du moteur B int BIN1 = 11; //Sens int BIN2 = 12; //Sens /* Constantes pour les broches */ const byte TRIGGER_PIN = 2; // Broche TRIGGER const byte ECHO_PIN = 3; // Broche ECHO const byte LED_PIN = 7; // Broche LED /* Constantes pour le timeout */ const unsigned long MEASURE_TIMEOUT = 50000UL; // 25ms = ~8m a 340m/s /* Vitesse du son dans l'air en mm/microsecondes */ const float SOUND_SPEED = 340.0 / 1000; long randNumber; /** Fonction setup() */ void setup() { /* Initialise le port serie */ Serial.begin(115200); /* Capteur Ultrasons */ // Initialise les broches pour le capteur à ultrasons pinMode(TRIGGER_PIN, OUTPUT); digitalWrite(TRIGGER_PIN, LOW); // La broche TRIGGER doit etre LOW au repos pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); pinMode(ECHO_PIN, INPUT); /* Moteurs */ // On définit tous les ports comme sorties pour piloter les moteurs pinMode(STBY, OUTPUT); // Commandes Moteur A pinMode(PWMA, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT); // Commandes Moteur B pinMode(PWMB, OUTPUT); pinMode(BIN1, OUTPUT); pinMode(BIN2, OUTPUT); } void loop() { move(1, 100, 1); //motor 1, full speed, left move(0, 100, 1); //motor 2, full speed, left /* 1. Lance une mesure de distance en envoyant une impulsion HIGH de 10 microsec sur la broche TRIGGER */ digitalWrite(TRIGGER_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER_PIN, LOW); /* 2. Mesure le temps entre l'envoi de l'impulsion ultrasonique et son echo (s'il existe) */ long measure = pulseIn(ECHO_PIN, HIGH, MEASURE_TIMEOUT); /* 3. Calcule la distance à partir du temps mesuré */ float distance_mm = measure / 2.0 * SOUND_SPEED; if (distance_mm / 1000 < 0.2) { digitalWrite(LED_PIN, HIGH); // si un obstacle est à moins de 30 cm stop(); // Stopper les moteurs delay(2000); //hold for 250ms until move again move(1, 50, 0); //motor 1, recule move(0, 50, 0); //motor 2, recule delay(3000); // recule pendant 2s stop(); // Stopper les moteurs delay(1000); //hold for 250ms until move again // On fait tourner le robot dans une direction aléatoire randNumber = random(100.0); if (randNumber < 50.0) { move(1, 100, 1); //motor 1, tourne à gauche move(0, 100, 0); //motor 2, tourne } else { move(1, 100, 0); //motor 1, tourne à droite move(0, 100, 1); //motor 2, tourne } delay(1500); // tourne pendant 1s stop(); // Stopper les moteurs delay(1000); //hold for 250ms until move again digitalWrite(LED_PIN, LOW); } // // Affiche les résultats en m // Serial.print(F("Distance: ")); // Serial.print(distance_mm / 1000.0, 2); // Serial.println(F("m)")); /* Délai d'attente pour éviter d'afficher trop de résultats à la seconde */ delay(200); } void move(int motor, int speed, int direction) { // Function move(): Move specific motor at speed and direction // // Inputs: // motor: 0 for B and 1 for A // speed: number between 0 and 255: 0 is off, and 255 is full speed // direction: 0 clockwise, 1 counter-clockwise // DEMARRAGE digitalWrite(STBY, HIGH); //On met ce Pin sur HIGH avant de démarrer les moteurs // SENS DE ROTATION // Sens Horaire // Le moteur A tourne dans le sens horaire si le pin AIN1 est LOW et le pin AIN2 est HIGH (si les deux sont LOW ou les deux sont HIGH, il ne tourne pas) boolean inPin1 = LOW; boolean inPin2 = HIGH; // Sens Anti-Horaire if (direction == 1) { // Si on change la direction dans le sens antihoraire inPin1 = HIGH; inPin2 = LOW; } // ENVOI DES INSTRUCTIONS DE SENS DE ROTATION ET DE VITESSE AUX MOTEURS if (motor == 1) { // MOTEUR A digitalWrite(AIN1, inPin1); digitalWrite(AIN2, inPin2); analogWrite(PWMA, speed); } else { // if motor == 0 (MOTEUR B) digitalWrite(BIN1, inPin1); digitalWrite(BIN2, inPin2); analogWrite(PWMB, speed); } } void stop() { //Function stop(): Appeler cette fonction dans le programme pour stopper les moteurs digitalWrite(STBY, LOW); }
La partie programmation est maintenant presque terminée. On peut à l'avenir rajouter un servo-moteur sur lequel on fixe le capteur à ultrasons pour balayer une zone plus large de vision devant le robot. On peut aussi rajouter un capteur à ultrasons derrière le robot pour éviter que le robot ne recule sur un mur. En effet, tel que le code est maintenant écrit, le robot ne “regarde” pas lorsqu'il manoeuvre.
Il faut maintenant construire le chassis du robot.
Il faut ensuite construire un chassis pour porter notre électronique.
Il y a pas de chassis type mais voici quelques astuces pour le construire:
<note tip>
- Placer le capteur à ultrasons en hauteur par rapport au sol (au moins 10 cm) sinon le capteur verra le sol comme un obstacle.
- Les deux roues motrices du robot sont situées à l'avant du robot. Pour la stabilité, il faut mettre au moins une roue à l'arrière. Il est en fait plus judicieux de ne mettre qu'une seule roue et avec une petite surface de contact avec le sol pour qu'il y ait peu de frottement lorsque le robot fait une manoeuvre. S'il y a trop de frottement, le robot risque de mal tourner, voire de ne pas tourner du tout…
- J'ai utilisé des LEGO Technic pour construire le chassis. On peut imprimer des pièces en 3D pour adapter les moteurs aux LEGO; les pièces sont en noir sur la photo (cf le fichier donné dans la section “Matériel Nécessaire”). Ça fonctionne plutôt bien !
- J'ai utilisé un module d'alimentation 5V pour Arduino que l'on peut alimenter avec une pile 9V, ce qui permet au robot d'être indépendant (sans connection avec l'ordinateur).
</note>
24 janvier 2018:
- création de la page wiki du projet - Test du capteur à ultrasons
25 janvier 2018:
- Test du driver Dual TB6612FNG pour piloter des moteurs DC avec la carte Arduino
3-4 février 2018:
- Construction du premier prototype du robot
19 février 2018:
- Mise à jour du wiki