Comment nous avons créé un prototype de robot “stalker” en 1 jour
Mélanger les domaines tel que le Machine Learning, le Computer Vision ou encore la robotique n’a jamais autant été à porté de mains qu’aujourd’hui.
Les constructeurs font des efforts conséquents pour abstraire les difficultés d’implémentation et fournissent des frameworks haut niveau laissant les ingénieurs et les dévelopeurs se concentrer sur la fonctionnalité.
Dans cet article j’explique comment utiliser un iPhone couplé à un micro-controller Arduino Yún pour créer un prototype de robot stalker qui suit du regard et voue à la personne se trouvant devant lui une obsession digne de R2D2.
“Computers can see” annonce Jeremy Howard dans son cours sur le Deep Learning : http://course.fast.ai. Les travaux de computer vision ne datent pas d’hier mais aujourd’hui plus que jamais le machine learning est à la portée de tous et inclure de la détection de visage ou la classification d’objets dans nos programmes est une affaire de minutes.
Pour ce proto nous utilisons le framework Vision qu’Apple a devoilé avec iOS11. Vision repose sur un autre framework de machine learning (Core ML) et permet entre autres de réaliser la détection de visages sur un flux video capté par la camera de l’iPhone. Nous utilisons les API du même framework pour traquer le visage s’il se déplace devant l’objectif.
Vision fournit également les informations quand à la position du visage traqué. Position qui est transmise à un Arduino Yún qui fait pivoter l’iPhone grace à un servomoteur afin que le sujet reste toujours dans le champ de vision du smartphone.
Notons que l’objectif ici est d’initier le lecteur et donner une idée de ce que cela implique d’utiliser ces technos mais qu’on est loin de l’état de l’art en ce qui concerne le tracking de visage ou encore la robotique.
La détection et le tracking de visages par l’iPhone
Commençons par l’app native qui s’exécute sur l’iPhone et qui est donc responsable de suivre le visage de la personne se trouvant devant la camera. Fort heureusement Vision simplifie la détection et le tracking pour nous.
Cet article n’étant pas un tuto, je ne rentrerai pas dans le détail du code mais celui ci est disponible ici (le code Arduino également) : https://github.com/OMTS/StalkerRobot
L’utilisation des API de Vision est chose relativement aisée :
- Création d’une requête (de détection ou de tracking)
- Démarrage de la tâche (de détection ou de tracking) à chaque fois qu’une nouvelle image est captée depuis la camera.
- Traitement du retour (de la détection ou du tracking) pour par exemple dessiner un cadre autour du visage, communiquer la position du visage au micro-controlleur mais également fournir l’observation du tracking à la demande de tracking suivante.
Ce que nous appelons observation est le résultat d’une requête de détection ou de tracking. Elle contient notamment les coordonnées de l’objet détecté ou traqué dans l’image.
Le tracking a besoin de deux paramètres en entrée : une image et une observation à traquer, contrairement à la détection qui fonctionne seulement avec une image en entrée, le framework sachant déjà que l’objet à trouver est un visage.
La première fois que nous démarrons notre tache de tracking nous disposons de la seule observation fournie par l‘API de détection (la position du visage pour faire simple). Pour toutes les autres requêtes de tracking en revanche (à chaque fois qu’une nouvelle image est capturée) nous utilisons l’observation fournie par la requête de tracking pour l’image précédente. Le framework réutilise donc le même traqueur pour suivre le visage au lieu de créer un nouveau traqueur à chaque requête.
A chaque réception d’une nouvelle observation provenant du traqueur nous faisons deux choses :
- Le maintient d’une référence sur l’observation en question pour l’injecter à nouveau dans le traqueur pour les frames à venir.
- Le traitement de l’observation courante : affichage d’un cadre autour du visage sur l’écran et surtout la communication des coordonnées du visage avec l’Arduino Yún pour que le servomoteur puisse tourner le smartphone en conséquence.
La communication entre l’iPhone et l’Arduino Yun
Il est important de comprendre que les coordonnées des observations retournées par Vision sont normalisées. Cela veut dire que la position de visage est compris entre 0 et 1 en x et en y.
Par ailleurs le repère dans lequel Vision fait sa détection est different de celui qui nous sert à l’affichage (notamment à cause de l’orientation des images). Nous effectuons alors une série de transformations permettant de passer d’un repère à l’autre. La méthode drawBox qui dessine un cadre autour du visage à l’écran n’est en revanche pas décrite ici.
La communication avec l’Arduino est un point d’amélioration du proto. Nous remontons les informations de position depuis l’iPhone vers une base de données Firebase et récupérons l’info la plus à jour possible coté Arduino en interrogeant Firebase via l’API REST qu’elle met à disposition. La lecture côté hardware se fait en interrogeant Firebase de manière fréquente pour obtenir l’information la plus à jour possible. On appelle ce procédé le polling.
Un moyen plus naturel est d’utiliser une connexion directe entre l’iPhone et le hardware comme par exemple le Bluetooth mais pour ce proto nous n’avions pas d’extension (Shield) Bluetooth pour l’Arduino. La version Yún du micro-controller embarque cependant une puce wifi. La voie des WebSocket est aussi à explorer.
Notre approche pour la communication est donc pour le moins naïve mais permet de boucler la boucle facilement.
Remarquons pour des raisons de performances mais aussi de coût (Firebase devient payant au delà de 20 000 requêtes en écriture par jour) nous n’envoyons pas toutes les valeurs de x trouvées par Vision. En effet seules les variations de plus d’un quart du champs de vision nous interessent. Nous filtrons également les variations trop rapprochées dans l’espace.
Le traitement des positions et la rotation du smartphone par le micro-controlleur
Pour finir notre proto, il nous faut monter un circuit électronique permettant de faire fonctionner un servomoteur qui supportera notre iPhone.
Notre choix s’est porté facilement vers Arduino pour sa simplicité d’utilisation, pour l’excellent kit de démarrage qu’il propose et pour les ressources disponibles partout sur internet pour les apprentis que nous sommes. Le coffret contient tous les composants nécessaires pour ce proto. Nous utilisons en revanche une carte Arduino Yún acheté à part plutôt que sa petite soeur Uno qui elle est fournit dans le coffret. 2 raisons à ce choix :
- La version Uno ne possède pas de puce wifi ou Bluetooth.
- Sa connexion en USB est pour le moins chaotique quand on utilise un hub USB coté ordinateur. Ce qui est souvent le cas avec les nouveau macbook pro qui possède uniquement des ports Thunderbolt 3.
La récupération de la donnée à jour se fait coté Arduino en utilisant cURL et une librairie externe du nom d’ArduinoJson (qui comme l’ensemble des technos Arduino software et hardware est open source).
La fonction runCurlRequest() décrite plus haut est appelée dans la boucle principale du code qui sera déployé. Cette fonction est donc appelée plusieurs milliers de fois par seconde par la carte.
Si nous avons moins de contraintes coté Firebase pour la lecture (50 000 requetes en lecture par jour) il est tout de même important de limiter le nombre d’appels en mettant un delay à la fin de la boucle. Dans notre cas 250 millisecondes (ce qui réduit le nombre d’appels à 4 par seconde).
La requête HTTP ne récupère pas toute la base de donnée mais seulement le dernier élément inséré. Pour cela nous trions d’abord nos éléments par date (la clef dans la base de données) et nous limitons le nombre d’element retourné au seul dernier.
“https://YOU_FIREBASE_DB_URL/deltas.json?orderBy=\"$key\"&limitToLast=1"
Il ne nous reste plus qu’à traiter cette donnée et à faire pivoter le servomoteur.
Le circuit
La carte Arduino alimente le servo avec du 5V et le pilote à travers une sortie numérique (1 ou 0) mais qui est capable de fournir un signal analogique grâce à une technique appelée “Pulse Width Modulation” ou PWM. Notez également la présence d’une capa de 100µF pour lisser tout changement de voltage lié au servomoteur.
Enfin voici le code qui permet de traiter une position reçue depuis la base de donnée distante Firebase et de faire tourner le servomoteur en conséquence.
Le fonction mapf fournit pour les valeurs de normalizedPoisition (variant entre 0.0 et 1.0), un angle compris entre 45 et 135 degrés. L’angle de vision que nous nous somme imposé dans le proto pour faciliter les calculs.
A chaque fois que le visage se déplace sur l’axe X de plus de 0.25 (1/4 de déplacement dans le champs de vision de la camera) l’Arduino récupère ce déplacement et fait tourner l’iPhone pour ramener le sujet le plus proche possible vers le centre de l’écran. Notez que les valeurs min et max de notre angle de rotation sont choisi après expérimentation. Ces valeurs sont à adapter et à améliorer.
Pour finir
Notre approche naïve nous a donc permis de rapidement prototyper un robot capable de voir et de suivre du regard un visage qui passe devant son oeil aguérri. Si beaucoup de choses peuvent être améliorées il est intéressant de voir qu’avec si peu de moyen et de temps on arrive à un résultat assez surprenant.
Voici donc notre robot stalker qui connait déjà son maître.
Il ne lui manque plus qu’une seule chose pour la décoration :)