GoogleEarth (anciennement Earth Viewer, développé par KeyHole Inc. et racheté par Google) offre un point de vue révolutionnaire sur notre planète.
ERASME à développé un interactif pour l’exposition Inuit, permettant de naviguer de manière intuitive dans GoogleEarth, afin d’explorer les régions arctiques, leurs habitants et leur climat.
Cet article présente les détails techniques de cet l’interactif.
- Vue d’ensemble du dispositif
- (dans la version finale, les couleurs entre le menu de l’écran et le touchpad sont identiques)
Aperçu
Le globe est constitué d’un socle en bois supportant une demi-sphère de plexiglass représentant l’hémisphère nord.
La demi-sphère est peinte sur sa face intérieure donnant une représentation artistique mais géographiquement correcte des terres et des océans.
Les objectifs de l’interactif étaient multiples :
- pouvoir facilement visualiser un point choisi sur le globe physique dans GoogleEarth (GE),
- pouvoir se déplacer facilement sur un endroit souhaité inaccessible sur le globe physique
- avoir accès aux fonctions de navigations habituelles de GoogleEarth (zoom, inclinaison, ...)
- avoir la possibilité d’afficher des informations supplémentaires (sous forme de couches KML) simplement.
En résumé, il s’agit d’utiliser GoogleEarth (GE) sans souris, et sans avoir à déplier des menus pour selectionner des couches à l’aide de cases à cocher.
Interfaces
Pour parvenir à ce résultat, trois interfaces "de saisie" ont été développées :
- détection de selection d’un point sur le globe physique
- pilotage de l’affichage de la vue à l’aide d’une Wiimote
- sélection des couches à afficher via un touchpad rétroéclairé, sur lequel 6 zones sont matérialisées, permettant d’effectuer 6 choix de couches. Les choix peuvent être arborescent (par exemple : Climat->Glace->Permafrost).
Ces interfaces pilotent l’affichage dans GE (déplacement sur le point choisi, mouvements, affichage des couches) via un lien réseau [1] KML (voir "NetworkLink") et des raccourcis claviers à envoyer à l’application.
En sortie, l’utilisateur dipose d’un retour d’information via GoogleEarth, dont l’image est projetée sur un écran, et via le rétroéclairage du touchpad. Sur ce dernier, les 6 zones de selection peuvent être éclairées individuellement, permettant de matérialiser les différents choix possibles.
Tous ces éléments doivent bien sûr être pilotés depuis Linux sous Linux.
Architecture Générale
Lors du développement, la tâche s’avère rapidement complexe.
Tout d’abord, chaque sous système d’entrée présente quelques défis. La gestion du gestion du touch pad nécessite une gestion matérielle pour la détection du point touché aussi bien que pour le rétroéclairage des 6 zones. La détection du toucher sur le globe physique est passé par différentes étapes, du choix de la technologie (voir Presentation du projet) à l’affinage des paramètres de détection (Premiers essais de vision).
La gestion de l’affichage via des fichiers KML était aussi plutôt confuse : GoogleEarth ne semblait pas réagir de manière consistante lorsque des placemarks" etaient utilisés, se promenant parfois de manière errratique.
La gestion de Wiimote, très simple, se heurte cependant à queques écueils : détection parfois difficile, reconnexion à la Wiimote impossible lorsqu’elle sort de la zone de couverture Bluetooth...
Un système de gestion de scénarios doit aussi être développé, afin de pouvoir facilement modifier le contenu des différentes couches affichées sans avori à recompiler du code.
Enfin, ces différents éléments doivent coopérer entre-eux afin de piloter GoogleEarth de manière cohérente, et doivent démarrer et se synchroniser correctement .
Le schéma ci-contre montre les différents sous-systèmes développés pour parvenir au résultat. Le canal de communication principal utilisé entre eux est l’OSC (Open Sound Control), protocole réseau qui permet de tranférer des couples clefs/valeurs entre applications relativement simplement.
Sous-systèmes
GoogleEarth
Sur la machine hébergeant l’interactif, GoogleEarth est l’élément final de la chaîne. Il est configuré à l’avance avec un lien réseau pointant vers un serveur Apache local permettant de servir un fichier KML update.kml.
Il démarre en dernier, et KeyEvents se charge de sélectionner le lien réseau dans la panneau "Lieux" et de le mettre en plein écran.
KeyEvents
KeyEvents est une application développée en Processing (un sur-ensemble de Java). Il permet de recevoir les différentes requètes des autres sous-systèmes en OSC (grâce à la librairie oscP5 ) et d’envoyer les évènements claviers correspondant à GoogleEarth.
Au démarrage, KeyEvents met GE en situation : selection du lien réseau et plein écran. Ensuite, KeyEvents écoute sur le port 1234 les différents paquets qui lui sont adressés. KeyEvents enverra ensuite les évenements claviers requis à GE afin d’effectuer l’action désirée.
Par exemple, lorsque l’on appuie sur le flèche du haut de la Wiimote, un paquet OSC sera reçu par KeyEvents qui génèrera un évènement clavier correspondant à "flèche du haut" et l’enverra à GE.
L’application utilise pour cela la classe java Robot, qui permet de simuler des évènements claviers et souris. Cette voie peut sembler peu robuste, mais sous Linux c’est le seul moyen de piloter GoogleEarth. Une API de pilotage est bien disponible, mais c’est un composant COM, uniquement disponible sous windows.
D’autres solutions ont été envisagées, notamment l’utilisation d’évènement X, mais les premiers développement effectués n’ont pas donné satisfaction et la voir Processing a finalement été selctionnée, et il faut reconnaitre que le résultat est surprenant de simplicité et d’éfficacité.
A titre d’exemple, un mode "panique" a été implementé : l’appui simultané sur les touches "A" et "Maison" de la Wiimote provoque la réinitialisation de l’interactif (redémarrage de X). Pour cela, il faut simuler l’envoi des touches "Control", "Alt" et "Backspace" en même temps [2]. Le code ci dessous réalise cela en quelques lignes :
void setup()
{
...
robot = new Robot();
...
}
void send_ctrl_alt_backspace()
{
println("Got Ctrl-Alt-Backspace code");
robot.keyPress(KeyEvent.VK_CONTROL);
robot.keyPress(KeyEvent.VK_ALT);
robot.keyPress(KeyEvent.VK_BACK_SPACE);
}
Généralement, quand on envoie un appui sur une touche, on envoie aussi un "relèvement" de cette touche (robot.KeyRelease). Dans ce cas particulier, un release ne sert à rien puisque X est tué sans autre forme de procès.
La réception de paquets OSC s’effectue dans un callback : le "callback" (une fonction) est appellée lorsuq’un paquet arrive. Cette fonction va alors analyser le contenu du paquet et prendre les actions appropriées.
Dans chaque paquet reçu, l’état de tous les boutons de la Wiimote disponibles. Le code suivant, issu du callback, remplit un tableau contenant l’état de ces boutons :
void oscEvent(OscMessage theOscMessage) {
//println("Got osc message");
/* get and print the address pattern and the */
/* typetag of the received OscMessage */
if(theOscMessage.checkAddrPattern("/erasme/globe/2/wiimote/0/state")==true) {
/* check if the typetag is the right one. */
if(theOscMessage.checkTypetag("iiiiiiiiiii")) {
/* parse theOscMessage and extract */
/* the values from the osc message arguments. */
for(int i=0; i<=ihome; i=i+1) {
wii_states[i] = theOscMessage.get(i).intValue();
}
....
La suit du code permet, en fonction de boutons appuyés sur la Wiimote, de déclencher l’’évenement claier correspondant.
KeyEvents recoit aussi, dans d’autres paquets (différenciés par checkAddrPattern) l’état de l’accéléromètre de la Wiimote, et peut traiter des demandes de rechargement de la vue GoogleEarth (en générant un Ctrl-R).
wii2osc
Sans surprise, wii2osc permet de transformer les évènements de la Wiimote en paquet OSC. Plus exactement, chaque fois que la Wiimote change d’état (valeurs de l’accéléromètre, appui de touche), un paquet OSC est envoyé à KeyEvents.
wii2osc utilise la librairie libwiimote, qui prend en charge les appels de la couche BlueTooth, et le décodage du protocole de la Wiimote et la librairie liblo, pour la création et l’envoi de paquets OSC. Cette dernière librairie à été patchée afin de permettre l’envoi de paquets OSC en broadcast [3].
libwiimote permet la découverte de périphériques Wii : on peut alors s’associer n’importe quelle Wiimote. Nous avons préféré utiliser une association dirigée, avec une adresse matérielle de Wiimote explicite, afin d’éviter qu’un utilisateur prenne le controle de l’application avec une Wiimote "pirate".
globetouch
C’est l’application centrale de vision de l’interactif. Elle est en charge de la détection du toucher sur le globe physique.
Le principe, simple, est détaillé dans un autre article.
En résumé, l’idée est de détecter l’ombre de la main de l’utilisateur, d’extrapoler la position du doigt pointant un endroit sur le globe, et d’envoyer cette information en OSC à padcontrol (voir ci-dessous).
L’application réalise cela en "apprenant" une image moyenne, et en comparant avec l’image acquise. Les différences indiquent alors qu’un objet (ici, à priori, une main)fait varier l’image. L’apprentissage de cette image moyenne est constant : une modification de l’éclairage global (extinction d’une ampoule, passage d’un nuage devant le soleil)
globetouch est relativement exigeant en terme de contraste : si l’éclairage au dessus du globe n’est pas suffisant, l’ombre portée ne sera pas détectée. D’autre part, la peinture utilisée pour représenter les terres et mers à l’intérieur du globe à une opacité qui varie en fonction de la couleur. et qui peut nuire à la détection dans certaines zones si le contraste d’éclairement n’est pas suffisant.
Un autre point digne d’intéret dans le code est la transformation des coordonnées et le calibrage de la caméra. L’image capturée par le capteur de la caméra est plane. Or, les coordonées que l’on doit obtenir à partir du point touché doivent être polaires : latitude, longitude. Il faut donc convertir les coordonées planes en coordonnées polaires.
Cela ne peut se faire qu’avec une caméra dont la position est correctement calibrée : elle doit être centrée, et son orientation ainsi que la taille du globe doivent être connus.
Pour cela, un lieu de référence est pointé par l’utilisateur lors du calibrage : cela permet de connaitre quelle est la position de la caméra par rapport au méridien de greenwich, et d’appliquer ensuite un décalage aux longitudes obtenues. De même, la latitude du lieu pointé permet de determiner la taille (en pixels) du globe tel qu’il est vu par la caméra. On pourra ensuite utiliser aisément les équations standard [4] de transformation planaire/polaire :
longitude = atan(y/x);
latitude = acos(sqrt(x * x +y * y)/rayonglobe);
(ou x et y sont les coordonnées du point detecté avec le centre du capteur comme référence).
Enfin, un problème a émergé rapidement : le repositionnement de GoogleEartth s’effectuait parfois de façon parasite lorsqu’une personne déplaçait sa main au dessus du globe sans pour autant le toucher.
Au premier abord, ce problème apparaissait particulièrement ennuyeux : comment savoir si le globe était touché ou nom. Un des seules solutions alors envisageable semblait être de mettre un capteur capacitif dans le globe, avec tous les problèmes que cela engendrait : pilotage du capteur, ajout d’une électrode en cuivre en spirale dans le globe avec des effets de bord potentiels sur la vision, etc... Rien de très engageant.
Une solution élégante à finalement été avancée par Patrick Vincent : il suffit de vérifier si le doigt reste dans la même position pendant un temps donné. On peut alors affirmer dans 99% des cas que la personne a le doigt posé sur le globe et qu’elle touche effectivement ce dernier.
Cette solution à l’avantage d’être très simple à implémenter, puisqu’elle reste dans le cadre de la vision et n’implique pas de périphérique supplementaire. Après quelques heures de paramètrage des differents paramètres (durée d’immobilité, tolérance dans la mobilité), elle s’est en plus révélée très efficace. La preuve que 5 ans d’INSA section Théatre ne sont pas forcément du temps perdu
padcontrol
padcontrol est, avec globetouch, l’élément le plus gros en terme de code de toute l’application. On reste tout de même dans le domaine du modeste (de l’ordre de 1000 lignes). Mais cette application est bien la plus sensible de toutes : elle gère le rafraîchissement des fichiers KML depuis les scénarions définis et les actions reçues du touch pad, elle parle à l’Arduino interfaçant le touch pad et le rétroéclairage, elle reçoit des évènement OSC depuis globetouch indiquant qu’un point à été touché sur le globe physique (générant ainsi un nouveau fichier KML), et, enfin, notifie KeyEvents des changements afin que ce dernier recherche le lien réseau dans GoogleEearth. padcontrol permet aussi de mettre l’interactif en mode "remplissage de cache" : après un temps d’inactivité paramétrable, il génère des fichiers KML avec des lieux aléatoires (compris dans une fourchette latitude/longitude paramétrable) qu’il fait charger à GoogleEarth. Cela permet de profiter des possibilités de cache de GE. Lorsqu’un utilisateur ira ensuite dans un endroit déja visité, le chargement de l’image sera immédiat.
L’abondance des sources de données à surveiller et des actions à mener de cette sorte de "multiplexeur" à rapidement impliqué des choix.
Ici, les threads se sont rapidement imposées. Tout d’abord à cause de liblo, librairie qui gère le protocole OSC. Cette dernière implémente le traitement des paquets OSC dans des callbacks qui traitent des évènement OSC différents. Par ailleurs, nous voulions tirer parti des processeurs multi-core de la machine hébergeant l’interactif, et avoir un thread de mise à jour des fichiers KML indépendant du fil d’exécution du reste du code.
Par ailleurs, l’utilisation de threads et d’OSC confèrent une grande robustesse à l’interactif malgré la multiplicité des applications msies en oeuvre : si la partie vision tombe en panne (caméra ou crash de globetouch), l’interactif continue de fonctionner sans le toucher, idem pour la Wiimote. Même si l’Arduino grille, padcontrol continue d’assurer ses autres fonctions, puisque sa gestion est confiée à un thread et n’est donc pas bloquante.
Loin être un chef d’oeuvre, padcontrol contient beaucoup de choses intéressantes pour le programmeur moyen que je suis : threads, mutex, pthread_cond, communication série, communication réseau via OSC, lecture d’un fichier de configuration "complexe" (via libconfuse), sighandlers... Bref, pas mal de code d’exemple ou réutilisable dans d’autres projets, en particulier en lien avec des microcontrolleurs.
Arduino & touch pad
La partie matérielle s’appuie sur un Arduino. Même si nos privilégions si possible les µcontrolleurs "nus" (pour des raisons de coût et de disponibilité), l’Arduino est bien pratique quand il est nécessaire de faire communiquer le micro-controlleur avec un PC. Avec un chip nu, il faudrait passer par la case conversion de niveaux TTL/Série. Ca n’a rien de compliqué, mais c’est un montage répétitif, peu engageant, et qui prend tout de même un certain temps à réaliser et à tester. Par ailleurs, la librairie de l’Arduino fournit beaucoup d’abstraction très pratiques (notamment pour le port série et la génération de PWM) qui réduisent considérablement les temps de développement et de déboguage. cela s’obtient en revanche au prix d’un encombrement non négligeable (Arduino NG), mais dans notre cas, la place disponible n’est pas un problème.
L’Arduino va donc gérer la lecture du toucher sur le touchpad, et le rétroéclairage via les 6 diodes Lumiled Superflux.
- Pilotage des LEDs
Les LEDs sont pilotés en PWM (Pulse Width Modulation). Cela permet d’avoir des niveaux de luminosité (ici, 256) intermédiaires entre rien (LED éteinte) et allumé "à fond". Grâce à l’ATmega168 de l’Arduino, nous disposons justement de 6 canaux pourvant générer du PWM. Tous les allumages et extinctions sont donc effectués par ce biais, en modifiant progressivement la luminosité entre 0 et 255 (ou 255 et 0) sur une période d’une seconde. Cela permet d’obtenir des effets "pulsatiles", plus doux, moins agressifs qu’en allumant la diode à fond brutalement.
Les diodes ont aussi été calibrées empiriquement : les diodes à tenance rouge (route, roure/or, ambre) étaient visiblement beaucoup plus puissantes que les diodes "froides" (vert, cyan, bleu). Les valeurs PWM ont donc été compensées en conséquence, afin de rétablir un équilibre de puissance entre ces 6 diodes.
Les diodes utilisées (Lumileds Superflux) consomment entre 50 et 70mA. Le microcontrolleur étant incapable de fournir autant de courant, un tout petit circuit basé sur l’excellent ULN2803 (fourni gracieusment par Texas Instruments) a été câblé pour alimenter les LEDs. Le circuit a été fait sous forme de "shield" pour Arduino, afin de faciliter la mise en place. Une nappe type HE-10 se charge d’amener le courant jusqu’aux diodes. Ces dernières sont situées sous un diffuseur (acrylique blanc), afin de donner un effet de bloc lumineux et d’éliminer le "point chaud" visuel de la diode.
- Gestion du touchpad
Le touchpad, vendu par Sparkfun, est de type 4-fils resistif. Ces fils sont reliées à deux films conducteurs séparés par un isolant. Lorsque l’on appuye sur le pad, les couches conductrices entrent en contact, créant un diviseur de tension (les couches ont une résistance comprise entre 500 et 700 Ohms environ).
Il y a deux fils par axe (X+, X- pour l’axe horizontal, et Y+, Y- pour l’axe vertical).
Donc pour mesurer un point, de contact, on utilisera la méthode suivante :
1- appliquer 5v à X+
2- appliquer 0v à X-
3- lire la tension sur Y+ (ou Y-, peu importe) grâce à l’un des convertisseurs analogique/numérique (ADC)
Si la tension lue est, par exemple, 2.5v, c’est que le doigt est au milieu de l’écran. Plus la tension est haute, plus le doigt est vers X+. Plus elle est basse, plus le doigt est vers X-.
Pour lire la "position" (la tension) sur l’axe Y, on effectuera la même opération en intervertissant les électrodes (Y+ à 5v, Y- à 0v, lecture sur X+).
Il faut donc deux mesures pour obtenir un résultat sur les deux axes.
Les convertisseurs échantillonant sur 10bits, on aura donc 2^10 valeurs possibles soient 1024. Autrement dit, on aura une résolution de 1024x1024 sur l’écran. Compte tenu de la taille de l’écran, c’est bien plus que nécessaire.
Le résultat sera en plus découpé pour déterminer dans laquelle des 6 zones l’utilisateur à appuyé.
Le touchpad est directement pilotable par l’Arduino. Il faudra juste ajouter deux resistance de "pull-down", afin de lire 0v lorsqu’aucun appui n’est effectué sur le pad. Sans cette résistance, la tension lue sur l’électrode est "flottante", et les valeurs lues sont invalides. Il faut noter que ces resistances de rappel doivent être les plus élevées possibles (mais pas trop non plus, afin d’éviter d’autres effets de bord). En effet, elles participent au diviseur de tension dans la mesure (RDY avec Y- pour X+ à 5v et X- à 0v par exemple) ! Les choisir élevées permet de minimiser leur impact sur les mesures obtenues.
L’Arduino renvoie les valeurs lues à la demande. C’est donc padcontrol qui pilote indirectement l’acquisition. padcontrol va déterminer une zone en fonction des valuers lues par les convertisseurs analogiques.numériques (ADC), et les comparer aux informations lues dans son fichier de configureation :
...
/* Buttons zones */
button 0 {
/* N, S, W, E limits */
limits = { 131, 469, 70, 358 }
}
button 1 {
/* N, S, W, E limits */
limits = { 131, 469, 359, 647 }
}
button 2 {
/* N, S, W, E limits */
limits = { 131, 469, 648, 937 }
}
button 3 {
...
Ces trois boutons sont ceux que l’on trouve dans la partie supérieure du touchpad. On remarquera que certaines valeurs sont exclues (ordonnée < 131, abscisse < 70, ...). ces values ne sont pas atteignables en raison des contraintes physiques (en effet, on ne lira jamais du 5volt depuis l’écran, une certaine marge étant inutilisable). Cela réduit la résolution, mais reste très largement suffisant pour notre application.
- Intégration
Les diodes, le diffuseur et le touchpad constituent un sandwich intégré dans une planchette en bois. Des cloisons séparent les diodes du diffuseur. Presque "toute" l’électronique (excepté les diodes et leur résistance) est déportée grâce à une nappe HE-10 jusqu’a l’intérieur du globe, minimisant ainsi les possibilités de l’utilisateur (en particulier lorsqu’il fait moins d’1m20...) d’intéragir un peu trop avec le matériel.
Scénarios
Les scénarios définis sont arborescents. Ils permettent de naviguer dans du contenu affiché dans GE. A chaque sélection sur le pad, l’affichage de GE et les boutons du pad sont mis à jour pour refléter les sous menus disponibles.
La définition du scénario est donnée dans le fichier de configuration de padcontrol ;
scenario Root {
path = { 0 }
kml = "0.kml"
}
scenario Populations {
path = { 0, 0 }
kml = "00.kml"
}
scenario Regions {
path = { 0, 1 }
kml = "01.kml"
}
scenario Territoires {
path = { 0, 2 }
kml = "02.kml"
}
scenario Nuvavut {
path = { 0, 2, 0 }
kml = "020.kml"
}
scenario Nuvaviq {
path = { 0, 2, 1 }
kml = "021.kml"
}
scenario Quit025 {
path = { 0, 2, 5 }
is_quit = 1
}
...
Dans cet extrait, GE démarre avec le scénario "Root", et padcontrol met à jour le lien réseau pour le faire pointer vers le fichier 0.kml. A cet instant, le pad est éteint. Un appui sur le pad fait apparaître le menu. Les boutons 1 et 2 du pad sont alors allumés (seuls sous-menus disponibles). Si l’utilisateur appuie ensuite sur 2, le fichier 02.kml sera chargé par GE, et les trois sous-menus seront disponibles (0, 1 et 5).
Si l’utilsateur choisit 1, un nouveau fichier KML sera chargé ajoutant une couche représentant le Nunaviq, en revanche, s’il sélectionne le bouton 5, il quitte le menu pour revenir à un affichage normal.
Ce système est très souple, et permet de rapidement changer le scénario. Il permet aussi de profiter de toute la richesse du KML (affichage de photos, récupérations de couches "live" depuis Internet...), puisque ce sont directement des fichiers KML qui sont utilisés.
Conclusion
Cet interactif permet d’offrir des interfaces tangibles GE, tout en se passant des habituels claviers+souris. Le globe est actuellement "de sortie" au musée Barthelemy Thimonnier d’Amplepuis, et permet aux visiteurs, seuls ou accompagnés d’une médiatrice, de découvrir autrement les territoires polaires.
Même si ce projet, dans sa partie technique, est difficilement reproductible par ailleurs (on n’a pas forcément une demi-sphére en plexiglass d’un mêtre de diamètre représentant la terre dans son garage), certaines parties sont très facilement transposable dans un autre cadre. En particulier, les outils wii2osc et KeyEvents suffisent pour permettre, par exemple, à une classe d’utiliser GE avec une Wiimote. Un simple vidéoprojecteur permet alors d’imaginer des séances de géographie pas comme les autres, où les élèves peuvent se transmettre la Wiimote et partir à la découverte leur planète.
Code
Tout le code produit est disponible en licence GPLv2. Un certain travail de nettoyage étant nécessaire, il sera mis en ligne dès que possible. N’hésitez pas à nous contacter si vous désirez l’obtenir avant.
Remerciements
Merci à tous ceux qui ont participé au développement : Pierre-Gilles, Daniel, Patrick, Christophe, Laetitia, Catherine, ont participé au projet d’une manière ou d’une autre.
Merci aussi aux béta testeurs (les ERASMiEns et les enfants).
Liens
G2 : Présentation du projet de globe interactif
G2 : Premiers essais de vision pour le globe interactif