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
00040
00041 #include <ros/ros.h>
00042
00043 #include <sensor_msgs/PointCloud2.h>
00044
00045 #include <pcl/point_types.h>
00046 #include <pcl/point_cloud.h>
00047 #include <pcl/ros/conversions.h>
00048 #include "pcl/sample_consensus/method_types.h"
00049 #include "pcl/sample_consensus/model_types.h"
00050 #include <pcl/segmentation/sac_segmentation.h>
00051 #include <pcl/filters/voxel_grid.h>
00052 #include <pcl/filters/passthrough.h>
00053 #include <pcl/filters/project_inliers.h>
00054 #include <pcl/surface/convex_hull.h>
00055 #include "pcl/filters/extract_indices.h"
00056 #include "pcl/segmentation/extract_polygonal_prism_data.h"
00057 #include "pcl/common/common.h"
00058 #include <pcl/io/pcd_io.h>
00059 #include "pcl/segmentation/extract_clusters.h"
00060 #include <pcl/features/normal_3d.h>
00061 #include <pcl/common/angles.h>
00062
00063 #include <pcl_ros/publisher.h>
00064 #include <pcl_ros/transforms.h>
00065
00066 #include <tf/transform_broadcaster.h>
00067 #include <tf/transform_listener.h>
00068 #include <tf/message_filter.h>
00069
00070 #include "kinect_cleanup/FilterObject.h"
00071 #include "kinect_cleanup/GrabObject.h"
00072 #include "kinect_cleanup/GetReleasedObject.h"
00073
00074
00075
00076 typedef pcl::PointXYZRGB Point;
00077 typedef pcl::PointCloud<Point> PointCloud;
00078 typedef PointCloud::Ptr PointCloudPtr;
00079 typedef PointCloud::ConstPtr PointCloudConstPtr;
00080 typedef pcl::KdTree<Point>::Ptr KdTreePtr;
00081
00083 class ObjectGrabber
00084 {
00085 public:
00087 ObjectGrabber (const ros::NodeHandle &nh) : nh_ (nh), to_select_ (false)
00088 {
00089 nh_.param("sac_distance", sac_distance_, 0.03);
00090 nh_.param("z_min_limit", z_min_limit_, 0.8);
00091 nh_.param("z_max_limit", z_max_limit_, 1.9);
00092 nh_.param("max_iter", max_iter_, 500);
00093 nh_.param("normal_distance_weight", normal_distance_weight_, 0.1);
00094 nh_.param("eps_angle", eps_angle_, 15.0);
00095 nh_.param("seg_prob", seg_prob_, 0.99);
00096 nh_.param("normal_search_radius", normal_search_radius_, 0.05);
00097 nh_.param("object_cluster_tolerance", object_cluster_tolerance_, 0.03);
00098 nh_.param("object_cluster_min_size", object_cluster_min_size_, 100);
00099 nh_.param("object_cluster_max_size", object_cluster_max_size_, 10000);
00100 nh_.param("k", k_, 10);
00101 nh_.param("base_link_head_tilt_link_angle", base_link_head_tilt_link_angle_, 0.277);
00102 nh_.param("min_table_inliers", min_table_inliers_, 100);
00103 nh_.param("cluster_min_height", cluster_min_height_, 0.01);
00104 nh_.param("cluster_max_height", cluster_max_height_, 0.4);
00105
00106 hull_pub_.advertise (nh_, "/hull", 10);
00107 object_pub_ = nh_.advertise<sensor_msgs::PointCloud2> ("/moved_object", 10);
00108 object_service_ = nh_.advertiseService("/get_released_object", &ObjectGrabber::getObject, this);
00109 ROS_INFO ("[%s] Advertising service on: %s", getName ().c_str (), object_service_.getService ().c_str ());
00110 grab_service_ = nh_.advertiseService("/grab_object", &ObjectGrabber::grabDirection, this);
00111 ROS_INFO ("[%s] Advertising service on: %s", getName ().c_str (), object_service_.getService ().c_str ());
00112 point_cloud_sub_ = nh_.subscribe("input", 1, &ObjectGrabber::inputCallback, this);
00113 ROS_INFO ("[%s] Subscribed to topic: %s", getName ().c_str (), point_cloud_sub_.getTopic ().c_str ());
00114
00115 object_released_ = false;
00116 vgrid_.setFilterFieldName ("z");
00117 vgrid_.setFilterLimits (z_min_limit_, z_max_limit_);
00118
00119 seg_.setDistanceThreshold (sac_distance_);
00120 seg_.setMaxIterations (max_iter_);
00121 seg_.setNormalDistanceWeight (normal_distance_weight_);
00122 seg_.setOptimizeCoefficients (true);
00123 seg_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00124 seg_.setEpsAngle(pcl::deg2rad(eps_angle_));
00125 seg_.setMethodType (pcl::SAC_RANSAC);
00126 seg_.setProbability (seg_prob_);
00127
00128 proj_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00129 clusters_tree_ = boost::make_shared<pcl::KdTreeFLANN<Point> > ();
00130 clusters_tree_->setEpsilon (1);
00131 normals_tree_ = boost::make_shared<pcl::KdTreeFLANN<Point> > ();
00132
00133 n3d_.setKSearch (k_);
00134 n3d_.setSearchMethod (normals_tree_);
00135 }
00136
00138 virtual ~ObjectGrabber ()
00139 {
00140 for (size_t i = 0; i < table_coeffs_.size (); ++i)
00141 delete table_coeffs_[i];
00142 }
00143
00144 private:
00145
00147 void
00148 inputCallback (const sensor_msgs::PointCloud2ConstPtr &cloud_in)
00149 {
00150 if (output_cloud_.width != 0)
00151 {
00152 if (!object_released_)
00153 {
00154 output_cloud_.header.stamp = ros::Time::now();
00155 object_pub_.publish (output_cloud_);
00156 float *xzy = (float*)&output_cloud_.data[0];
00157 ROS_INFO("Republished object in frame %s (%g,%g,%g)", output_cloud_.header.frame_id.c_str(), xzy[0], xzy[1], xzy[2]);
00158 }
00159 return;
00160 }
00161
00162
00163 if (!to_select_)
00164 return;
00165
00166
00167 ros::Time time = ros::Time::now();
00168 bool found_transform = tf_listener_.waitForTransform("right_hand", "openni_rgb_optical_frame", time, ros::Duration(1));
00169 if (!found_transform)
00170 {
00171 std::cerr << "no transform found" << std::endl;
00172 return;
00173 }
00174 tf::StampedTransform c2h_transform;
00175 tf_listener_.lookupTransform("right_hand", "openni_rgb_optical_frame", time, c2h_transform);
00176 to_select_ = false;
00177
00178 ROS_INFO_STREAM ("[" << getName ().c_str () << "] Received cloud: cloud time " << cloud_in->header.stamp);
00179
00180
00181 PointCloud cloud_raw, cloud;
00182 pcl::fromROSMsg (*cloud_in, cloud_raw);
00183 vgrid_.setInputCloud (boost::make_shared<PointCloud> (cloud_raw));
00184 vgrid_.filter (cloud);
00185
00186
00187 pcl::ModelCoefficients table_coeff;
00188 pcl::PointIndices table_inliers;
00189 PointCloud cloud_projected;
00190 pcl::PointCloud<Point> cloud_hull;
00191
00192
00193 pcl::PointCloud<pcl::Normal> cloud_normals;
00194 n3d_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00195 n3d_.compute (cloud_normals);
00196 cloud_normals_.reset (new pcl::PointCloud<pcl::Normal> (cloud_normals));
00197
00198
00199
00200
00201 btVector3 axis(0.0, 0.0, 1.0);
00202
00203
00204 btVector3 axis2 = axis.rotate(btVector3(1.0, 0.0, 0.0), btScalar(base_link_head_tilt_link_angle_ + pcl::deg2rad(90.0)));
00205
00206
00207 seg_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00208 seg_.setInputNormals (cloud_normals_);
00209 seg_.setAxis (Eigen::Vector3f(fabs(axis2.getX()), fabs(axis2.getY()), fabs(axis2.getZ())));
00210
00211 seg_.segment (table_inliers, table_coeff);
00212 ROS_INFO ("[%s] Table model: [%f, %f, %f, %f] with %d inliers.", getName ().c_str (),
00213 table_coeff.values[0], table_coeff.values[1], table_coeff.values[2], table_coeff.values[3], (int)table_inliers.indices.size ());
00214 if ((int)table_inliers.indices.size () <= min_table_inliers_)
00215 {
00216 ROS_ERROR ("table has to few inliers");
00217 return;
00218 }
00219
00220
00221 proj_.setInputCloud (boost::make_shared<PointCloud> (cloud));
00222 proj_.setIndices (boost::make_shared<pcl::PointIndices> (table_inliers));
00223 proj_.setModelCoefficients (boost::make_shared<pcl::ModelCoefficients> (table_coeff));
00224 proj_.filter (cloud_projected);
00225
00226
00227
00228 chull_.setInputCloud (boost::make_shared<PointCloud> (cloud_projected));
00229 chull_.reconstruct (cloud_hull);
00230 ROS_INFO ("Convex hull has: %d data points.", (int)cloud_hull.points.size ());
00231 hull_pub_.publish (cloud_hull);
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270 pcl::PointIndices cloud_object_indices;
00271 prism_.setHeightLimits (cluster_min_height_, cluster_max_height_);
00272 prism_.setInputCloud (boost::make_shared<PointCloud> (cloud_raw));
00273 prism_.setInputPlanarHull (boost::make_shared<PointCloud>(cloud_hull));
00274 prism_.segment (cloud_object_indices);
00275
00276
00277 pcl::PointCloud<Point> cloud_object;
00278 pcl::ExtractIndices<Point> extract_object_indices;
00279
00280 extract_object_indices.setInputCloud (boost::make_shared<PointCloud> (cloud_raw));
00281 extract_object_indices.setIndices (boost::make_shared<const pcl::PointIndices> (cloud_object_indices));
00282 extract_object_indices.filter (cloud_object);
00283
00284
00285 std::vector<pcl::PointIndices> clusters;
00286 cluster_.setInputCloud (boost::make_shared<PointCloud>(cloud_object));
00287 cluster_.setClusterTolerance (object_cluster_tolerance_);
00288 cluster_.setMinClusterSize (object_cluster_min_size_);
00289 cluster_.setMaxClusterSize (object_cluster_max_size_);
00290 cluster_.setSearchMethod (clusters_tree_);
00291 cluster_.extract (clusters);
00292
00293
00294 for (size_t i = 0; i < clusters.size (); i++)
00295 {
00296
00297 int cluster_center = clusters[i].indices[clusters[i].indices.size () / 2];
00298 Eigen::Vector4f pt = Eigen::Vector4f (cloud_object.points[cluster_center].x, cloud_object.points[cluster_center].y, cloud_object.points[cluster_center].z, 0);
00299 Eigen::Vector4f c = line_dir_.cross3 (line_point_ - pt); c[3] = 0;
00300 #ifndef TEST
00301 if (c.squaredNorm () / line_dir_squaredNorm_ > 0.25*0.25)
00302 continue;
00303 #endif
00304 std::cerr << "found cluster" << std::endl;
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318 pcl::PointCloud<Point> cloud_object_clustered ;
00319 pcl::copyPointCloud (cloud_object, clusters[i], cloud_object_clustered);
00320
00321
00322
00323
00324 sensor_msgs::PointCloud2 cluster;
00325 pcl::toROSMsg (cloud_object_clustered, cluster);
00326
00327
00328 pcl_ros::transformPointCloud("right_hand", (tf::Transform)c2h_transform, cluster, output_cloud_);
00329 output_cloud_.header.frame_id = "right_hand";
00330
00331
00332
00333
00334
00335
00336
00337 object_pub_.publish (output_cloud_);
00338 ROS_INFO("Published object with %d points", (int)clusters[i].indices.size ());
00339
00340
00341 unsigned center_idx = cloud_object_indices.indices.at (cluster_center);
00342 unsigned row = center_idx / 640;
00343 unsigned col = center_idx - row * 640;
00344 ros::ServiceClient client = nh_.serviceClient<kinect_cleanup::FilterObject>("/filter_object");
00345 kinect_cleanup::FilterObject srv;
00346 srv.request.min_row = std::max (0, (int)row-50);
00347 srv.request.min_col = std::max (0, (int)col-40);
00348 srv.request.max_row = std::min (479, (int)row+50);
00349 srv.request.max_col = std::min (479, (int)col+40);
00350 srv.request.rgb = cloud_raw.points[640 * srv.request.max_row + srv.request.max_col].rgb;
00351 for (int i=0; i<4; i++)
00352 srv.request.plane_normal[i] = table_coeff.values[i];
00353 if (client.call(srv))
00354 ROS_INFO("Object filter service responded: %s", srv.response.error.c_str ());
00355 else
00356 ROS_ERROR("Failed to call object filter service!");
00357
00358
00359 break;
00360 }
00361 }
00362
00364 bool grabDirection (kinect_cleanup::GrabObject::Request &req,
00365 kinect_cleanup::GrabObject::Response &res)
00366 {
00367 if (to_select_)
00368 {
00369 res.error = "already grabbed";
00370 return true;
00371 }
00372 to_select_ = true;
00373 line_point_ = Eigen::Vector4f (req.point_line[0], req.point_line[1], req.point_line[2], 0);
00374 line_dir_ = Eigen::Vector4f (req.point_line[3], req.point_line[4], req.point_line[5], 0);
00375 line_dir_squaredNorm_ = line_dir_.squaredNorm ();
00376 res.error = "grabbing enabled";
00377 std::cerr << "point: " << line_point_.transpose() << " direction: " << line_dir_.transpose() << std::endl;
00378 return true;
00379 }
00380
00382 bool getObject (kinect_cleanup::GetReleasedObject::Request &req,
00383 kinect_cleanup::GetReleasedObject::Response &res)
00384 {
00385
00386 object_released_ = req.object_released;
00387 res.object = output_cloud_;
00388 return true;
00389 }
00390
00392
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 ros::NodeHandle nh_;
00428 tf::TransformBroadcaster transform_broadcaster_;
00429 tf::TransformListener tf_listener_;
00430 ros::Subscriber point_cloud_sub_;
00431 pcl_ros::Publisher<Point> hull_pub_;
00432 ros::Publisher object_pub_;
00433 ros::ServiceServer object_service_;
00434 ros::ServiceServer grab_service_;
00435
00436
00437 bool to_select_, object_released_;
00438 Eigen::Vector4f line_point_;
00439 Eigen::Vector4f line_dir_;
00440 float line_dir_squaredNorm_;
00441
00442
00443 double object_cluster_tolerance_, cluster_min_height_, cluster_max_height_;
00444 int object_cluster_min_size_, object_cluster_max_size_;
00445 double sac_distance_, normal_distance_weight_, z_min_limit_, z_max_limit_;
00446 double eps_angle_, seg_prob_, base_link_head_tilt_link_angle_;
00447 int k_, max_iter_, min_table_inliers_;
00448 double normal_search_radius_;
00449
00450
00451 pcl::PassThrough<Point> vgrid_;
00452 pcl::NormalEstimation<Point, pcl::Normal> n3d_;
00453 pcl::SACSegmentationFromNormals<Point, pcl::Normal> seg_;
00454 pcl::ProjectInliers<Point> proj_;
00455 pcl::ExtractIndices<Point> extract_;
00456 pcl::ConvexHull<Point> chull_;
00457 pcl::ExtractPolygonalPrismData<Point> prism_;
00458 pcl::PointCloud<Point> cloud_objects_;
00459 pcl::EuclideanClusterExtraction<Point> cluster_;
00460 KdTreePtr clusters_tree_, normals_tree_;
00461
00462
00463
00464 sensor_msgs::PointCloud2 output_cloud_;
00465
00466
00467 std::vector<Eigen::Vector4d *> table_coeffs_;
00468 pcl::PointCloud<pcl::Normal>::ConstPtr cloud_normals_;
00469
00471
00472 std::string getName () const { return ("ObjectGrabber"); }
00473 };
00474
00475
00476 int
00477 main (int argc, char** argv)
00478 {
00479 ros::init (argc, argv, "object_grabber");
00480 ros::NodeHandle nh("~");
00481 ObjectGrabber ptu_calibrator (nh);
00482 ros::spin ();
00483
00484 return 0;
00485 }
00486