Lego+Arduino=Legoduino car – Un petit véhicule autonome

Dominique Liard

Ce tutoriel vidéo vous montre comment construire un petit véhicule autonome à base de composants électroniques Arduino et de briques de construction. Un programme de déplacement autonome du véhicule est proposé. Il peut aussi être piloté par Bluetooth ou par télécommande infrarouge.

Commentez

- Titre : Lego+Arduino=Legoduino car – Un petit véhicule autonome

- Auteur : Dominique Liard

- Parution : 28 novembre 2018

- Mise à jour : 23 janvier 2019

- Public visé :

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2018 Dominique Liard. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. La vidéo

- II. Présentation de la Legoduino Car

- III. Les principaux composants électroniques

- IV. Le câblage de la carte Arduino

- V. Le programme de contrôle

- VI. Remerciements

I - La vidéo

Animation en format "youtube" non supportée.
Contenu uniquement consultable en ligne à cette adresse :
https://www.youtube.com/watch?v=2s2stZ4O6ZA

II - Présentation de la Legoduino Car

La Legoduino est un petit véhicule autonome géré par une carte Arduino. Il peut aussi être contrôlé soit par Bluetooth, soit par télécommande infrarouge. Il a été fabriqué par les jeunes du club d'informatique de Valderoure (petite commune française).

La structure est basée sur des briques de construction célèbres que vous aurez certainement reconnues. Peut-être en avez-vous déjà un peu chez vous. Sinon, vous pouvez même commander les pièces sur ce site. Il en va de même pour les moteurs qui sont produits par le même fabricant de jouets.

Le véhicule peut très facilement avancer ou reculer, tant que les deux roues motorisées tournent dans le même sens. Il peut aussi effectuer des rotations sur lui-même, dans un sens ou dans l'autre, en inversant le sens de rotation des deux roues. Voici une seconde vue, avec un autre angle de vision sur la structure du véhicule : on y voit notamment que les moteurs sont solidement attachés sur la structure formant le châssis (dans un prototype précédent, nous avions les moteurs qui se décrochaient trop facilement). On y voit aussi, en bas et à droite, un support pour l'un des deux modules de détection d'obstacles par ultrasons (HC-SR04). Il y en a un devant et un autre derrière le véhicule. Enfin, on y voit différentes briques permettant de bloquer la breadboard ainsi que la carte Arduino.

Et voici encore une autre vue sur le dessous du véhicule. Notez la présence des petites roues qui peuvent pivoter sur elles-mêmes en cas de rotation du véhicule. Ce point serait très certainement à améliorer ! Les plaques permettent de bien renforcer la structure (le châssis).

III - Les principaux composants électroniques

Bien entendu, il nous faut une carte Arduino : nous avons choisi une carte Arduino Uno R3 (ou compatible). Mais d'autres cartes pourraient aussi convenir (Arduino Mega…).

Le véhicule est donc motorisé via deux blocs moteurs. Ils doivent pouvoir fonctionner indépendamment l'un de l'autre pour pouvoir permettre la rotation du véhicule. Nous avons donc choisi d'utiliser un composant L293D : il permet de contrôler chaque moteur indépendamment de l'autre (arrêt, allumage, puissance et sens de rotation). Un schéma de câblage vous est présenté plus bas dans ce document.

Les connectiques proposées par les briques ne sont pas compatibles avec le système Arduino. Quelques soudures sont donc nécessaires. Ensuite, j'y ai placé des cales en liège pour bloquer le tout, mais je ne pense pas que cela soit très utile. Relativement par rapport à la connectique des moteurs, je vous propose de consulter ce lien.

Ensuite, nous avons utilisé deux modules de détection d'obstacles par ultrasons (HC-SR04). Ils permettent de détecter les risques de collisions et d'arrêter le véhicule avant impact. Il y en a un pour quand le véhicule recule et un autre pour quand il avance. Notez que nous avons cherché à coincer les câbles avec des axes situés dans la structure (moins ça bouge, mieux c'est). La bibliothèque de code associée à ce composant est disponible en activant le lien suivant : HCSR04 - Library for HC-SR04 ultrasonic distance sensor.

En termes de communication avec le véhicule, nous avons deux solutions : soit par Bluetooth, soit par télécommande infrarouge. Pour le Bluetooth, nous avons utilisé un composant de type HC-05.

Pour l'infrarouge, les kits Arduino en fournissent plus ou moins systématiquement. La bibliothèque de code associée au composant IR-Receiver est disponible à l'adresse suivante : https://www.arduinolibraries.info/libraries/i-rremote

On utilise aussi un buzzer qui signale toute détection d'obstacle. Quelques résistances seront aussi utiles (buzzer et composant HC-05). Enfin, on alimente le tout avec une pile 9 V.

IV - Le câblage de la carte Arduino

Maintenant, il faut assembler le tout. Je vous propose de suivre le schéma de câblage ci-dessous. Vous pouvez de plus télécharger le fichier d'origine de ce câblage : LegoduinoCar.fzz

V - Le programme de contrôle

Et voici maintenant le programme. Pour les explications complémentaires, je vous renvoie vers la vidéo.

Le code permettant au véhicule d'évoluer

#include "Arduino.h"
#include "stdlib.h"
#include "time.h"
#include "IRremote.h"

// Configuration du récepteur infrarouge
const int receiver = 7;
IRrecv irrecv(receiver);
decode_results results;

// Configuration des capteurs ultrasons
#include "SR04.h"
const int TRIG_PIN1 = 3;
const int ECHO_PIN1 = 2;
const int TRIG_PIN2 = 6;
const int ECHO_PIN2 = 5;
SR04 sr04_1 = SR04(ECHO_PIN1,TRIG_PIN1);
SR04 sr04_2 = SR04(ECHO_PIN2, TRIG_PIN2);

// Configuration du buzzer
const int pinBuzzer = 4;

// Gestion bas-niveau des moteurs
const int motor1_enablePin = 11; //pwm
const int motor1_in1Pin = 13;
const int motor1_in2Pin = 12;

const int motor2_enablePin = 10; //pwm
const int motor2_in1Pin = 9;
const int motor2_in2Pin = 8;

void setMotor1(int speed, bool reverse) {
analogWrite(motor1_enablePin, speed);
digitalWrite(motor1_in1Pin, ! reverse);
digitalWrite(motor1_in2Pin, reverse);
}

void setMotor2(int speed, bool reverse) {
analogWrite(motor2_enablePin, speed);
digitalWrite(motor2_in1Pin, ! reverse);
digitalWrite(motor2_in2Pin, reverse);
}

// Gestion plus haut niveau des moteurs
const int SENS_AVANCE = 1;
const int SENS_RECULE = 0;

bool modeAuto = false;    // false -> pilotage manuel
// true -> mode autonome

int speed1 = 0;
bool sens1 = true;

int speed2 = 0;
bool sens2 = true;

bool choisirSens()  {
return rand() % 2 == 0;
}

void stop() {
speed1 = 0;    sens1 = SENS_AVANCE;
speed2 = 0;    sens2 = SENS_AVANCE;
setMotor1( speed1, sens1 );
setMotor2( speed2, sens2 );
}

void avance() {
speed1 = 255;    sens1 = SENS_AVANCE;
speed2 = 255;     sens2 = SENS_AVANCE;
setMotor1( speed1, sens1 );
setMotor2( speed2, sens2 );
}

void recule() {
speed1 = 255;    sens1 = SENS_RECULE;
speed2 = 255;     sens2 = SENS_RECULE;
setMotor1( speed1, sens1 );
setMotor2( speed2, sens2 );
}

bool checkAvance() {
return sens1 == sens2 && sens1 == SENS_AVANCE && speed1 == 255;
}

bool checkRecule() {
return sens1 == sens2 && sens1 == SENS_RECULE && speed1 == 255;
}

void tourneGauche() {
speed1 = 255;    sens1 = 0;
speed2 = 255;     sens2 = 1;

setMotor1( speed1, sens1 );        // Right
setMotor2( speed2, sens2 );        // Left
delay( 1650 );

speed1 = 0;        sens1 = 0;
speed2 = 0;     sens2 = 0;
setMotor1( speed1, sens1 );        // Right
setMotor2( speed2, sens2 );        // Left
}

void tourneDroite() {
speed1 = 255;    sens1 = 1;
speed2 = 255;     sens2 = 0;

setMotor1( speed1, sens1 );        // Right
setMotor2( speed2, sens2 );        // Left
delay( 1650 );

speed1 = 0;        sens1 = 0;
speed2 = 0;     sens2 = 0;
setMotor1( speed1, sens1 );        // Right
setMotor2( speed2, sens2 );        // Left
}

// Traduit la pression des boutons de la télécommande par une des actions
void translateIR() {

switch(results.value) {

// Détecte si la touche pressée est la touche Power
case 0xFFA25D:
//Serial.println("POWER");
modeAuto = true;
avance();
break;

// Détecte si la touche pressée est la touche Volume+
case 0xFF629D:
avance();
modeAuto = false;
break;

// Détecte si la touche pressée est la touche Arrière
case 0xFF22DD:
tourneGauche();
modeAuto = false;
break;

// Détecte si la touche pressée est la touche Play/Pause
case 0xFF02FD:
stop();
modeAuto = false;
break;

// Détecte si la touche pressée est la touche Avance
case 0xFFC23D:
tourneDroite();
modeAuto = false;
break;

// Détecte si la touche pressée est la touche Volume-
case 0xFFA857:
recule();
modeAuto = false;
break;

//default:
//Serial.println(" Other button ");

}

}

void setup(){
Serial.begin( 9600 );

// On initialise les pins du moteur 1
pinMode(motor1_in1Pin, OUTPUT);
pinMode(motor1_in2Pin, OUTPUT);
pinMode(motor1_enablePin, OUTPUT);

// On initialise les pins du moteur 2
pinMode(motor2_in1Pin, OUTPUT);
pinMode(motor2_in2Pin, OUTPUT);
pinMode(motor2_enablePin, OUTPUT);

// On active l'infrarouge
irrecv.enableIRIn();

// On initialise le générateur de nombre pseudo aléatoire
srand( time( NULL ) );
}

void loop(){

if (irrecv.decode(&results)) {
translateIR();
irrecv.resume();
}

while ( Serial.available() > 0 ) {
int code = Serial.read();
Serial.print( code );

switch( code ) {
case 'a':
avance();
modeAuto = false;
break;
case 's':
stop();
modeAuto = false;
break;
case 'r':
recule();
modeAuto = false;
break;
case 'g':
tourneGauche();
modeAuto = false;
break;
case 'd':
tourneDroite();
modeAuto = false;
break;
case 't':
modeAuto = true;
avance();
}

}

// capteur avant
long distance_1 = sr04_1.Distance();
if ( distance_1 > 0 && distance_1 < 10 ) {    // si on voit un obstacle
if ( checkAvance() ) {
tone( pinBuzzer, 1000, 500 );
if ( modeAuto == true ) {
recule();
delay(2000);
if ( choisirSens() ) {
tourneGauche();
} else {
tourneDroite();
}
avance();
} else {
stop();
}
}
}

// capteur arrière
long distance_2 = sr04_2.Distance();
if ( distance_2 > 0 && distance_2 < 10 ) {    // si on voit un obstacle
if ( checkRecule() ) {
tone( pinBuzzer, 1000, 500 );
stop();
}
}

delay( 100 );
}

Il est possible que le code ne compile pas directement. Par ma part, cela a été le cas. Effectivement, il y a un conflit entre la librairie Tone et celle permettant la gestion de la télécommande infrarouge, IRremote. Si c'est aussi votre cas, alors ouvrez le fichier IrremoteInt.h et localisez le bloc de code suivant.

#else
//#define IR_USE_TIMER1   // tx = pin 9
#define IR_USE_TIMER2     // tx = pin 3
#endif

Normalement, si vous permutez les deux lignes de commentaires, le problème doit être résolu. Voici le code après permutation.

#else
#define IR_USE_TIMER1   // tx = pin 9
//#define IR_USE_TIMER2     // tx = pin 3
#endif

VI - Remerciements

Merci à F-Leb de m’avoir activement accompagné dans la mise en œuvre de cet article.

Merci à Jacques Jean pour sa relecture orthographique.