00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
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 }