mapping_integration_web

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
mapping_integration_web [2018/02/13 15:09] – Début de l'écriture de la partie 3 mgp3202018mapping_integration_web [2020/07/03 17:38] (current) – Tag0 Added: pepper mai
Line 2: Line 2:
  
 Cette documentation explique comment créer une carte à partir d'un robot pour l'intégrer ensuite dans une interface Web et afficher la localisation du robot. Cette documentation explique comment créer une carte à partir d'un robot pour l'intégrer ensuite dans une interface Web et afficher la localisation du robot.
 +Ce travail se base sur cet article : [[https://arxiv.org/pdf/1704.04797.pdf|Voir l'article]]
 +
 +Vidéo de démonstration de la solution : [[https://youtu.be/xF7-tTI0zrE|Voir la vidéo YouTube]]
  
 ===== Liste des packages utilisés ===== ===== Liste des packages utilisés =====
  
-  * http://wiki.ros.org/rosbag +  * http://wiki.ros.org/rosbag : Un ensemble d'outils pour l'enregistrement et la lecture de topics ROS. 
-  * http://wiki.ros.org/gmapping +  * http://wiki.ros.org/gmapping : Création d'une carte 2D à partir des lasers d'un robot. 
-  * http://wiki.ros.org/map_server +  * http://wiki.ros.org/map_server : Permet de publier et d'enregistrer des cartes. 
-  * http://wiki.ros.org/rosbridge_server +  * http://wiki.ros.org/rosbridge_server : Publie les topics ROS dans un WebSocket au format JSON. 
-  * http://wiki.ros.org/roslibjs/ +  * http://wiki.ros.org/roslibjs : Librairie JS, permet la lecture et l'écriture de topics en interaction avec le WebSocket. 
-  * http://wiki.ros.org/ros2djs +  * http://wiki.ros.org/ros2djs : Librairie JS, permet d'afficher les cartes dans une interface web. 
-  * http://wiki.ros.org/amcl +  * http://wiki.ros.org/amcl : Un système de localisation probabiliste pour un robot se déplaçant en 2D. 
-  * http://wiki.ros.org/nodelet +  * http://wiki.ros.org/nodelet : Fournit un moyen d'exécuter plusieurs algorithmes dans le même processus. 
-  * http://wiki.ros.org/image_proc +  * http://wiki.ros.org/image_proc : Rectification d'image et traitement de couleurs. 
-  * http://wiki.ros.org/depth_image_proc +  * http://wiki.ros.org/depth_image_proc : Traitement des "depth images" (transformation en "point cloud" notamment). 
-  * http://wiki.ros.org/pointcloud_to_laserscan +  * http://wiki.ros.org/pointcloud_to_laserscan : Transforme un "point cloud" en "laser scan". 
-  * http://wiki.ros.org/rb1_base_common+  * http://wiki.ros.org/rb1_base_common : Contrôle de la base de RB-1. 
 +  * http://wiki.ros.org/pepper_bringup : Connexion à Pepper. 
 + 
 +Tous ces packages peuvent être installés en utilisant la commande : 
 +  sudo apt-get install ros-<ros_distribution>-<ros_package> 
 +La liste des packages disponibles sur une distribution ROS peut être affichée comme ceci : 
 +  apt-cache search ros-<ros_distribution> 
 + 
 +Pour Pepper, il faut utiliser ROS Kinetic (installable sur Ubuntu 16.04).\\ 
 +Pour RB-1, il faut utiliser ROS Indigo (installable sur Ubuntu 14.04).
  
 ===== Création d'un rosbag ===== ===== Création d'un rosbag =====
Line 25: Line 37:
 On se connecte au robot. Pour Pepper : On se connecte au robot. Pour Pepper :
   roslaunch pepper_bringup pepper_full_py.launch nao_ip:=<ip_de_pepper> roscore_ip:=127.0.0.1   roslaunch pepper_bringup pepper_full_py.launch nao_ip:=<ip_de_pepper> roscore_ip:=127.0.0.1
-Pour RB1 :+Pour RB1 (il faut au préalable être connecté au WiFi du robot) :
   export ROS_MASTER_URI=http://rb1:11311   export ROS_MASTER_URI=http://rb1:11311
 On lance l'enregistrement des topics fournis par le robot :\\ On lance l'enregistrement des topics fournis par le robot :\\
Line 42: Line 54:
 NB : il faut **impérativement** lancer cette commande avant de lancer tous les nodes. NB : il faut **impérativement** lancer cette commande avant de lancer tous les nodes.
   rosparam set use_sim_time true   rosparam set use_sim_time true
-On génère ensuite une carte à l'aide de [[http://wiki.ros.org/gmapping#slam_gmapping|slam_gmapping]]. Il faut passer en argument le topic du robot correspondant au type //sensor_msgs/LaserScan// (i.e. les lasers du robot). Par défaut, les cartes générées sont trop "dé-zoomées", il faut donc jouer sur la résolution (paramètre //delta//) et éventuellement sur les dimensions.\\Par exemple, pour RB1, la commande suivante génère une carte exploitable :+On génère ensuite une carte à l'aide de [[http://wiki.ros.org/gmapping#slam_gmapping|slam_gmapping]]. Il faut passer en argument le topic du robot correspondant au type //sensor_msgs/LaserScan// (i.e. les lasers du robot). Par défaut, les cartes générées sont trop "dé-zoomées", il faut donc jouer sur la résolution (paramètre //delta//) et éventuellement sur les dimensions.\\ 
 +Par exemple, pour RB1, la commande suivante génère une carte exploitable :
   rosrun gmapping slam_gmapping scan:=/front_laser/scan _xmin:=-1 _xmax:=1 _ymin:=-1 _ymax:=1 _delta:=0.035 _maxUrange:=10   rosrun gmapping slam_gmapping scan:=/front_laser/scan _xmin:=-1 _xmax:=1 _ymin:=-1 _ymax:=1 _delta:=0.035 _maxUrange:=10
 On rejoue le bag : On rejoue le bag :
-  rosbag play --clock </chemin/et/nomDuBag> +  rosbag play --clock <nom_du_bag>.bag 
-Avant d'annuler le gmapping, on sauve la carte :+Avant d'annuler le gmapping, on enregistre la carte :
   rosrun map_server map_saver -f <map_name>   rosrun map_server map_saver -f <map_name>
 +Exemple de carte obtenue avec les lasers de RB-1 :\\
 +{{:screenshot_19.png?200|}}\\
 +Les traits que l'on voit sont des aberrations dues aux lasers, on peut réduire leur impact en jouant sur les paramètres lors de la génération de la carte (paramètre //Urange//).
  
 ==== Spécificités de Pepper ==== ==== Spécificités de Pepper ====
Line 63: Line 79:
 On lance le package //pointcloud_to_laserscan// qui génère un //sensor_msgs/LaserScan// à partir du nuage de points : On lance le package //pointcloud_to_laserscan// qui génère un //sensor_msgs/LaserScan// à partir du nuage de points :
   rosrun pointcloud_to_laserscan pointcloud_to_laserscan_node cloud_in:=/points   rosrun pointcloud_to_laserscan pointcloud_to_laserscan_node cloud_in:=/points
-Puis on génère la carte comme dans le cas général.+Puis on génère la carte comme vu dans la partie 3.
  
 ===== Intégration de la carte dans une interface Web ===== ===== Intégration de la carte dans une interface Web =====
  
-Le principe est le suivant : //rosbridge_server// publie un WebSocket contenant les informations des topics au format JSON. //roslibjs// vient ensuite souscrire aux topics et permet la récupération des données dans une page Web (voir [[http://wiki.ros.org/roslibjs/Tutorials/BasicRosFunctionality|le tutoriel roslibjs]]). Enfin, //ros2djs// permet l’affichage d’un élément de type //nav_msgs/OccupancyGrid// (le format ROS des cartes) dans l’interface (voir [[http://wiki.ros.org/ros2djs/Tutorials/VisualizingAMap|le tutoriel ros2djs]]).+Le principe est le suivant : //rosbridge_server// publie un WebSocket contenant les informations des topics au format JSON. //roslibjs// vient ensuite souscrire aux topics et permet la récupération des données dans une page Web (voir [[http://wiki.ros.org/roslibjs/Tutorials/BasicRosFunctionality|le tutoriel roslibjs]]). Enfin, //ros2djs// permet l’affichage d’un élément de type //nav_msgs/OccupancyGrid// (le format ROS des cartes) dans l’interface (voir [[http://wiki.ros.org/ros2djs/Tutorials/VisualizingAMap|le tutoriel ros2djs]]).\\ 
 +\\ 
 +Pour cela, on publie un topic de map à partir du fichier de map enregistré.\\ 
 +NB : il faut bien utiliser le fichier .yaml et non le fichier .pgm de la carte. 
 +  rosrun map_server map_server <yaml_file>.yaml 
 +On lance le WebSocket : 
 +  roslaunch rosbridge_server rosbridge_websocket.launch 
 + 
 +Voici le code HTML de la page que nous avons utilisé. Attention à bien avoir les fichiers des librairies css et javascript positionnées au bon endroit. 
 +<hidden> 
 +<code html> 
 + 
 +<!doctype html> 
 +<html lang="fr"> 
 + <head> 
 + <!-- Required meta tags --> 
 + <meta charset="utf-8"> 
 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 
 + <link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png"> 
 + <link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png"> 
 + <link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png"> 
 + <link rel="manifest" href="manifest.json"> 
 + <link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5"> 
 + <meta name="theme-color" content="#ffffff"> 
 + <link rel="icon" href="favicon.ico"> 
 + 
 + <!-- jQuery first, then Popper.js, then Bootstrap JS --> 
 + <script src="js/jquery-3.3.1.min.js"></script> 
 + <script src="js/popper.min.js"></script> 
 + <script src="js/bootstrap.min.js"></script> 
 + <script src="https://static.robotwebtools.org/EaselJS/current/easeljs.js"></script> 
 + <script src="https://static.robotwebtools.org/EventEmitter2/current/eventemitter2.min.js"></script> 
 + <script src="https://static.robotwebtools.org/roslibjs/current/roslib.js"></script> 
 + <script src="js/ros2d.min.js"></script> 
 + 
 + <!-- Bootstrap CSS --> 
 + <link rel="stylesheet" href="css/bootstrap.min.css"> 
 + <link rel="stylesheet" href="css/style.css"> 
 + 
 + <title>Interface de navigation</title> 
 + </head> 
 + 
 + <body> 
 + <nav class="navbar navbar-expand-md bg-dark navbar-dark"> 
 + <div class="container"> 
 + 
 + <!-- Brand --> 
 + <a class="navbar-brand" href="#"> 
 + <img src="android-chrome-192x192.png" alt="Logo" style="width:25px;">&nbsp;&nbsp;Interface de navigation 
 + </a> 
 + </div>  
 + </nav> 
 + 
 + <div class="container"> 
 + <div class="row mt-3"> 
 + <div class="col"> 
 + <div id="map"></div> 
 + </div> 
 + <div class="col"> 
 + <h2>Contrôle du robot</h2> 
 + <p>Pour contrôler le robot, cliquez simplement sur la map.</p> 
 + <p>Le point bleu représente la localisation actuelle du robot. Le point rouge représente la destination choisie.</p> 
 + </div> 
 + </div> 
 + </div> 
 + 
 + <footer class="footer mt-4"> 
 + <div class="container"> 
 + <span class="text-muted">© IMT Atlantique</span> 
 + </div> 
 + </footer> 
 + 
 + <script> 
 +  
 + $(document).ready(function() { 
 +  
 + // Variables sur la map 
 + var posImageX = 0; 
 + var posImageY = 0; 
 + var widthMap = 0; 
 + var heightMap = 0; 
 + var resolutionMap = 0; 
 + var svg; 
 + 
 + // Position d'origine du robot 
 + var posOriginX = 0; 
 + var posOriginY = 0; 
 + 
 + // Position du clic 
 + var posClicX, posClicY; 
 + 
 + // Connexion à ROS 
 + var ros = new ROSLIB.Ros({ 
 + url : 'ws://localhost:9090' 
 + }); 
 + 
 + ros.on('connection', function() { 
 + console.log("Connecté au Web Socket"); 
 + }); 
 + 
 + // On souscrit au topic /map_metadata 
 + // ---------------------------------- 
 + var listenerMetaData = new ROSLIB.Topic({ 
 + ros : ros, 
 + name : '/map_metadata', 
 + messageType : 'nav_msgs/MapMetaData' 
 + }); 
 + 
 + listenerMetaData.subscribe(function(message) { 
 + 
 + console.log("Abonnement au topic /map_metadata"); 
 + 
 + // On récupère les métadonnées de la map 
 + posOriginX = message.origin.position.x; 
 + posOriginY = message.origin.position.y; 
 + widthMap = message.width; 
 + heightMap = message.height; 
 + resolutionMap = message.resolution; 
 + listenerMetaData.unsubscribe(); 
 + 
 + console.log('OriginX = ' + posOriginX + ', OriginY = ' + posOriginY); 
 + 
 + // On créé le viewer 
 + var viewer = new ROS2D.Viewer({ 
 + divID : 'map', 
 + width : widthMap, 
 + height : heightMap 
 + }); 
 + 
 + // Préparation de la map 
 + var gridClient = new ROS2D.OccupancyGridClient({ 
 + ros : ros, 
 + rootObject : viewer.scene 
 + }); 
 + 
 + // Scale the canvas to fit to the map 
 + gridClient.on('change', function() { 
 + viewer.scaleToDimensions(gridClient.currentGrid.width, gridClient.currentGrid.height); 
 + viewer.shift(gridClient.currentGrid.pose.position.x, gridClient.currentGrid.pose.position.y); 
 + }); 
 + 
 + var image = $('#map canvas'); 
 + posImageX = image.offset().left; 
 + posImageY = image.offset().top; 
 + 
 + // On créé le svg 
 + svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); 
 + svg.setAttribute('id', 'svg-canvas'); 
 + svg.setAttribute('style', 'position: absolute; top: ' + posImageY + 'px; left: ' + posImageX + 'px;'); 
 + svg.setAttribute('width', widthMap); 
 + svg.setAttribute('height', heightMap); 
 + document.body.appendChild(svg); 
 + 
 + $('#svg-canvas').on("click", function(e){ 
 + posClicX = e.pageX - posImageX; 
 + posClicY = e.pageY - posImageY; 
 +  
 + console.log('Clic X = ' + posClicX + ' / ' + widthMap + ', clic Y = ' + posClicY + ' / ' + heightMap); 
 + 
 + $('circle.destination').remove(); 
 + var shape = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 
 + shape.setAttributeNS(null, "cx", posClicX); 
 + shape.setAttributeNS(null, "cy", posClicY); 
 + shape.setAttributeNS(null, "r", 3); 
 + shape.setAttributeNS(null, "fill", 'red'); 
 + shape.setAttributeNS(null, "class", 'destination'); 
 + svg.appendChild(shape); 
 + }); 
 + 
 + // On souscrit au topic /amcl_pose 
 + // ---------------------------------- 
 + var listener = new ROSLIB.Topic({ 
 + ros : ros, 
 + name : '/amcl_pose', 
 + messageType : 'geometry_msgs/PoseWithCovarianceStamped' 
 + }); 
 + 
 + console.log("Abonnement au topic /amcl_pose"); 
 + 
 + listener.subscribe(function(message) { 
 + 
 + var posX = (0 + (message.pose.pose.position.x - posOriginX)/resolutionMap); 
 + var posY = (heightMap + -1*(message.pose.pose.position.y - posOriginY)/resolutionMap); 
 + 
 + console.log('X = ' + posX + ', Y = ' + posY); 
 + $('circle.source').remove(); 
 + var shape = document.createElementNS("http://www.w3.org/2000/svg", "circle"); 
 + shape.setAttributeNS(null, "cx", posX); 
 + shape.setAttributeNS(null, "cy", posY); 
 + shape.setAttributeNS(null, "r", 3); 
 + shape.setAttributeNS(null, "fill", 'blue'); 
 + shape.setAttributeNS(null, "class", 'source'); 
 + svg.appendChild(shape); 
 + //listener.unsubscribe(); 
 + 
 + }); 
 + }); 
 + 
 + }); 
 + 
 + </script> 
 + </body> 
 +</html> 
 +</code> 
 +</hidden>
  
 ===== Affichage de la localisation du robot ===== ===== Affichage de la localisation du robot =====
 +
 +Pour la localisation du robot, on utilise le package //amcl//.\\
 +Si ce n'est pas déjà fait, on publie la carte :
 +  rosrun map_server map_server <yaml_file>.yaml
 +On lance la lecture du bag :
 +  rosbag play --clock <nom_du_bag>.bag
 +On lance la détection AMCL à partir des lasers (pour RB1, ///front_laser/scan//)
 +  rosrun amcl amcl scan:=<nom_du_topic_laser_scan>
 +La page Web s'abonne au topic de positionnement et affiche dynamiquement la position du robot sur la carte.
 +
 +===== Travaux futurs =====
 +==== Pour Pepper ====
 +
 +  * Troubleshooting de l'erreur avec le package //depth_image_proc/////convert_metric// pour pouvoir générer une carte à partir de la camera de Pepper
 +  * Normalement, la suite devrait fonctionner comme avec RB-1
 +
 +==== Pour le déplacement de RB-1 à la localisation spécifiée ====
 +
 +  * Publier les coordonnées de destination depuis l'interface web vers ROS en utilisant //roslibjs//
 +  * Utiliser ces coordonnées pour déplacer RB-1 (probablement grâce au package //rb1_base_common// / //navigation// ou //control//)
 +{{tag> pepper ros }}
  • mapping_integration_web.1518534597.txt.gz
  • Last modified: 2019/04/25 14:08
  • (external edit)