Initiation à la programmation embarquée/robotique avec Arduino Audrey Robinel (arobinel@univ-ag.fr) 13 mai 2014 1 Introduction Pour cette partie de l option informatique, notre objectif sera de programmer un robot pour le rendre autonome. Nous disposons d un robot programmable, mais qui par défaut ne sait rien faire. Notre objectif sera de créer un programme afin de permettre à celui ci de se déplacer efficacement dans un environnement en évitant les obstacles. Bien que cela puisse sembler complexe, il n y a pas d inquiétude à avoir, car la programmation se fait depuis un logiciel très performant et simple, et le langage de programmation est très proche du C que l on utilise en TP. En pratique, on écrira du code dans le programme, avec la même syntaxe qu en C, et on utilisera quelques fonctions prédéfinies pour activer les moteurs, ou lire les capteurs du robot. Par la suite, on branche le robot en USB, et on appuie sur un bouton dans l interface de programmation pour compiler et envoyer le programme au robot. Ainsi, aucune compétence en robotique n est requise, et quelqu un qui connaît quelques bases de programmation pourra sans problème programmer ce robot. 1.1 Arduino Notre robot est basé sur une plate-forme matérielle et logicielle libre appelée Arduino. Il s agit d une carte contenant un micro-contrôleur programmable, à savoir une puce électronique qui peut exécuter des commandes. Cette carte sera le cerveau de notre robot, et on pourra la programmer pour qu elle puisse faire des entrées (lire les capteurs par exemple), ou des sorties (allumer une LED, faire un bip, faire tourner les moteurs...). On pourra trouver davantage d informations sur cette carte sur le site officiel de Arduino : http://arduino.cc/fr/. Pour ceux qui souhaitent se pencher davantage sur l électronique programmable, sachez que ce genre de carte coûte une vingtaine d euros (la carte seule, pas le robot entier!) et que vous pouvez tout à fait vous en servir pour fabriquer toutes sortes de choses. N hésitez pas à me contacter pour plus d informations à ce sujet. 1
Figure 1 La base du robot que nous utilisons est le Zumo de pololu. Si on ajoute une carte Arduino et un capteur de distance, on obtient notre robot. 1.2 Notre robot Le robot que nous utiliserons ici est basé sur le robot Zumo, de l entreprise Pololu : http://www.pololu.com/product/2506, visible sur la figure 1 p 2. Ce robot est un mini sumo, utilisé entre autres pour des compétitions de robots sumo. Il s agit d un robot dit à conduite différentielle, c est à dire qu il y a deux moteurs, un à gauche et un à droite. Chaque moteur entraîne une chenille. Si l on veut avancer, il faudra faire tourner les deux moteurs dans le sens positif. Pour reculer, on les fait tourner dans le sens négatif. Enfin, pour tourner, on fait tourner les moteurs dans des sens contraires. Ainsi, le robot tournera sur lui même. Le robot fait 10*10cm, et est alimenté par 4 batteries AA rechargeables. Nous lui avons ajouté un capteur infrarouge, qui permet de connaître la distance des obstacles situés devant le robot. Le robot embarque une boussole magnétique, permettant de connaître l orientation du robot, un accéléromètre, permettant de de mesurer les chocs et vibration, ainsi qu un buzzer, permettant de faire de petits bruits. Pour ce projet, nous nous baserons principalement sur le capteur infrarouge pour éviter les obstacles. 2
2 Processing : le logiciel de programmation Nous utiliserons pour programmer notre robot un IDE (Integrated Development Environement, environnement de développement intégré) appelé processing. Ce logiciel permet d éditer le code source, le compiler, et l envoyer au robot. Le code source est enregistré sur l ordinateur sur lequel vous travaillez, et ne peut pas être lu depuis le robot, pensez donc à l enregistrer d une séance à l autre! Vous pouvez télécharger l archive contenant l IDE et les librairies à cette adresse : https://www.dropbox.com/s/h8h31cb0yriwufn/arduino-1.0.5libs. tar.gz Suite au téléchargement, décompressez l archive quelquepart, puis ouvrez le répertoire ainsi créé. Vous trouverez un fichier arduino, qui permet de lancer le programme par la commande./arduino. Figure 2 Le logiciel processing permet d éditer le code, de le compiler et de l envoyer au robot. Sur la figure 2, on peut voir une capture d écran du logiciel, avec un programme qui fait clignoter une LED sur le robot. 3
La zone centrale, sur fond blanc, est celle dans laquelle on édite le code source. La zone bleue en haut contient les boutons permettant de sauvegarder, enregistrer, compiler et envoyer le programme. La zone noire en bas affiche les messages d information et d erreur. Figure 3 Lorsque l on appuie sur le bouton Téléverser (en blanc sur la capture), le programme est compilé, et s il n y a pas d erreur, envoyé au robot. Pour pouvoir envoyer le programme au robot, il faudra brancher le câble USB, et vérifier que le port série est bien sélectionné dans le logiciel, en cliquant sur outils, puis port série, et vérifier si la première case est bien cochée, comme illustré sur la figure 3. Figure 4 Lorsque l on appuie sur le bouton Téléverser (en blanc sur la capture), le programme est compilé, et s il n y a pas d erreur, envoyé au robot. Dans ce contexte, si le programme est complet, on peut alors cliquer sur le bouton Téléverser (en blanc sur la figure 4) pour compiler et exécuter le programme. Si la zone inférieure affiche des erreurs, il faut alors les corriger et recommencer l envoi. Dans le cas contraire, le bas de la fenêtre affichera Téléversement effectué, comme illustré sur la figure 5. 4
Figure 5 Le programme n affiche aucune erreur, et indique que le programme a été exécuté avec succès. 3 Les bases du langage : rappels génériques 3.1 Déclaration de variables On déclare les variables comme en C/C++. On utilisera ainsi les types int, pour les entiers, float pour les réels, char pour les caractères. Nous aurons en plus un type booléen, qui prendra les valeurs true et false, et un type String pour les chaines de caractères. La façon d utiliser ces types de données est résumée brièvement dans la table 1. D autres types spéciaux existent, que nous verrons lorsque nous en aurons besoin. type description valeurs exemple int entier Z int i=0 ; float réel R float pi=3.1415 ; char caractère un caractère char c= a ; boolean booléen true ou false boolean finished=true ; String chaine de caractères texte String msg= ok! ; Table 1 Les différents types de variables. 3.1.1 Les tableaux Les tableaux sont déclarés et se comportent comme en C classique. On déclarera un tableau d entier par exemple : int tableau[10] ; Les tableaux ont des indices commençant à 0, donc pour un tableau de N cases, on va de 0 à N-1. On peut également déclarer et initialiser notre tableau comme suit : Listing 1 Déclaration de tableaux int tableau [5] = 2, 4, 8, 3, 6; int tableau [] = 2, 4, 8, 3, 6; 5
3.2 Déclaration de fonctions On pourra déclarer des fonctions comme en C comme suit : type nomfonction(arguments)... Listing 2 Déclaration de fonction type nomfonction ( type argument,...)... Dans ce contexte, type sera le type de retour de la fonction (int, float, etc). Si l on ne veut pas retourner de valeur, on mettra void nomfonction(...), Arguments est la liste des paramètres de la fonction, comme en C, chaque paramètre ayant un type et un nom, ceux ci étant séparés par des virgules. Si il n y a aucun paramètre, on laissera cette partie vide. On peut, comme en C classique, appeler une fonction depuis une autre. En bref, on fait comme pour les TP de C! 3.3 structures conditionnelles On peut également utiliser les structures conditionnelles classiques, à savoir if, else if, else, mais également switch/case. références : if/else : http://arduino.cc/en/reference/if switch/case : http://arduino.cc/en/reference/switchcase 3.3.1 Rappel sur les opérateurs de comparaison Ce qu on mettra entre les parenthèses du if sera identique à ce qu on utilise jusqu ici en C. Voyons un très bref rappel : x == y (x est égal à y) x!= y (x différent de y) x < y (x inférieur à y) x > y (x supérieur à y) x <= y (x inférieur ou égal à y) x >= y (x supérieur ou égal à y) 4 Fonctions spécifiques pour le robot En dehors des points classiques exposés dans la section précédente, nous disposons de quelques éléments spécifiques pour programmer le robot. Voyons tout d abord la structure de notre programme. Celui ci se compose de deux fonctions : setup() est exécutée UNE fois, au démarrage du robot ; 6
loop() est une boucle infinie, exécutée continuellement après l exécution de setup(). 4.1 les fonctions setup() et loop() La fonction setup permettra par exemple d initialiser des variables, ou de faire divers traitements avant de commencer l algorithme principal du robot. Si vous n avez rien de particulier à faire, laissez la fonction vide. La fonction loop quand à elle, est exécutée juste après la fonction setup, et sera exécutée à l infini. Ainsi, lorsqu on arrive en bas des instructions de la fonction loop, on recommence au début de loop. Comme avec une boucle, si vous déclarez une variable dans la fonction loop, elle sera recréée et réinitialisée à chaque fois. S il faut conserver une valeur d une itération à l autre, il faudra penser à déclarer la variable avant la fonction loop, un peu comme une variable globale déclarée avant le main en C. 4.2 les broches du robot Le robot dispose d un certain nombre de broches. Certaines sont analogiques, ce qui signifie en pratique qu on lira une valeur entière, dans ce contexte, entre 0 et 1023. Cela servira par exemple à lire la valeur d un capteur branché sur une broche analogique. Pour notre robot, par exemple, la broche A2 est connectée au capteur de distance. Ces broches seront toujours utilisées en lecture, et jamais en écriture. Pour cela, il faudra mettre dans la fonction setup() l appel suivant : pinmode(a2, INPUT) ; D autres broches seront des broches numériques, c est à dire qu elles peuvent prendre uniquement des valeurs binaires, soit 0, soit 1. Par exemple, pour la LED connectée sur la broche 13, on pourra écrire la valeur 0, qui éteindra la LED, ou la valeur 1, qui l allumera. Sur notre robot, nous utiliserons ces broches en écriture uniquement. Pour cela, il faudra mettre dans la fonction setup() l appel suivant : pinmode(13, OUTPUT) ; 4.3 Attendre avant d exécuter d autres instructions La fonction loop() est une boucle infinie. Elle se répète donc jusqu à ce qu on redémarre le robot, en effectuant successivement toutes les instructions entre les accolades. Lorsque l on veut attendre un certain temps entre deux instructions, il faut utiliser la commande delay(duree). Ainsi, par exemple si l on souhaite faire clignoter la LED du robot, l algorithme sera le suivant : 1. allumer la LED ; 2. attendre un certain temps ; 3. éteindre la LED ; 4. attendre un certain temps. 7
En pratique cela donnera quelquechose du genre : Listing 3 Clignotement de LED loop () digitalwrite ( ledpin, HIGH ); // allumage de la LED delay ( 250); // attente de 250 ms digitalwrite ( ledpin, LOW ); // extinction de la LED delay ( 250); // attente de 250 ms Ici, la LED s allumera pendant 250 millisecondes, puis s éteindra pendant la même durée, avant de recommencer. 4.3.1 Faire une action jusqu à nouvel ordre Si on appelle la fonction avancer() ;, le robot avancera jusqu à ce qu il reçoive un nouvel ordre. Ainsi, pour l arrêter, on pourra utiliser stop() ;, qui l arrêtera immédiatement. Pour tourner à gauche, il faudra utiliser la fonction gauche() ;, et pour tourner à droite, la fonction droite() ; Par exemple si l on souhaite avancer pendant une seconde, tourner à gauche un peu, puis recommencer pour tourner en rond, on pourra faire dans loop : Listing 4 Exemple de contrôle du robot loop () avancer (); // on avance delay ( 250); // attente de 250 ms gauche (); // extinction de la LED delay ( 200); // attente de 200 ms Avec ce programme, le robot avancera pendant une seconde (1000 millisecondes), puis tournera pendant 0.2s (200 millisecondes), et répétera l opération à l infini. 4.4 Afficher des valeurs Pour afficher des valeurs en provenance du robot, il faudra utiliser le moniteur série. Pour cela, il faut écrire dans la fonction setup() l instruction suivante : Serial.begin(9600) ;. Cette instruction permet d initialiser le moniteur série, et le paramètre entre parenthèses est la vitesse de communication (9600 bits par seconde). Une fois ceci fait, nous pourrons faire des affichages, un peu comme avec un printf. Nous utiliserons deux fonctions : Serial.print(paramètres...) Serial.println(paramètres...) Les deux fonctions permettent d afficher ce que l on passe en paramètres entre les parenthèses. La seule différence est que la seconde fonction fait automatiquement un retour à la ligne après l affichage. On pourra afficher du texte brut, 8
par exemple : Serial.println( Lancement de l algorithme : recherche des humains ) ; De même, on pourra afficher des variables comme suit : Listing 5 Exemple d affichage composé loop () int vitesse =120; Serial. print (" vitesse : "); Serial. println ( vitesse ); Cela affichera dans le moniteur série :vitesse : 120, avec un retour à la ligne. Pour pouvoir visualiser le moniteur série, il faudra cliquer sur outils puis moniteur série, en vous assurant que le câble USB est bien branché. 4.5 Allumer et éteindre la LED On utilisera la fonction digitalwrite(ledpin,high) ; pour allumer la LED. Elle restera allumée jusqu à ce que le robot soit éteint, ou que l on éteigne la LED. Pour ce faire, on utilisera la fonction digitalwrite(ledpin,low) ;, qui éteindra la LED jusqu à ce qu on la rallume. 9
5 Exercice à réaliser 5.1 Récupération du logiciel Téléchargez le logiciel contenant les librairies à l adresse suivante : https: //www.dropbox.com/s/xcoceuw0cvgeaht/arduino-1.0.5_32bits_libs.zip Décompressez l archive dans votre répertoire personnelle, puis rendez vous dans le dossier ainsi créé avec un terminal. Lancez le logiciel Arduino via la commande./arduino. Une fois cela fait, ouvrez le squelette du programme à réaliser en cliquant sur fichier, exemples, puis robot. Vous aurez alors sous les yeux le code source à compléter du robot. Cet exemple est en lecture seule, donc pour le modifier, enregistrez le sous un autre nom (fichier puis enregistrer sous) dans votre session. Notez bien l endroit ou vous enregistrez pour pouvoir récupérer le fichier ultérieurement! 5.2 Allumer/éteindre la LED du robot Le premier travail a réaliser sera de contrôler la LED du robot. Pour cela, nous allons commander la broche sur laquelle est connectée la LED. Cette broche est la broche 13, ainsi qu il est spécifié dans la variable ledpin. On pourra changer l état de la broche en utilisant les commandes suivantes : digitalwrite(ledpin, HIGH) ; pour allumer la LED ; digitalwrite(ledpin, LOW) ; pour éteindre la LED ; 5.3 Détecter les obstacles Lire la valeur retournée La première étape sera de lire la valeur du capteur infrarouge, en utilisant la fonction analogread() : int sensorvalue = analogread(analoginpin) ; De cette façon, nous obtenons une valeur entière nous indiquant la distance devant le robot. Essayez d afficher cette valeur en utilisant Serial.print(). Vous constaterez que plus l objet est proche du robot, plus la valeur est grande. En effet, la valeur retournée par le capteur est inversement proportionnelle à la distance. Inverser la valeur Nous allons donc inverser la valeur retournée, par exemple en faisant par exemple float distance=10000/(float)sensorvalue ; Nous obtenons ainsi une valeur numérique plus faible si l objet est proche, et plus grande si il est lointain. Écrivez maintenant une fonction qui se chargera de : lire la valeur brute du capteur ; calculer l inverse de cette valeur ; retourner la valeur calculée. Détection des obstacles L étape suivante sera alors d allumer la LED lorsqu un obstacle est trop proche du robot. Pour cela, essayez expérimentalement 10
de trouver la valeur correspondant à une distance que vous jugerez correcte. Dès lors, faites en sorte que la LED du robot s allume quand un objet est trop proche du robot, et s éteigne quand il n y a pas d obstacle proche. 5.4 Déplacer le robot La seconde étape sera de programmer les déplacements du robot. Nous commencerons donc par tester les fonctions de déplacement du robot. Pour cela, essayez successivement les fonctions suivantes : avancer() ; reculer() ; gauche() ; droite() ; arret() ; Modifiez la fonction loop() du robot pour tester ces fonctions. N oubliez pas qu il faut utiliser delay(x) après chaque appel pour que le robot exécute l instruction pendant X millisecondes. Par exemple, en appelant la succession de commandes suivantes : loop () avancer (); delay (1000); arret (); delay (2000); Listing 6 Exemple de contrôle simple du robot le robot avancera pendant une seconde (1000 millisecondes), puis s arrêtera pendant deux secondes. Pour pouvoir tester le programme, la procédure est la suivante : 1. compilez le programme 2. une fois fait débranchez le câble USB 3. posez le robot au sol sur une surface plane 4. allumez le robot en déplaçant délicatement le petit interrupteur 5. observez votre programme en action! 5.5 objectif : écrire un programme permettant au robot d éviter les obstacles L objectif sera maintenant de modifier le programme précédent pour permettre au robot de se déplacer en évitant les obstacles situés sur son chemin! Le code source sera à remettre, pensez donc bien à le sauvegarder à l endroit de votre choix, vous enverrez le fichier.ino par mail. 11
5.6 Code source de base Le code source de base est accessible en faisant fichier/exemples/robot. Toutefois voici, à titre de référence, le listing de ce code source : Listing 7 Squelette de base du programme de controle du robot /* * Squelette du programme de base # include < ZumoMotors.h> # include < Pushbutton.h> # include <Wire.h> # include < LSM303.h> ZumoMotors motors ; // necessaire pour utiliser les moteurs // Ces valeurs sont dans des constantes, car elle ne changeront pas pendant l // elles permettent de definir les broches utilisees const int analoginpin = A0; // Broche du capteur de distance const int ledpin = 13; // Broche de la LED int speed01 =120; // Vitesse du robot Pushbutton button ( ZUMO_ BUTTON ); // permet d utiliser le bouton poussoir en app * Cette fonction fait avancer le robot jusqu a nouvel ordre void avancer () motors. setleftspeed ( speed01 ); motors. setrightspeed ( speed01 ); * Cette fonction fait reculer le robot jusqu a nouvel ordre void reculer () motors. setleftspeed ( -1* speed01 ); motors. setrightspeed ( -1* speed01 ); * Cette fonction fait tourner le robot a gauche jusqu a nouvel ordre 12
void gauche () motors. setleftspeed ( -1* speed01 ); motors. setrightspeed (1* speed01 ); * Cette fonction fait tourner le robot a droite jusqu a nouvel ordre void droite () motors. setleftspeed (1* speed01 ); motors. setrightspeed ( -1* speed01 ); * Cette fonction arrete immediatement les moteurs. void arret () motors. setleftspeed (0); motors. setrightspeed (0); * fonction executee une fois au demarrage du robot void setup () pinmode ( ledpin, OUTPUT ); // configure la broche de la LED comme une broch Serial. begin ( 9600); // permet d utiliser le moniteur serie pour fair * Fonction principale, qui est une boucle infinie, a completer void loop () 13