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 #include <string>
00038
00039 #include <ros/ros.h>
00040
00041 #include <sensor_msgs/PointCloud.h>
00042 #include <sensor_msgs/PointCloud2.h>
00043 #include <sensor_msgs/point_cloud_conversion.h>
00044 #include <visualization_msgs/Marker.h>
00045
00046 #include <tf/transform_listener.h>
00047 #include <tf/transform_broadcaster.h>
00048 #include <tf/transform_datatypes.h>
00049
00050 #include <pcl/point_cloud.h>
00051 #include <pcl/point_types.h>
00052 #include <pcl/io/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/sample_consensus/method_types.h>
00058 #include <pcl/sample_consensus/model_types.h>
00059 #include <pcl/segmentation/sac_segmentation.h>
00060 #include <pcl/filters/project_inliers.h>
00061 #include <pcl/surface/convex_hull.h>
00062 #include <pcl/search/kdtree.h>
00063 #include <pcl/segmentation/extract_polygonal_prism_data.h>
00064 #include <pcl/segmentation/extract_clusters.h>
00065 #include <pcl_ros/transforms.h>
00066
00067 #include "tabletop_object_detector/marker_generator.h"
00068 #include "tabletop_object_detector/TabletopSegmentation.h"
00069
00070 namespace tabletop_object_detector {
00071
00072 class TabletopSegmentor
00073 {
00074 typedef pcl::PointXYZRGB Point;
00075 typedef pcl::search::KdTree<Point>::Ptr KdTreePtr;
00076
00077 private:
00079 ros::NodeHandle nh_;
00081 ros::NodeHandle priv_nh_;
00083 ros::Publisher marker_pub_;
00085 ros::ServiceServer segmentation_srv_;
00086
00088 int num_markers_published_;
00090 int current_marker_id_;
00091
00093 int inlier_threshold_;
00095 double plane_detection_voxel_size_;
00097 double clustering_voxel_size_;
00099 double z_filter_min_, z_filter_max_;
00100 double y_filter_min_, y_filter_max_;
00101 double x_filter_min_, x_filter_max_;
00103 double table_z_filter_min_, table_z_filter_max_;
00105 double cluster_distance_;
00107 int min_cluster_size_;
00110 std::string processing_frame_;
00112 double up_direction_;
00113 bool flatten_table_;
00115 double table_padding_;
00116
00118 tf::TransformListener listener_;
00119
00120
00121
00123 bool serviceCallback(TabletopSegmentation::Request &request, TabletopSegmentation::Response &response);
00124
00125
00126
00128 template <class PointCloudType>
00129 Table getTable(std_msgs::Header cloud_header, const tf::Transform &table_plane_trans,
00130 const PointCloudType &table_points);
00131
00133 template <class PointCloudType>
00134 void addConvexHullTable(Table &table, const PointCloudType &convex_hull, bool flatten_table);
00135
00137 template <class PointCloudType>
00138 void publishClusterMarkers(const std::vector<PointCloudType> &clusters, std_msgs::Header cloud_header);
00139
00140
00141
00143 void processCloud(const sensor_msgs::PointCloud2 &cloud,
00144 TabletopSegmentation::Response &response,
00145 Table table);
00146
00148 void clearOldMarkers(std::string frame_id);
00149
00151 template <class PointCloudType>
00152 bool tableMsgToPointCloud (Table &table, std::string frame_id, PointCloudType &table_hull);
00153
00154 public:
00156
00157 TabletopSegmentor(ros::NodeHandle nh) : nh_(nh), priv_nh_("~")
00158 {
00159 num_markers_published_ = 1;
00160 current_marker_id_ = 1;
00161
00162 marker_pub_ = nh_.advertise<visualization_msgs::Marker>(nh_.resolveName("markers_out"), 10);
00163
00164 segmentation_srv_ = nh_.advertiseService(nh_.resolveName("segmentation_srv"),
00165 &TabletopSegmentor::serviceCallback, this);
00166
00167
00168 priv_nh_.param<int>("inlier_threshold", inlier_threshold_, 300);
00169 priv_nh_.param<double>("plane_detection_voxel_size", plane_detection_voxel_size_, 0.01);
00170 priv_nh_.param<double>("clustering_voxel_size", clustering_voxel_size_, 0.003);
00171 priv_nh_.param<double>("z_filter_min", z_filter_min_, 0.4);
00172 priv_nh_.param<double>("z_filter_max", z_filter_max_, 1.25);
00173 priv_nh_.param<double>("y_filter_min", y_filter_min_, -1.0);
00174 priv_nh_.param<double>("y_filter_max", y_filter_max_, 1.0);
00175 priv_nh_.param<double>("x_filter_min", x_filter_min_, -1.0);
00176 priv_nh_.param<double>("x_filter_max", x_filter_max_, 1.0);
00177 priv_nh_.param<double>("table_z_filter_min", table_z_filter_min_, 0.01);
00178 priv_nh_.param<double>("table_z_filter_max", table_z_filter_max_, 0.50);
00179 priv_nh_.param<double>("cluster_distance", cluster_distance_, 0.03);
00180 priv_nh_.param<int>("min_cluster_size", min_cluster_size_, 300);
00181 priv_nh_.param<std::string>("processing_frame", processing_frame_, "");
00182 priv_nh_.param<double>("up_direction", up_direction_, -1.0);
00183 priv_nh_.param<bool>("flatten_table", flatten_table_, false);
00184 priv_nh_.param<double>("table_padding", table_padding_, 0.0);
00185 if(flatten_table_) ROS_DEBUG("flatten_table is true");
00186 else ROS_DEBUG("flatten_table is false");
00187
00188 }
00189
00191 ~TabletopSegmentor() {}
00192 };
00193
00196 bool TabletopSegmentor::serviceCallback(TabletopSegmentation::Request &request,
00197 TabletopSegmentation::Response &response)
00198 {
00199 ros::Time start_time = ros::Time::now();
00200 std::string topic = nh_.resolveName("cloud_in");
00201 ROS_INFO("Tabletop detection service called; waiting for a point_cloud2 on topic %s", topic.c_str());
00202
00203 sensor_msgs::PointCloud2::ConstPtr recent_cloud =
00204 ros::topic::waitForMessage<sensor_msgs::PointCloud2>(topic, nh_, ros::Duration(3.0));
00205
00206 if (!recent_cloud)
00207 {
00208 ROS_ERROR("Tabletop object detector: no point_cloud2 has been received");
00209 response.result = response.NO_CLOUD_RECEIVED;
00210 return true;
00211 }
00212
00213 pcl::PointCloud<Point>::Ptr table_hull (new pcl::PointCloud<Point>);
00214 ROS_INFO_STREAM("Point cloud received after " << ros::Time::now() - start_time << " seconds; processing");
00215 if (!processing_frame_.empty())
00216 {
00217
00218 sensor_msgs::PointCloud old_cloud;
00219 sensor_msgs::convertPointCloud2ToPointCloud (*recent_cloud, old_cloud);
00220 int current_try=0, max_tries = 3;
00221 while (1)
00222 {
00223 bool transform_success = true;
00224 try
00225 {
00226 listener_.transformPointCloud(processing_frame_, old_cloud, old_cloud);
00227 }
00228 catch (tf::TransformException ex)
00229 {
00230 transform_success = false;
00231 if (++current_try >= max_tries)
00232 {
00233 ROS_ERROR("Failed to transform cloud from frame %s into frame %s in %d attempt(s)", old_cloud.header.frame_id.c_str(),
00234 processing_frame_.c_str(), current_try);
00235 response.result = response.OTHER_ERROR;
00236 return true;
00237 }
00238 ROS_DEBUG("Failed to transform point cloud, attempt %d out of %d, exception: %s", current_try, max_tries, ex.what());
00239
00240 ros::Duration(0.1).sleep();
00241 }
00242 if (transform_success) break;
00243 }
00244 sensor_msgs::PointCloud2 converted_cloud;
00245 sensor_msgs::convertPointCloudToPointCloud2 (old_cloud, converted_cloud);
00246 ROS_INFO_STREAM("Input cloud converted to " << processing_frame_ << " frame after " <<
00247 ros::Time::now() - start_time << " seconds");
00248 processCloud(converted_cloud, response, request.table);
00249 clearOldMarkers(converted_cloud.header.frame_id);
00250 }
00251 else
00252 {
00253 processCloud(*recent_cloud, response, request.table);
00254 clearOldMarkers(recent_cloud->header.frame_id);
00255 }
00256
00257
00258 response.table.pose.header.stamp = recent_cloud->header.stamp;
00259 for(size_t i; i<response.clusters.size(); i++)
00260 {
00261 response.clusters[i].header.stamp = recent_cloud->header.stamp;
00262 }
00263
00264 ROS_INFO_STREAM("In total, segmentation took " << ros::Time::now() - start_time << " seconds");
00265 return true;
00266 }
00267
00268 template <class PointCloudType>
00269 void TabletopSegmentor::addConvexHullTable(Table &table,
00270 const PointCloudType &convex_hull,
00271 bool flatten_table)
00272 {
00273 if (convex_hull.points.empty())
00274 {
00275 ROS_ERROR("Trying to add convex hull, but it contains no points");
00276 return;
00277 }
00278
00279 geometry_msgs::Point centroid;
00280 centroid.x = centroid.y = centroid.z = 0.0;
00281 for (size_t i=0; i<convex_hull.points.size(); i++)
00282 {
00283 centroid.x += convex_hull.points[i].x;
00284 centroid.y += convex_hull.points[i].y;
00285 centroid.z += convex_hull.points[i].z;
00286 }
00287 centroid.x /= convex_hull.points.size();
00288 centroid.y /= convex_hull.points.size();
00289 centroid.z /= convex_hull.points.size();
00290
00291
00292 table.convex_hull.type = table.convex_hull.MESH;
00293 for (size_t i=0; i<convex_hull.points.size(); i++)
00294 {
00295 geometry_msgs::Point vertex;
00296 vertex.x = convex_hull.points[i].x;
00297 vertex.y = convex_hull.points[i].y;
00298 if (table_padding_ > 0.0)
00299 {
00300 double dx = vertex.x - centroid.x;
00301 double dy = vertex.y - centroid.y;
00302 double l = sqrt(dx*dx + dy*dy);
00303 dx /= l; dy /= l;
00304 vertex.x += table_padding_ * dx;
00305 vertex.y += table_padding_ * dy;
00306 }
00307 if(flatten_table) vertex.z = 0;
00308 else vertex.z = convex_hull.points[i].z;
00309 table.convex_hull.vertices.push_back(vertex);
00310
00311 if(i==0 || i==convex_hull.points.size()-1) continue;
00312 table.convex_hull.triangles.push_back(0);
00313 table.convex_hull.triangles.push_back(i);
00314 table.convex_hull.triangles.push_back(i+1);
00315 }
00316 visualization_msgs::Marker tableMarker = MarkerGenerator::getConvexHullTableMarker(table.convex_hull);
00317 tableMarker.header = table.pose.header;
00318 tableMarker.pose = table.pose.pose;
00319 tableMarker.ns = "tabletop_node";
00320 tableMarker.id = current_marker_id_++;
00321 marker_pub_.publish(tableMarker);
00322
00323 visualization_msgs::Marker originMarker =
00324 MarkerGenerator::createMarker(table.pose.header.frame_id, 0, .0025, .0025, .01, 0, 1, 1,
00325 visualization_msgs::Marker::CUBE, current_marker_id_++,
00326 "tabletop_node", table.pose.pose);
00327 marker_pub_.publish(originMarker);
00328 }
00329
00330 template <class PointCloudType>
00331 Table TabletopSegmentor::getTable(std_msgs::Header cloud_header,
00332 const tf::Transform &table_plane_trans,
00333 const PointCloudType &table_points)
00334 {
00335 Table table;
00336
00337
00338 if (!table_points.points.empty())
00339 {
00340 table.x_min = table_points.points[0].x;
00341 table.x_max = table_points.points[0].x;
00342 table.y_min = table_points.points[0].y;
00343 table.y_max = table_points.points[0].y;
00344 }
00345 for (size_t i=1; i<table_points.points.size(); ++i)
00346 {
00347 if (table_points.points[i].x<table.x_min && table_points.points[i].x>-3.0) table.x_min = table_points.points[i].x;
00348 if (table_points.points[i].x>table.x_max && table_points.points[i].x< 3.0) table.x_max = table_points.points[i].x;
00349 if (table_points.points[i].y<table.y_min && table_points.points[i].y>-3.0) table.y_min = table_points.points[i].y;
00350 if (table_points.points[i].y>table.y_max && table_points.points[i].y< 3.0) table.y_max = table_points.points[i].y;
00351 }
00352
00353 geometry_msgs::Pose table_pose;
00354 tf::poseTFToMsg(table_plane_trans, table_pose);
00355 table.pose.pose = table_pose;
00356 table.pose.header = cloud_header;
00357
00358
00359 visualization_msgs::Marker tableMarker = MarkerGenerator::getTableMarker(table.x_min, table.x_max,
00360 table.y_min, table.y_max);
00361 tableMarker.header = cloud_header;
00362 tableMarker.pose = table_pose;
00363 tableMarker.ns = "tabletop_node";
00364 tableMarker.id = current_marker_id_++;
00365 marker_pub_.publish(tableMarker);
00366
00367 return table;
00368 }
00369
00370 template <class PointCloudType>
00371 void TabletopSegmentor::publishClusterMarkers(const std::vector<PointCloudType> &clusters, std_msgs::Header cloud_header)
00372 {
00373 for (size_t i=0; i<clusters.size(); i++)
00374 {
00375 visualization_msgs::Marker cloud_marker = MarkerGenerator::getCloudMarker(clusters[i]);
00376 cloud_marker.header = cloud_header;
00377 cloud_marker.pose.orientation.w = 1;
00378 cloud_marker.ns = "tabletop_node";
00379 cloud_marker.id = current_marker_id_++;
00380 marker_pub_.publish(cloud_marker);
00381 }
00382 }
00383
00384 void TabletopSegmentor::clearOldMarkers(std::string frame_id)
00385 {
00386 for (int id=current_marker_id_; id < num_markers_published_; id++)
00387 {
00388 visualization_msgs::Marker delete_marker;
00389 delete_marker.header.stamp = ros::Time::now();
00390 delete_marker.header.frame_id = frame_id;
00391 delete_marker.id = id;
00392 delete_marker.action = visualization_msgs::Marker::DELETE;
00393 delete_marker.ns = "tabletop_node";
00394 marker_pub_.publish(delete_marker);
00395 }
00396 num_markers_published_ = current_marker_id_;
00397 current_marker_id_ = 0;
00398 }
00399
00401 tf::Transform getPlaneTransform (pcl::ModelCoefficients coeffs, double up_direction, bool flatten_plane)
00402 {
00403 ROS_ASSERT(coeffs.values.size() > 3);
00404 double a = coeffs.values[0], b = coeffs.values[1], c = coeffs.values[2], d = coeffs.values[3];
00405
00406 tf::Vector3 position(-a*d, -b*d, -c*d);
00407 tf::Vector3 z(a, b, c);
00408
00409
00410 if(flatten_plane)
00411 {
00412 ROS_INFO("flattening plane");
00413 z[0] = z[1] = 0;
00414 z[2] = up_direction;
00415 }
00416 else
00417 {
00418
00419 ROS_DEBUG("in getPlaneTransform, z: %0.3f, %0.3f, %0.3f", z[0], z[1], z[2]);
00420 if ( z.dot( tf::Vector3(0, 0, up_direction) ) < 0)
00421 {
00422 z = -1.0 * z;
00423 ROS_INFO("flipped z");
00424 }
00425 }
00426
00427
00428
00429 tf::Vector3 x(1, 0, 0);
00430 if ( fabs(z.dot(x)) > 1.0 - 1.0e-4) x = tf::Vector3(0, 1, 0);
00431 tf::Vector3 y = z.cross(x).normalized();
00432 x = y.cross(z).normalized();
00433
00434 tf::Matrix3x3 rotation;
00435 rotation[0] = x;
00436 rotation[1] = y;
00437 rotation[2] = z;
00438 rotation = rotation.transpose();
00439 tf::Quaternion orientation;
00440 rotation.getRotation(orientation);
00441 ROS_DEBUG("in getPlaneTransform, x: %0.3f, %0.3f, %0.3f", x[0], x[1], x[2]);
00442 ROS_DEBUG("in getPlaneTransform, y: %0.3f, %0.3f, %0.3f", y[0], y[1], y[2]);
00443 ROS_DEBUG("in getPlaneTransform, z: %0.3f, %0.3f, %0.3f", z[0], z[1], z[2]);
00444 return tf::Transform(orientation, position);
00445 }
00446
00447 template <class PointCloudType>
00448 bool TabletopSegmentor::tableMsgToPointCloud (Table &table, std::string frame_id, PointCloudType &table_hull)
00449 {
00450
00451 ros::Time now = ros::Time::now();
00452 PointCloudType table_frame_points;
00453 table_frame_points.header.stamp = now;
00454 table_frame_points.header.frame_id = "table_frame";
00455 table_frame_points.points.resize(table.convex_hull.vertices.size());
00456 for(size_t i=0; i < table.convex_hull.vertices.size(); i++)
00457 {
00458 table_frame_points.points[i].x = table.convex_hull.vertices[i].x;
00459 table_frame_points.points[i].y = table.convex_hull.vertices[i].y;
00460 table_frame_points.points[i].z = table.convex_hull.vertices[i].z;
00461 }
00462
00463
00464 tf::Transform table_trans;
00465 tf::poseMsgToTF(table.pose.pose, table_trans);
00466 tf::StampedTransform table_pose_frame(table_trans, now, table.pose.header.frame_id, "table_frame");
00467 listener_.setTransform(table_pose_frame);
00468 ROS_INFO("transforming point cloud from table frame to frame %s", frame_id.c_str());
00469
00470
00471 std::string error_msg;
00472 if (!listener_.waitForTransform(frame_id, "table_frame", now, ros::Duration(2.0), ros::Duration(0.01), &error_msg))
00473 {
00474 ROS_ERROR("Can not transform point cloud from table frame to frame %s; error %s",
00475 frame_id.c_str(), error_msg.c_str());
00476 return false;
00477 }
00478
00479
00480 int current_try=0, max_tries = 3;
00481 while (1)
00482 {
00483 bool transform_success = true;
00484 try
00485 {
00486 pcl_ros::transformPointCloud<Point>(frame_id, table_frame_points, table_hull, listener_);
00487 }
00488 catch (tf::TransformException ex)
00489 {
00490 transform_success = false;
00491 if ( ++current_try >= max_tries )
00492 {
00493 ROS_ERROR("Failed to transform point cloud from table frame into frame %s; error %s",
00494 frame_id.c_str(), ex.what());
00495 return false;
00496 }
00497
00498 ros::Duration(0.1).sleep();
00499 }
00500 if (transform_success) break;
00501 }
00502 table_hull.header.frame_id = frame_id;
00503 table_hull.header.stamp = now;
00504
00505
00506 for(size_t i=0; i < table.convex_hull.vertices.size(); i++)
00507 {
00508 table.convex_hull.vertices[i].x = table_hull.points[i].x;
00509 table.convex_hull.vertices[i].y = table_hull.points[i].y;
00510 table.convex_hull.vertices[i].z = table_hull.points[i].z;
00511 }
00512 geometry_msgs::PoseStamped iden;
00513 iden.pose.orientation.w = 1;
00514 table.pose = iden;
00515 table.pose.header.frame_id = frame_id;
00516
00517
00518 arm_navigation_msgs::Shape mesh;
00519 mesh.vertices.resize(table_hull.points.size());
00520 for(size_t i = 0; i < table_hull.points.size(); i++)
00521 {
00522 mesh.vertices[i].x = table_hull.points[i].x;
00523 mesh.vertices[i].y = table_hull.points[i].y;
00524 mesh.vertices[i].z = table_hull.points[i].z;
00525 }
00526 mesh.triangles = table.convex_hull.triangles;
00527 visualization_msgs::Marker tableMarker = MarkerGenerator::getConvexHullTableMarker(mesh);
00528 tableMarker.header = table_hull.header;
00529 tableMarker.pose.orientation.w = 1.0;
00530 tableMarker.ns = "tabletop_node";
00531 tableMarker.id = current_marker_id_++;
00532 marker_pub_.publish(tableMarker);
00533
00534 return true;
00535 }
00536
00537
00538 template <typename PointT>
00539 bool getPlanePoints (const pcl::PointCloud<PointT> &table,
00540 const tf::Transform& table_plane_trans,
00541 sensor_msgs::PointCloud &table_points)
00542 {
00543
00544 table_points.header = table.header;
00545 table_points.points.resize (table.points.size ());
00546 for (size_t i = 0; i < table.points.size (); ++i)
00547 {
00548 table_points.points[i].x = table.points[i].x;
00549 table_points.points[i].y = table.points[i].y;
00550 table_points.points[i].z = table.points[i].z;
00551 }
00552
00553
00554 tf::TransformListener listener;
00555 tf::StampedTransform table_pose_frame(table_plane_trans, table.header.stamp,
00556 table.header.frame_id, "table_frame");
00557 listener.setTransform(table_pose_frame);
00558 std::string error_msg;
00559 if (!listener.canTransform("table_frame", table_points.header.frame_id, table_points.header.stamp, &error_msg))
00560 {
00561 ROS_ERROR("Can not transform point cloud from frame %s to table frame; error %s",
00562 table_points.header.frame_id.c_str(), error_msg.c_str());
00563 return false;
00564 }
00565 int current_try=0, max_tries = 3;
00566 while (1)
00567 {
00568 bool transform_success = true;
00569 try
00570 {
00571 listener.transformPointCloud("table_frame", table_points, table_points);
00572 }
00573 catch (tf::TransformException ex)
00574 {
00575 transform_success = false;
00576 if ( ++current_try >= max_tries )
00577 {
00578 ROS_ERROR("Failed to transform point cloud from frame %s into table_frame; error %s",
00579 table_points.header.frame_id.c_str(), ex.what());
00580 return false;
00581 }
00582
00583 ros::Duration(0.1).sleep();
00584 }
00585 if (transform_success) break;
00586 }
00587 table_points.header.stamp = table.header.stamp;
00588 table_points.header.frame_id = "table_frame";
00589 return true;
00590 }
00591
00592
00593 template <class PointCloudType>
00594 void straightenPoints(PointCloudType &points, const tf::Transform& table_plane_trans,
00595 const tf::Transform& table_plane_trans_flat)
00596 {
00597 tf::Transform trans = table_plane_trans_flat * table_plane_trans.inverse();
00598 pcl_ros::transformPointCloud(points, points, trans);
00599 }
00600
00601
00602 template <typename PointT> void
00603 getClustersFromPointCloud2 (const pcl::PointCloud<PointT> &cloud_objects,
00604 const std::vector<pcl::PointIndices> &clusters2,
00605 std::vector<sensor_msgs::PointCloud> &clusters)
00606 {
00607 clusters.resize (clusters2.size ());
00608 for (size_t i = 0; i < clusters2.size (); ++i)
00609 {
00610 pcl::PointCloud<PointT> cloud_cluster;
00611 pcl::copyPointCloud(cloud_objects, clusters2[i], cloud_cluster);
00612 sensor_msgs::PointCloud2 pc2;
00613 pcl::toROSMsg( cloud_cluster, pc2 );
00614 sensor_msgs::convertPointCloud2ToPointCloud (pc2, clusters[i]);
00615 }
00616 }
00617
00618 void TabletopSegmentor::processCloud(const sensor_msgs::PointCloud2 &cloud,
00619 TabletopSegmentation::Response &response,
00620 Table input_table)
00621 {
00622 ROS_INFO("Starting process on new cloud in frame %s", cloud.header.frame_id.c_str());
00623
00624
00625 KdTreePtr normals_tree_, clusters_tree_;
00626 pcl::VoxelGrid<Point> grid_, grid_objects_;
00627 pcl::PassThrough<Point> pass_;
00628 pcl::NormalEstimation<Point, pcl::Normal> n3d_;
00629 pcl::SACSegmentationFromNormals<Point, pcl::Normal> seg_;
00630 pcl::ProjectInliers<Point> proj_;
00631 pcl::ConvexHull<Point> hull_;
00632 pcl::ExtractPolygonalPrismData<Point> prism_;
00633 pcl::EuclideanClusterExtraction<Point> pcl_cluster_;
00634 pcl::PointCloud<Point>::Ptr table_hull_ptr (new pcl::PointCloud<Point>);
00635
00636
00637 grid_.setLeafSize (plane_detection_voxel_size_, plane_detection_voxel_size_, plane_detection_voxel_size_);
00638 grid_objects_.setLeafSize (clustering_voxel_size_, clustering_voxel_size_, clustering_voxel_size_);
00639 grid_.setFilterFieldName ("z");
00640 grid_.setFilterLimits (z_filter_min_, z_filter_max_);
00641 grid_.setDownsampleAllData (false);
00642 grid_objects_.setDownsampleAllData (true);
00643
00644 normals_tree_ = boost::make_shared<pcl::search::KdTree<Point> > ();
00645 clusters_tree_ = boost::make_shared<pcl::search::KdTree<Point> > ();
00646
00647
00648 n3d_.setKSearch (10);
00649 n3d_.setSearchMethod (normals_tree_);
00650
00651 seg_.setDistanceThreshold (0.05);
00652 seg_.setMaxIterations (10000);
00653 seg_.setNormalDistanceWeight (0.1);
00654 seg_.setOptimizeCoefficients (true);
00655 seg_.setModelType (pcl::SACMODEL_NORMAL_PLANE);
00656 seg_.setMethodType (pcl::SAC_RANSAC);
00657 seg_.setProbability (0.99);
00658
00659 proj_.setModelType (pcl::SACMODEL_PLANE);
00660
00661
00662 pcl_cluster_.setClusterTolerance (cluster_distance_);
00663 pcl_cluster_.setMinClusterSize (min_cluster_size_);
00664 pcl_cluster_.setSearchMethod (clusters_tree_);
00665
00666
00667 pcl::PointCloud<Point>::Ptr cloud_ptr (new pcl::PointCloud<Point>);
00668 try
00669 {
00670 pcl::fromROSMsg (cloud, *cloud_ptr);
00671 }
00672 catch (...)
00673 {
00674 ROS_ERROR("Failure while converting the ROS Message to PCL point cloud. Tabletop segmentation now requires the input cloud to have color info, so you must input a cloud with XYZRGB points, not just XYZ.");
00675 throw;
00676 }
00677 pass_.setInputCloud (cloud_ptr);
00678 pass_.setFilterFieldName ("z");
00679 pass_.setFilterLimits (z_filter_min_, z_filter_max_);
00680 pcl::PointCloud<Point>::Ptr z_cloud_filtered_ptr (new pcl::PointCloud<Point>);
00681 pass_.filter (*z_cloud_filtered_ptr);
00682
00683 pass_.setInputCloud (z_cloud_filtered_ptr);
00684 pass_.setFilterFieldName ("y");
00685 pass_.setFilterLimits (y_filter_min_, y_filter_max_);
00686 pcl::PointCloud<Point>::Ptr y_cloud_filtered_ptr (new pcl::PointCloud<Point>);
00687 pass_.filter (*y_cloud_filtered_ptr);
00688
00689 pass_.setInputCloud (y_cloud_filtered_ptr);
00690 pass_.setFilterFieldName ("x");
00691 pass_.setFilterLimits (x_filter_min_, x_filter_max_);
00692 pcl::PointCloud<Point>::Ptr cloud_filtered_ptr (new pcl::PointCloud<Point>);
00693 pass_.filter (*cloud_filtered_ptr);
00694
00695 ROS_INFO("Step 1 done");
00696 if (cloud_filtered_ptr->points.size() < (unsigned int)min_cluster_size_)
00697 {
00698 ROS_INFO("Filtered cloud only has %d points", (int)cloud_filtered_ptr->points.size());
00699 response.result = response.NO_TABLE;
00700 return;
00701 }
00702
00703 pcl::PointCloud<Point>::Ptr cloud_downsampled_ptr (new pcl::PointCloud<Point>);
00704 grid_.setInputCloud (cloud_filtered_ptr);
00705 grid_.filter (*cloud_downsampled_ptr);
00706 if (cloud_downsampled_ptr->points.size() < (unsigned int)min_cluster_size_)
00707 {
00708 ROS_INFO("Downsampled cloud only has %d points", (int)cloud_downsampled_ptr->points.size());
00709 response.result = response.NO_TABLE;
00710 return;
00711 }
00712
00713
00714 pcl::PointCloud<pcl::Normal>::Ptr cloud_normals_ptr (new pcl::PointCloud<pcl::Normal>);
00715 n3d_.setInputCloud (cloud_downsampled_ptr);
00716 n3d_.compute (*cloud_normals_ptr);
00717 ROS_INFO("Step 2 done");
00718
00719
00720
00721 tf::Transform table_plane_trans;
00722 tf::Transform table_plane_trans_flat;
00723 if(input_table.convex_hull.vertices.size() != 0)
00724 {
00725 ROS_INFO("Table input, skipping Step 3");
00726 bool success = tableMsgToPointCloud<pcl::PointCloud<Point> >(input_table, cloud.header.frame_id, *table_hull_ptr);
00727 if(!success)
00728 {
00729 ROS_ERROR("Failure in converting table convex hull!");
00730 return;
00731 }
00732 response.table = input_table;
00733 if(flatten_table_)
00734 {
00735 ROS_ERROR("flatten_table mode is disabled if table is given!");
00736 flatten_table_ = false;
00737 }
00738 }
00739 else
00740 {
00741 pcl::PointIndices::Ptr table_inliers_ptr (new pcl::PointIndices);
00742 pcl::ModelCoefficients::Ptr table_coefficients_ptr (new pcl::ModelCoefficients);
00743 seg_.setInputCloud (cloud_downsampled_ptr);
00744 seg_.setInputNormals (cloud_normals_ptr);
00745 seg_.segment (*table_inliers_ptr, *table_coefficients_ptr);
00746
00747 if (table_coefficients_ptr->values.size () <=3)
00748 {
00749 ROS_INFO("Failed to detect table in scan");
00750 response.result = response.NO_TABLE;
00751 return;
00752 }
00753
00754 if ( table_inliers_ptr->indices.size() < (unsigned int)inlier_threshold_)
00755 {
00756 ROS_INFO("Plane detection has %d inliers, below min threshold of %d", (int)table_inliers_ptr->indices.size(),
00757 inlier_threshold_);
00758 response.result = response.NO_TABLE;
00759 return;
00760 }
00761
00762 ROS_INFO ("[TableObjectDetector::input_callback] Model found with %d inliers: [%f %f %f %f].",
00763 (int)table_inliers_ptr->indices.size (),
00764 table_coefficients_ptr->values[0], table_coefficients_ptr->values[1],
00765 table_coefficients_ptr->values[2], table_coefficients_ptr->values[3]);
00766 ROS_INFO("Step 3 done");
00767
00768
00769 pcl::PointCloud<Point>::Ptr table_projected_ptr (new pcl::PointCloud<Point>);
00770 proj_.setInputCloud (cloud_downsampled_ptr);
00771 proj_.setIndices (table_inliers_ptr);
00772 proj_.setModelCoefficients (table_coefficients_ptr);
00773 proj_.filter (*table_projected_ptr);
00774 ROS_INFO("Step 4 done");
00775
00776 sensor_msgs::PointCloud table_points;
00777 sensor_msgs::PointCloud table_hull_points;
00778 table_plane_trans = getPlaneTransform (*table_coefficients_ptr, up_direction_, false);
00779
00780
00781 hull_.setInputCloud (table_projected_ptr);
00782 hull_.reconstruct (*table_hull_ptr);
00783
00784 if(!flatten_table_)
00785 {
00786
00787
00788 if (!getPlanePoints<Point> (*table_projected_ptr, table_plane_trans, table_points))
00789 {
00790 response.result = response.OTHER_ERROR;
00791 return;
00792 }
00793
00794
00795 response.table = getTable<sensor_msgs::PointCloud>(cloud.header, table_plane_trans, table_points);
00796
00797
00798 if (!getPlanePoints<Point> (*table_hull_ptr, table_plane_trans, table_hull_points))
00799 {
00800 response.result = response.OTHER_ERROR;
00801 return;
00802 }
00803 }
00804 if(flatten_table_)
00805 {
00806
00807 table_plane_trans_flat = getPlaneTransform (*table_coefficients_ptr, up_direction_, flatten_table_);
00808 tf::Vector3 flat_table_pos;
00809 double avg_x, avg_y, avg_z;
00810 avg_x = avg_y = avg_z = 0;
00811 for (size_t i=0; i<table_projected_ptr->points.size(); i++)
00812 {
00813 avg_x += table_projected_ptr->points[i].x;
00814 avg_y += table_projected_ptr->points[i].y;
00815 avg_z += table_projected_ptr->points[i].z;
00816 }
00817 avg_x /= table_projected_ptr->points.size();
00818 avg_y /= table_projected_ptr->points.size();
00819 avg_z /= table_projected_ptr->points.size();
00820 ROS_INFO("average x,y,z = (%.5f, %.5f, %.5f)", avg_x, avg_y, avg_z);
00821
00822
00823 flat_table_pos[0] = avg_x;
00824 flat_table_pos[1] = avg_y;
00825 flat_table_pos[2] = avg_z;
00826 table_plane_trans_flat.setOrigin(flat_table_pos);
00827
00828
00829 table_plane_trans.setOrigin(flat_table_pos);
00830
00831
00832
00833 sensor_msgs::PointCloud flat_table_points;
00834 if (!getPlanePoints<Point> (*table_projected_ptr, table_plane_trans_flat, flat_table_points))
00835 {
00836 response.result = response.OTHER_ERROR;
00837 return;
00838 }
00839
00840
00841 response.table = getTable<sensor_msgs::PointCloud>(cloud.header, table_plane_trans_flat, flat_table_points);
00842
00843
00844 if (!getPlanePoints<Point> (*table_hull_ptr, table_plane_trans_flat, table_hull_points))
00845 {
00846 response.result = response.OTHER_ERROR;
00847 return;
00848 }
00849 }
00850
00851 ROS_INFO("Table computed");
00852
00853 addConvexHullTable<sensor_msgs::PointCloud>(response.table, table_hull_points, flatten_table_);
00854 }
00855
00856
00857 pcl::PointIndices cloud_object_indices;
00858
00859 prism_.setInputCloud (cloud_filtered_ptr);
00860 prism_.setInputPlanarHull (table_hull_ptr);
00861 ROS_INFO("Using table prism: %f to %f", table_z_filter_min_, table_z_filter_max_);
00862 prism_.setHeightLimits (table_z_filter_min_, table_z_filter_max_);
00863 prism_.segment (cloud_object_indices);
00864
00865 pcl::PointCloud<Point>::Ptr cloud_objects_ptr (new pcl::PointCloud<Point>);
00866 pcl::ExtractIndices<Point> extract_object_indices;
00867 extract_object_indices.setInputCloud (cloud_filtered_ptr);
00868 extract_object_indices.setIndices (boost::make_shared<const pcl::PointIndices> (cloud_object_indices));
00869 extract_object_indices.filter (*cloud_objects_ptr);
00870
00871 ROS_INFO (" Number of object point candidates: %d.", (int)cloud_objects_ptr->points.size ());
00872 response.result = response.SUCCESS;
00873
00874 if (cloud_objects_ptr->points.empty ())
00875 {
00876 ROS_INFO("No objects on table");
00877 return;
00878 }
00879
00880
00881 pcl::PointCloud<Point>::Ptr cloud_objects_downsampled_ptr (new pcl::PointCloud<Point>);
00882 grid_objects_.setInputCloud (cloud_objects_ptr);
00883 grid_objects_.filter (*cloud_objects_downsampled_ptr);
00884
00885
00886 if(flatten_table_) straightenPoints<pcl::PointCloud<Point> >(*cloud_objects_downsampled_ptr,
00887 table_plane_trans, table_plane_trans_flat);
00888
00889
00890 std::vector<pcl::PointIndices> clusters2;
00891
00892 pcl_cluster_.setInputCloud (cloud_objects_downsampled_ptr);
00893 pcl_cluster_.extract (clusters2);
00894 ROS_INFO ("Number of clusters found matching the given constraints: %d.", (int)clusters2.size ());
00895
00896
00897 std::vector<sensor_msgs::PointCloud> clusters;
00898 getClustersFromPointCloud2<Point> (*cloud_objects_downsampled_ptr, clusters2, clusters);
00899 ROS_INFO("Clusters converted");
00900 response.clusters = clusters;
00901
00902 publishClusterMarkers(clusters, cloud.header);
00903 }
00904
00905
00906 }
00907
00908 int main(int argc, char **argv)
00909 {
00910 ros::init(argc, argv, "tabletop_segmentation_node");
00911 ros::NodeHandle nh;
00912
00913 tabletop_object_detector::TabletopSegmentor node(nh);
00914 ros::spin();
00915 return 0;
00916 }