$search
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::KdTreeFLANN<PointT>::Ptr tree (new pcl::KdTreeFLANN<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::KdTree<PointT>::Ptr tree(new pcl::KdTreeFLANN<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 }