Ce robot a été conçu pour tester et valider nos modèles de navigation bio-inspirés en environnement réel. Le choix de la plateforme a été dictée par différentes contraintes : déplacements omni-directionnels pour simuler les déplacements d'une personne, abordable pour pouvoir s'équiper d'une flottille de robot, et d'une taille suffisante pour pouvoir être équipé d'un nano-ordinateur et d'une caméra binoculaire. Notre choix s'est porté sur la plateforme Mecanum Wheel produit par Osoyoo. Cette plateforme a ensuite été équipée d'un étage supplémentaire pour accueillir de nouveaux composants.
La plateforme Mecanum Wheel de Osoyoo constitue une base intéressante pour notre plateforme de test : elle est relativement simple à assembler et propose des déplacements omni-directionnel. Son architecture, reposant sur des composants répandus (Arduino et carte de contrôle moteur L293), facilite l'interface avec des composants additionnels.
La plateforme est équipée de quatre roues dite "mecanum", c'est à dire dotées de petites roulettes internes. Contrairement aux roues omni-directionnelles (ou roues holonomes), dont les roulettes sont perpendiculaires à l'axe de la roue, les roues mecanum ont des roulettes inclinées de 45° par rapport à l'axe. Cette configuration particulière permet, avec un robot équipé de quatre de ces roues, de pouvoir se déplacer dans toutes les directions, avec une direction particulière (avant/arrière) ne générant pas de frottement supplémentaire à cause du roulement des roulettes internes, contrairement aux robots holonomes à trois roues, dont les roulettes génèrent un frottement supplémentaire quelle que soit la direction.
Démonstration des possibilités de déplacement de la plateforme, ici contrôlé avec une manette de jeux :
Code source :
- Code Arduino : robot_control_bluetooth.ino
- Interface Java :
Contrôle avec souris : robot_control_mouse.zip (nécessite la librairie JSSC)
Contrôle avec manette : robot_control_gamepad.zip (nécessite les librairies JSSC et jinput)
Instructions :
- Connecter le robot avec votre PC, en suivant le tutoriel d'Osoyoo
- Téléverser le code .ino dans l'Arduino du robot
- Créer un nouveau projet Java, et importer les fichiers d'une des deux archives
- A l'aide de l'IDE Arduino, déterminer sur quel port le dongle Bluetooth est connecté : le programme envoie le mot "test" en boucle, que vous pouvez lire avec le moniteur série. Sélectionner ensuite le port permettant de se connecter à l'Arduino par USB pour libérer le port Bluetooth
- Dans la classe Main, indiquer le port utilisé par le dongle Bluetooth (ligne 6)
- Lancer le programme java. Une fenêtre doit s'ouvrir
- Cliquer dans la fenêtre pour faire apparaître un joystick virtuel. Sans relâcher le bouton, de déplacer le curseur autour du point initial pour contrôler le robot : bouton gauche pour avant/arrière/translations et bouton droit pour avant/arrière/rotations. Le robot s'arrête quand on relâche le bouton.
Pour utiliser la manette :
- Déterminer le nom du périphérique dans la liste qui s'affiche dans le terminal
- Copier le nom du périphérique dans la classe Main, ligne 7
- Relancer l'application Java
Intégration d'une centrale inertielle (IMU) GY-87 :
La centrale inertielle GY-87 est une carte de développement munie d'un accéléromètre et gyroscope MPU6050 permettant de mesurer les accélérations et les rotations sur 3 axes, d'un magnétomètre HMC5883L pouvant servir de boussole, et d'un capteur de pression barométrique BMP180. La carte peut être alimenté en 5V ou en 3.3V. Ces capteurs permettent une estimation relativement précise des déplacements du robot dans son environnement.
La connexion de la carte IMU à l'Arduino est très simple :
- l'alimentation est reliée aux broches 5V et Gnd de l'Arduino.
- les données sont transmises par les broches SCL et SDA, connectées aux broches correspondantes sur l'Arduino (broches 'SCL 21' et 'SDA 20' de l'Arduino Mega du robot).
Vous trouverez de nombreux détails pour intégrer cette carte IMU sur le site d'Olivier Georgeon.
La carte est maintenue dans un support imprimé en 3D et placée à l'avant du robot. Vous trouverez ci-dessous le modèle du support et les codes Arduino et java permettant de lire les informations issues de la carte.
Code source :
- Modèle support : IMU.stl
- Code Arduino : robot_control_bluetooth_IMU.ino
- Interface Java :
Contrôle avec souris : robot_control_mouse_IMU.zip (nécessite la librairie JSSC)
Contrôle avec manette : robot_control_gamepad_IMU.zip (nécessite les librairies JSSC et jinput)
Afin de rendre le robot autonome, tout en permettant du traitement d'image et la localisation, la plateforme a été équipée d'un nano-ordinateur. Les célèbres Raspberry Pi 4 étant en rupture de stock en cette période, notre choix s'est porté sur un modèle équivalent : le Banana Pi M5. Celui-ci n'étant pas équipé d'une connexion Wifi, un dongle doit être ajouté. Nous avons utilisé un dongle basé sur un chipset Ralink RT5370-MT7601 (attention, le chipset RT5370-MTK7601 ne fonctionne pas avec la version de Raspbian utilisée). La caméra binoculaire est une caméra de Playstation 4 (PS4Eye) modifiée pour pouvoir être branchée sur un port USB3 (tutoriel disponible ici). L'ensemble est alimenté par une batterie de type powerbank de 20Ah, indépendante de la batterie des moteurs. Nous utilisons un cable USB avec interrupteur pour ne pas avoir à débrancher le câble pour couper l'alimentation. Le système communique avec la plateforme robotique par une connexion USB avec l'Arduino du robot.
Les composants additionnels sont placés sur une plaque de plexi de 5mm d'épaisseur maintenue au châssis par des entretoises, et tenu par des supports imprimés en 3D. Le boîtier du Banana Pi est basé sur ce modèle. Le support de la batterie est conçue pour une powerbank modèle Intenso XS20000, de dimensions 128 x 70 x 25,2 mm. Ce support maintien également le dongle Wifi, relié au Banana Pi par une rallonge USB de 10cm. Les supports de la caméra sont conçus pour la version 1 de la PS4 eye. Il faut en imprimer deux, dont un qui doit être inversé (supports symétriques).
- Support batterie : batterie_support.stl
- Support caméra : camera_support.stl
D'un point de vue logiciel, le Banana Pi du robot tourne sous une version de Raspbian adaptée à cette carte (version 2023-05-03, avec un noyau 5.17.2-meson64) disponible sur ce wiki. Il est conseillé d'installer les logiciels suivants :
- openssh-server : pour se connecter en ssh depuis un autre poste,
- xrdp : pour se connecter à distance sur une session graphique (e.g. Remmina),
- OpenJDK : pour utiliser le code fourni sur cette page. Le robot utilise la version 17,
- Eclipse : une IDE pour développer en Java directement sur le robot,
- Python3 : pour utiliser le programme qui transfère le firmware dans la caméra,
- guvcview : pour tester la caméra,
- libjssc-java : une librairie Java pour utilsier le port série.
La communication s'effectue par le port série USB. Voici le code source pour l'Arduino et une classe Java servant d'interface :
- code arduino : robot_control_usb.ino
- interface Java : Robot.java (nécessite la librairie JSSC, sur le robot, il faudra utiliser le .jar disponible dans /usr/share/java/ après avoir installé libjssc-java)
Il faut ensuite installer OpenCV pour Java. Vous pouvez suivre ce tutoriel. Il faut toutefois s'assurer, après la commande CMake
, que le compilateur a bien trouvé les exécutables ANT et JNI. Il faut également sauter la commande make clean
pour ne pas supprimer le fichier .jar généré. Si tout s'est bien déroulé, après une longue compilation, le fichier .jar doit être présent dans le dossier /home/pi/opencv/build/bin, et les librairies .so dans le dossier /home/pi/opencv/build/lib.
Utilisation de la caméra PS4Eye :
La PS4Eye necessite de transférer un firmware avant utilisation. Il faut dans un premier temps débrancher puis rebrancher la caméra. Il est possible de tester si la caméra est reconnue avec un lsusb
dont le résultat doit faire apparaître la caméra :
ID 05a9:0580 OmniVision Technologies
Dans le cas contraire, il faut vérifier les connexions.
On transfère ensuite le firmware à l'aide du code fourni ici :
cd Firmware_loader
sudo python3 ps4eye_init.py
Si tout se passe bien, le message suivant doit s'afficher :
PS4 camera firmware uploaded and device reset
La caméra peut ensuite être utilisée comme une simple webcam (ici, avec guvcview) :
Les images fournies par la caméra ont une taille de 3748x808 pixels, et comprennent les images issues des deux caméras, de taille 1280x800 pixels chacunes, ainsi que des versions réduites de ces images. Le code suivant permet d'obtenir deux images à partir du flux de la caméra :
- camera.zip
Pour utiliser ce code, créez un nouveau projet (par exemple dans Eclipse), et importez les fichiers de l'archive ci-dessus. Associez la librairie opencv.jar (Build path->Configure build path->Libraries->add external JAR) obtenue après la compilation, puis spécifiez l'emplacement des librairies natives :
Dans la classe Main, il est possible de spécifier le numéro de la caméra, le framerate (si l'image est trop sombre, baisser à 30fps pour augmenter le temps d'exposition), et activer le réglage automatique de la luminosité (uniquement sous Linux).
L'exécution du programme ouvre une fenêtre affichant les deux images gauche et droite séparées :
A noter : ce programme fonctionne aussi sur un PC Linux ou Windows.
Un système de vision stéréoscopique simplifié :
Les algorithmes de correspondance stéréoscopiques consistent à comparer deux images stéréoscopiques d'une scène, puis de déterminer la position de chaque pixel de la première image dans la seconde. La différence de position, ou disparité, permet de calculer la distance de ce point. Les algorithmes de concordance stéréoscopiques permettent ainsi d'obtenir la carte de profondeur de la scène.
Cependant, les algorithmes de concordance stéréoscopiques sont particulièrement lourd, surtout pour un nano-ordinateur. Comme nous visons une navigation à deux dimensions, la carte de disparité n'est pas nécessaire : seule la position de points d'intérêts dans le plan est nécessaire. Nous avons choisi d'utiliser les lignes verticales visibles dans la scène : elles sont en effet facilement identifiables, omniprésentes dans l'environnement, et localisable à l'aide d'images stéréoscopiques. Dans une carte 'vue du dessus', ces lignes verticales deviennent des points d'intérêt exploitables pour se localiser.
L'algorithme utilise les étapes suivantes :
- détection de contours verticaux,
- détection des principales lignes verticales, par accumulation des contours d'une même colonne de pixels,
- filtrage des lignes pour ne garder que les plus marquées,
- récupérations des points de contours situés sur ces lignes,
- utilisation d'un algorithme de flux optique sur ces points, utilisés entre les deux images,
- calcul de la position des points à partir de la disparité,
- récupération de la distance médiane de chaque ligne verticale,
- projections des lignes sur la carte de navigation.
On notera l'utilisation d'un algorithme de flux optique (ici, la fonction calcOpticalFlowPyrLK
d'OpenCV), détourné de son utilisation usuelle : plutôt que de déterminer le mouvement de points entre deux images consécutives, on détermine la disparité de ces points entre les images gauche et droite. D'autre part, on sépare les points selon le sens du gradient du contours, permettant d'obtenir deux types de points d'intérêt.
Cet algorithme nous a permis de tester notre système de navigation bio-inspiré en environnement réel, dans un premier temps avec un PC portable, puis sur la plateforme robotique.
Le code suivant permet d'obtenir la carte de navigation à partir de l'image issue d'une PS4Eye. Il fonctionne à environ 40-50 images/s sur un PC avec un CPU i5-10210U, et à environ 8-12 images/s sur le Banana Pi M5.
- stereo.zip
Comme précédemment, il faudra associer les librairies OpenCV à votre projet, et éventuellement modifier les paramètres dans la classe Main. Pour plus d'efficacité, il faudra également changer la hauteur maximale et minimale (en cm) des points considérés en fonction de la hauteur de la camera, dans la classe StereoVision, ligne 277.
Un contrôle par interface web:
Afin d'effectuer des tests sur le robot sans forcément utiliser une session graphique à distance, nous avons développé une interface web reposant sur un serveur web minimaliste. Ce serveur propose une unique page web comportant des fonctions pour interpréter les informations issues du robot et dessiner le contenu des afficheurs dans le navigateur. Le serveur se connecte également au client à l'aide d'un websocket Java autorisant l'échange d'informations entre le client et le serveur.
Ce serveur est constitué de trois éléments :
- Server.java : il s'agit du serveur Web à propement parler. Il reçois les requêtes du client et retourne la page HTML. C'est également lui qui initialise les deux modules suivants,
- DataWebSocket.java : une classe initialisant un websocket entre le client et le serveur. La fonction onMessage
permet de traiter les messages issus du client. La fonction broadcast permet d'envoyer un message (sous forme de chaine de catactères) au client,
- VideoSocket.java : un socket dédié à l'envoi d'un flux vidéo. Ce module fonctionne sur un thread séparé, et envoie l'image de la caméra gauche toute les 100ms.
Pour la communication avec le client, le protocole consiste en un mot-clé suivi d'éventuels arguments séparés par des espaces :
Côté serveur, le traitement des messages a lieu dans la fonction onMessage de la classe DataWebSocket,
- 'robot' permet d'envoyer des commandes de direction au robot (par exemple 'robot forward'),
- 'system' permet de contrôler l'application. La commande 'system stop' permet d'arrêter l'application,
- 'message' permet d'afficher un message sur la console.
Côté client, le traitement a lieu dans une fonction JavaScript associée à un eventListener.
- 'framerate' permet d'afficher le nombre d'images/s actuel,
- 'context' fourni le contenu du contexte de points sous forme de triplet 'type posx distance'.
L'interface avec l'Arduino du robot est similaire à celle utilisée pour le contrôle avec une manette, avec envoi de messages pour contrôler les moteurs, et un Listener pour récupérer les informations de déplacement, si une IMU est connectée à l'Arduino.
Code source :
- robot_control_usb.ino
- robot_control_usb_IMU.ino
- stereo_server.zip
Pour utiliser ce code, Il faudra associer au projet non seulement les librairies OpenCV, mais également la librairie JSSC.jar se trouvant dans le dossier /usr/share/java/ (installer le package si la librairie n'y est pas) pour la communication avec l'Arduino du robot, et les librairies Java-WebSocket.jar, slf4j-api.jar et slf4j-simple.jar pour le serveur. Dans la classe Main, il faudra spécifier, si nécessaire, le port utilisé par l'Arduino (par défaut "/dev/ttyACM0") et l'emplacement de la page HTML (par défaut "./"). Il est aussi possible d'activer/désactiver la fenêtre affichant l'image de la caméra et le contexte.
Le nano ordinateur et le PC client doivent être connectés à un réseau Wifi ouvert. Une fois l'application Java lancée sur le robot, il suffit de se connecter, sur le PC client, avec un navigateur Web à l'adresse du robot, en utilisant le port 8080 (par exemple 192.168.1.100:8080). L'interface Web affiche le framerate, l'image de la caméra gauche et le contexte reconstruit à partir des informations issues du robot.
Le robot peut être contrôlé à l'aide des six boutons. Trois boutons ont été ajoutés pour illustrer l'envoi de message au serveur. Le contenu des messages peut être modifié dans le fichier index.html, dans la fonction JavaScript 'sendMsg', et leur traitement, dans la fonction onMessage de la classe DataWebSocket. Dans le cas où une IMU est connectée à l'Arduino, le robot corrige sa trajectoire lorsqu'il avance pour garder son cap.
Si l'application fonctionne correctement, il est possible d'exporter un JAR exécutable, qui pourra être lancé depuis une simple session SSH.
L'étude de nos modèles de navigation portent également sur la distribution du modèle sur plusieurs robots. Cette distribution doit permettre de répartir les besoins en ressources, et donc les ressources individuelles nécessaires pour chaque robot. Le principe implique que pendant qu'un robot guide le groupe, les autres peuvent être affectés à d'autres tâches, tout en suivant le robot-guide. Nous avons ainsi développé un système de suivi très simple mais efficace basé sur un code-barre vertical inscrit sur un tube en papier placé au sommet de chaque robot. Ce code-barre permet d'inscrire un code sur 4 bits et fourni une approximation de l'orientation (Voir figure ci-dessous).
Un système de localisation visuel :
Ce système de localisation permet aux robots de se localiser mutuellement, mais peut également servir de balise pour des projets liés à la navigation. Il repose sur l'utilisation de codes-barres verticaux inscrits sur des tubes de papiers permettant d'être visibles depuis n'importe quelle direction, contrairement aux marqueurs AR (ou ARtags). Cependant, il est nécessaire, pour mesurer les distances, que le code-barre soit à la même hauteur que la caméra.
Ces codes-barres comportent un en-tête et un pied-de-page permettant de les reconnaitre, mais également d'en estimer la distance à partir de leur taille apparente. Le contenu comprend un code binaire sur 4 bits, permettant d'identifier le porteur de ce code. Une bande de hauteur variable permet également d'estimer l'orientation du code-barre, la hauteur de la partie visible étant dépendante du point de vue.
L'algorithme scanne chaque colonne de l'image, de haut en bas, et recherche les bordures horizontales. Lorsqu'un fort gradient négatif (passage du blanc au noir) est détecté, l'algorithme cherche ensuite un fort gradient positif (passage du noir au blanc), permettant la détection d'une bande noire.
Lorsque deux bandes sont détectées, leur hauteurs et espacement sont comparés : la première bande doit être deux fois plus haute que la seconde, et l'espacement doit être équivalent à la hauteur de la seconde bande. Si ces rapports sont respectés, un en-tête est détecté.
L'algorithme détermine, à partir de la hauteur de l'en-tête, une fenêtre de recherche pour le pied-de-page. Si une bande noire est détectée dans cette fenêtre, un code-barre est considéré comme présent.
L'algorithme détermine ensuite la position des quatre bandes du code binaire, et lit la couleur sur ces quatre positions. Le code binaire obtenu donne l'identifiant du code-barre.
Puis, une fenêtre de recherche pour la bande variable est définie. La hauteur relative de cette bande par rapport à la heuteur du code est mesurée.
Une fois l'image scannée, les codes identiques sont fusionnés pour obtenir la position horizontale et l'orientation du code-barre.
Ce détecteur a été intégré dans l'application de la plateforme robotique, qui propose désormais trois modes de fonctionnement :
- le mode mapping, qui génère un contexte de points d'intérêts par vision stéréoscopique,
- le mode scanner, qui détecte et affiche le ou les codes-barres visibles,
- le mode suivi, qui permet au robot de suivre un code-barre.
Ces différents modes peuvent être sélectionnés via trois boutons de l'interface web. Le protocole de communication s'enrichit de nouveaux mots-clés : côté client, 'changemode' permet de changer le mode de fonctionnement de l'application. Côté serveur, 'mode' permet d'afficher le mode actuel dans l'interface web.
Le mode scanner affiche le ou les codes-barres visibles, en affichant l'identifiant, l'estimation de l'orientation, la position horizontale et la distance (donnée ici par la hauteur apparente). Le mode scanner désactive le système de vision stéréoscopique, permettant à l'application d'attendre entre 25 et 30 images/s.
Le mode suivi permet au robot de suivre un code-barre particulier. L'identifiant du code-barre à suivre doit être spécifié dans les paramètres de la classe Main (paramètre 'CODE'). Le système de suivi cherche simplement à maintenir la position du code-barre à une certaine distance et orientation du robot.
Mode suivi vue interface :
Mode suivi vue observateur :
Code source :
- robot_control_usb.ino
- robot_control_usb_IMU.ino
- stereo_server_scanner.zip
- barcode.svg
L'utilisation du code est identique à la version serveur : le projet doit être associé aux librairies OpenCV, JSSC.jar, Java-WebSocket.jar, slf4j-api.jar et slf4j-simple.jar. Il faut ensuite spécifier dans la classe Main le port utilisé par l'Arduino et l'emplacement de la page HTML. L'application peut être exportée en exécutable JAR pour une utilisation depuis une connexion SSH.
Un système de communication entre robots :
Pour permettre aux différents robots de la flotte de communiquer entre eux, nous avons ajouté un système de communication. Ce système repose sur le serveur déjà développé, et utilise les requêtes de type POST pour transmettre des messages.
Les robots sont identifiés par un numéro d'identification défini comme le quatrième nombre de son adresse IP sur le réseau. Ainsi, un robot avec une adresse IP de 192.168.1.120 aura l'identifiant 120. Le protocole de communication retenu est le suivant : une communication est constituée du numéro de l'expéditeur et du message à envoyer sous forme d'une chaine de caractère, séparés par un point-virgule. Ce message est constitué soit d'un mot-clé seul, soit d'un mot-clé suivi de paramètres. Dans ce second cas, le mot-clé et ses paramètres sont séparés par un point-virgule, et les paramètres sont séparés entre eux par des virgules. L'exemple suivant montre une syntaxe valide, avec 120 l'adresse de l'expéditeur, "control" le mot-clé et "wait" et "5000" des paramètres :
"120;control;wait,5000"
La classe Serveur est modifiée pour traiter les requêtes de type POST : un message reçu est décomposé en identifiant, mot-clé et liste de paramètres. Le serveur stocke les informations liées au dernier message reçu pour un traitement ultérieur (le message peut ensuite être effacé avec la procédure clear()
). Il est également possible de traiter directement le message dans la procédure PostHandler
de la classe Serveur.
Pour l'envoi de message, une nouvelle classe a été ajouté : la classe Talky. Celle-ci définit l'identifiant du robot à partir de son adresse IP et fournit une procédure sendMsg
permettant d'envoyer un message à un autre robot. La procédure prend en argument l'identifiant du destinataire et le message (la procédure ajoute automatiquement l'identifiant expéditeur au message avant l'envoi).
L'interface Web se voit ajouter un champ et un bouton illustrant l'envoi d'un message à un robot dont l'identifiant est spécifié. Ce message s'affichera dans le terminal du robot destinataire. Notons l'ajout du mot-clé 'message' dans l'interface Web pour demander au serveur d'envoyer un message via la procédure sendMsg
.
Code source :
- robot_control_usb.ino
- robot_control_usb_IMU.ino
- stereo_server_scanner_talky.zip
L'utilisation du code est identique à la version précédente, et nécessite l'association des mêmes librairies et la spécification des mêmes paramètres.
Ce système de communication a été utilisé pour étudier les possibilités de cartographie et de guidage sur plusieurs robots. Ici, un robot est piloté à distance et cartographie l'environnement. Le second robot suit le premier, puis, à intervalles réguliers, échangent leurs positions. Le système de communication permet aux robots de se synchroniser pendant l'échange. Chaque robot n'enregistre qu'une partie de la carte. Notre modèle de navigation bio-inspiré autorise cette distribution, mais aussi une exploitation d'un tel modèle distribué.