Projet de Weekend : Tourelle de surveillance

Après plusieurs billets assez pauvre en contenu je me suis dit que résumer un micro-projet mettant en oeuvre plusieurs techniques différentes serait une bonne idée. Voici donc le résumé de la création d'un petit jouet qui m'a couté environ 7€ [1] ! Je vous laisse découvrir un à un les différents domaines que j'ai associés et essayer de deviner au fil de l'eau ce que j'obtient à la fin et qui constitue le but de ce projet (ainsi que le titre du billet...j'aurai d'ailleurs du y mettre des balises SPOIL ^^ ). Bonne lecture !

Tour de surveillance - Creative Common CC-BY-SA by Ozwald (me)

Un peu de vision

J'ai déjà expliqué sur ce blog comment on peut facilement programmer un micro-controlleur[2], et nous avons également déjà vu comment utiliser ces micro-controlleurs pour faire tourner des moteurs, ce qui permet (par exemple) de faire bouger un robot. Nous savons donc à présent comment fabriquer un robot qui agit sur le monde (en s'y déplaçant) mais nous n'avons pas encore vu de méthode pour que le robot prenne connaissance du monde (or d'aucun diront qu'il est intéressant de comprendre son environnement avant d'agir). La première connaissance nouvelle que nous allons aborder dans ce billet c'est justement comment mesurer la distance qui sépare notre robot d'un obstacle situé devant lui.

Beaucoup de méthodes existent pour mesurer des distances sans contact. On peut par exemple citer les mesures de déphasages entre un rayon laser émis et son reflet renvoyé par l'obstacle, les mesures de focalisation d'un signal lumineux (généralement infra-rouge) émis puis réfléchi par l'obstacle, ou encore la mesure de temps entre l'émission d'un signal acoustique (généralement ultra-son) et la réception de son écho produit par l'obstacle.

Dans ce mini-projet j'ai opté pour la mesure par ultra-son, pour deux raisons :

  • c'est la méthode la moins chère (on trouve des modules tout fait à moins de 1.50€ sur eBay, à moins de 6$ sur amazon, à moins de 10€ sur robotshop, ou à 15€ chez gotronic pour la classe au dessus).
  • Ces modèles peu onéreux permettent pourtant déjà de mesurer des distances allant, classiquement, de quelques centimètres à environ 4m, ce qui est parfait pour un usage en intérieur.

Plus précisément j'ai opté pour un HC-SR04, qui semble être l'un des modèles les plus répandus (mais pas l'un des plus fiables).
HC-SR04 - Creative Common by "scanlime" on Flickr
Le fonctionnement de ce module est simplissime : on branche la PIN "GND" du module à la masse de notre circuit, la PIN "VCC" à une alimentation 5V, et les PIN "trig(ger)" et "echo" chacune à une I/O de notre microcontrolleur. Pour mesurer la distance qui sépare ce module d'un obstacle situé devant lui on envoie simplement une impulsion positive d'environ 10us sur la PIN "trigger" du module, puis on mesure la longueur de l'impulsion positive que nous envoie le module en réponse sur sa PIN "echo". La longueur de l'impulsion de réponse (sur la PIN "echo") est proportionnelle au temps mis par le son pour faire l'aller-retour entre le module et le premier obstacle rencontré (la vitesse du son étant à peu près constante on en déduit alors la distance qui sépare le module du premier obstacle situé devant lui). Pour être précis on considère que 1cm se traduit en environ 58uS d'impulsion "echo"; si je reçois une impulsion retour qui dure 580uS j'en déduis donc qu'il y a un obstacle à environ 10cm devant mon module. Trivial[3] !


Un peu d'action (controllée)

La denière fois que nous avons parlé d'agir sur le monde depuis un microcontrolleur nous avions fait tourner des moteurs, dans un sens ou dans l'autre, à une vitesse arbitraire. C'est déjà très pratique, mais pas vraiment adapté à des mouvements finements controllés; en effet nous ignorons tout de la vitesse réelle de rotation du moteur (en fonction de la résistance qu'il rencontrera lors de sa rotation il tournera plus ou moins vite pour une même commande donnée). Pour palier ce manque de précision nous avons, là encore, de nombreuses options qui s'offrent à nous. Nous pouvons par exemple asservir la commande du moteur à la lecture de capteurs qui nous renseigneront sur la rotation réellement effectuée, ou nous pouvons opter pour des moteurs "pas à pas" plus compliqués à commander mais qui offrent une bien meilleure précision de commande, ou encore nous pouvons utiliser des servomoteurs qui permettent d'obtenir directement un déplacement asservi.

Pour ce petit projet nous allons opter pour un servomoteur. J'aurai pu avantageusement partir sur un moteur pas à pas mais j'avais besoin de jouer avec des servomoteurs pour un autre projet donc j'ai décidé de faire d'un pierre deux coups et de mettre également un servomoteur dans ce micro-projet. En plus, comme les capteurs ultrason hc-sr04, les servomoteurs ont le bon gout d'être simple à utiliser et peu cher (on en trouve à moins de 2€ sur eBay, à 3$ sur amazon, à un peu plus de 4€ chez gotronic, ou encore à une quinzaine d'euros chez selectronic pour la gamme au dessus).
Servo premier prix - Creative Common by "Nick Ames" on Flickr
Généralement les servomoteurs se connectent via 3 fils. Les couleurs varient selon les constructeurs mais vous pourrez trouver facilement les équivalences en cherchant sur internet. Les servomoteurs que j'ai (les même que celui de la photo ci-dessus) suivent les couleurs traditionnelles du constructeur Graupner :

  • un fil marron que je dois brancher à la masse
  • un fil rouge que je dois brancher à mon "VCC" (alimentation 5V);
  • un fil orange sur lequel je dois envoyer la commande.

Comme je vous l'ai déjà dit la commande d'un servomoteur est simple : on doit envoyer sur le fil de commande, à intervalle régulier (environ toutes les 20ms au plus), une impulsion positive dont la largeur est notre commande[4]. L'impulsion doit avoir une largeur comprise, environ, entre 0,5ms et 2,5ms. Lorsque la commande est correctement envoyée l'axe du servomoteur s'aligne sur un angle compris entre 0° et 180°, proportionnellement à la commande envoyée[5]. Donc si j'envoie, toutes les 20ms, des impulsions de 0,5ms sur la commande de mon servomoteur celui-ci va s'aligner sur -90° et ne plus en bouger. Si j'allonge mes impulsions jusqu'à 1,5ms l'axe du servomoteur va tourner environ jusqu'à 0° et ne plus bouger. Enfin si je rallonge mon impulsion jusqu'à 2,5ms l'axe va sagement s'aligner sur +90°. L'avantage d'avoir un asservissement directement dans le servomoteur c'est que son axe va s'aligner à un angle correspondant à ma commande quelque soit la résistance à son mouvement, je n'ai pas à me préoccuper d'asservir le bouzin, c'est prévu dans le forfait de base.


Un peu de dialogue

Les amateurs d'arduino sont habitués à communiquer avec leur joujou préféré depuis leur PC en le branchant simplement en USB, seulement voilà : les atmega8 n'ont pas de port USB et donc pas de méthodes native pour discutter avec un PC. Pour palier ce "problème" nous avons, encore une fois, plusieurs solutions :

Les microcontrolleurs incluant directement le support de l'USB ont l'inconvénient d'être plus cher que nos composants "low cost" (atmega8, atmega328, attinyX5, etc.), mais surtout ils sont plutôt disponible au format TQFP qu'au format DIP[6], donc j'exclus cette solution pour ce mini projet.

Implémenter la pile USB sur un atmega8 c'est sympa, mais comme je ne maitrise pas encore toutes les subtilités du débugage sur microcontrolleurs et que, de toute façon, la gestion USB pomperait quasiment toutes les resources du microcontrolleur, j'exclus aussi cette solution.

Il nous reste donc le passage par un "interprète". Ca tombe bien, sur internet on peut trouver des petits montages tout fait autour du chipset cp2102 pour moins de 2€ sur eBay ou moins de 9$ sur DX.

Convertisseur UART-USB à base de CP2102

Ces petits montages discutent en USB avec l'ordinateur, et en UART avec notre microcontrolleur. Avantage certain : les chipsets cp2102 sont pris en charge directement par le noyau linux depuis la version 2.6.12, il suffit donc de le brancher pour se retrouver avec un /dev/ttyUSBX fonctionnel qui fait le pont jusqu'à notre microcontrolleur :) Coté microcontrolleur l'UART est un protocole de communication série "universel" intégré en hardware dans les atmega8 et supérieurs[7] ce qui permet de ne consommer quasiment aucune ressource de calcul !

Une fois le montage branché il nous suffit donc de coder la communication. Coté microcontrolleur c'est supporté en hardware, donc après la routine d'initialisation le code d'envoi d'un octet se résume à vérifier que le flag "REady" est positionné, puis à inscrire l'octet à envoyer dans le registre dédié "UDR" (on fait difficilement plus simple...) :

/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) ) {};
/* Put data into buffer, sends the data */ 
UDR = data;

Coté PC c'est de la communication série standard, vous pouvez donc utiliser des softs "tout fait" comme minicom, ou dénicher une librairie de communication série pour dialoguer à partir de votre language de script préféré :

import serial
ser = serial.Serial('/dev/ttyUSB0', 4800, stopbits = serial.STOPBITS_TWO, parity = serial.PARITY_ODD)
while True:
    print ord(ser.read(1))

Bien entendu la communication peut être bi-directionnelle sans aucune modification matérielle et sans vraiment plus d'effort de code (il faut remplacer "ser.read" par "ser.write" en python et lire un registre au lieu d'en écrire un du coté de l'atmega...dur n'est ce pas ?).


Un peu de superficiel

Je suppose maintenant que vous voyez où on va avec ces différentes briquettes et le titre de ce billet : nous montons une "tourelle de surveillance" (visible sur la photo tout en haut de ce billet). Cette "tourelle" va être constituée d'un capteur de distance à ultra-son, monté sur un servomoteur, controllé par un atmega8, communiquant avec un PC via UART/USB. L'idée c'est que nous allons mesurer la distance qui nous sépare d'un obstacle "devant nous", puis nous allons faire tourner le servomoteur de quelques degrés et nous allons à nouveau faire une mesure de distance. En itérant ainsi le procédé nous pouvons obtenir une cartographie à 180° de la distance qui nous sépare du premier obstacle; obtenant ainsi une vision partielle de la pièce dans laquelle nous sommes. Grace à la communication avec le PC nous pouvons, en prime, obtenir une jolie visualisation de ces informations à la façon "radar".

Pour ne pas changer les bonnes habitudes j'ai codé tout le bouzin en python :

  • Pour réaliser la communication j'utilise la librairie "pyserial"
  • Pour faire une "jolie visualisation" j'utilise pygame.

Avec ces deux éléments, un peu d'huile de coude, et 93 lignes de python (commentaires inclus), j'obtiens un joli écran "radar" ^^ Grace à la fonction "pygame.image.save" c'est un jeu d'enfant de sauvegarder chaque frame affichée, et grace à la toute puissance de la ligne de commande linux je peux vous faire partager le résultat facilement :

Radar - Creative Common par ozwald

Pour ceux qui ne connaissent pas la pièce ça ne ressemble sans doute pas à grand chose, mais moi je vois bien les deux murs de ma pièce, la porte (ouverte) entre les deux murs, et mon armoire tout à droite (quand au gros obstacle collé tout à gauche du capteur...c'est moi -_-)

Conclusion

Et c'est tout pour ce résumé de la création d'un micro jouet conçu et bidouillé en quelques heures seulements :) Vous noterez que j'ai jeté un voile pudique sur la partie mécanique du projet, mais j'ai bien l'intention de revenir dessus dans un article ultérieur puisque c'est un domaine sur lequel j'ai également réalisé pas mal de recherches et progrès récemment, notamment grace à l'excellent "Guerrilla guide to CNC machining, mold making, and resin casting"[8] et à ce produit miracle que l'on appelle "polymorph plastic".

En guise d'ultime conclusion un petit rappel des couts de construction (hors mécanique; comptez moins de 5€ de mécanique) :

Atmega8		1.50
UART-USB	1.80
servo		2
HC-SR04		1.50
============
TOTAL		6.80 €

Notes

[1] Sans compter la main d'oeuvre, évidemment ;-)

[2] Vous pouvez également utiliser un arduino, c'est juste un peu plus cher

[3] Attention, le fonctionnement de ce module HC-SR04 vu "de l'extérieur" est trivial mais je vous rassure : son fonctionnement complet est un poil plus complexe. En effet, afin d'éviter d'être perturbé par des bruits parasites, le module se charge d'envoyer en réalité plusieurs trains d'ondes séparés pour fiabiliser sa mesure. C'est l'intérêt d'un module "tout fait" par rapport à un système bricolé à la main à partir des éléments d'émission et de réception ultra-son uniquement.

[4] Une méthode simple de faire ça sans consommer de temps de calcul de notre microcontrolleur c'est d'utiliser une PWM dont la fréquence avoisinne les 50/60Hz

[5] je ne parierai pas sur la linéarité de la relation entre l'angle du servomoteur et la largeur de l'impulsion ceci-dit...

[6] Petite parenthèse : le format "DIP" c'est le format standard du prototypage, c'est facilement manipulable et soudable à la main, les pattes des composants sont espacés de 2.54mm. Le format "TQFP" en revanche est nettement plus petit et, bien qu'il soit soudable à la main, vous allez galérer pour le manipuler et le mettre en place correctement puisque les pates peuvent s'y trouver espacées de seulement 0.4mm.

[7] Les attiny n'embarquent qu'une version "light" de l'UART que je n'ai pas testé, mais j'ose espérer que ça marche quand même

[8] Guide rédigé par Michal Zalewski alias lcamtuf qui ne se contente pas d'être un expert reconnu en sécurité informatique mais qui réalise aussi de bien jolis robots. Bref : un gars certainement très bien :-D !

Commentaires

1. Le vendredi, décembre 13 2013, 10:07 par Alexis

Bonjour,

Dans le cadre de notre projet de Terminale, mon groupe et moi-même sommes intéressés par ce capteur.

Cependant, une phrase nous bloque: "Appliquer à la pin "TRIGGER" une impulsion de niveau haut (5V) durant au moins 10µs pour que le module démarre sa lecture" Comment déclencher cette impulsion ? Est-ce difficile pour notre niveau ?

Merci d'avance.
Cordialement,

2. Le lundi, décembre 16 2013, 14:44 par ozwald

Déclencher cette impulsion (dont la précision n'est pas importante) est bien plus facile que de mesurer la durée d'impulsion retournée par le module... Par exemple : si vous utilisez un microcontrolleur atmel pour envoyer le signal un code comme celui-ci fera l'affaire (en supposant que la pin TRIGGER du module est branchée sur le pin PB1 du microcontrolleur) : "PORTB |= 1<<PB1; _delay_us(10); PORTB &= ~(1<<PB1);". En arduino on aurai un code similaire (en supposant cette fois que la pin TRIGGER du module est branchée sur le pin 13 de l'arduino) : "digitalWrite(13, HIGH); delayMicroseconds(10); digitalWrite(13, LOW);"

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs

URL de rétrolien : http://www.ozwald.fr/index.php?trackback/61

Fil des commentaires de ce billet