dimanche 3 juin 2012

Lecture multi-senseurs

Voila bien longtemps que je n'ai plus eu l'occasion de blogger sur ArduinoCar.
C'est que les activités de MCHobby (y compris les traductions française) nous occupent à plein temps.
Et oui, je dis nous car depuis sa création, je peux compter sur la précieuse aide de mon épouse.

Ma voiture est-elle folle?
Dans un précédent article, je relatais un problème que je pensais être un bug logiciel.
En effet, ma voiture se met en marche avant (état asMove) puis passe immédiatement dans l'état asChangeDir pour éviter un mûr ou un obstacle qui n'existe pas!

C'est en traduisant une documentation AdaFruit sur les senseurs pĥoto-sensibles (pour MCHobby) que j'ai trouvé la raison à mon problème.

Lecture multi-senseur et lectures erronées
Si vous utilisez plusieurs senseurs de type analogiques, vous pourriez rencontrer quelques problèmes. Voici donc le contenu issus du cas des photo-résistance... qui s'applique aussi aux senseurs Sharp Infra-Rouges.

Si vous ajoutez plusieurs senseurs, vous pourriez remarquer que la température (détection de niveau lumineux) est inconstante. Cela indique que les senseurs interfèrent les un avec les autres lorsque vous passer la lecture analogique d'une broche à l'autre.

Cela peut être corrigé en faisant deux lectures (avec pause et ignorant le résultat de la première lecture) sur le premier senseur. Cette méthode est décrite dans l'article "How to multiplex analog readings what can go wrong with high impedance sensors and how to fix it".

En effet, si le senseur présente une haute impédance et qu'il y a une capacitance (capacité ou toute sorte de matériel équivalent) sur le convertisseur ADC, cela prendra un certain temps pour que la capacitance se charge.
Raison de la première lecture erronée :-)

Vous trouverez plus de détail à ce sujet à partir du wiki de MCHobby.

samedi 25 février 2012

Lancement de MCHobby, vente de kit et matériel Arduino en Belgique

Il y a un an, j'ai découvert la plateforme de prototypage électronique Arduino.
Avec du recul, Arduino est une plateforme idéale pour hobbyiste, elle ne réclame pas de connaissances poussées en électronique et en programmation.

J'ai commencé par le kit d'exploration ARDX et je dois bien le reconnaître, j'y ai pris un plaisir monstre (et c'est peu dire). C'est un kit de prise en main parfait (un article paraîtra bientôt à ce sujet sur une version française du kit).

Un an plus tard, je décide d'ouvrir un WebShop Francophone pour distribuer du Matériel Arduino en Belgique (et pays voisins).
C'est ainsi qu'est né Microcontrôleur Hobby (alias MC Hobby).
MC Hobby, vente de kit et matériel pour Arduino



Dans toutes mes tribulations Arduino, j'ai rencontré d'excellents produits et une communauté francophone très actives. Malheureusement, cet univers pêche par un gros défaut, ses fournisseurs sont principalement anglophones.
En conséquence, les produits sont peu traduits (voire pas du tout) et les achats intéressants doivent se faire à l'étranger (Pays-Bas, Etats-Unis, Grande-Bretagne, Chine, voire Japon) dans la langue de Shakespeare. 

Pour palier à cette situation, j'ai décidé de lancer un WebShop francophone distribuant des produits traduits et des documentations traduites (dans les limites de mes capacités humaines).

Mon premier objectif fut la traduction de l'ARDX d'AdaFruit (kit vendu sur MC Hobby). Accompagné de quelques autres produits, j'ai donc ouvert MC Hobby, un WebShop Francophone principalement destiné à la Belgique mais aussi aux pays environnants comme la France, le Luxembourg, la Suisse, etc.

Mon deuxième objectif est de mettre en place un Wiki pour supporter la traduction des tutoriels et instructions de montages des autres produits mis en ventes sur MC Hobby. J'ai d'ores et déjà décidé de consacrer 3 heures de traductions tous les mercredi car je reste convaincu que c'est essentiel pour promouvoir la plateforme Arduino.

La politique de MC Hobby c'est la transparence

MC Hobby ne se cache pas derrière un nom de domaine et une adresse e-mail.
Des conditions de ventes, de livraisons sont mises à votre disposition mais c'est  surtout les mentions légales qui sont importantes pour établir une relation de transparence entre le client et le vendeur.
Ces mentions légales sont obligatoires et reprennent toutes les informations d'identification de MC Hobby.
Bref, tout ce qu'il faut pour commencer une vraie relation de confiance. 

Votre avis compte, il est primordial!
MC Hobby démarre et dispose de quelques produits. J'ai pourtant l'intention d'en faire un WebShop où le hobbyiste pourra trouver tout ce dont il a besoin pour effectuer ses montages (y compris le matériel électronique de base) au prix le plus juste (car je suis aussi un hobbyiste :-) ).
J'ai donc du pain sur la planche et les meilleurs produits seront ceux dont vous aurez besoin. N'hésitez donc pas à m'envoyer vos suggestions sur info@mchobby.be
Vos avis et suggestions sont primordiaux, cela me permet de concentrer mon énergie sur vos besoins et d'en préserver un maximum pour investir dans les traductions. Qu'en pensez-vous?

Par où poursuivre l'effort traduction?

En sus d'ajouter de nouveaux produits au WebShop, un long travail m'attends encore sur les produits existants.
Il me faut encore traduire les tutoriels et notes de montages des autres produits proposés.
La question que je me pose, c'est "par quel produit et quel tutoriel commencer"?
Auriez-vous un avis en tant que client potentiel ?
Cet avis là compte aussi pour moi.

MC Hobby est une Entreprise Open-Source
Un des points vraiment important de la réalisation du projet MC Hobby était sa concrétisation résolument orientée Open-Source. Tout le processus de création de MC Hobby (inclus Web, WebShop, produits, traductions, documentation, système d'exploitation, traitement de texte, traitement d'image, etc) ne repose que sur des éléments Open-Source.
Il est donc possible, aujourd'hui, de concevoir une entreprise sans faire appel à des logiciels propriétaires.

L'hébergement de MC Hobby
MC Hobby est actuellement hébergé sur un Web Farm chez OVH.
Si le démarrage est trop populaire, il est possible que vous rencontriez des problèmes pour y accéder. Merci de m'en tenir informé (info @ mchobby.be)... dans l'attente de vos réactions, je planifie déjà l'utilisation d'un serveur dédicacé. Merci pour votre patiente.

dimanche 18 décembre 2011

Améliorations pour faire du debugging

Introduction
Après avoir corrigé un problème de détection sur le capteur infra-rouge droit (ArduinoCar ne faisait que des détections à gauche), j'ai re-téléchargé le programme sur Arduino.

Malgré cette correction ma voiture persiste à croire qu'elle a des obstacles devant elle et forcément passe son temps en marche arrière.
C'est ennuyant... encore plus à cause d'un défaut de conception majeur qui provoque une décharge rapide de mes accus.
Il est donc difficile de débugger mon programme longtemps... encore plus parce que la voiture prend le large... difficile à suivre avec le cable USB qui lui est pourtant nécessaire pour voir les messages de Debug.

J'ai donc décidé de corriger ce défaut de conception afin de m'aider a débugger mes problèmes.

Erreur de conception
L'erreur de conception est la suivante: "je n'ai pas de bouton start/stop".
En effet, le programme démarre directement dès la mise sous tension d'Arduino (pour une durée de 60 sec).
A la fin du programme, si je veux recommencer, il me faut appuyer sur le bouton "reset" d'Arduino.
Mais en quoi est-ce une erreur de conception?
  1. Parce qu'il ne faut pas oublier de mettre le circuit de puissance sous tension... sinon, c'est l'alimentation d'Arduino qui se décharge dans le circuit de puissance.
  2. Dès que je branche le cable USB pour faire du debuging, Arduino est mis sous tension et le programme démarre immédiatement.
    En plus de m'imposer la mise sous tension du circuit de puissance (sinon c'est le régulateur d'Arduino qui alimente l'étage de puissance... à éviter donc!), je peux difficilement placer ArduinoCar dans certaines conditions initiales.
Une seconde erreur ergonomique est également présente: "Il n'est pas possible de désactiver l'étage de puissance" (à comprendre comme "il n'est pas possible d'interrompre la commande moteur") pendant le fonctionnement du programme.

C'est un problème ergonomique du point de vue de la mise au point/Debugging.
Il n'est pas facile de faire le monitoring du programme (via le port série) lorsque l'étage des moteurs (avancer/reculer) est alimenté.
Imaginez-vous courir avec votre pc portable derrière votre véhicule entrain de circuler dans le salon au bout du câble USB. Difficile de lire les messages de Log qui passent à l'écran!

Amélioration de la conception
Pin 7: Marche/Arret - Bouton Pull Down
Je vais donc ajouter un bouton marche/arrêt (pull-down) pour commander le démarrage , l'arrêt et le redémarrage du programme.

Une fois le bouton pressé, la Led de contôle (voir plus loin) s'allumera (ou s'éteindra selon l'état), le programme démarre 3 secondes plus tard.


Pin 8: Debug Start/Stop - Bouton Pull Down
Bouton qui va activer ou désactiver le mode de debug.
En mode débug, les fonctions marche et arret ne commandent plus les pins de sorties des moteurs de propulsion. Autrement dit, la voiture ni n'avancera, ni ne reculera.
Cela pourrait être pratique pour faire du monitoring avec un PC.

En sortant du mode debug, le programme s'arrête (état asEnd).


Pin 9: Led de contrôle
Indique l'état du programme Arduino.
  • Éteinte: Véhicule en attente de mise en marche.
  • Allumée continu: Programme en court de démarrage (1 sec) ou en cours d'exécution (pendant 60 sec)
  • Allumée clignotante: idem allumage continu mais en mode Debuging (pas de commande effective de la propulsion avant/arrière, juste une commande logique).
Schéma de montage
Voici le complément de montage aux précédents schéma déjà publiés.
Ajout des boutons start/stop, debug/no-debug
et de la led de contrôle.
Bien entendu, ce dernier schéma vient se greffer sur les autres schéma existant...
Etage de puissance (voir cet article, mais aussi celui-ci)
Des yeux pour ArduinoCar (voir cet article)
Bornier moteurs (voir cet article)


Nouveaux états
Il faudra également compléter la liste des états affichés dans le précédent article "Premier essai avec les détecteur infrarouge".
Il faut en effet tenir compte des conditions de démarrage et d'arrêt comme par exemple attendre que l'utilisateur presse le bouton "start" :-) .
Le nouveau diagramme d'état est disponible un peu plus loin.

Modification d'état existant:
L'état asInitialized a été modifié et ne correspond plus à la description faite dans l'article "Premier essai avec les detecteur infrarouge".

asInitialized:
La machine à état fini reste dans l'état asInitialized jusqu'au moment ou le bouton start/stop est pressé.
Il passe ensuite dans l'état asWaitToGo.

Nouveaux états:
En plus des états décrits dans les précédents articles (et toujours utilisés), la machine à état fini se voit agrémentée de nouveaux états pour supporter les nouvelles fonctionnalités.

asWaitToGo:
Etat intermédiaire destiné à installer un temps de pause de deux secondes entre le moment ou l'utilisateur démarre le programme (utilisateur presse le bouton start depuis l'état asInitialized) et le démarrage effectif du programme (mise en mouvement du véhicule correspondant à l'état asMove).
asWaitToGo attends deux secondes et passe automatiquement à l'état asMove.

asToggleDebug:
Etat atteignable uniquement depuis asInitialized (donc aussi longtemps que le bouton start n'est pas pressé).
Sa seule action est d'inverser la variable debugState pour ensuite revenir à l'état asInitialized.
Avant de retourner à l'état asInitialized, le programme prend le contrôle de la LED pour indiquer, sans équivoque possible, l'état de la variable debugState (voir diagramme ci-dessous)

asToggleDebugOnRunning:
Tout comme pour asToggleDebug, la seule action de l'état est d'inverser la variable debugState et de prendre le contrôle de la LED pour indiquer, sans équivoque possible, l'état de la variable debugState (voir diagramme ci-dessous).
Seulement, asToggleDebugOnRunning est atteignable uniquement depuis les états asWaitToGo (avant que le programme ne démarre réellement) ou asMove (en attente de détection d'obstacle pendant la marche du véhicule).

Note: Il n'est donc pas possible d'activer le mode Debug pendant que le véhicule est en phase d'évitement.

Ensuite le programme retourner à l'état asEnd (donc arrêt moteur) avant transition vers asInitialized (ce qui revient à une pseudo initialization et un redémarrage du programme dès que l'utilisateur presse le bouton start).

Le nouveau diagramme d'états:
Vous constaterez sans mal que la machine à état fini est sensiblement plus complexe que la précédente version.
Cliquer pour agrandir
Erratum: dans le schéma ci-dessus, j'ai oublié de placer la transistion de asMove vers asToggleDebugOnRunning.

Une nouvelle version logiciel
Cette nouvelle version logiciel inclus les modifications nécessaire à l'intégration des améliorations de conception.
Les classes PushButton et MonoLed (voir article précédent "Inclusion des librairies LedTools et ButtonTools") sont respectivement utilisées pour pour détecter la pression des boutons et faire clignoter la Led (lorsque cela s'applique) de façon non bloquante.

Le code contient également la correction concernant la détection infra-rouge défaillante à droite...
Par contre, la correction du comportement anormal fera, lui, l'objet d'un autre article.
Code Source: a venir

mardi 11 octobre 2011

Inclusion des librairies LedTools et ButtonTools

Je me suis mis en tête de développer deux librairies afin de me faciliter la tâche avec ArduinoCar.
Cela à produit les librairies LedTools et ButtonTools publiées sur Arduino Notepad
Les articles de conception de ces librairies sont également disponibles sur Arduino Notepad:

dimanche 7 août 2011

Premier essai avec les detecteur infrarouge

Introduction
Après avoir monté et testé les détecteurs infrarouge (voir article précédent), j'ai décidé de mettre en place mon premier programme de commande pour Arduino Car.


Principes du programme
Le principe est simple, avancer tout droit jusqu'à rencontrer un obstacle.
Lorsqu'un obstacle est rencontré, le véhicule entame une marche arrière en tournant. Suivant que cela soit le détecteur droit ou gauche ou les deux, le changement de direction en marche arrière est différent.
Finalement, le programme cesse toute activité après 60 secondes (histoire que la voiture ne se mette pas à dévaler toute seule tout le quartier :-) 

Une machine à état fini
Pour facilité la prise en mains, j'ai réduit le fonctionnement d'ArduinoCar en différents états (asSetup, asInitialized, asMove, asChangeDir, asEnd).
La machine peut passer d'un état à l'autre en fonction de conditions visibles sur le graphique.

Différents états de l'ArduinoCar

A chaque étape correspond une ou plusieurs actions exécutées par ArduinoCar.
Dès que l'exécution des actions (correspondant à un état) sont terminés le programme principale termine l'exécution de loop() et rend la main à la couche hardware/logiciel Arduino.
Lorsque qu'Arduino ré-exécutera une nouvelle fois la fonction loop(), l'état de la "machine à état fini" sera très utile pour savoir quelles actions loop() doit entreprendre/exécuter.

Voici une liste des différents états accompagnés d'une description et des actions effectuée par l'état.

asSetup
ArduinoCar entame son initialisation, phase durant laquelle le logiciel configure les pins et initialise les différentes variables/structures.
Cet état est assigné à la première ligne de la fonction Setup().
Si plus tard j'utilise des interruptions, cela permettra au code d'interruption de savoir qu'ArduinoCar n'est pas encore initialisée.

asInitialized
Cet état est assigné à la dernière ligne de la fonction setup().
Cela permet de savoir que tout le processus d'initialisation est terminé... et ArduinoCar peut commencer à fonctionner.
asInitialized n'est qu'un état transitoire destiné à la phase d'initialisation matérielle.
Dès que cette valeur est détecté dans la fonction loop(), ArduinoCar passe automatiquement de l'état asInitialized --> asMove

asMove
Etat quasi permanent d'ArduinoCar.
Dans cet état, ArduinoCar avance en ligne droite et effectue une détection d'obstacle à chaque exécution de loop().
Pour le moment, la détection d'obstacle consiste à tester la proximité des deux premiers détecteurs Infra-Rouge à l'aide de la fonction hasProximity().

Dès qu'un obstacle est détecté, ArduinoCar passe immédiatement dans l'état asChangeDir.

asChangeDir
Etat qui n'intervient que lorsque ArduinoCar à détecté un obstacle.
Le but de cet état est de mettre en place une stratégie d'évitemment qui pour le moment se résume à:
  • Arrêter la marche avant.
  • Entamer une marche arrière.
  • Braquer en marche arrière en fonction du/des détecteurs IR activés.
  • Faire une marche arrière pendant une seconde.
Le but étant de faire 1/4 de tour pour partir dans la direction opposée du détecteur IR activé.
Si les deux détecteurs sont activé, la voiture part vers la gauche.

Lorsqu'ArduinoCar a terminé sa phase d'évitement, le code termine son éxécution en réactivant l'état asMove.

asEnd
Permet d'arrêté ArduinoCar après un temps de fonctionnement défini (60 secondes).
Cet état spécial est activé dans la loop() lorsque le temps d'exécution maximim est atteind.
Lorsque cet état est activé, ArduinoCar arrête les moteurs et replace la direction dans le sens "tout droit".

A part cela, l'état asEnd ne fait rien d'autre que de systématiquement terminer l'exécution de loop().

Premiers résultats
Voici une vidéo du premier résultat obtenu.
De prime abord, je suis assez content de moi... mais... visiblement, cette voiture à un problème!
Elle passe son temps à reculer... comme s'il y avait toujours un obstacle devant elle! Cela me fait penser à un chien atteint de la maladie du carré et donc qui se comporte en dépit de tout sens logique :-)

C'est en fait un bug de conversion-comparaison dans la détection de proximité (voir plus loin... c'est très instructif)



Bug de conversion-comparaison

Comme précisé, ArduinoCar passait son temps a reculer... comme si elle détectait constamment un obstacle!
Sur base de ce constat, j'ai jeter un oeil sur la fonction qui détecte la proximité d'un objet hasProximity().
Le code est assez simple à la base:

boolean hasProximity( byte Proximity ){
  return (Proximity < 255);
}

Cela semble enfantin, pourtant il y a un gros bug!
En effet, la variable Proximity est de type Byte... et par conséquent le compilateur fait le nécessaire pour convertir un Byte (non signé) en un entier (signé) avant de faire la comparaison < 255.
Et comme il y a une différence d'encodage binaire, la valeur Proximity prise telle quelle comme un nombre entier signé apparaît comme négative (si je ne me trompe pas).
En conséquence, hasProximity() est toujours vrai... même s'il n'y a pas d'obstacle.

Pour résoudre le problème, il suffit de faire une conversion explicite vers le type entier signé int :-)
Cela produit le code suivant:

boolean hasProximity( byte Proximity ){
  return ((int)Proximity < (int)255);
}

Il est peut être un peu excessif de forcer explicitement la conversion de 255.
Heureusement que j'ai eu l'occasion de rencontrer des articles mentionnant les problèmes de conversion implicite.

Premières conclusions
Mais aussi premières conclusions.

Conclusion 1: l'arrêt
Ne pas oublier de couper la motorisation quand on passe dans l'état asEnd.
Si cela semble évident, je n'y avais pas pensé dans mon empressement à tester mon premier jet de code.
Comme ArduinoCar est passer de l'état asMove vers asEnd après 60 secondes, j'ai dut me mettre à galoper dans le quartier pour la rattraper... en effet, je n'avais pas pensé à positionner la marche sur arrêt en passant à l'état asEnd!
Ce bug là est déjà corrigé :-)

Conclusion 2: Led et bouton
Il serait bien d'utiliser une led pour indiquer la fin de l'exécution du programme (état asEnd).
Eteind = en cours de fonctionnement
Clignotante = fin de programme.
De même, utiliser un bouton pour démarrer/arrêter la séquence serait plus approprié que d'utiliser le bouton Reset (pour recommencer)


Conclusion 3: Apprendre à freiner et a ralentir
Sur sol plat, la marche à 100 % est très efficace.
Le temps qu'ArduinoCar détecte l'obstacle et passe la marche arrière, le véhicule se fracasse sur l'obstacle du seul fait de son inertie.
Le problème est identique si l'obstacle arrive de biais.

Il y a plusieurs options pour corriger ce problème:
  1. Freiner à la détection de l'obstacle
  2. Diminuer la vitesse de Marche.
    Inférieur à 100% de régime, soit configurable avec un bouton.
Conclusion 4: Obstacle de biais en reculant
Après un changement de direction, il faut avancer un peu avant de faire une nouvelles détection d'obstacle.
En effet, si ArduinoCar recule sur une bordure de trottoir de biais (ou une rigole), les détecteurs sont dirigés vers le sol.
Il en resulte donc une nouvelle détection d'obstacle et une nouvelle tentative de recul et changement de direction.
Pour contourner ce problème facilement, ArduinoCar devrait avancer un peu avant de quitter l'état asChangeDir.

D'autres stratégies pourraient également se monter efficaces.
L'utilisation d'un IMU (inertial measurement unit) pourraient également faciliter la détection d'une telle situation (car modification d'angle du véhicule durant le retour).

Conclusion 5: Redresser la direction
Le redressement de la direction est assuré par un moyen mécanique (ressort).
Ce n'est pas toujours très efficace lorsque le véhicule se déplace.
Il faudrait aider la direction à se redresser en envoyant une impulsion de changement de direction dans l'autre sens.
Il est également possible d'envisager l'usage d'un capteur a effet Hall (et d'un aimant placé sur l'arbre de direction) pour savoir si la direction est bien revenue au point mort.

Conclusion 6: Se méfier de la détection Infrarouge
A plein régime, le véhicule tressaute un peu dans tous les sens.
Cela crée des faux positif de détection d'obstacle (suivant le sens dans lequel le véhicule est bousculer)... et elle se met à faire un changement de direction spontanément.
Pour résoudre ce problème, il faudrait faire un déparasitage logiciel (comme pour les boutons).
Dans ce cas-ci, plutôt que de bloquer l'exécution 10 ms, il serait plus approprié de s'assurer qu'il y ait deux détections consécutives.
A noter qu'après avoir tourné, il faut annuler la dernière détection en date (puisque l'on a changé de direction).

Code Source
Sources: ArduinoCar_StateMachine_v1.zip
Cette archive contient un répertoire avec les deux fichiers sources que j'utilise (en autre ArduinoStates.h qui contient la définition des états sous forme d'un enum).
Il suffit de dézipper le répertoire et ses fichiers dans votre répertoire sketchbook.

Informations utiles sur le code source
La fonction irProximity( int pinIr )
Cette fonction   fait une capture de la proximité à l'aide d'un senseur infrarouge Sharp 2Y0A21 branché sur la pin pinIr.
Cette valeur est transformée en indice de proximité (voir les valeurs de retour).
Pour info, la tension de reférence ARef d'Arduino est fixé à 3.3v pour augmenter la précision de la lecture.

Paramètres:
pinIr - pin analogique sur laquelle le senseur est branché (ex: pinIrGauche, pinIrGauche)

Résultat:Retourne un byte qui représente un indice de proximité.
  • 255: hors du champs de détection
  • 24: 24 cm ou moins
  • 15: 15 cm ou moins
La fonction hasProximity( byte Proximity )
Fonction très simple qui prend l'indice de proximité en paramètre (valeur retournée par la fonction irProximity.

Résultat:Retourne un booléen à  true s'il y a quelque-chose à proximité.

La fonction DoMove()
Cette fonction, appelée continuellement lorsque la voiture est dans l'état asMove à pour principale utilité:
  1. De donner l'ordre de déplacement d'ArduinoCar
  2. De détecter les objets à proximité (appel à irProximity et hasProximity)
  3. De faire le nécessaire pour changer l'état vers asChangeDir si un obstacle est détecté.
Voici le détail de la fonction DoMove():
enum ArduinoState doMove(){
  // Detection de proximité
  irGaucheProximity = irProximity( pinIrGauche );
  irDroiteProximity = irProximity( pinIrDroite );
 
  // Changement de direction ?
  if( hasProximity( irGaucheProximity ) || hasProximity( irDroiteProximity ) )
    return asChangeDir;
 
  marche( AVANT, 100 );
   
  return asMove;
}


Variables volatiles
Dans le point précédent, la fonction DoMove capture les indices de proximités sur les deux détecteurs infrarouges.
Les deux valeurs sont stockées dans les variables globales irGaucheProximity et irDroiteProximity qui sont déclarées comme suit:

volatile byte irGaucheProximity;
volatile byte irDroiteProximity;


Le mot clé volatile informe le compilateur qu'il ne faut pas optimiser le code relatif à l'utilisation des variables en questions.
En effet, sans volatile, le compilateur pourrait décider de changer l'ordre des instructions pour amélioré la rapidité d'exécution.
C'est ainsi que par exemple, le compilateur pourrait changer l'ordre d'exécution d'une boucle for pour qu'elle "décompte" au lieu de "compter". Si en générale cela à peu d'implication sur l'exécution du programme, cela peut avoir des conséquences importantes dans d'autres cas.

Dans le cas d'utilisation de variables tels que irGaucheProximity et irDroiteProximity, l'optimisation du code par le compilateur pourrait réserver des surprises.

Finalement, l'usage d'une variable globale pour le stockage de irGaucheProximity et irDroiteProximity peu paraître à première vue comme "une lacune".
Cependant, à terme, j'ai l'intention de faire une moyenne des deux ou trois dernières lectures de proximité pour éliminer les faux positifs (voir plus haut dans l'article).
Dans ce cas, l'utilisation d'un tableau en variable globale sera approprié... la raison pour laquelle les variables sont déjà déclarées comme globales.

vendredi 22 juillet 2011

Des yeux pour Arduino Car

Introduction
Jusqu'a maintenant, je me suis focalisé sur la propulsion d'Arduino Car.
Si cela en fait un véhicule capable de se déplacer seul, il est aussi terriblement stupide dans ce sens ou il ira se jeter sur le premier obstacle sans même s'en rendre compte.

Voila pourquoi je vais équiper Arduino Car de senseur.
Mon dévolu se porte sur les capteurs infrarouge gp2y0a21 de Sharp.


Capteur infrarouge Sharp gp2y0a21
J'ai justement eu l'occasion d'en acheter 4 chez Antratek.
J'ai d'ailleurs écris l'article "Detecteur de proximité infrarouge - Sharp gp2y0a21" sur Arduino Notepad, un occasion unique pour se faire la main dessus.
L'article précité indique d'ailleurs ou se fournir ce type de capteur.


Montage des capteurs
Je vais commencer par monter deux capteurs sur l'avant de la voiture histoire de détecter les obstacles frontaux.
Une bonne partie de l'article sera consacré à ce montage et aux tests des capteurs.

D'autres capteurs seront nécessaires

Par contre, je sais déjà qu'il me faudra envisager deux autres capteurs, ce que viendra confirmer les premiers tests.
Ces capteurs serviront:
  • Détecter les obstacles en hauteur (pour éviter à la voiture de se jeter sous les meubles bas ou les voitures).
  • Un détecteur de profondeur (orienté vers le sol) pour détecter les trous ou simplement les bordures de trottoir en oblique (45°).
  • Si le détecteur de profondeur est placé au centre de pare-chocs, cela permettra aussi de détecter des objets tels que des pieds de chaises ayant échappé aux deux premiers détecteurs.
Truc et astuces
Pour faciliter le montage, je vous suggère de préparer:
  1. L'utilisation de tout petits colsons (exemple ici) pour faciliter le montage. 
  2. Un fer à souder pour marquer les emplacements des trous à forer pour placer les senseurs infrarouges.
  3. Une mèche pour faire les trous en vue de laisser passer les colsons.
  4. Du fil et des pins headers pour le raccordement sur les entrées A0 et A1 d'Arduino.
Le montage en images
Eléments du montage

Percage des trous de fixation

Fixation à l'aide de Colsons

Autre vue de la fixation

Raccordement électrique

Détail du raccordement

Placement sur la voiture

Raccordement Arduino
  • Entrée Analogique A0 : Senseur Gauche (fil brun)
  • Entrée Analogique A1 : Senseur Droit (fil orange)
  • Sortie 3.3V raccordée sur entrée AREF.
    Comme les capteurs infrarouges ne retournent pas une tension supérieure à 3.3V, cet important raccordement permet d'augmenter la précision des lectures analogiques.
Schéma de raccordement des détecteurs
Test de la détection
Pour faire les tests de détections, j'ai créé un petit logiciel spécifique lit les valeurs analogiques sur A0 et A1 et envoi l'information sur le port série (qu'il est possible de visualiser avec le moniteur d'Arduino IDE).
L'information est lue toutes les secondes et envoyées sur le port série comme suit:
----------------------------
GAUCHE 478, volt=1.54, prox=24
DROITE 71, volt=0.23, prox=255
-----------------------------
GAUCHE 470, volt=1.51, prox=24
DROITE 232, volt=0.75, prox=255

A savoir:
  • Détecteur de Gauche
    • Valeur lue sur le port analogique (0 à 1024)
    • Conversion en volts (sur base de la tension de référence de 3.3v)
    • Un indice de proximité (voir documentation plus loin).
  • Détecteur Droit
    Idem
Indice de proximité
Sur base du graphique suivant disponible dans la datasheet (accessible depuis cet article) et sachant que la dite "distance de jugement" est de ~24cm, j'ai écris un bout de code évaluant grossièrement la proximité d'un objet (l'indice de proximité).
Source: datasheet
Ce bout de code retourne les valeurs suivantes:
  • 255: hors du champs de proximité. Pas d'évaluation plus précise.
  • 24: distance de jugement atteinte. Soit 24 cm ou moins.
  • 15: distance proche atteinte. Soit 15cm ou moins (il est grand temps d'agir!).
Code source
Source: Infrared_Sensor_Test.pde
/*
    ArduinoCar - http://arduinocar103.blogspot.com
    Test des detecteurs Infra-rouge Avant  d'ArduinoCar
    
    Date: 21 juillet 2011
      
    Description:
      Ce programme affiche les valeurs detectées par les deux 
      détecteurs IR placé à l'avant de la voiture (branchés 
      sur A0 et A1).
      
      Les valeurs sont écrites sur le port série
    
    Montage:
      ARef = 3.3 volts
      Infrared left=pin A0
      Infrared right=Pin A1
 */
 
 int pinIrGauche = 0; // détecteur Infrarouge a Gauche
 int pinIrDroite = 1; // détecteur Infrarouge a Droite
 
 void setup(){
     analogReference( EXTERNAL );
     Serial.begin( 9600 );
 }
 
 void loop(){
   // Capture de la valeur
   int valeurIrGauche = analogRead( pinIrGauche );
   int valeurIrDroite = analogRead( pinIrDroite );
   
   // Transformation en volts (référence=3.3v)
   float voltIrGauche = ((float)valeurIrGauche)*3.3/1024;
   float voltIrDroite = ((float)valeurIrDroite)*3.3/1024;
   
   // Evaluation de la proximité (par ordre de grandeur en CM)
   // Les valeurs possibles sont:
   //  * 255 = valeur maximale, hors du champs de la perception
   //  * 24  = objet à la distance de jugement (24 cm ou moins)
   //  * 15  = objet très proche (15 cm ou moins)
   // 
   byte proximiteGauche = 255; // distance > 24 cm
   if( voltIrGauche > 1.6 ) 
     proximiteGauche = 15; // distance < 15 cm
   else
     if( voltIrGauche > 1.1 ) // distance de jugement +/- 24 cm
       proximiteGauche = 24;

   byte proximiteDroite = 255; // distance > 24 cm
   if( voltIrDroite > 1.6 ) 
     proximiteDroite = 15; // distance < 15 cm
   else
     if( voltIrDroite > 1.1 ) // distance de jugement +/- 24 cm
       proximiteDroite = 24;
   
   // Affichage des valeurs
   Serial.print( "GAUCHE " );
   Serial.print( valeurIrGauche ); Serial.print( ", volt=" ); 
   Serial.print( voltIrGauche ); Serial.print( ", prox=" ); Serial.println( (int)proximiteGauche );

   Serial.print( "DROITE " );
   Serial.print( valeurIrDroite ); Serial.print( ", volt=" ); 
   Serial.print( voltIrDroite ); Serial.print( ", prox=" ); Serial.println( (int)proximiteDroite );
   
   // Affichage d'un séparateur
   Serial.println( "-----------------------------" );
   
   delay( 1000 ); // attendre 1 seconde avant lecture suivante.
 }

Conclusions
Premièrement: la fonction de proximité
Le code de détection de proximité va grandement facilité la détection d'objet.
Il faudra donc en faire une fonction.

Deuxièmement: les défauts de détections
Il existe des situations dans lesquelles la détection de proximité est peu inefficace, voire pas du tout.
Certaines imperfections peuvent être palliées par une action logicielle... d'autres nécessiterons des capteurs complémentaires.

La bordure de trottoir en biais:
Dans ce cas, la détection des 24 cm n'intervient que très tard... très vite suivie par la détection des 15 cm (pour une avancée du véhicule d'un cm seulement).
ArduinoCar détecte pas rapidement les bordures en biais.

Dans ce cas, la détection est tellement tardive que le véhicule ira se jeter dans la bordure rien qu'avec son inertie.
Il faudra donc prévoir un système de freinage pour que le véhicule (une impulsion en marche arrière par exemple :-) ).

Les objets au dessus des détecteurs:
C'est le cas des dessous d'armoire, ou de voiture.
Si les senseurs passent, ce n'est pas forcement la cas du restant du véhicule exemple.
Arduino Car ne détecte pas les objets en hauteur
Il est possible de résoudre cet inconvénient en plaçant un senseur en hauteur.
 
Les objects sous les détecteur:
Voici un cas pratique
Arduino Car ne détecte pas les objets au ras du sol
Ce cas de figure (tout comme la bordure de trottoir en biais) peu être contourné en plaçant un détecteur infrarouge pointant vers le bas.

Le pied de chaise:
Encore un autre cas pratique...
La pied de chaise n'est pas détecté!
Ce problème, tout comme celui des objets au dessus des détecteurs peuvent être résolu à l'aide d'un senseur infrarouge en hauteur (et orientable).


... il ne reste plus qu'a passer à la suite

mercredi 20 juillet 2011

Premières constatations d'ordre technique

J'ai eu l'occasion de faire quelques essais avec Arduino Car maintenant que la motorisation est terminée.

Et trois choses sautent immédiatement aux yeux.

Premièrement: détection des obstacles
C'est prévu depuis le début mais il faudra vraiment que j'ajoute des détecteurs infrarouge sur Arduino Car.
Lorsque l'accu est bien chargé, une marche avant de 4 secondes est suffisant pour que la voiture quitte le parking (pour aller s'encastrer sous un autre véhicule).

Deuxièmement: autonomie, une histoire d'accu
Le véhicule ne dispose pas d'une très longue autonomie.
En moins de 10 minutes, l'accumulateur est suffisamment a plat pour que la voiture n'avance presque plus à régime de 78% alors que c'est honorable à pleine charge.
Ensuite, comme le véhicule manque de nerf, la direction ne se redresse pas vraiment bien... probablement que le fait d'avancer aide la direction à se redresser.

L'accumulateur à une autonomie de 650mAh.
Les précédentes mesures ont démontrés que la propulsion consomme environ 800mA en charge et que chaque train de direction consomme ~330mA en butée.
Comme il y a deux trains de directions, cela fait 660 mA
Dans la pire des situations, la consommation globale est de  ~ 800 + (330 * 2) = 1460mA.
Si je veux faire fonctionne mon montage pendant une heure, j'ai besoin d'un accu de 1500 mAh.
Hors, je n'ai qu'un accu de 650mAh (usagé). Je ne peux compter au mieux que sur 30 minutes d'activité.
Quoi que 15 à 20 min semble plus vraisemblable!


Troisièmement: la distance parcourue
C'est bien beau de faire une marche arrière pour changer de direction (face a un mur par exemple).
Mais la distance parcourue lors de la marche arrière dépends de la charge de batterie.
Lorsque la batterie est presque morte, le véhicule ne parcours par 1/4 de tour en 2.5 secondes... alors qu'a pleine charge, elle fait plus d'un tour sur le même laps de temps.
Il faudra donc un moyen pour mesurer/estimer la distance parcourue... probablement d'un capteur à effet Hall et de quelques aimants permanent sur la roue.

En conclusion, je dirais qu'il y a encore du boulot :-D