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
00042 #include <ros/ros.h>
00043 #include <sensor_msgs/PointCloud2.h>
00044 #include <mapping_msgs/CollisionObject.h>
00045
00046 #include <pcl/point_types.h>
00047 #include <pcl/common/eigen.h>
00048 #include <pcl/features/feature.h>
00049 #include <pcl/segmentation/extract_clusters.h>
00050
00051 #include <table_objects/GetObjects.h>
00052 #include <table_objects/GetLastObjects.h>
00053
00054 #include <table_objects/table_objects.h>
00055
00056
00057 #include <Eigen3/Core>
00058 #include <Eigen3/Geometry>
00059
00060 using namespace pcl;
00061
00062 bool classify;
00063 flann::Index< flann::L2<float> > *flann_index;
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 bool
00080 getTableObjects (table_objects::GetObjects::Request &req, table_objects::GetObjects::Response &res)
00081 {
00082
00083 PointCloud<PointXYZ> cloud;
00084 pcl::fromROSMsg (req.data, cloud);
00085
00086
00087 std::vector<pcl::PointIndices> clusters;
00088 pcl::EuclideanClusterExtraction<PointXYZ> cluster;
00089 cluster.setInputCloud (cloud.makeShared ());
00090 cluster.setClusterTolerance (0.03);
00091 cluster.setMinClusterSize (20);
00092 cluster.extract (clusters);
00093 ROS_INFO ("[getTableObjects] Number of clusters found matching the given constraints: %d.", (int)clusters.size ());
00094
00095
00096 res.table_objects.reserve (clusters.size ());
00097 for (size_t i = 0; i < clusters.size (); ++i)
00098 {
00099 pcl::PointCloud<PointXYZ> cloud_object_cluster;
00100 pcl::copyPointCloud (cloud, clusters[i], cloud_object_cluster);
00101 std::stringstream ss;
00102 ss << "cluster_" << i;
00103 std::string label = ss.str ();
00104
00105
00106 if (classify)
00107 {
00108
00109 pcl::PointCloud<pcl::Normal>::Ptr normals = boost::make_shared<pcl::PointCloud<pcl::Normal> >();
00110 pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> n3d;
00111 n3d.setKSearch (30);
00112 pcl::KdTree<pcl::PointXYZ>::Ptr tree = boost::make_shared<pcl::KdTreeANN<pcl::PointXYZ> > ();
00113 n3d.setSearchMethod (tree);
00114 n3d.setInputCloud(cloud_object_cluster.makeShared ());
00115 n3d.compute(*normals);
00116
00117
00118 pcl::PointCloud<pcl::VFHSignature308> vfh_signature;
00119 pcl::VFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::VFHSignature308> vfh;
00120 vfh.setSearchMethod (tree);
00121 vfh.setInputCloud(cloud_object_cluster.makeShared ());
00122 vfh.setInputNormals(normals);
00123 vfh.compute(vfh_signature);
00124
00125
00126 int k = 1;
00127 flann::Matrix<float> p = flann::Matrix<float>(vfh_signature.points.at (0).histogram, 1, 308);
00128 flann::Matrix<int> k_indices = flann::Matrix<int>(new int[k], 1, k);
00129 flann::Matrix<float> k_distances = flann::Matrix<float>(new float[k], 1, k);
00130 flann_index->knnSearch (p, k_indices, k_distances, k, flann::SearchParams (512));
00131
00132
00133 std::string vfh_model = models.at (k_indices[0][0]).first.c_str ();
00134 size_t pos1 = vfh_model.find_last_of ("/\\");
00135 std::string vfh_label = vfh_model;
00136 if (pos1 != std::string::npos)
00137 {
00138 size_t pos2 = vfh_model.find ('.', pos1+1);
00139 vfh_label = vfh_model.substr (pos1+1, pos2-pos1-1);
00140 }
00141 while (vfh_label.find (".bag") != std::string::npos || vfh_label.find (".pcd") != std::string::npos)
00142 vfh_label = vfh_label.substr (0, vfh_label.size () - 4);
00143
00144
00145 if (vfh_label.find ("cereal") != std::string::npos)
00146 label = "BreakfastCereal";
00147 else if (vfh_label.find ("milk") != std::string::npos)
00148 label = "CowsMilk-Product";
00149 else if (vfh_label.find ("lego") != std::string::npos)
00150 label = "Bowl-Eating";
00151 ROS_INFO ("VFH label for cluster %zu: %s", i, label.c_str ());
00152 }
00153
00154
00155 Eigen3::Vector4f centroid;
00156 pcl::compute3DCentroid (cloud_object_cluster, centroid);
00157
00158
00159 pcl::PointCloud<PointXYZ> cloud_object_cluster_2D = cloud_object_cluster;
00160 for (size_t cp = 0; cp < cloud_object_cluster_2D.points.size (); cp ++)
00161 cloud_object_cluster_2D.points[cp].z /= 1000;
00162
00163
00164 Eigen3::Vector4f centroid2D;
00165 pcl::compute3DCentroid (cloud_object_cluster_2D, centroid2D);
00166 Eigen3::Matrix3f covariance_matrix;
00167 computeCovarianceMatrix (cloud_object_cluster_2D, centroid2D, covariance_matrix);
00168 Eigen3::Matrix3f evecs;
00169 Eigen3::Vector3f evals;
00170 pcl::eigen33 (covariance_matrix, evecs, evals);
00171
00172
00173 Eigen3::Matrix3f rotation;
00174 rotation.row (2) = evecs.col (0);
00175 rotation.row (1) = evecs.col (1);
00176 rotation.row (0) = rotation.row (1).cross (rotation.row (2));
00177
00178 cerr << "Rotation matrix: " << endl;
00179 cerr << rotation << endl;
00180
00181
00182
00183
00184 Eigen3::Array3f min_point_projected (+FLT_MAX, +FLT_MAX, +FLT_MAX);
00185 Eigen3::Array3f max_point_projected (-FLT_MAX, -FLT_MAX, -FLT_MAX);
00186 Eigen3::Array3f min_point_base (+FLT_MAX, +FLT_MAX, +FLT_MAX);
00187 Eigen3::Array3f max_point_base (-FLT_MAX, -FLT_MAX, -FLT_MAX);
00188 for (size_t cp = 0; cp < cloud_object_cluster.points.size (); cp ++)
00189 {
00190 Eigen3::Map<Eigen3::Vector3f> point (&cloud_object_cluster.points[cp].x);
00191 Eigen3::Array3f transformed = rotation * (point - centroid.head<3> ());
00192
00193
00194 min_point_base = min_point_base.min (point.array());
00195 max_point_base = max_point_base.max (point.array());
00196
00197 min_point_projected = min_point_projected.min (transformed);
00198 max_point_projected = max_point_projected.max (transformed);
00199 }
00200
00201
00202 Eigen3::Array3f center_offset = min_point_base + (max_point_base - min_point_base)/2;
00203
00204
00205 rotation.transposeInPlace ();
00206 Eigen3::Quaternion<float> qt (rotation);
00207 qt.normalize ();
00208
00209
00210 mapping_msgs::CollisionObject collision_object;
00211 collision_object.header = cloud.header;
00212 collision_object.id = label;
00213 collision_object.operation.operation = mapping_msgs::CollisionObjectOperation::ADD;
00214 collision_object.shapes.resize (1);
00215 collision_object.shapes[0].type = geometric_shapes_msgs::Shape::BOX;
00216 collision_object.shapes[0].dimensions.resize (3);
00217 collision_object.shapes[0].dimensions[0] = std::max (0.01f, max_point_projected[0] - min_point_projected[0]);
00218 collision_object.shapes[0].dimensions[1] = std::max (0.01f, max_point_projected[1] - min_point_projected[1]);
00219 collision_object.shapes[0].dimensions[2] = std::max (0.01f, max_point_projected[2] - min_point_projected[2]);
00220 collision_object.poses.resize (1);
00221 collision_object.poses[0].position.x = center_offset[0];
00222 collision_object.poses[0].position.y = center_offset[1];
00223 collision_object.poses[0].position.z = center_offset[2];
00224 collision_object.poses[0].orientation.x = qt.x ();
00225 collision_object.poses[0].orientation.y = qt.y ();
00226 collision_object.poses[0].orientation.z = qt.z ();
00227 collision_object.poses[0].orientation.w = qt.w ();
00228 res.table_objects.push_back (collision_object);
00229
00230
00231
00232
00233
00234 }
00235
00236 return (true);
00237 }
00238
00239
00240 int
00241 main (int argc, char** argv)
00242 {
00243
00244 if (argc < 1)
00245 {
00246 print_error ("Syntax is: %s [options] {kdtree.idx} {training_data.h5} {training_data.list}\n", argv[0]);
00247 print_info (" where options are: -metric = metric/distance type: 1 = Euclidean, 2 = Manhattan, 3 = Minkowski, 4 = Max, 5 = HIK, 6 = JM, 7 = Chi-Square (default: "); print_value ("%d", metric); print_info (")\n\n");
00248
00249 print_info (" * note: the metric_type has to match the metric that was used when the tree was created.\n");
00250 print_info (" the last three parameters are optional and represent: the kdtree index file (default: "); print_value ("kdtree.idx"); print_info (")\n"
00251 " the training data used to create the tree (default: "); print_value ("training_data.h5"); print_info (")\n"
00252 " the list of models used in the training data (default: "); print_value ("training_data.list"); print_info (")\n");
00253 return (-1);
00254 }
00255
00256
00257 classify = false;
00258 if (getParameters (argc, argv) == 1)
00259 {
00260 classify = true;
00261
00262 loadFileList (models, training_data_list_file_name);
00263 flann::Matrix<float> data;
00264 flann::load_from_file (data, training_data_h5_file_name, "training_data");
00265 print_highlight ("Training data found. Loaded %d VFH models from %s/%s.\n", (int)data.rows, training_data_h5_file_name.c_str (), training_data_list_file_name.c_str ());
00266
00267 flann_index = new flann::Index< flann::L2<float> > (data, flann::SavedIndexParams (kdtree_idx_file_name));
00268 flann_index->buildIndex ();
00269 }
00270 else
00271 ROS_WARN ("VFH classification is disabled!");
00272
00273
00274
00275 ros::init (argc, argv, "table_objects");
00276
00277 ros::NodeHandle nh;
00278
00279 ros::ServiceServer service = nh.advertiseService ("get_table_objects", getTableObjects);
00280
00281
00282 ros::spin ();
00283
00284 return (0);
00285 }
00286