Robotique : Evitement d'obstacles avec caméra de profondeur

De fablab
Aller à : navigation, rechercher

Informations générales

  • Encadrants: Jérôme Maisonnasse

Ce projet est pour un groupe de 2 à 4 étudiants intéressés par la programmation en C++/Python et la robotique en général.

Contexte

Le contexte est identique au sujet sur la navigation des robots. http://youtu.be/PNIkW_5RSFA


Sujet

Les étudiants développeront un logiciel de détection d'obstacles de proximité permettant au robot de s'arrêter avant la collision. L'approche préconisée est d'utiliser une caméra de profondeur orientée de façon appropriée pour identifier les obstacles. Cette brique logicielle interagira avec d'autres commandes que ce soit l'utilisateur ou le module de navigation.


  • percevoir et commander les moteurs dans le cas d'un risque de collision,

Différents aspects pourront être traités :

  • Intégration du capteur basé vision par ordinateur,
  • implémenter un module ROS (http://www.ros.org/),
  • vision par ordinateur


Equipe

  • BOUDIER Arnaud
  • MORIEUX Florent
  • MOURIN Florian

Hypothèses de départ

Nos idées de départ concernant l'implantation d'un programme de détection d'obstacles sur le robot RobAir était d'utiliser une caméra de profondeur afin de détecter les formes proches du robot. Pour ce faire nous voulions développer un algorithme permettant de détecter une forme assez grande pour être considérée comme un obstacle. Nous pensions au départ utiliser des fonctions existantes de la librairie de vision par ordinateur OpenCV comme la "blob detection" qui permet de détecter des formes selon les caractéristiques passés en paramètres. Nous verrons dans la suite de ce rapport que nous avons changé notre approche en fonction de nos différents tests d'algorithmes. Nous avons imaginé que, une fois notre algorithme fonctionnel, celui-ci devrait envoyer l'information à propos de la présence ou non d'obstacle (booléen) sous la forme d'un topic ROS et donc d'une communication entre notre nœud et le nœud des moteurs.

Travail Réalisé

Organisation

1ère semaine :

  • 1 personne sur l'intégration à robAir
  • 2 personnes sur l'utilisation de la camera de profondeur et l'algorithme

2/3/4ièmes semaines :

Ensuite du fait du peu de parallélisme du travail, nous ne disposons que d'un robot et d'une tablette, tout le monde a travaillé à la mise en place du code sur la tablette et les tests avec robAir.

Utilisation

En premier lieu lorsque l'on allume la tablette, il faut vérifier que les cartes Arduino des moteurs ainsi que la camera sont bien vus par la tablette en utilisant la commande lsusb. Si ce n'est pas le cas il faut vérifier que tout est bien connecté au niveau usb et redémarrer la tablette. Il faut impérativement que la caméra soit connectée à un hub USB auto-alimenté comme celui embarqué par Robair, car elle consomme plus de 500mA (courant standard pour un port USB).

!!! ATTENTION !!!
Il faut bien vérifier que les lignes suivantes sont présentes en fin de fichier .bashrc dans votre répertoire Home (~)

source ~/GroupeBoudierMorieuxMourin/catkin_ws/src/obstacle_avoidance/Obstacle_Avoidance/OpenNI/OpenNIDevEnvironment
source ~/GroupeBoudierMorieuxMourin/catkin_ws/devel/setup.bash
source ~/groupeDufour/catkin/devel/setup.bash

Cela permet de compiler notre noeud correctement (ainsi que le noeud de suivi ) et de pouvoir les lancer, avec la commande rosrun obstacle_avoidance main_obstacle_avoidance et/ou rosrun suivi tracker n'importe où dans le terminal.

Le programme d'évitement d'obstacle est un exécutable compilé via catkin. Il utilise ROS pour communiquer avec les moteurs ainsi que avec le serveur. Il faut donc vérifier que les topics motorcmd et cmdProxy sont présents avec la commande rostopic list.

Si le topic motorcmd n'est pas présent, il faut exécuter la commande rosrun rosserial_python serial_node.py _port/dev/ttyACM0. Attention, si des erreurs s'affichent dans la console à l'exécution de cette commande, il faut s'assurer que les cartes Arduino soient bien connectées avec lsusb.

Pour lancer notre nœud ( couplé avec le nœud de suivi ) il faut aller dans ~/catkin_ws/src/robair/launch et lancer la commande roslaunch obs_avoid_follow . Si tout ce passe bien au bout de quelques secondes vous devriez voir afficher une fenêtre avec l'image de profondeur en niveau de gris.

Pour utiliser le serveur à distance vous devez dans un terminal lancer la commande suivante roslaunch rosbridge_server rosbridge_websocket.launch _port=9090

Pour démarrer le suivi à distance, le serveur nodejs d'interface du robot doit être démarré, il se trouve dans le répertoire ~/RobAirInterfaces/server/. Avant de le lancer avec la commande nodejs server.js il faut vérifier que le fichier config.json n'est pas vide, si tel est le cas alors faut faut executer la commande cp config.json.default config.json. Si toutefois vous n'arrivez toujours pas à lancer le serveur, redémarrez la tablette. Le serveur est ensuite accessible à l'adresse http://192.168.1.xxx:8087/ (voir adresse IP de la tablette avec ifconfig).

La tablette doit avoir la page "robair1" (login robair1) ouverte sur le serveur, tandis que l'utilisateur distant se connecte en tant qu'utilisateur de robair1 (login mjk). Le bouton démarrer le suivi affiché à l'écran permet de démarrer le suivi vers la cible au centre de l'image. Un nouveau clic sur le bouton arrête le suivi.

Installation de l'environnement de développement OpenNI

Page wiki pour installer l'environnement de travail OpenNI (+caméra ASUS) sur des nœuds ROS : http://fablab.ensimag.fr/index.php/OpenNI_sous_Ubuntu_12.04

Détection d'obstacles

Dans cette partie nous nous intéresserons à l'implantation du système de détection d'obstacle fonctionnant indépendamment du système de suivi de personnes. C'est à dire que l'on considère que l'on est les seuls à utiliser la caméra. La deuxième partie portera sur l'adaptation du code qui permettra à la détection d'obstacles et au suivi de personne d'accéder au flux d'images provenant de la caméra de profondeur simultanément.

Traitement de l'image de profondeur

La caméra de profondeur Asus Xtion renvoie une image en niveau de gris. Plus un objet est proche de la caméra, plus il sera affiché en noir sur l'image.Voici un exemple de l'image renvoyée sans traitement:

ImageProfondeur.jpg

Afin de ne détecter que les éléments proches de la caméra, nous avons procédé au traitement des images de la caméra. La fonction filtrage_image du fichier utilitaire.cpp utilise une matrice de pixels et 2 niveaux de gris (lowergray et uppergray) pour transformer l'image (via la matrice passée en paramètre) de la caméra.Une des difficultés que nous avons rencontrées était liée au fait que la caméra de profondeur renvoie une image sous 16 bits et que nous avions besoin d'un format classique 8bits pour nos algorithmes de détection d'obstacles. Ainsi les 8 bits de poids fort de la matrice passée en paramètre nous étaient inutiles, nous avons donc décidé de créer une version 8 bits de l'image en divisant par 256 la valeur de chaque pixel. Ensuite nous traitons cette nouvelle image avec la fonction cvInRangeS ( http://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#inrange )qui filtre le tableau de pixels passé en paramètre en ne gardant que les valeurs comprisent entre les deux niveaux de gris.Nous appelons ensuite sur cette nouvelle image les fonctions de la librairies OpenCv cvDilate et cvErode (cf documentation OpenCV 2.4)afin d'obtenir une image plus nette et d'enlever les imperfections.Voici le résultat obtenu après traitement de l'image:

ImageProfondeurTraitee.jpg

Algorithme de détection avec OpenCV

Dans un premier temps, pour nous familiariser avec l'environnement et la programmation OpenCV, nous avons développé un petit algorithme qui devait détecter les carrés de neuf pixels proches de la caméra. Cela nous a permis de mieux comprendre la structure des images et du flux renvoyés par la caméra de profondeur. Comme expliqué dans la partie hypothèses, nous pensions au départ détecter les formes grâce à des fonctions existantes. Le principe de "blob detection" nous paraissait correspondre parfaitement à la détection d'obstacle. Il s'est avéré que ce principe se basait essentiellement sur la détection d'une couleur et d'une forme spécifique (pour plus d'informations sur le principe: http://www.learnopencv.com/blob-detection-using-opencv-python-c/). La caméra Asus envoyant une image en nuance de gris, la détection de couleur n'était pas pertinente. Bien qu'on aurait pu détecter le blanc caractéristique d'une forme proche de la caméra, la fonction de filtrage de forme par couleur ne semblait pas bien adaptée au flux envoyé par l'Asus Xtion (de plus le lien semble dire que la fonction n'est pas stable). Pour ce qui est de la détection de formes, il était impossible de définir une forme spécifique en paramètre (filter by convexity, circularity ...) en sachant que les obstacles peuvent adopter n'importe quelle forme. En fait cette fonctionnalité OpenCV est très efficace pour reconnaître les objets tels que les balles de couleur mais inutilisable dans notre cas. Notre solution était donc de développer notre propre algorithme.

L'idée est de scanner chaque pixel ligne par ligne pour chaque image envoyée par la caméra. Lorsqu'on détecte un pixel blanc, la fonction detect_pixel appelle la fonction detect_forme qui est chargée de vérifier si le pixel détecté fait partie d'une forme assez grande pour être un obstacle et ne correspond pas à du bruit sur l'image (qui aurait été oublié par la fonction erode lors du traitement de l'image). Cette fonction est récursive et s'appelle sur chaque pixel blanc limitrophe au pixel étudié et remplie un tableau 2D (passé en paramètre) permettant de savoir si un pixel a déjà été observé. Cette fonction s’arrête lorsque tous les pixels blanc cote à cote ont été comptés (d'une même forme). La fonction detect_forme renvoie donc le nombre de pixels blancs d'une forme et on peut donc savoir grâce au paramètre seuil de la fonction detect_pixel si le pixel détecté fait partie d'une forme assez imposante pour être considérée comme un obstacle, au quel cas la fonction renvoie la valeur booléenne vraie. Au passage nous avons profité pour faire un calcul de barycentre nous permettant d'encercler en rouge les obstacles.

Intégration au suivi de personne

Ensuite il nous a fallu intégrer notre nœud d'évitement d'obstacle avec le nœud de suivi de personne. Le problème étant que nous ne pouvions pas ouvrir deux flux de caméra dans deux processus différents. Nous avons donc utilisé ROS pour transmettre le flux de la camera ( matrice en niveau de gris ) sur un topic grâce aux librairies image_transport et cv_bridge qui permettent de faire le lien entre un objet OpenCV et un message envoyé sur un topic ROS. Le nœud d'évitement d'obstacle envoie l'image et le nœud de suivi la reçoit.

Architecture ROS

Il s'agit d'initialiser les topics des publishers que nous utiliserons pour la détection d'obstacles. Lorsque la détection est lancée seule (sans suivi de personne), un publisher commande_proxy qui permet de contrôler pour toutes les commandes envoyées au moteur s'il n'y a pas un obstacle devant le robot avant de les exécuter. Ainsi, tous les noeuds envoyant des commandes au moteur vont à présent les envoyer au nœud de détection d'obstacles.

Fichier002.jpg

Le publisher "img_depth" quant à lui permet d'envoyer l'image lue par la caméra à d'autres noeuds qui pourront eux aussi exploiter ces images étant donné que la caméra ne peut pas être utilisée par 2 noeuds lancés en parallèle.
Le message "obstacle_event" permet de signaler au tracker que l'on a trouvé un obstacle devant le robot et que ce dernier ne peut plus avancer et donc de permettre à celui-ci de tourner sur place plutôt que de tourner en avançant ce qui serait impossible puisqu'il se retrouverait bloqué par le noeud "Détection Obstacle".