Comment utiliser Lidar avec le Raspberry Pi
La capacité de mesurer à peu de frais mais avec précision la distance entre un véhicule autonome ou un robot et des objets à proximité est un problème difficile pour les pirates. Connaître la distance est la clé pour éviter les obstacles. Se heurter à quelque chose avec un petit robot peut être un problème trivial, mais peut être mortel avec un gros comme un véhicule autonome.
Mon intérêt pour la mesure de distance pour l'évitement d'obstacles découle de ma participation au concours de robots de retour d'échantillons (SRR) de la NASA en 2013. J'ai utilisé une caméra Web pour le traitement de la vision et j'ai essayé diverses techniques visuelles pour effectuer des mesures, sans grand succès. Lors du concours, deux participants ont utilisé des lidars à balayage, ce qui a piqué mon intérêt pour eux.
Un lidar est un appareil de mesure de distance laser. Le nom est une combinaison des termes LUMIÈRE et raDAR et non, comme on le suggère généralement, un acronyme dérivé d'une manière similaire à son prédécesseur, "RAdio Detection And Ranging". Le terme a été utilisé pour la première fois en 1963 selon Merriam-Webster. Une des premières utilisations du lidar consistait à mesurer les nuages et la surface de la lune par Apollo 13. Comme les lasers étaient de taille réduite, d'autres utilisations ont été trouvées, notamment comme télémètre à des fins militaires.
Un seul faisceau laser ne peut fournir la distance qu'à un seul objet. Tout comme le radar de contrôle de l'avion envoie un faisceau dans le ciel, un lidar à balayage balaie le laser. L'application du lidar pour les appareils mobiles autonomes nécessite le balayage d'une vaste zone à la fois verticalement et horizontalement pour fournir un nuage de points de mesures de distance. Quelque chose de similaire pourrait être fait avec un capteur infrarouge, comme nous l'avons vu précédemment, mais la précision n'est pas aussi bonne qu'avec un laser.
La mesure de distance peut être effectuée de plusieurs manières, mais il en existe deux principales. L'un mesure le temps de vol d'une impulsion laser tandis que l'autre utilise l'angle de déviation du faisceau laser.
Vous connaissez le fonctionnement de base du radar et du sonar : envoyez une impulsion et mesurez le temps qu'il faut pour recevoir le signal de retour. Le temps divisé par la vitesse de la lumière ou du son vous donne la distance parcourue par le signal aller-retour. Divisez cela par deux pour obtenir la distance à l'objet. C'est la mesure du temps de vol (ToF).
Comme vous vous en doutez, les choses deviennent délicates compte tenu de la vitesse de la lumière. Un pionnier des ordinateurs, le contre-amiral Grace "Amazing Grace" Hopper distribuait des morceaux de fil de 11,80" pour démontrer la distance parcourue par la lumière en une nanoseconde dans le vide. Avec les robots, c'est l'ampleur de la distance que nous souhaitons mesurer. C'est difficile de mesurer moins d'un mètre en envoyant juste une impulsion et en chronométrant le signal de retour car le signal revient en environ 7 nanosecondes.
Une technique autour de cela consiste à moduler en continu le signal en amplitude ou en fréquence. La différence de phase entre les signaux émis et reçus est proportionnelle à la distance à l'objet. Un lidar utilisant la modulation peut mesurer jusqu'à quelques centimètres.
Il existe un certain nombre de fournisseurs commerciaux de lidars à balayage basés sur ToF, mais le prix est un peu plus élevé que ce que la plupart des amateurs dépenseraient. Un entrant relativement nouveau, PulsedLight, a proposé un lidar ToF à faisceau unique dans la fourchette de prix des pirates, mais leurs fournisseurs sont tous en rupture de stock.
Le lidar à triangulation utilise la même technique que les capteurs de mesure de distance infrarouge Sharp que les pirates utilisent depuis des années. L'émetteur est une source unique mais le récepteur est un réseau de récepteurs à 1 ou 2 dimensions. Le décalage des éléments récepteurs par rapport à l'émetteur crée la ligne de base d'un triangle. Le signal de départ et de retour sont les deux autres côtés du triangle. La trigonométrie simple fournit la distance entre la ligne de base et l'objet.
L'Optical Society décrit ces techniques et d'autres utilisées pour mesurer la distance.
Ce que je ne savais pas lors de la compétition au NASA SRR 2013, c'est que Neato Robotics a sorti un aspirateur en 2010 en utilisant un lidar à balayage pour détecter l'environnement de l'aspirateur. Cela permet au robot d'éviter les obstacles au lieu de les heurter comme le faisaient les robots aspirateurs précédents.
Sparkfun a démonté l'aspirateur et a enquêté sur le lidar. Une longue discussion à partir de novembre 2010 s'est ensuivie sur le forum Trossen Robotic alors que les pirates ont disséqué le vide en accordant une grande attention au lidar. Il y avait même de petits prix offerts pour le piratage du lidar.
Malheureusement, un certain nombre de liens dans ce fil n'existent plus, mais il vaut toujours la peine d'être lu car de nombreux détails sont exposés dans les messages. D'autres fils de discussion sur le forum contiennent des informations supplémentaires. Une découverte particulièrement intéressante est un document de recherche qui a précédé le lidar Neato mais qui a servi de base à la conception finale. Il décrivait les détails nécessaires à Neato pour créer un produit d'ingénierie.
La bonne nouvelle est qu'un wiki existe avec un résumé des informations sur le vide et le lidar. L'un des participants actifs au piratage, [Nicolas "Xevel" Saugnier] a créé une petite carte d'interface USB pour alimenter le lidar et se connecter à son interface série. À l'été 2014, j'ai obtenu quelques unités lidar et les cartes alors que j'envisageais d'entrer dans le SRR 2015 de la NASA. J'ai fait fonctionner les unités lidar à l'aide du logiciel Python [de Xevel] et des packages disponibles dans le système d'exploitation du robot.
Le lidar à balayage a permis à Neato Robotics de mettre en œuvre la localisation et la cartographie simultanées (SLAM) à l'aide des données de mesure de distance. Cela permet au robot de planifier le chemin de nettoyage plutôt que d'utiliser la bosse précédente et les mouvements aléatoires, une marche d'ivrogne, des aspirateurs précédents. Cela permet au Neato de couvrir complètement une pièce plus rapidement. Notez dans cette vidéo d'une démonstration de Neato comment le robot construit une carte de l'endroit où il a été et des obstacles qu'il rencontre. Notez en particulier comment il utilise les données lidar pour contourner soigneusement le seul obstacle.
Je suis toujours très intéressé par les robots même si j'ai abandonné le concours SRR. Les lidars étaient assis sur l'étagère, m'attirant comme des sirènes mythiques. J'ai finalement succombé à leur appel lorsque j'ai réalisé que l'interface série lidar correspondait parfaitement à un Raspberry Pi puisque les deux fonctionnent aux niveaux 3V3. Cela éliminerait l'interface USB. Un effort similaire est celui de [Thomas Jesperson] en 2014 qui a utilisé une carte STM32F429 et a produit une vidéo du lidar en action.
Le lidar est une unité scellée avec un moteur suspendu à une extrémité. Le moteur entraîne une tourelle qui tourne à environ 300 tr/min. La tourelle contient le laser et le capteur de réception et, en tournant, fournit un balayage à 360 degrés de la zone environnante. Le laser et le capteur de réception ont deux ports optiques hors de la tourelle. Il y a deux câbles courts avec des connecteurs JST provenant du lidar. Un connecteur à deux broches alimente le moteur. Un connecteur à quatre broches fournit une alimentation de 5 V aux circuits de commande et à l'interface série 3V3. Les brochages sont :
Dans le vide, le moteur est alimenté par une source 12V utilisant PWM à un cycle de service d'environ 25%. Cela signifie que le moteur a besoin d'environ 3 V pour fonctionner à la bonne vitesse et les tests ultérieurs par les pirates ont montré que c'était vrai. La carte d'interface USB exécute le lidar à partir de l'entrée 5V du connecteur USB en utilisant PWM contrôlé par une boucle PID (Proportional Integral Differential) pour maintenir la vitesse du moteur. Pourquoi utiliser PWM et PID ? Pour maintenir un régime constant pour la tourelle en rotation pendant qu'elle s'use et recueille la saleté, la poussière et d'autres détritus. Lors de mes tests, j'ai noté que le moteur ferait tourner la tourelle dans les deux sens en fonction de la connexion positive et négative. L'interface fonctionne toujours bien mais la séquence des points de données va être inversée. Normalement, la tourelle tourne dans le sens antihoraire.
Un mot d'avertissement : dans certaines premières unités, l'interface utilisait 3V3, donc les connecter à 5V peut détruire l'interface.
Ma connexion d'origine entre le Pi et le lidar était rapide et sale. J'ai connecté le moteur à la sortie 3V3 du Pi et cela a fonctionné. La sortie du Pi 3V3 est limitée à 50 mA par les spécifications et le lidar Wiki indique que le moteur consommerait 64 mA. J'ai mesuré le mien et il a tiré beaucoup plus. J'ai également connecté la broche TX de l'interface au RX du Pi (broche 10). En utilisant CuteCom sous Raspbian Jessie, je pouvais lire les données lorsque la tourelle était tournée manuellement. Une fois ces tests de base terminés, il était temps de devenir un peu plus sérieux.
J'ai choisi d'utiliser un réseau de transistors Darlington ULN2803A que j'ai trouvé dans mon armoire de pièces pour entraîner le moteur. Ce circuit intégré gère facilement le courant nécessaire pour piloter le moteur et comprend les diodes de protection nécessaires pour piloter une charge inductive. Je n'avais pas l'intention de faire du PWM du moteur mais je voulais l'éteindre et le rallumer. J'ai connecté le 5V du Pi sur la broche 2 au fil rouge du connecteur du moteur. Le câble noir est connecté à l'ULN2803A via une résistance de 15 ohms pour faire chuter la tension. Cette configuration de configuration côté bas pour contrôler le moteur. Le câble d'interface est connecté aux broches 5V, masse et RX du Pi.
Pour tester le contrôle moteur, j'ai utilisé le mappage des broches GPIO au répertoire du système de fichiers /sys/class/gpio. Chaque GPIO peut être contrôlé ici une fois la broche exportée. Les commandes peuvent ensuite définir la direction des broches et activer et désactiver la broche. Les commandes que j'ai utilisées contrôlaient GPIO 18 (broche 12):
Le '0' et le '1', lorsqu'ils sont répétés, éteignent et rallument respectivement la broche. Cela a fonctionné et j'ai pu voir les données en utilisant CuteCom.
Un résultat intéressant se produit si vous êtes connecté au port série puis mettez l'interface sous tension. Le lidar génère un message de bienvenue :
Piccolo Laser Distance Scanner Copyright (C) 2009-2011 Neato Robotics, Inc. Tous droits réservés chargeur \ 0x09v2.5.15295 CPU \ 0x09f2802x / C001 Serial \ 0x09Ksh34313AA-0140854 LastCal \ 0x09 [5371726c]
De plus, si vous faites tourner manuellement la tourelle, un message indiquant "Spin" apparaît et indique qu'il existe une capacité de commande disponible en envoyant une pause ou trois caractères esc. Des informations sur les commandes sont disponibles sur le Wiki.
Maintenant, créez un logiciel approximatif pour voir comment tout cela fonctionne. Bien sûr, j'ai utilisé C++, j'ai donc installé la chaîne d'outils pour le Pi 2 et j'ai trouvé que la vitesse de compilation était suffisante pour le développement. J'ai commencé à utiliser l'éditeur de programmation Geany et les makefiles ; une configuration que j'ai apprise en travaillant avec le code Python pour le lidar. Il gère plusieurs langages de programmation et je l'ai adopté comme éditeur de texte à usage général sur Ubuntu et Raspian. Mais j'ai finalement abandonné Geany et installé Eclipse CDT sur le Pi lorsqu'il ne reformatait pas correctement le code C++ après l'édition. Eclipse fonctionne étonnamment bien sur le Pi. J'étais en fait un peu déçu par le changement car avec Geany, je réapprenais à travailler avec les makefiles, une compétence que j'ai perdue en travaillant avec Eclipse.
En cherchant des informations sur la programmation du GPIO avec C++, j'ai trouvé la bibliothèque WiringPi de [Gordon Henderson]. Il prend non seulement en charge la programmation GPIO brute, mais prend également en charge de nombreuses cartes filles Pi. Un autre attrait pour moi est qu'il possède une interface de port série, donc je n'ai pas eu à entrer dans les détails de cela sous Linux, ce qui aurait été nouveau pour moi. Finalement, j'ai même utilisé sa capacité de thread simple pour gérer une interface utilisateur absurdement minimale. [Gordon] a également un utilitaire que vous devriez vérifier pour contrôler les broches à partir de la ligne de commande plus complètement que d'écrire dans les répertoires comme je l'ai montré ci-dessus.
La dernière pièce que j'ai trouvée dans WiringPi est la possibilité de faire du PWM matériel sur la seule broche GPIO capable de le faire sur le Pi. C'est GPIO 18. J'avais initialement utilisé GPIO 4 (broche 7) pour contrôler le moteur, mais j'ai changé lorsque j'ai trouvé cette capacité. Le code définit actuellement une valeur constante pour le PWM, mais je souhaite éventuellement ajouter (et écrire un article à ce sujet) une boucle de système de contrôle PID (Proportional Integral Differential) pour maintenir une vitesse constante. La configuration de WiringPi, d'un thread et du PWM sur GPIO 18 est simple :
Le thread est là simplement pour arrêter le programme lorsqu'une touche est entrée et renvoyer le coup. Lorsque run est défini sur false, le thread principal lisant l'entrée série se ferme et le programme se termine après avoir éteint la tourelle. Le fil réel est très simple:
La lecture de l'entrée série est simple, il suffit de lire des caractères, mais les données elles-mêmes, bien que simples, nécessitent un peu de frappe. Il y a 360 échantillons par révolution de la tourelle, donc la quantité de données à 5 révolutions par seconde est énorme. Il y a 90 paquets contenant chacun quatre points de données. Chaque point est représenté par quatre octets de données : distance, intensité du signal, bit de distance invalide et bit d'intensité invalide. De plus, chaque paquet commence par un octet de départ, un octet d'index qui est le numéro de paquet et deux octets pour la vitesse de rotation de la tourelle. Le paquet se termine par deux octets de somme de contrôle. Voici comment j'ai mappé ces données dans des structures :
Je n'ai pas commencé à stocker les paquets de données, je les ai simplement signalés pour vérifier que je voyais de bonnes données et que mes calculs semblaient bons. Les données de distance sont un nombre entier exprimé en millimètres que j'ai converti en pouces. Le code pour la somme de contrôle est sur le Wiki mais je ne l'ai pas encore implémenté.
Ce n'est que le début du travail avec le lidar Neato, mais un bon. Le matériel fonctionne bien et les bases du logiciel sont en main. Je peux obtenir les données et vérifier qu'elles sont valides. La bibliothèque WiringPi est une bonne trouvaille pour poursuivre mes efforts avec le Pi.
Les prochaines étapes consistent à faire fonctionner la routine de somme de contrôle, à étendre la soi-disant interface utilisateur pour permettre le contrôle du fonctionnement de la tourelle et à ajouter une boucle PID pour maintenir une vitesse de rotation constante. Puisque la puce UNL2803A est là, je vais l'utiliser pour contrôler l'alimentation de l'interface. Je souhaite également examiner la compilation croisée du code à partir de mon bureau et le débogage à distance. Au fur et à mesure que j'apporterai ces modifications, j'organiserai le code en classes et je verrai comment je peux l'utiliser sur un robot.
Câble moteur Câble d'interface