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
00040 object_manipulation_msgs::FindContainerFeedback feedback_;
00041
00042
00043 tf::TransformListener tfl_;
00044
00045 ros::Publisher pub_cloud_, pub_contents_, pub_container_;
00046 ros::Publisher pub_marker_, pub_clusters_;
00047
00048
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
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
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
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
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
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
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
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
00306 splitCloudRegions(cloud_ds, contents, container);
00307
00308
00309 findBoundingBox(cloud_ds, result.box_dims, result.box_pose);
00310
00311 std::vector< pcl::PointCloud<PointT>::Ptr > clusters;
00312
00313
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
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
00338 ROS_INFO("%s: Succeeded", action_name_.c_str());
00339
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 }