pointcloud_to_maps.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014-2017, the neonavigation authors
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the copyright holder nor the names of its 
00014  *       contributors may be used to endorse or promote products derived from 
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include <ros/ros.h>
00031 #include <sensor_msgs/PointCloud2.h>
00032 #include <nav_msgs/OccupancyGrid.h>
00033 #include <map_organizer_msgs/OccupancyGridArray.h>
00034 #include <pcl_ros/point_cloud.h>
00035 #include <pcl_ros/transforms.h>
00036 
00037 #include <pcl/point_types.h>
00038 #include <pcl/conversions.h>
00039 #include <pcl_conversions/pcl_conversions.h>
00040 #include <pcl/io/vtk_lib_io.h>
00041 #include <pcl/filters/voxel_grid.h>
00042 #include <pcl/sample_consensus/ransac.h>
00043 #include <pcl/sample_consensus/prosac.h>
00044 #include <pcl/sample_consensus/msac.h>
00045 #include <pcl/sample_consensus/sac_model_plane.h>
00046 #include <pcl/filters/extract_indices.h>
00047 #include <pcl/filters/passthrough.h>
00048 #include <pcl/registration/icp.h>
00049 #include <pcl/segmentation/sac_segmentation.h>
00050 #include <pcl/segmentation/extract_clusters.h>
00051 
00052 #include <cmath>
00053 #include <random>
00054 #include <string>
00055 #include <iostream>
00056 #include <sstream>
00057 #include <map>
00058 #include <utility>
00059 #include <vector>
00060 
00061 #include <neonavigation_common/compatibility.h>
00062 
00063 pcl::PointXYZ operator-(const pcl::PointXYZ& a, const pcl::PointXYZ& b)
00064 {
00065   auto c = a;
00066   c.x -= b.x;
00067   c.y -= b.y;
00068   c.z -= b.z;
00069   return c;
00070 }
00071 pcl::PointXYZ operator+(const pcl::PointXYZ& a, const pcl::PointXYZ& b)
00072 {
00073   auto c = a;
00074   c.x += b.x;
00075   c.y += b.y;
00076   c.z += b.z;
00077   return c;
00078 }
00079 pcl::PointXYZ operator*(const pcl::PointXYZ& a, const float& b)
00080 {
00081   auto c = a;
00082   c.x *= b;
00083   c.y *= b;
00084   c.z *= b;
00085   return c;
00086 }
00087 
00088 class PointcloudToMapsNode
00089 {
00090 private:
00091   ros::NodeHandle pnh_;
00092   ros::NodeHandle nh_;
00093   std::map<std::string, ros::Publisher> pub_maps_;
00094   ros::Publisher pub_map_array_;
00095   ros::Subscriber sub_points_;
00096 
00097 public:
00098   PointcloudToMapsNode()
00099     : pnh_("~")
00100     , nh_()
00101   {
00102     neonavigation_common::compat::checkCompatMode();
00103     sub_points_ = neonavigation_common::compat::subscribe(
00104         nh_, "mapcloud",
00105         pnh_, "map_cloud", 1, &PointcloudToMapsNode::cbPoints, this);
00106     pub_map_array_ = nh_.advertise<map_organizer_msgs::OccupancyGridArray>("maps", 1, true);
00107   }
00108   void cbPoints(const sensor_msgs::PointCloud2::Ptr& msg)
00109   {
00110     pcl::PointCloud<pcl::PointXYZ>::Ptr pc(new pcl::PointCloud<pcl::PointXYZ>());
00111     pcl::fromROSMsg(*msg, *pc);
00112 
00113     double grid;
00114     int min_points;
00115     double robot_height_f;
00116     int robot_height;
00117     double floor_height_f;
00118     int floor_height;
00119     double min_floor_area;
00120     double floor_area_thresh_rate;
00121     double floor_tolerance_f;
00122     int floor_tolerance;
00123     double points_thresh_rate;
00124 
00125     pnh_.param("grid", grid, 0.05);
00126     pnh_.param("points_thresh_rate", points_thresh_rate, 0.5);
00127     pnh_.param("robot_height", robot_height_f, 1.0);
00128     pnh_.param("floor_height", floor_height_f, 0.1);
00129     pnh_.param("floor_tolerance", floor_tolerance_f, 0.2);
00130     pnh_.param("min_floor_area", min_floor_area, 100.0);
00131     pnh_.param("floor_area_thresh_rate", floor_area_thresh_rate, 0.8);
00132     robot_height = lroundf(robot_height_f / grid);
00133     floor_height = lroundf(floor_height_f / grid);
00134     floor_tolerance = lroundf(floor_tolerance_f / grid);
00135 
00136     std::map<int, int> hist;
00137     int x_min = INT_MAX, x_max = 0;
00138     int y_min = INT_MAX, y_max = 0;
00139     int h_min = INT_MAX, h_max = 0;
00140 
00141     for (auto& p : pc->points)
00142     {
00143       int h = (p.z / grid);
00144       int x = (p.x / grid);
00145       int y = (p.y / grid);
00146       if (x_min > x)
00147         x_min = x;
00148       if (y_min > y)
00149         y_min = y;
00150       if (h_min > h)
00151         h_min = h;
00152       if (x_max < x)
00153         x_max = x;
00154       if (y_max < y)
00155         y_max = y;
00156       if (h_max < h)
00157         h_max = h;
00158       if (hist.find(h) == hist.end())
00159         hist[h] = 0;
00160       hist[h]++;
00161     }
00162     int max_height = h_max;
00163     int min_height = h_min;
00164     std::map<int, float> floor_area;
00165     std::map<int, float> floor_runnable_area;
00166     for (int i = min_height; i < max_height; i++)
00167       floor_area[i] = 0;
00168 
00169     nav_msgs::MapMetaData mmd;
00170     mmd.resolution = grid;
00171     mmd.origin.position.x = x_min * grid;
00172     mmd.origin.position.y = y_min * grid;
00173     mmd.origin.orientation.w = 1.0;
00174     mmd.width = x_max - x_min;
00175     mmd.height = y_max - y_min;
00176     ROS_INFO("width %d, height %d", mmd.width, mmd.height);
00177     std::vector<nav_msgs::OccupancyGrid> maps;
00178 
00179     int hist_max = INT_MIN;
00180     for (auto h : hist)
00181       if (h.second > hist_max)
00182         hist_max = h.second;
00183 
00184     min_points = hist_max * points_thresh_rate;
00185 
00186     double floor_area_max = 0;
00187     double floor_runnable_area_max = 0;
00188     std::map<int, std::map<std::pair<int, int>, char>> floor;
00189     for (int i = min_height; i < max_height; i++)
00190     {
00191       if (hist[i] > min_points)
00192       {
00193         for (auto& p : pc->points)
00194         {
00195           int x = (p.x / grid);
00196           int y = (p.y / grid);
00197           int z = (p.z / grid);
00198           auto v = std::pair<int, int>(x, y);
00199 
00200           if (abs(i - z) <= floor_height)
00201           {
00202             if (floor[i].find(v) == floor[i].end())
00203             {
00204               floor[i][v] = 0;
00205             }
00206           }
00207           else if (i + floor_height + floor_tolerance < z && z <= i + robot_height)
00208           {
00209             floor[i][v] = 1;
00210           }
00211         }
00212 
00213         int cnt = 0;
00214         for (auto& m : floor[i])
00215         {
00216           if (m.second == 0)
00217             cnt++;
00218         }
00219         floor_runnable_area[i] = cnt * (grid * grid);
00220         floor_area[i] = floor[i].size() * (grid * grid);
00221         if (floor_area_max < floor_area[i])
00222           floor_area_max = floor_area[i];
00223         if (floor_runnable_area_max < floor_runnable_area[i])
00224           floor_runnable_area_max = floor_runnable_area[i];
00225       }
00226       else
00227       {
00228         floor_area[i] = 0;
00229       }
00230     }
00231     const double floor_area_filter = floor_runnable_area_max * floor_area_thresh_rate;
00232     int map_num = 0;
00233     for (int i = min_height + 1; i < max_height - 1; i++)
00234     {
00235       if (hist[i] > min_points &&
00236           floor_runnable_area[i - 1] < floor_runnable_area[i] &&
00237           floor_runnable_area[i + 1] < floor_runnable_area[i])
00238       {
00239         if (floor_runnable_area[i] > floor_area_filter)
00240         {
00241           nav_msgs::OccupancyGrid map;
00242           map.info = mmd;
00243           map.info.origin.position.z = i * grid;
00244           map.header = msg->header;
00245           map.data.resize(mmd.width * mmd.height);
00246           for (auto& c : map.data)
00247             c = -1;
00248           for (auto& m : floor[i])
00249           {
00250             int addr = (m.first.first - x_min) + (m.first.second - y_min) * mmd.width;
00251             if (m.second == 0)
00252             {
00253               map.data[addr] = 0;
00254             }
00255             else if (m.second == 1)
00256               map.data[addr] = 100;
00257           }
00258           auto map_cp = map.data;
00259           for (unsigned int i = 0; i < map_cp.size(); i++)
00260           {
00261             if (map_cp[i] == 0)
00262             {
00263               const int width = 6;
00264               int floor_width = width;
00265               for (int xp = -width; xp <= width; xp++)
00266               {
00267                 for (int yp = -width; yp <= width; yp++)
00268                 {
00269                   int width_sq = xp * xp + yp * yp;
00270                   if (width_sq > width * width)
00271                     continue;
00272                   unsigned int x = i % mmd.width + xp;
00273                   unsigned int y = i / mmd.width + yp;
00274                   if (x >= mmd.width || y >= mmd.height)
00275                     continue;
00276                   int addr = x + y * mmd.width;
00277                   if (map_cp[addr] == 100)
00278                   {
00279                     if (width_sq < floor_width * floor_width)
00280                       floor_width = sqrtf(width_sq);
00281                   }
00282                 }
00283               }
00284               floor_width--;
00285               for (int xp = -floor_width; xp <= floor_width; xp++)
00286               {
00287                 for (int yp = -floor_width; yp <= floor_width; yp++)
00288                 {
00289                   if (xp * xp + yp * yp > floor_width * floor_width)
00290                     continue;
00291                   unsigned int x = i % mmd.width + xp;
00292                   unsigned int y = i / mmd.width + yp;
00293                   if (x >= mmd.width || y >= mmd.height)
00294                     continue;
00295                   int addr = x + y * mmd.width;
00296                   if (map_cp[addr] != 100)
00297                   {
00298                     map.data[addr] = 0;
00299                   }
00300                 }
00301               }
00302             }
00303           }
00304 
00305           maps.push_back(map);
00306           map_num++;
00307         }
00308       }
00309     }
00310     ROS_INFO("Floor candidates: %d", map_num);
00311     auto it_prev = maps.rbegin();
00312     for (auto it = maps.rbegin() + 1; it != maps.rend(); it++)
00313     {
00314       if (fabs(it_prev->info.origin.position.z - it->info.origin.position.z) < grid * 1.5)
00315       {
00316         // merge slopes
00317         for (unsigned int i = 0; i < it->data.size(); i++)
00318         {
00319           if (it->data[i] == 100 && it_prev->data[i] == 0)
00320           {
00321             it->data[i] = 0;
00322             it_prev->data[i] = -1;
00323           }
00324         }
00325       }
00326       it_prev = it;
00327     }
00328     for (int i = max_height - 1; i >= min_height; i--)
00329     {
00330       printf(" %6.2f ", i * grid);
00331       for (int j = 0; j <= 16; j++)
00332       {
00333         if (j <= hist[i] * 16 / hist_max)
00334           printf("#");
00335         else
00336           printf(" ");
00337       }
00338       if (floor_runnable_area[i] == 0.0)
00339         printf("  (%7d points)\n", hist[i]);
00340       else
00341         printf("  (%7d points, %4.0f m^2 of floor)\n", hist[i], floor_runnable_area[i]);
00342     }
00343     int num = -1;
00344     int floor_num = 0;
00345     map_organizer_msgs::OccupancyGridArray map_array;
00346     for (auto& map : maps)
00347     {
00348       num++;
00349       int h = map.info.origin.position.z / grid;
00350       if (floor_runnable_area[h] < min_floor_area)
00351       {
00352         ROS_ERROR("floor %d (%5.2fm^2), h = %0.2fm skipped",
00353                   floor_num, floor_runnable_area[num], map.info.origin.position.z);
00354         continue;
00355       }
00356       std::string name = "map" + std::to_string(floor_num);
00357       pub_maps_[name] = pnh_.advertise<nav_msgs::OccupancyGrid>(name, 1, true);
00358       pub_maps_[name].publish(map);
00359       map_array.maps.push_back(map);
00360       ROS_ERROR("floor %d (%5.2fm^2), h = %0.2fm",
00361                 floor_num, floor_runnable_area[h], map.info.origin.position.z);
00362       floor_num++;
00363     }
00364     pub_map_array_.publish(map_array);
00365   }
00366 };
00367 
00368 int main(int argc, char** argv)
00369 {
00370   ros::init(argc, argv, "pointcloud_to_maps");
00371 
00372   PointcloudToMapsNode p2m;
00373   ros::spin();
00374 
00375   return 0;
00376 }


map_organizer
Author(s): Atsushi Watanabe
autogenerated on Sat Jun 22 2019 20:07:17