Le capteur Kinect de Microsoft a ravivé l'intérêt porté aux techniques de mesure des distances et a donné naissance à bon nombre d'applications populaires. L’ironie de l'histoire est que son fonctionnement ne repose pas sur l'ancienne et éprouvée technologie LIDAR – utilisée p. ex. depuis des décennies par le secteur militaire – mais sur la déformation de rayons infrarouges.


Sans doute titillés par le tentacule commercial de Microsoft venu s'étendre jusque chez eux, des entrepreneurs chinois ont fondé YDlidar, une société qui depuis 2015 conçoit des lidars pour le « marché de masse ».

C’est la mise en service d’un de ces lidars que je vous invite à découvrir ici, plus précisément celle du modèle omnidirectionnel TG15 (fig. 1). Si vous souhaitez l’acquérir, l’e-choppe d'Elektor vous le propose pour environ 340 €.

.

Figure 1. Vendu à partir de 340 €, ce lidar aurait coûté deux zéros de plus il y a quelques années.
Le modèle TG15 peut être piloté depuis Windows 7/10 ou Linux. Peu enclin à transiger avec mes bonnes pratiques de programmation, c'est depuis mon environnement de travail habituel, Ubuntu 18.04, que je décrirai les étapes à suivre. La première consiste à installer les dépendances logicielles nécessaires :
 
sudo apt install cmake pkg-config
sudo apt-get install python swig
sudo apt-get install python-pip

Certains de ces paquets seront probablement déjà installés sur votre machine si vous codez régulièrement en Python et compilez des programmes écrits en C.

Il nous faut ensuite télécharger le SDK présent sur le dépôt GitHub d'YDLidar, puis le compiler avec les incantations habituelles cmake, make, et make install :
 
tamhan@TAMHAN18:~$ git clone https://github.com/YDLIDAR/YDLidar-SDK.git
tamhan@TAMHAN18:~$ cd YDLidar-SDK/build
tamhan@TAMHAN18:~/YDLidar-SDK/build$ cmake ..
-- Build files have been written to: /home/tamhan/YDLidar-SDK/build
tamhan@TAMHAN18:~/YDLidar-SDK/build$ make
tamhan@TAMHAN18:~/YDLidar-SDK/build$ sudo make install


YDLidar a publié sur sa page GitHub une procédure permettant de n’installer que les composantes Python du SDK, mais je recommande de suivre la procédure « complète ». À titre d’exemple, la compilation n’a demandé que quelques secondes de labeur à mon processeur à 8 cœurs.

Test du matériel

La figure 2 montre le contenu de ma commande passée auprès de l’e-choppe. Le câble à cinq fils dont est équipé le lidar TG15 alimente en 5 V le capteur et fournit un UART classique. Le boîtier noir est un convertisseur USB-série permettant de relier le TG15 à une station de travail, un Raspberry Pi ou tout autre hôte USB similaire.

Figure 2. Le TG15 est prêt à l'emploi.

Point intéressant, le câble USB-C fourni sert uniquement au transfert des données. L'alimentation se branche sur le port micro-USB ; j'ai utilisé celle d'une liseuse Kindle affichant une intensité nominale de 1,8 A.

Une fois l'ensemble correctement branché, lancez la commande dmesg pour vérifier que votre matériel est reconnu :

tamhan@TAMHAN18:~/YDLidar-SDK/build$ dmesg
. . .
[ 7043.684654] usb 6-2: cp210x converter now attached to ttyUSB0


Cette étape franchie, une lecture des caractéristiques du lidar YG15 s'impose : https://github.com/YDLIDAR/YDLidar-SDK/blob/master/doc/Dataset.md. À chaque ligne du tableau correspond un modèle de lidar, et l'on voit p. ex. que l'UART du YG15 travaille avec un débit 512000 bauds. Notez les caractéristiques de votre modèle, elles vous serviront à lancer le programme de test :

tamhan@TAMHAN18:~/YDLidar-SDK/build$ ./ydlidar_test
Please select the lidar baudrate:4
Whether the Lidar is one-way communication[yes/no]:yes
. . .


Si votre lidar est un YG15, les deux infos ci-dessus suffisent. Incidemment, ne soyez pas surpris d'entendre votre lidar bourdonner durant le test. Le ronflement provient de la rotation continue du capteur effectuant la mesure des distances. Protégez son roulement à billes en terminant le programme de test avec Ctrl-C.

Programmation et utilisation

YDLidar fournit des API pour les langages C, C++ et Python 2. Si comme moi vous préférez coder avec une version 3 de Python, les bibliothèques numpy et matplotlib seront nécessaires au bon déroulement du programme que nous allons lancer :

pip install numpy
pip install matplotlib


Le programme en question est plot_tof_test.py et utilise les fonctions de matplotlib pour créer un diagramme polaire. Tentons de l'exécuter :

tamhan@TAMHAN18:~/YDLidar-SDK/python/examples$ python plot_tof_test.py
. . .
ImportError: No module named functools_lru_cache


Comme vous le voyez, le programme n'a pas été très loin sur ma machine. Si vous rencontrez la même erreur que ci-dessus, installez le paquet manquant (functools_lru_cache) :

sudo apt install python-backports.functools-lru-cache


J'ai relancé le programme, mais il a de nouveau été arrêté par une dépendance non satisfaite. Il est probable qu'il en soit de même pour vous. Utilisez PIP, PIP3 ou apt-get pour satisfaire chaque prérequis, et retentez votre chance. Les deux lignes ci-dessous montrent un exemple d'erreur suivi de sa correction :

ImportError: No module named _tkinter, please install the python-tk package
tamhan@TAMHAN18:~/YDLidar-SDK/python/examples$ sudo apt install python-tk


Enfin pleinement satisfait, le programme affiche une représentation de votre environnement évoquant l'écran d'un radar, l'affichage étant actualisé en temps réel. Un test intéressant consiste à saisir le lidar entre ses paumes (fig. 3). Les points rouges tracés par matplotib correspondent à mes mains, à l'exception de l'îlot rouge filiforme dont le tracé montre ce que voit le lidar à travers l'extrémité de mes doigts laissée entrouverte.

Figure 3. Tenir le lidar entre ses paumes permet de voir les distances minimales mesurables.

Le lidar fonctionne par projection circulaire de lumière sur un plan. Rappelez-vous que ledit plan n'est « actif » qu'à quelques centimètres au-dessus de l'appareil. Il m'a ainsi fallu placer mon lidar sur un carton pour obtenir une cartographie de mon atelier (fig. 4).

Figure 4. Tout est bon dans le cochon (le carton de ma commande).

Les lidars sont des dispositifs primitifs en ce sens qu'ils ne font « que » fournir une distance pour chaque coordonnée polaire. Il est donc plus commode de visualiser leur sortie en chargeant le fichier des données dans un éditeur. La première chose à faire est d'initialiser certaines fonctions de matplotlib. Les développeurs d'YDLidar ont choisi 32 pour le rayon polaire maximal. Cette valeur, par ailleurs initialisée avec la fonction set_rmax(), est irréaliste. Un premier pas vers une certaine « stabilisation » serait de fournir une valeur plus petite via la constante RMAX :

fig = plt.figure()
fig.canvas.set_window_title('YDLidar LIDAR Monitor')
lidar_polar = plt.subplot(polar=True)
lidar_polar.autoscale_view(True,True,True)
lidar_polar.set_rmax(RMAX)
lidar_polar.grid(True)


Le code de démonstration contacte ensuite le convertisseur USB-série afin que l'ordinateur hôte puisse accéder aux données du lidar :

ports = ydlidar.lidarPortList();
port = "/dev/ydlidar";
for key, value in ports.items():
    port = value;


La bibliothèque de développement devant prendre en charge différents modèles de capteurs, une certaine configuration manuelle est nécessaire. Nous devons par exemple paramétrer le port, la vitesse de transmission et la fréquence de balayage de l'objet laser :

laser = ydlidar.CYdLidar();
laser.setlidaropt(ydlidar.LidarPropSerialPort, port);
laser.setlidaropt(ydlidar.LidarPropSerialBaudrate, 512000)
laser.setlidaropt(ydlidar.LidarPropLidarType, ydlidar.TYPE_TOF);
laser.setlidaropt(ydlidar.LidarPropDeviceType, ydlidar.YDLIDAR_TYPE_SERIAL);
laser.setlidaropt(ydlidar.LidarPropScanFrequency, 10.0);
laser.setlidaropt(ydlidar.LidarPropSampleRate, 20);
laser.setlidaropt(ydlidar.LidarPropSingleChannel, False);
scan = ydlidar.LaserScan()


Le programme utilise ensuite la fonction animate() pour « préparer » les données fournies :

def animate(num):
   
    r = laser.doProcessSimple(scan);
    if r:
        angle = []
        ran = []
        intensity = []
        for point in scan.points:
            angle.append(point.angle);
            ran.append(point.range);
            intensity.append(point.intensity);
        lidar_polar.clear()
        lidar_polar.scatter(angle, ran, c=intensity, cmap='hsv', alpha=0.95)


La boucle for ci-dessus joue un rôle crucial. Elle parcourt les entrées du tableau scan.points et en insère les valeurs dans trois tableaux : angle, ran et intensity. La transmission des valeurs de ces trois tableaux à l'instance polar_lidar met à jour le diagramme polaire.

La boucle principale du programme peut dès lors traiter la réception des données :

ret = laser.initialize();
if ret:
    ret = laser.turnOn();
    if ret:
        ani = animation.FuncAnimation(fig, animate, interval=50)
        plt.show()
    laser.turnOff();
laser.disconnecting();
plt.close();


La fonction TurnOn() est elle aussi essentielle. Elle « active » le lidar physique et retourne la valeur False s’il n’est pas activé. Il est intéressant de noter que le capteur surveille également l'alimentation, ce qui lui permet de ne pas démarrer « en cas de problème ».

Applications

Sans doute seriez-vous capable d'énoncer d'une traite une vingtaine d'applications possibles des lidars si vous travaillez dans la défense ou l'aéronautique. Qu'elle soit domestique ou récréative, la robotique exploite elle aussi les lidars. C'est le cas des robots fabriqués par YDLidar, qui par ailleurs reposent sur le populaire système d'exploitation ROS.

Il serait intéressant d'accéder directement au flux de données de l'UART et d'exploiter ces données avec un microcontrôleur basique. Dans un drone ou autre appareil dont le poids est un facteur critique, ce serait un bon moyen d'économiser de précieuse ressources.

Pour terminer, soulignons une fois encore que le TG15 n'est qu'un des représentants (haut de gamme) de la famille YDLidar. Vous trouverez dans l'e-choppe des modèles moins coûteux (ainsi qu'un mini-lidar de SeeedStudio) mais dont la programmation sera pour l'essentiel identique à celle du TG15.