find_container_action.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 
00003 #include <actionlib/server/simple_action_server.h>
00004 #include <object_manipulation_msgs/FindContainerAction.h>
00005 #include <pcl/common/common.h>
00006 #include <pcl/io/pcd_io.h>
00007 #include <pcl/point_types.h>
00008 #include <pcl/filters/voxel_grid.h>
00009 #include <pcl/ModelCoefficients.h>
00010 #include <pcl/features/normal_3d.h>
00011 #include <pcl/filters/extract_indices.h>
00012 #include <pcl/filters/passthrough.h>
00013 #include <pcl/sample_consensus/method_types.h>
00014 #include <pcl/sample_consensus/model_types.h>
00015 #include <pcl/segmentation/sac_segmentation.h>
00016 #include <pcl/surface/mls.h>
00017 #include <pcl/segmentation/extract_clusters.h>
00018 #include <pcl/filters/statistical_outlier_removal.h>
00019 #include <visualization_msgs/Marker.h>
00020 #include <visualization_msgs/MarkerArray.h>
00021 
00022 #include <tf/transform_listener.h>
00023 
00024 
00025 #include <pcl_ros/transforms.h>
00026 #include <Eigen/Geometry>
00027 
00028 
00029 typedef pcl::PointXYZRGB PointT;
00030 
00031 class FindContainerNode
00032 {
00033 protected:
00034 
00035   ros::NodeHandle nh_;
00036   ros::NodeHandle pnh_;
00037   actionlib::SimpleActionServer<object_manipulation_msgs::FindContainerAction> as_;
00038   std::string action_name_;
00039   // create messages that are used to published feedback/result
00040   object_manipulation_msgs::FindContainerFeedback feedback_;
00041   //object_manipulation_msgs::FindContainerResult result_;
00042 
00043   tf::TransformListener tfl_;
00044 
00045   ros::Publisher pub_cloud_, pub_contents_, pub_container_;
00046   ros::Publisher pub_marker_, pub_clusters_;
00047 
00048   //the direction taken to be 'vertical' (can actually be horizontal in the world)
00049   geometry_msgs::Vector3 opening_dir_;
00050 
00051 public:
00052 
00053   FindContainerNode(std::string name) :
00054     pnh_("~"),
00055     as_(nh_, name, boost::bind(&FindContainerNode::executeCB, this, _1), false),
00056     action_name_(name)
00057   {
00058     as_.start();
00059     pub_cloud_ = pnh_.advertise<sensor_msgs::PointCloud2>("cloud", 1, true);
00060     pub_contents_ = pnh_.advertise<sensor_msgs::PointCloud2>("contents", 1, true);
00061     pub_container_ = pnh_.advertise<sensor_msgs::PointCloud2>("container", 1, true);
00062     pub_marker_ = pnh_.advertise<visualization_msgs::Marker>("box", 1, true);
00063     pub_clusters_ = pnh_.advertise<visualization_msgs::MarkerArray>("clusters_array", 100, true);
00064     ROS_INFO("%s: Server ready.", action_name_.c_str());
00065   }
00066 
00067   ~FindContainerNode(void)
00068   {
00069   }
00070 
00071 
00072   void splitCloudRegions( const pcl::PointCloud<PointT>::Ptr &cloud_in,
00073                               const pcl::PointCloud<PointT>::Ptr &horizontal_cloud,
00074                               const pcl::PointCloud<PointT>::Ptr &vertical_cloud)
00075   {
00076     pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud<pcl::Normal>());
00077 
00078     // Estimate point normals
00079     pcl::search::KdTree<PointT>::Ptr tree (new pcl::search::KdTree<PointT> ());
00080     pcl::MovingLeastSquares<PointT, pcl::Normal> ne;
00081     ne.setSearchMethod (tree);
00082     ne.setInputCloud (cloud_in);
00083     ne.setOutputNormals(normals);
00084     ne.setSearchRadius (0.012);
00085     ne.reconstruct (*cloud_in);
00086 
00087     for(size_t i = 0; i < cloud_in->points.size(); i++)
00088     {
00089 
00090       pcl::Normal normal = normals->points[i];
00091       if(fabs(normal.normal_x*opening_dir_.x + normal.normal_y*opening_dir_.y + normal.normal_z*opening_dir_.z) > 0.7)
00092       //if(fabs(normal.normal_z) > 0.7)
00093       {
00094         horizontal_cloud->points.push_back(cloud_in->points[i]);
00095       }
00096       else
00097       {
00098         vertical_cloud->points.push_back(cloud_in->points[i]);
00099       }
00100     }
00101 
00102     horizontal_cloud->width = horizontal_cloud->points.size();
00103     horizontal_cloud->height = 1;
00104     horizontal_cloud->is_dense = false;
00105 
00106     vertical_cloud->width = vertical_cloud->points.size();
00107     vertical_cloud->height = 1;
00108     vertical_cloud->is_dense = false;
00109 
00110   }
00111 
00112   void removeOutliers(const pcl::PointCloud<PointT>::Ptr &cloud_in,
00113                       const pcl::PointCloud<PointT>::Ptr &cloud_out)
00114   {
00115     // Create the filtering object
00116     pcl::StatisticalOutlierRemoval<PointT> sor;
00117     sor.setInputCloud (cloud_in);
00118     sor.setMeanK (50);
00119     sor.setStddevMulThresh (1.0);
00120     sor.filter (*cloud_out);
00121   }
00122 
00123   void findClusters(const pcl::PointCloud<PointT>::Ptr &cloud, std::vector< pcl::PointCloud<PointT>::Ptr > &clusters)
00124   {
00125     if(cloud->points.size() == 0) return;
00126 
00127     // Create a KdTree for the search method of the extraction
00128     pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);
00129     tree->setInputCloud(cloud);
00130 
00131     std::vector<pcl::PointIndices> cluster_indices;
00132     pcl::EuclideanClusterExtraction<PointT> ec;
00133     ec.setClusterTolerance(0.01);
00134     ec.setMinClusterSize(100);
00135     ec.setMaxClusterSize(25000);
00136     ec.setSearchMethod(tree);
00137     ec.setInputCloud(cloud);
00138     ec.extract(cluster_indices);
00139 
00140     int j = 0;
00141     for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin (); it != cluster_indices.end (); ++it)
00142     {
00143       pcl::PointCloud<PointT>::Ptr cloud_cluster (new pcl::PointCloud<PointT>);
00144       pcl::copyPointCloud(*cloud, *it, *cloud_cluster );
00145       clusters.push_back(cloud_cluster);
00146       j++;
00147     }
00148   }
00149 
00150   visualization_msgs::Marker makeMarkerFromCloud( const pcl::PointCloud<PointT>::Ptr &cloud_ptr, const std::string &ns, int id = 0, float scale = 0.03)
00151   {
00152     pcl::PointCloud<PointT>& cloud = *cloud_ptr;
00153     ROS_DEBUG("Making a marker with %d points.", (int)cloud.points.size());
00154 
00155     visualization_msgs::Marker marker;
00156     marker.action = marker.ADD;
00157     marker.header = cloud.header;
00158     marker.id = id;
00159     marker.ns = ns;
00160     marker.pose.orientation.w = 1;
00161     marker.color.r = 1.0;
00162     marker.color.g = 1.0;
00163     marker.color.b = 1.0;
00164     marker.color.a = 1.0;
00165     marker.frame_locked = false;
00166 
00167     if(cloud.points.size())
00168     {
00169       marker.scale.x = scale;
00170       marker.scale.y = scale;
00171       marker.scale.z = scale;
00172       marker.type = visualization_msgs::Marker::SPHERE_LIST;
00173 
00174       int num_points = cloud.points.size();
00175       marker.points.resize( num_points );
00176       marker.colors.resize( num_points );
00177 
00178       for ( int i=0; i<num_points; i++)
00179       {
00180         marker.points[i].x = cloud.points[i].x;
00181         marker.points[i].y = cloud.points[i].y;
00182         marker.points[i].z = cloud.points[i].z;
00183         marker.colors[i].r = cloud.points[i].r/255.;
00184         marker.colors[i].g = cloud.points[i].g/255.;
00185         marker.colors[i].b = cloud.points[i].b/255.;
00186         marker.colors[i].a = 1.0;
00187       }
00188     }
00189 
00190     return marker;
00191   }
00192 
00193   void findBoundingBox(const pcl::PointCloud<PointT>::Ptr &cloud,
00194                        geometry_msgs::Vector3 &box_dims,
00195                        geometry_msgs::PoseStamped &box_pose)
00196   {
00197     PointT min_pt, max_pt;
00198     pcl::getMinMax3D(*cloud, min_pt, max_pt);
00199 
00200     box_dims.x = max_pt.x - min_pt.x;
00201     box_dims.y = max_pt.y - min_pt.y;
00202     box_dims.z = max_pt.z - min_pt.z;
00203 
00204     box_pose.pose.orientation.w = 1.0;
00205     box_pose.header = cloud->header;
00206     box_pose.pose.position.x = (max_pt.x + min_pt.x)/2.0;
00207     box_pose.pose.position.y = (max_pt.y + min_pt.y)/2.0;
00208     box_pose.pose.position.z = (max_pt.z + min_pt.z)/2.0;
00209   }
00210 
00211   int boxFilter(const pcl::PointCloud<PointT>::Ptr &cloud, const pcl::PointCloud<PointT>::Ptr &cloud_filtered,
00212                  const geometry_msgs::Vector3 &dims,
00213                  const geometry_msgs::Pose &pose)
00214   {
00215     Eigen::Vector4f center(pose.position.x, pose.position.y, pose.position.z, 0);
00216 
00217     // TODO: maybe allow for box pose that is not aligned with its header frame
00218     Eigen::Vector4f min_pt;
00219     min_pt = center - Eigen::Vector4f(dims.x/2, dims.y/2, dims.z/2, 0);
00220     Eigen::Vector4f max_pt;
00221     max_pt = center + Eigen::Vector4f(dims.x/2, dims.y/2, dims.z/2, 0);
00222 
00223     std::vector<int> indices;
00224     pcl::getPointsInBox(*cloud, min_pt, max_pt, indices);
00225 
00226     pcl::copyPointCloud(*cloud, indices, *cloud_filtered);
00227     return cloud_filtered->points.size();
00228   }
00229 
00230   void drawBox(const geometry_msgs::Vector3 &box_dims,
00231                const geometry_msgs::PoseStamped &box_pose, const std::string &ns = "box")
00232   {
00233     visualization_msgs::Marker marker;
00234     marker.header = box_pose.header;
00235     marker.ns = ns;
00236     marker.id = 0;
00237     marker.type = visualization_msgs::Marker::CUBE; 
00238     marker.action = (int32_t)visualization_msgs::Marker::ADD;
00239     marker.pose = box_pose.pose;
00240     marker.scale = box_dims;
00241     marker.color.b = 1.0;
00242     marker.color.a = 0.5;
00243     marker.lifetime = ros::Duration(0);
00244     marker.frame_locked = false;
00245     pub_marker_.publish(marker);
00246   }
00247 
00248   void executeCB(const object_manipulation_msgs::FindContainerGoalConstPtr &goal)
00249   {
00250     object_manipulation_msgs::FindContainerResult result;
00251 
00252     ROS_INFO("%s: Processing a goal request!", action_name_.c_str());
00253 
00254     opening_dir_ = goal->opening_dir;
00255 
00256     pcl::PointCloud<PointT>::Ptr cloud              (new pcl::PointCloud<PointT>());
00257     pcl::PointCloud<PointT>::Ptr cloud_filtered     (new pcl::PointCloud<PointT>());
00258     pcl::PointCloud<PointT>::Ptr cloud_ds           (new pcl::PointCloud<PointT>());
00259     pcl::PointCloud<PointT>::Ptr contents           (new pcl::PointCloud<PointT>());
00260     pcl::PointCloud<PointT>::Ptr container          (new pcl::PointCloud<PointT>());
00261 
00262     pcl::fromROSMsg(goal->cloud, *cloud);
00263     std::vector<int> non_NaN_indices;
00264     pcl::removeNaNFromPointCloud(*cloud, *cloud, non_NaN_indices);
00265     cloud->header.stamp = ros::Time(0);
00266 
00267     tf::StampedTransform T;
00268     Eigen::Matrix4f M;
00269     tfl_.waitForTransform(goal->box_pose.header.frame_id, cloud->header.frame_id, ros::Time(0), ros::Duration(2.0));
00270     tfl_.lookupTransform(goal->box_pose.header.frame_id, cloud->header.frame_id, ros::Time(0), T);
00271     pcl_ros::transformAsMatrix( T, M);
00272     pcl::transformPointCloud( *cloud, *cloud, M);
00273     cloud->header.frame_id = goal->box_pose.header.frame_id;
00274 
00275     drawBox(goal->box_dims, goal->box_pose, "input_bounds");
00276     if( !boxFilter(cloud, cloud_filtered, goal->box_dims, goal->box_pose.pose) )
00277     {
00278       ROS_ERROR("%s: No cloud points found in input bounding box.", action_name_.c_str());
00279       as_.setAborted();
00280       return;
00281     }
00282 
00283     contents->header = goal->box_pose.header;
00284     container->header = goal->box_pose.header;
00285     cloud_ds->header = goal->box_pose.header;
00286     cloud_filtered->header = goal->box_pose.header;
00287 
00288     // Down-sample the cloud on a grid
00289     pcl::VoxelGrid<PointT> vox;
00290     vox.setInputCloud (cloud_filtered);
00291     vox.setLeafSize (0.002, 0.002, 0.002);
00292     vox.setFilterFieldName("z");
00293     vox.setFilterLimits(-100, 100);
00294     vox.filter (*cloud_ds);
00295 
00296     // Remove outlier points (speckles)
00297     removeOutliers(cloud_ds, cloud_ds);
00298 
00299     if(cloud_ds->points.size() == 0)
00300     {
00301       ROS_ERROR("%s: No cloud points remain after outlier removal.", action_name_.c_str());
00302       as_.setSucceeded(result);
00303     }
00304 
00305     // Estimate normals and separate horizontal and vertical surfaces
00306     splitCloudRegions(cloud_ds, contents, container);
00307 
00308     // Find the bounding box for all the points
00309     findBoundingBox(cloud_ds, result.box_dims, result.box_pose);
00310 
00311     std::vector< pcl::PointCloud<PointT>::Ptr > clusters;
00312 
00313     // Split the contents into clusters 
00314     findClusters(contents, clusters);
00315 
00316     result.clusters.resize(clusters.size());
00317     visualization_msgs::MarkerArray cluster_markers;
00318 
00319     for(size_t i = 0; i < clusters.size(); i++)
00320     {
00321       pcl::toROSMsg( *(clusters[i]), result.clusters[i]);
00322       cluster_markers.markers.push_back( makeMarkerFromCloud(clusters[i], "clusters", i, 0.003));
00323     }
00324     pub_clusters_.publish(cluster_markers);
00325 
00326     // Publish the clouds and box
00327     drawBox(result.box_dims, result.box_pose, "container_box");
00328     sensor_msgs::PointCloud2 cloud_debug;
00329     pcl::toROSMsg(*cloud_ds, cloud_debug);
00330     pcl::toROSMsg(*contents, result.contents);
00331     pcl::toROSMsg(*container, result.container);
00332 
00333     pub_cloud_.publish(cloud_debug);
00334     pub_contents_.publish(result.contents);
00335     pub_container_.publish(result.container);
00336 
00337     // Return the result
00338     ROS_INFO("%s: Succeeded", action_name_.c_str());
00339     // set the action state to succeeded
00340     as_.setSucceeded(result);
00341 
00342   }
00343 
00344 
00345 };
00346 
00347 
00348 int main(int argc, char** argv)
00349 {
00350   ros::init(argc, argv, "find_container_action");
00351 
00352   FindContainerNode find_it(ros::this_node::getName());
00353   ros::spin();
00354 
00355   return 0;
00356 }


segmented_clutter_grasp_planner
Author(s): Kaijen Hsiao
autogenerated on Fri Jan 3 2014 12:08:13