I. La vidéo▲
Legoduino Car - Un petit véhicule autonome
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éoLa vidéo.
#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.