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
00031
00032
00033
00034
00035
00036
00037
00046 #include <ros/ros.h>
00047 #include "pcl/io/io.h"
00048 #include "pcl_ros/publisher.h"
00049 #include "pcl/point_types.h"
00050
00051 #include "pcl/io/io.h"
00052 #include "pcl/io/pcd_io.h"
00053 #include "pcl/filters/voxel_grid.h"
00054 #include "pcl/filters/passthrough.h"
00055 #include "pcl/filters/extract_indices.h"
00056 #include "pcl/features/normal_3d.h"
00057 #include "pcl/kdtree/kdtree.h"
00058 #include "pcl/kdtree/kdtree_flann.h"
00059 #include "pcl/kdtree/organized_data.h"
00060 #include "pcl/sample_consensus/method_types.h"
00061 #include "pcl/sample_consensus/model_types.h"
00062 #include "pcl/segmentation/sac_segmentation.h"
00063 #include "pcl/filters/project_inliers.h"
00064 #include "pcl/surface/convex_hull.h"
00065 #include "pcl/segmentation/extract_polygonal_prism_data.h"
00066 #include "pcl/segmentation/extract_clusters.h"
00067
00068
00069 #include <sensor_msgs/PointCloud2.h>
00070 #include "pcl/PointIndices.h"
00071 #include "pcl/ModelCoefficients.h"
00072
00073
00074
00075
00076
00077
00078
00079
00080 class TableObjectDetector
00081 {
00082 typedef pcl::PointXYZRGB Point;
00083 typedef pcl::KdTree<Point>::Ptr KdTreePtr;
00084
00085 public:
00086 ros::NodeHandle nh_;
00087
00088 ros::Subscriber cloud_sub_;
00089 ros::Publisher cloud_pub_;
00090
00091
00092 KdTreePtr normals_tree_, clusters_tree_;
00093 pcl::PassThrough<Point> pass_;
00094 pcl::VoxelGrid<Point> grid_, grid_objects_;
00095 pcl::NormalEstimation<Point, pcl::Normal> n3d_;
00096 pcl::SACSegmentationFromNormals<Point, pcl::Normal> seg_;
00097 pcl::ProjectInliers<Point> proj_;
00098 pcl::ConvexHull<Point> hull_;
00099 pcl::ExtractPolygonalPrismData<Point> prism_;
00100 pcl::EuclideanClusterExtraction<Point> cluster_;
00101
00102
00103
00104 double downsample_leaf_, downsample_leaf_objects_;
00105 int k_;
00106 double min_z_bounds_, max_z_bounds_;
00107 double sac_distance_threshold_;
00108 double normal_distance_weight_;
00109
00110
00111 double object_min_height_, object_max_height_;
00112
00113
00114 double object_cluster_tolerance_, object_cluster_min_size_;
00115
00116
00117 pcl::PointCloud<Point>::ConstPtr cloud_;
00118
00119 pcl::PointCloud<Point>::ConstPtr cloud_filtered_, cloud_downsampled_;
00120
00121 pcl::PointCloud<pcl::Normal>::ConstPtr cloud_normals_;
00122
00123 pcl::PointIndices::ConstPtr table_inliers_;
00124
00125 pcl::ModelCoefficients::ConstPtr table_coefficients_;
00126
00127 pcl::PointCloud<Point>::ConstPtr table_projected_;
00128
00129 pcl::PointCloud<Point>::ConstPtr table_hull_;
00130
00131 pcl::PointCloud<Point>::ConstPtr cloud_objects_, cloud_objects_downsampled_;
00132
00134 TableObjectDetector () : nh_ ("~")
00135 {
00136 cloud_sub_ = nh_.subscribe ("input", 1, &TableObjectDetector::input_callback, this);
00137 cloud_pub_ = nh_.advertise<sensor_msgs::PointCloud2> ("cloud_out", 1);
00138
00139
00140
00141
00142 downsample_leaf_ = 0.01;
00143 downsample_leaf_objects_ = 0.003;
00144 nh_.getParam ("downsample_leaf", downsample_leaf_);
00145 grid_.setLeafSize (downsample_leaf_, downsample_leaf_, downsample_leaf_);
00146 grid_objects_.setLeafSize (downsample_leaf_objects_, downsample_leaf_objects_, downsample_leaf_objects_);
00147 grid_.setFilterFieldName ("z");
00148 pass_.setFilterFieldName ("z");
00149
00150 min_z_bounds_ = 0.4;
00151 max_z_bounds_ = 1.6;
00152 nh_.getParam ("min_z_bounds", min_z_bounds_);
00153 nh_.getParam ("max_z_bounds", max_z_bounds_);
00154 grid_.setFilterLimits (min_z_bounds_, max_z_bounds_);
00155 pass_.setFilterLimits (min_z_bounds_, max_z_bounds_);
00156 grid_.setDownsampleAllData (false);
00157 grid_objects_.setDownsampleAllData (false);
00158
00159 normals_tree_ = boost::make_shared<pcl::KdTreeFLANN<Point> > ();
00160 clusters_tree_ = boost::make_shared<pcl::KdTreeFLANN<Point> > ();
00161 clusters_tree_->setEpsilon (1);
00162
00163
00164
00165
00166 k_ = 10;
00167 nh_.getParam ("search_k_closest", k_);
00168 n3d_.setKSearch (k_);
00169
00170 n3d_.setSearchMethod (normals_tree_);
00171
00172
00173 sac_distance_threshold_ = 0.1;
00174 nh_.getParam ("sac_distance_threshold", sac_distance_threshold_);
00175 seg_.setDistanceThreshold (sac_distance_threshold_);
00176 seg_.setMaxIterations (10000);
00177
00178 normal_distance_weight_ = 0.1;
00179 nh_.getParam ("normal_distance_weight", normal_distance_weight_);
00180 seg_.setNormalDistanceWeight (normal_distance_weight_);
00181 seg_.setOptimizeCoefficients (true);
00182 seg_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00183 seg_.setMethodType (pcl::SAC_RANSAC);
00184 seg_.setProbability (0.99);
00185
00186 proj_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00187
00188
00189 object_min_height_ = 0.01;
00190 object_max_height_ = 0.5;
00191 nh_.getParam ("object_min_height", object_min_height_);
00192 nh_.getParam ("object_max_height", object_max_height_);
00193 prism_.setHeightLimits (object_min_height_, object_max_height_);
00194
00195
00196 object_cluster_tolerance_ = 0.05;
00197 object_cluster_min_size_ = 100;
00198 nh_.getParam ("object_cluster_tolerance", object_cluster_tolerance_);
00199 nh_.getParam ("object_cluster_min_size", object_cluster_min_size_);
00200 cluster_.setClusterTolerance (object_cluster_tolerance_);
00201 cluster_.setMinClusterSize (object_cluster_min_size_);
00202 cluster_.setSearchMethod (clusters_tree_);
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214 }
00215
00217
00218 void
00219 input_callback (const sensor_msgs::PointCloud2ConstPtr &cloud2_in)
00220 {
00221 ros::Time t1 = ros::Time::now ();
00222 ROS_INFO ("[TableObjectDetector::input_callback] PointCloud with %d data points (%s), stamp %f, and frame %s on topic %s received.", cloud2_in->width * cloud2_in->height, pcl::getFieldsList (*cloud2_in).c_str (), cloud2_in->header.stamp.toSec (), cloud2_in->header.frame_id.c_str (), nh_.resolveName ("input").c_str ());
00223
00224
00225 pcl::PointCloud<Point> cloud;
00226 pcl::fromROSMsg (*cloud2_in, cloud);
00227 cloud_ = boost::make_shared<const pcl::PointCloud<Point> > (cloud);
00228
00229
00230 pcl::PointCloud<Point> cloud_filtered;
00231 pass_.setInputCloud (cloud_);
00232 pass_.filter (cloud_filtered);
00233 cloud_filtered_.reset (new pcl::PointCloud<Point> (cloud_filtered));
00234 ROS_INFO ("[TableObjectDetector::input_callback] Number of points left after filtering (%f -> %f): %d out of %d.", min_z_bounds_, max_z_bounds_, (int)cloud_filtered.points.size (), (int)cloud.points.size ());
00235
00236 pcl::PointCloud<Point> cloud_downsampled;
00237 grid_.setInputCloud (cloud_filtered_);
00238
00239 grid_.filter (cloud_downsampled);
00240 cloud_downsampled_.reset (new pcl::PointCloud<Point> (cloud_downsampled));
00241
00242 if ((int)cloud_filtered_->points.size () < k_)
00243 {
00244 ROS_WARN ("[TableObjectDetector::input_callback] Filtering returned %d points! Continuing.", (int)cloud_filtered_->points.size ());
00245
00246
00247
00248 return;
00249 }
00250
00251
00252 pcl::PointCloud<pcl::Normal> cloud_normals;
00253
00254 n3d_.setInputCloud (cloud_downsampled_);
00255 n3d_.compute (cloud_normals);
00256 cloud_normals_.reset (new pcl::PointCloud<pcl::Normal> (cloud_normals));
00257 ROS_INFO ("[TableObjectDetector::input_callback] %d normals estimated.", (int)cloud_normals.points.size ());
00258
00259
00260
00261
00262 pcl::PointIndices table_inliers; pcl::ModelCoefficients table_coefficients;
00263 seg_.setInputCloud (cloud_downsampled_);
00264 seg_.setInputNormals (cloud_normals_);
00265 seg_.segment (table_inliers, table_coefficients);
00266 table_inliers_.reset (new pcl::PointIndices (table_inliers));
00267 table_coefficients_.reset (new pcl::ModelCoefficients (table_coefficients));
00268 if (table_coefficients.values.size () > 3)
00269 ROS_INFO ("[TableObjectDetector::input_callback] Model found with %d inliers: [%f %f %f %f].", (int)table_inliers.indices.size (),
00270 table_coefficients.values[0], table_coefficients.values[1], table_coefficients.values[2], table_coefficients.values[3]);
00271
00272 if (table_inliers_->indices.size () == 0)
00273 return;
00274
00275
00276 pcl::PointCloud<Point> table_projected;
00277
00278 proj_.setInputCloud (cloud_downsampled_);
00279 proj_.setIndices (table_inliers_);
00280 proj_.setModelCoefficients (table_coefficients_);
00281 proj_.filter (table_projected);
00282 table_projected_.reset (new pcl::PointCloud<Point> (table_projected));
00283 ROS_INFO ("[TableObjectDetector::input_callback] Number of projected inliers: %d.", (int)table_projected.points.size ());
00284
00285
00286 pcl::PointCloud<Point> table_hull;
00287 hull_.setInputCloud (table_projected_);
00288 hull_.reconstruct (table_hull);
00289 table_hull_.reset (new pcl::PointCloud<Point> (table_hull));
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 pcl::PointIndices cloud_object_indices;
00305
00306 prism_.setInputCloud (cloud_filtered_);
00307
00308 prism_.setInputPlanarHull (table_hull_);
00309 prism_.segment (cloud_object_indices);
00310 ROS_INFO ("[TableObjectDetector::input_callback] Number of object point indices: %d.", (int)cloud_object_indices.indices.size ());
00311
00312 pcl::PointCloud<Point> cloud_objects;
00313 pcl::ExtractIndices<Point> extract_object_indices;
00314
00315 extract_object_indices.setInputCloud (cloud_filtered_);
00316
00317 extract_object_indices.setIndices (boost::make_shared<const pcl::PointIndices> (cloud_object_indices));
00318 extract_object_indices.filter (cloud_objects);
00319 cloud_objects_.reset (new pcl::PointCloud<Point> (cloud_objects));
00320 ROS_INFO ("[TableObjectDetector::input_callback] Number of object point candidates: %d.", (int)cloud_objects.points.size ());
00321
00322 if (cloud_objects.points.size () == 0)
00323 return;
00324
00325
00326 pcl::PointCloud<Point> cloud_objects_downsampled;
00327 grid_objects_.setInputCloud (cloud_objects_);
00328 grid_objects_.filter (cloud_objects_downsampled);
00329 cloud_objects_downsampled_.reset (new pcl::PointCloud<Point> (cloud_objects_downsampled));
00330 ROS_INFO ("[TableObjectDetector::input_callback] Number of object point candidates left after downsampling: %d.", (int)cloud_objects_downsampled.points.size ());
00331
00332
00333 std::vector<pcl::PointIndices> clusters;
00334 cluster_.setInputCloud (cloud_objects_downsampled_);
00335 cluster_.extract (clusters);
00336 ROS_INFO ("[TableObjectDetector::input_callback] Number of clusters found matching the given constraints: %d.", (int)clusters.size ());
00337
00338 for (size_t i = 0; i < clusters.size (); ++i)
00339 {
00340 std::stringstream ss;
00341 ss << "cluster_" << i << ".pcd";
00342
00343 pcl::PointCloud<Point> cloud_object_cluster;
00344 pcl::copyPointCloud (cloud_objects_downsampled, clusters[i], cloud_object_cluster);
00345
00346 pcl::io::savePCDFile (ss.str (), cloud_object_cluster);
00347 }
00348
00349
00350 pcl::PointCloud<Point> cloud_objects_clusters;
00351 pcl::copyPointCloud (cloud_objects_downsampled, clusters, cloud_objects_clusters);
00352
00353
00354 sensor_msgs::PointCloud2 cloud_out;
00355 pcl::toROSMsg (cloud_objects_clusters, cloud_out);
00356 cloud_pub_.publish (cloud_out);
00357 ROS_WARN ("Spent %f seconds.", (ros::Time::now () - t1).toSec ());
00358 }
00359 };
00360
00361
00362 int
00363 main (int argc, char** argv)
00364 {
00365 ros::init (argc, argv, "table_object_detector");
00366
00367 TableObjectDetector p;
00368 ros::spin ();
00369
00370 return (0);
00371 }
00372
00373