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
00042
00043 #include <ros/ros.h>
00044
00045 #include <visualization_msgs/Marker.h>
00046 #include <sensor_msgs/PointCloud2.h>
00047
00048 #include <pcl/point_types.h>
00049 #include <pcl/point_cloud.h>
00050 #include <pcl/ros/conversions.h>
00051 #include "pcl/sample_consensus/method_types.h"
00052 #include "pcl/sample_consensus/model_types.h"
00053 #include <pcl/segmentation/sac_segmentation.h>
00054 #include <pcl/filters/voxel_grid.h>
00055 #include <pcl/filters/passthrough.h>
00056 #include <pcl/filters/project_inliers.h>
00057 #include <pcl/surface/convex_hull.h>
00058 #include "pcl/filters/extract_indices.h"
00059 #include "pcl/segmentation/extract_polygonal_prism_data.h"
00060 #include "pcl/common/common.h"
00061 #include <pcl/io/pcd_io.h>
00062 #include "pcl/segmentation/extract_clusters.h"
00063 #include <pcl/features/normal_3d.h>
00064 #include <pcl/common/angles.h>
00065
00066 #include <pcl_ros/publisher.h>
00067
00068 #include <tf/transform_broadcaster.h>
00069 #include <tf/transform_listener.h>
00070 #include <tf/message_filter.h>
00071
00072 #include "pcl/common/common.h"
00073
00074 #include "pcl_cloud_tools/GetClusters.h"
00075 typedef pcl::PointXYZ Point;
00076 typedef pcl::PointCloud<Point> PointCloud;
00077 typedef PointCloud::Ptr PointCloudPtr;
00078 typedef PointCloud::ConstPtr PointCloudConstPtr;
00079 typedef pcl::search::KdTree<Point>::Ptr KdTreePtr;
00080
00081 const tf::Vector3 wp_normal(1, 0, 0);
00082 const double wp_offset = -1.45;
00083
00084 class ExtractClustersServer
00085 {
00086 public:
00088 ExtractClustersServer (const ros::NodeHandle &nh) : nh_ (nh)
00089 {
00090 nh_.param("sac_distance", sac_distance_, 0.03);
00091 nh_.param("z_min_limit", z_min_limit_, 0.3);
00092 nh_.param("z_max_limit", z_max_limit_, 1.5);
00093 nh_.param("max_iter", max_iter_, 500);
00094 nh_.param("normal_distance_weight", normal_distance_weight_, 0.1);
00095 nh_.param("eps_angle", eps_angle_, 20.0);
00096 nh_.param("seg_prob", seg_prob_, 0.99);
00097 nh_.param("normal_search_radius", normal_search_radius_, 0.05);
00098
00099 nh_.param("rot_table_frame", rot_table_frame_, std::string("rotating_table"));
00100 nh_.param("object_cluster_tolerance", object_cluster_tolerance_, 0.03);
00101
00102 nh_.param("object_cluster_min_size", object_cluster_min_size_, 100);
00103 nh_.param("k", k_, 10);
00104 nh_.param("base_link_head_tilt_link_angle", base_link_head_tilt_link_angle_, 0.8);
00105 nh_.param("min_table_inliers", min_table_inliers_, 100);
00106 nh_.param("cluster_min_height", cluster_min_height_, 0.02);
00107 nh_.param("cluster_max_height", cluster_max_height_, 0.4);
00108 nh_.param("nr_cluster", nr_cluster_, 1);
00109 nh_.param("downsample", downsample_, true);
00110 nh_.param("voxel_size", voxel_size_, 0.01);
00111 nh_.param("save_to_files", save_to_files_, false);
00112 nh_.param("point_cloud_topic", point_cloud_topic, std::string("/narrow_stereo_textured/points2"));
00113 nh_.param("publish_token", publish_token_, false);
00114 nh_.param("padding", padding_, 0.85);
00115 nh_.param("target_frame", target_frame_, std::string("base_link"));
00116
00117 service_ = nh_.advertiseService("cluster_tracking", &ExtractClustersServer::clustersCallback, this);
00118
00119 cloud_pub_.advertise (nh_, "table_inliers", 1);
00120
00121 cloud_objects_pub_.advertise (nh_, "cloud_objects", 10);
00122
00123 vgrid_.setFilterFieldName ("z");
00124 vgrid_.setFilterLimits (z_min_limit_, z_max_limit_);
00125 if (downsample_)
00126 vgrid_.setLeafSize (voxel_size_, voxel_size_, voxel_size_);
00127
00128 seg_.setDistanceThreshold (sac_distance_);
00129 seg_.setMaxIterations (max_iter_);
00130 seg_.setNormalDistanceWeight (normal_distance_weight_);
00131 seg_.setOptimizeCoefficients (true);
00132 seg_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00133 seg_.setEpsAngle(pcl::deg2rad(eps_angle_));
00134 seg_.setMethodType (pcl::SAC_RANSAC);
00135 seg_.setProbability (seg_prob_);
00136
00137 proj_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00138 clusters_tree_ = boost::make_shared<pcl::search::KdTree<Point> > ();
00139 clusters_tree_->setEpsilon (1);
00140 normals_tree_ = boost::make_shared<pcl::search::KdTree<Point> > ();
00141
00142 n3d_.setKSearch (k_);
00143 n3d_.setSearchMethod (normals_tree_);
00144
00145
00146 }
00147
00149 virtual ~ExtractClustersServer ()
00150 {
00151 for (size_t i = 0; i < table_coeffs_.size (); ++i)
00152 delete table_coeffs_[i];
00153 }
00154
00155 private:
00157 bool
00158 clustersCallback (pcl_cloud_tools::GetClusters::Request & req,
00159 pcl_cloud_tools::GetClusters::Response & res)
00160 {
00161
00162 sensor_msgs::PointCloud2 cloud_in;
00163 cloud_in = req.input_cloud;
00164
00165
00166 if (cloud_in.width == 0)
00167 {
00168 res.result = false;
00169 return false;
00170 }
00171
00172
00173 bool found_transform = tf_listener_.waitForTransform(cloud_in.header.frame_id.c_str(), target_frame_,
00174 cloud_in.header.stamp, ros::Duration(1.0));
00175 tf::StampedTransform transform;
00176 if (found_transform)
00177 {
00178 tf_listener_.lookupTransform(cloud_in.header.frame_id.c_str(), target_frame_, cloud_in.header.stamp, transform);
00179 double yaw, pitch, roll;
00180 transform.getBasis().getEulerZYX(yaw, pitch, roll);
00181 ROS_INFO("[ExtractCluster:] Transform X: %f Y: %f Z: %f R: %f P: %f Y: %f", transform.getOrigin().getX(),
00182 transform.getOrigin().getY(), transform.getOrigin().getZ(), roll, pitch, yaw);
00183 base_link_head_tilt_link_angle_ = pitch;
00184 }
00185 else
00186 {
00187 ROS_WARN("[ExtractCluster:] No transform found between %s and %s", cloud_in.header.frame_id.c_str(), target_frame_.c_str());
00188 ROS_WARN("[ExtractCluster:] Taking the following base_link_head_tilt_link_angle: %f", base_link_head_tilt_link_angle_);
00189 }
00190
00191 ROS_INFO_STREAM ("[" << getName ().c_str () << "] Received cloud: in frame " << cloud_in.header.frame_id);
00192
00193
00194 PointCloud cloud_raw, cloud;
00195 pcl::fromROSMsg (cloud_in, cloud_raw);
00196 vgrid_.setInputCloud (boost::make_shared<PointCloud> (cloud_raw));
00197 vgrid_.filter (cloud);
00198
00199
00200
00201
00202 pcl::ModelCoefficients table_coeff;
00203 pcl::PointIndices table_inliers;
00204 PointCloud cloud_projected;
00205 pcl::PointCloud<Point> cloud_hull;
00206
00207 pcl::PointCloud<pcl::Normal> cloud_normals;
00208 n3d_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00209 n3d_.compute (cloud_normals);
00210
00211
00212 cloud_normals_.reset (new pcl::PointCloud<pcl::Normal> (cloud_normals));
00213
00214 seg_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00215 seg_.setInputNormals (cloud_normals_);
00216
00217 btVector3 axis(0.0, 0.0, 1.0);
00218
00219
00220 btVector3 axis2 = axis.rotate(btVector3(1.0, 0.0, 0.0), btScalar(base_link_head_tilt_link_angle_ + pcl::deg2rad(90.0)));
00221
00222 seg_.setAxis (Eigen::Vector3f(fabs(axis2.getX()), fabs(axis2.getY()), fabs(axis2.getZ())));
00223
00224 seg_.segment (table_inliers, table_coeff);
00225 ROS_INFO ("[%s] Table model: [%f, %f, %f, %f] with %d inliers.", getName ().c_str (),
00226 table_coeff.values[0], table_coeff.values[1], table_coeff.values[2], table_coeff.values[3], (int)table_inliers.indices.size ());
00227 if ((int)table_inliers.indices.size () <= min_table_inliers_)
00228 {
00229 ROS_ERROR ("table has to few inliers");
00230 res.result = false;
00231 return false;
00232 }
00233
00234 proj_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00235 proj_.setIndices (boost::make_shared<pcl::PointIndices> (table_inliers));
00236 proj_.setModelCoefficients (boost::make_shared<pcl::ModelCoefficients> (table_coeff));
00237 proj_.filter (cloud_projected);
00238
00239
00240
00241 chull_.setInputCloud (boost::make_shared<PointCloud> (cloud_projected));
00242 chull_.reconstruct (cloud_hull);
00243 ROS_INFO ("Convex hull has: %d data points.", (int)cloud_hull.points.size ());
00244 cloud_pub_.publish (cloud_hull);
00245
00246
00247
00248
00249
00250
00251
00252
00253 pcl::PointIndices cloud_object_indices;
00254 prism_.setHeightLimits (cluster_min_height_, cluster_max_height_);
00255 prism_.setInputCloud (boost::make_shared<PointCloud> (cloud_raw));
00256 prism_.setInputPlanarHull (boost::make_shared<PointCloud>(cloud_hull));
00257 prism_.segment (cloud_object_indices);
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 std::vector<pcl::PointIndices> clusters;
00270 cluster_.setInputCloud (boost::make_shared<PointCloud>(cloud_raw));
00271 cluster_.setIndices (boost::make_shared<const pcl::PointIndices> (cloud_object_indices));
00272 cluster_.setClusterTolerance (object_cluster_tolerance_);
00273 cluster_.setMinClusterSize (object_cluster_min_size_);
00274 cluster_.setSearchMethod (clusters_tree_);
00275 cluster_.extract (clusters);
00276
00277 res.clusters_indices.clear();
00278
00279 if (clusters.size() > 0)
00280 {
00281 res.clusters_indices = clusters;
00282 res.result = true;
00283 }
00284 else
00285 {
00286 res.result = false;
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 return true;
00309 }
00310
00311 ros::NodeHandle nh_;
00312 tf::TransformBroadcaster transform_broadcaster_;
00313 tf::TransformListener tf_listener_;
00314 bool save_to_files_, downsample_, publish_token_, got_cluster_, action_called_;
00315
00316 double normal_search_radius_;
00317 double voxel_size_;
00318 double padding_;
00319
00320 std::string rot_table_frame_, object_name_, point_cloud_topic, target_frame_;
00321 double object_cluster_tolerance_, cluster_min_height_, cluster_max_height_;
00322 int object_cluster_min_size_, object_cluster_max_size_;
00323
00324 pcl::PCDWriter pcd_writer_;
00325 double sac_distance_, normal_distance_weight_, z_min_limit_, z_max_limit_;
00326 double eps_angle_, seg_prob_, base_link_head_tilt_link_angle_;
00327 int k_, max_iter_, min_table_inliers_, nr_cluster_;
00328
00329 ros::Subscriber point_cloud_sub_;
00330
00331 std::vector<Eigen::Vector4d *> table_coeffs_;
00332
00333 pcl_ros::Publisher<Point> cloud_pub_;
00334
00335 pcl_ros::Publisher<Point> cloud_objects_pub_;
00336 pcl_ros::Publisher<Point> token_pub_;
00337
00338
00339
00340 pcl::VoxelGrid<Point> vgrid_;
00341 pcl::NormalEstimation<Point, pcl::Normal> n3d_;
00342
00343 pcl::PointCloud<pcl::Normal>::Ptr cloud_normals_;
00344 pcl::SACSegmentationFromNormals<Point, pcl::Normal> seg_;
00345 pcl::ProjectInliers<Point> proj_;
00346 pcl::ExtractIndices<Point> extract_;
00347 pcl::ConvexHull<Point> chull_;
00348 pcl::ExtractPolygonalPrismData<Point> prism_;
00349 pcl::PointCloud<Point> cloud_objects_;
00350 pcl::EuclideanClusterExtraction<Point> cluster_;
00351 KdTreePtr clusters_tree_, normals_tree_;
00352
00354
00355 std::string getName () const { return ("ExtractClustersServer"); }
00356
00357 ros::ServiceServer service_;
00358 };
00359
00360
00361 int
00362 main (int argc, char** argv)
00363 {
00364 ros::init (argc, argv, "extract_clusters_server");
00365 ros::NodeHandle nh("~");
00366 ExtractClustersServer clusters (nh);
00367 ros::spin ();
00368 }
00369