00001
00002
00003
00004
00005
00006
00007
00008 #include <object_manipulation_tools/grasp_planners/OverheadGraspPlanner.h>
00009 #include <sensor_msgs/PointCloud.h>
00010 #include <sensor_msgs/PointCloud2.h>
00011 #include <sensor_msgs/point_cloud_conversion.h>
00012 #include <pcl/point_cloud.h>
00013 #include <pcl_ros/point_cloud.h>
00014 #include <tf/LinearMath/Scalar.h>
00015 #include <ros/console.h>
00016 #include <tf_conversions/tf_eigen.h>
00017 #include <pcl/common/transforms.h>
00018 #include <pcl/ModelCoefficients.h>
00019 #include <pcl/surface/convex_hull.h>
00020 #include <pcl/segmentation/extract_clusters.h>
00021 #include <pcl/filters/passthrough.h>
00022 #include <pcl/filters/project_inliers.h>
00023 #include <pcl/filters/extract_indices.h>
00024 #include <cmath>
00025
00026 const std::string OverheadGraspPlanner::_GraspPlannerName = GRASP_PLANNER_NAME;
00027 typedef pcl::PointCloud<pcl::PointXYZ> PclCloud;
00028 OverheadGraspPlanner::OverheadGraspPlanner():
00029 _ParamVals(ParameterVals()),
00030 _WorldFrameId("/NO_PARENT"),
00031 _tfListener()
00032 {
00033
00034 fetchParameters(true);
00035
00036 }
00037
00038 OverheadGraspPlanner::~OverheadGraspPlanner() {
00039
00040 }
00041
00042 void OverheadGraspPlanner::fetchParameters(bool useNodeNamespace)
00043 {
00044 std::string paramNamespace = "";
00045 if(useNodeNamespace)
00046 {
00047 paramNamespace = "/" + ros::this_node::getName();
00048 }
00049
00050 ros::param::param(paramNamespace + PARAM_NAME_DEFAULT_PREGRASP_DISTANCE,_ParamVals.DefaultPregraspDistance,
00051 PARAM_DEFAULT_PREGRASP_DISTANCE);
00052 ros::param::param(paramNamespace + PARAM_NAME_PLANE_PROXIMITY_THRESHOLD,_ParamVals.PlaneProximityThreshold,
00053 PARAM_DEFAULT_PLANE_PROXIMITY_THRESHOLD);
00054 ros::param::param(paramNamespace + PARAM_NAME_SEARCH_RADIUS,_ParamVals.SearchRadius,
00055 PARAM_DEFAULT_SEARCH_RADIUS);
00056 ros::param::param(paramNamespace + PARAM_NAME_NUM_CANDIDATE_GRASPS,_ParamVals.NumCandidateGrasps,
00057 PARAM_DEFAULT_NUM_CANDIDATE_GRASPS);
00058 ros::param::param(paramNamespace + PARAM_NAME_GRASP_IN_WORLD_COORDINATES,_ParamVals.GraspInWorldCoordinates,
00059 PARAM_DEFAULT_GRASP_IN_WORLD_COORDINATES);
00060
00061
00062 XmlRpc::XmlRpcValue list;
00063 _UsingDefaultApproachVector = true;
00064 _ParamVals.ApproachVector = PARAM_DEFAULT_APPROACH_VECTOR;
00065
00066 if(ros::param::get(paramNamespace + PARAM_NAME_APPROACH_VECTOR,list) && (list.getType() == XmlRpc::XmlRpcValue::TypeArray) &&
00067 (list.size() > 2) && (list[0].getType() == XmlRpc::XmlRpcValue::TypeDouble))
00068 {
00069 double val;
00070 val = static_cast<double>(list[0]);_ParamVals.ApproachVector.setX(val);
00071 val = static_cast<double>(list[1]);_ParamVals.ApproachVector.setY(val);
00072 val = static_cast<double>(list[2]);_ParamVals.ApproachVector.setZ(val);
00073
00074 _UsingDefaultApproachVector = false;
00075
00076 tf::Vector3 &v = _ParamVals.ApproachVector;
00077 ROS_INFO_STREAM("Overhead Grasp Planer found approach vector: ["<<v.x()<<", "<<v.y()<<", "<<v.z()<<"]");
00078 }
00079 else
00080 {
00081 _UsingDefaultApproachVector = true;
00082
00083 tf::Vector3 &v = _ParamVals.ApproachVector;
00084 ROS_ERROR_STREAM("Overhead Grasp Planner could not read/find approach vector under parameter "<< PARAM_NAME_APPROACH_VECTOR<<
00085 ", will use default vector: ["<<v.x()<<", "<<v.y()<<", "<<v.z()<<"] instead");
00086 }
00087
00088 }
00089
00090 std::string OverheadGraspPlanner::getPlannerName()
00091 {
00092 return _GraspPlannerName;
00093 }
00094
00095 bool OverheadGraspPlanner::planGrasp(object_manipulation_msgs::GraspPlanning::Request &req,
00096 object_manipulation_msgs::GraspPlanning::Response &res)
00097 {
00098
00099 fetchParameters(true);
00100
00101
00102 if(!req.grasps_to_evaluate.empty())
00103 {
00104 object_manipulation_msgs::Grasp candidateGrasp;
00105 std::vector<geometry_msgs::Pose> candidatePoses;
00106 sensor_msgs::JointState jointState;
00107 jointState.name = std::vector<std::string>();
00108 jointState.position = std::vector<double>();
00109 jointState.velocity = std::vector<double>();
00110 candidateGrasp.grasp_pose = geometry_msgs::Pose();
00111
00112
00113 candidateGrasp.grasp_posture = jointState;
00114 candidateGrasp.pre_grasp_posture = jointState;
00115 candidateGrasp.desired_approach_distance = _ParamVals.DefaultPregraspDistance;
00116 candidateGrasp.min_approach_distance = _ParamVals.DefaultPregraspDistance;
00117 for(std::size_t i = 0;i < req.grasps_to_evaluate.size(); i++)
00118 {
00119 object_manipulation_msgs::Grasp &graspEval = req.grasps_to_evaluate[i];
00120 geometry_msgs::Pose &poseToEval = graspEval.grasp_pose;
00121 generateGraspPoses(poseToEval,_ParamVals.NumCandidateGrasps,candidatePoses);
00122 }
00123
00124
00125 for(std::size_t i = 0; i < candidatePoses.size(); i++)
00126 {
00127 candidateGrasp.grasp_pose = candidatePoses[i];
00128 res.grasps.push_back(candidateGrasp);
00129 }
00130
00131 res.error_code.value = object_manipulation_msgs::GraspPlanningErrorCode::SUCCESS;
00132 return true;
00133 }
00134
00135
00136 ros::NodeHandle nh;
00137 std::stringstream stdOut;
00138
00139
00140 sensor_msgs::PointCloud2 inCloudMsg;
00141 sensor_msgs::convertPointCloudToPointCloud2(req.target.cluster,inCloudMsg);
00142 PclCloud cloud = PclCloud(),transformedCloud = PclCloud();
00143 pcl::fromROSMsg(inCloudMsg,cloud);
00144
00145
00146
00147 tf::StampedTransform clusterTf; clusterTf.setIdentity();
00148 tf::StampedTransform clusterTfInWorld;
00149 std::string clusterFrameId = req.target.cluster.header.frame_id;
00150 _WorldFrameId = req.target.reference_frame_id;
00151 try
00152 {
00153 _tfListener.lookupTransform(_WorldFrameId ,clusterFrameId,
00154 ros::Time(0),clusterTf);
00155 }
00156 catch(tf::TransformException ex)
00157 {
00158 ROS_ERROR("%s",std::string(getPlannerName() + " , failed to resolve transform from " +
00159 _WorldFrameId + " to " + clusterFrameId + " \n\t\t" + " tf error msg: " + ex.what()).c_str());
00160 ROS_WARN("%s",std::string(getPlannerName() + ": Will use Identity as cluster transform").c_str());
00161 clusterTf.setData(tf::Transform::getIdentity());
00162 }
00163 clusterTfInWorld.setData(clusterTf);
00164
00165
00166
00167
00168 tf::Transform approachTf;approachTf.setIdentity();
00169 if(!_UsingDefaultApproachVector)
00170 {
00171
00172
00173 tf::Matrix3x3 rotMat;rotMat.setIdentity();
00174 tf::Vector3 zVec = -_ParamVals.ApproachVector.normalize();
00175
00176
00177 tfScalar tolerance = 0.01f;
00178 tfScalar angle = zVec.angle(tf::Vector3(0.0f,0.0f,1.0f));
00179 if(std::abs(angle) > tolerance)
00180 {
00181 tf::Vector3 xVec = tf::Vector3((zVec.cross(tf::Vector3(0.0f,0.0f,1.0f))).normalize());
00182 tf::Vector3 yVec = tf::Vector3((zVec.cross(xVec)).normalize());
00183 rotMat.setValue(xVec.x(),yVec.x(),zVec.x(),
00184 xVec.y(),yVec.y(),zVec.y(),
00185 xVec.z(),yVec.z(),zVec.z());
00186 approachTf = tf::Transform(rotMat,tf::Vector3(0.0f,0.0f,0.0f));
00187
00188 stdOut<< getPlannerName() << ": Approach transform found is:";
00189 for(int i = 0; i < 3; i++)
00190 {
00191 tf::Vector3 row = approachTf.getBasis().getRow(i);
00192 stdOut<< "\n\t" << "|\t" << row.x() << ",\t" << row.y() << ",\t" << row.z()<<",\t" << approachTf.getOrigin()[i]<<"\t|";
00193 }
00194 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00195
00196
00197 clusterTf.setData((approachTf.inverse())*(tf::Transform)clusterTf);
00198 stdOut << getPlannerName() << ": Aligned the input cluster to the transform corresponding to the modified approach vector";
00199 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00200 }
00201 else
00202 {
00203 stdOut << getPlannerName() << ": Requested approach vector and world z-vector are too close, using default world frame";
00204 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00205 }
00206 }
00207
00208
00209
00210 Eigen::Affine3d tfEigen;
00211 tf::TransformTFToEigen(clusterTf,tfEigen);
00212 pcl::transformPointCloud(cloud,cloud,Eigen::Affine3f(tfEigen));
00213
00214
00215 pcl::PointXYZ pointMax, pointMin;
00216 pcl::getMinMax3D(cloud,pointMin,pointMax);
00217
00218
00219 double maxZ = pointMax.z;
00220 stdOut << getPlannerName() << ": Found Min Point at x: "<<pointMin.x<<", y: "<<pointMin.y<<", z: " << pointMin.z;
00221 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00222 stdOut << getPlannerName() << ": Found Max Point at x: "<<pointMax.x<<", y: "<<pointMax.y<<", z: " << maxZ;
00223 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00224
00225
00226
00227 PclCloud::Ptr planeCloud = boost::make_shared<PclCloud>();
00228 pcl::PassThrough<pcl::PointXYZ> passFilter;
00229 passFilter.setInputCloud(boost::make_shared<PclCloud>(cloud));
00230 passFilter.setFilterFieldName("z");
00231 passFilter.setFilterLimits(maxZ - std::abs(_ParamVals.PlaneProximityThreshold),
00232 maxZ + std::abs(_ParamVals.PlaneProximityThreshold));
00233 passFilter.filter(*planeCloud);
00234 if(planeCloud->size() > 0)
00235 {
00236 stdOut << getPlannerName() << ": Found " << planeCloud->size()<<" points at a proximity of "
00237 <<_ParamVals.PlaneProximityThreshold << " to top plane";
00238
00239 BOOST_FOREACH(pcl::PointXYZ point,*planeCloud)
00240 {
00241 stdOut<<"\n\t"<<"x: "<<point.x<<", y: "<<point.y<<", z: "<<point.z;
00242 }
00243 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00244 }
00245 else
00246 {
00247 stdOut<<getPlannerName()<< ": Found not points on top plane, canceling request";
00248 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00249 return false;
00250 }
00251
00252
00253
00254
00255 tf::Vector3 normal = tf::Vector3(0.0f,0.0f,1.0f);
00256 double offset = -normal.dot(tf::Vector3(0.0f,0.0f,maxZ));
00257 pcl::ModelCoefficients::Ptr coefficients = boost::make_shared<pcl::ModelCoefficients>();
00258 coefficients->values.resize(4);
00259 coefficients->values[0] = normal.getX();
00260 coefficients->values[1] = normal.getY();
00261 coefficients->values[2] = normal.getZ();
00262 coefficients->values[3] = offset;
00263 stdOut << getPlannerName() << ": Created Contact Plane with coefficients: " << coefficients->values[0]
00264 << ", " << coefficients->values[1] << ", " << coefficients->values[2] << ", " << coefficients->values[3];
00265 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00266
00267
00268 PclCloud::Ptr projectedCloud = boost::make_shared<PclCloud>();
00269 pcl::ProjectInliers<pcl::PointXYZ> projectObj;
00270 projectObj.setModelType(pcl::SACMODEL_PLANE);
00271 projectObj.setInputCloud(planeCloud);
00272 projectObj.setModelCoefficients(coefficients);
00273 projectObj.filter(*projectedCloud);
00274
00275
00276
00277
00278
00279
00280 if(_ParamVals.SearchRadius > 0)
00281 {
00282 bool continueSearch = true;
00283 unsigned int index = 0;
00284 pcl::search::KdTree<pcl::PointXYZ>::Ptr searchTree = boost::make_shared<pcl::search::KdTree<pcl::PointXYZ> >();
00285 std::vector<int> indices;
00286 std::vector<float> sqDistances;
00287 searchTree->setInputCloud(projectedCloud);
00288
00289 while(continueSearch)
00290 {
00291 int found = searchTree->radiusSearch(projectedCloud->points[index],
00292 _ParamVals.SearchRadius,indices,sqDistances);
00293
00294 if(found > 0)
00295 {
00296 continueSearch = false;
00297 }
00298 else
00299 {
00300 index++;
00301 if(index == projectedCloud->points.size())
00302 {
00303 ROS_ERROR_STREAM(getPlannerName() << ": Did not find points within search radius: " <<
00304 _ParamVals.SearchRadius <<", exiting");
00305
00306 res.grasps = std::vector<object_manipulation_msgs::Grasp>();
00307 res.error_code.value = object_manipulation_msgs::GraspPlanningErrorCode::OTHER_ERROR;
00308
00309 return false;
00310 }
00311 }
00312 }
00313
00314
00315 pcl::PointIndices::Ptr inliers = boost::make_shared<pcl::PointIndices>();
00316 inliers->indices = indices;
00317 pcl::ExtractIndices<pcl::PointXYZ> extractObj;
00318 extractObj.setInputCloud(projectedCloud);
00319 extractObj.setIndices(inliers);
00320 extractObj.setNegative(false);
00321 extractObj.filter(*projectedCloud);
00322
00323 stdOut << getPlannerName() << ": Found " << projectedCloud->size() << " points within search radius: " <<
00324 _ParamVals.SearchRadius;
00325 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00326 }
00327 else
00328 {
00329 ROS_INFO_STREAM(getPlannerName()<<": search radius <= 0, skipping search and using all points found instead");
00330 }
00331
00332
00333 Eigen::Vector4f centroid;
00334 pcl::compute3DCentroid(*projectedCloud,centroid);
00335 stdOut << getPlannerName() << ": Found centroid at: x = " << centroid[0] << ", y = " << centroid[1] << ", z = " << centroid[2];
00336 ROS_INFO("%s",stdOut.str().c_str());stdOut.str("");
00337
00338
00339
00340 geometry_msgs::Pose graspPose;
00341 sensor_msgs::JointState jointsGrasp;
00342 sensor_msgs::JointState jointsPreGrasp;
00343 jointsPreGrasp.name = jointsGrasp.name = std::vector<std::string>();
00344 jointsPreGrasp.position = jointsGrasp.position = std::vector<double>();
00345
00346
00347 tf::Transform graspTf;
00348 tf::Quaternion rot = tf::Quaternion::getIdentity();
00349 rot.setRotation(tf::Vector3(1.0f,0.0f,0.0f),M_PI);
00350 graspTf.setOrigin(tf::Vector3(centroid[0],centroid[1],centroid[2]));
00351 graspTf.setRotation(rot);
00352
00353 if(_UsingDefaultApproachVector)
00354 {
00355 graspTf = approachTf*graspTf;
00356 }
00357
00358 if(!_ParamVals.GraspInWorldCoordinates)
00359 {
00360 graspTf = clusterTfInWorld.inverse()*graspTf;
00361 }
00362
00363
00364 std::vector<object_manipulation_msgs::Grasp> grasps;
00365
00366
00367 if(_ParamVals.NumCandidateGrasps > 1)
00368 {
00369 int numGrasps = _ParamVals.NumCandidateGrasps;
00370 tfScalar angle = tfScalar(2*M_PI/(double(numGrasps)));
00371 tf::Transform rotatedGraspTf = graspTf;
00372 for(int i = 1; i < numGrasps - 1;i++)
00373 {
00374
00375 rotatedGraspTf.setRotation(rotatedGraspTf.getRotation()*tf::Quaternion(tf::Vector3(0.0f,0.0f,1.0f),angle*i));
00376
00377
00378 object_manipulation_msgs::Grasp candidateGrasp;
00379 candidateGrasp.grasp_pose = geometry_msgs::Pose();
00380 tf::quaternionTFToMsg(graspTf.getRotation(),candidateGrasp.grasp_pose.orientation);
00381 candidateGrasp.grasp_pose.position.x = rotatedGraspTf.getOrigin().getX();
00382 candidateGrasp.grasp_pose.position.y = rotatedGraspTf.getOrigin().getY();
00383 candidateGrasp.grasp_pose.position.z = rotatedGraspTf.getOrigin().getZ();
00384 candidateGrasp.grasp_posture = jointsGrasp;
00385 candidateGrasp.pre_grasp_posture = jointsPreGrasp;
00386 candidateGrasp.desired_approach_distance = _ParamVals.DefaultPregraspDistance;
00387 candidateGrasp.min_approach_distance = _ParamVals.DefaultPregraspDistance;
00388
00389
00390 grasps.push_back(candidateGrasp);
00391 }
00392 }
00393 else
00394 {
00395 object_manipulation_msgs::Grasp candidateGrasp;
00396 candidateGrasp.grasp_pose = geometry_msgs::Pose();
00397 tf::quaternionTFToMsg(graspTf.getRotation(),candidateGrasp.grasp_pose.orientation);
00398 candidateGrasp.grasp_pose.position.x = graspTf.getOrigin().getX();
00399 candidateGrasp.grasp_pose.position.y = graspTf.getOrigin().getY();
00400 candidateGrasp.grasp_pose.position.z = graspTf.getOrigin().getZ();
00401 candidateGrasp.grasp_posture = jointsGrasp;
00402 candidateGrasp.pre_grasp_posture = jointsPreGrasp;
00403 candidateGrasp.desired_approach_distance = _ParamVals.DefaultPregraspDistance;
00404 candidateGrasp.min_approach_distance = _ParamVals.DefaultPregraspDistance;
00405
00406
00407 grasps.push_back(candidateGrasp);
00408 }
00409
00410
00411
00412
00413 res.grasps = grasps;
00414 res.error_code.value = object_manipulation_msgs::GraspPlanningErrorCode::SUCCESS;
00415 return true;
00416 }
00417
00418 void OverheadGraspPlanner::generateGraspPoses(const geometry_msgs::Pose &pose,int numCandidates,
00419 std::vector<geometry_msgs::Pose> &poses)
00420 {
00421 tf::Transform graspTf = tf::Transform::getIdentity();
00422 tf::Transform candidateTf;
00423 tfScalar angle = tfScalar(2*M_PI/(double(numCandidates)));
00424
00425
00426 tf::poseMsgToTF(pose,graspTf);
00427
00428 for(int i = 0; i < numCandidates; i++)
00429 {
00430 candidateTf = graspTf*tf::Transform(tf::Quaternion(_ParamVals.ApproachVector,i*angle),
00431 tf::Vector3(0.0f,0.0f,0.0f));
00432
00433
00434 geometry_msgs::Pose candidatePose = geometry_msgs::Pose();
00435 tf::poseTFToMsg(candidateTf,candidatePose);
00436 poses.push_back(candidatePose);
00437 }
00438 }
00439
00440