OverheadGraspPlanner.cpp
Go to the documentation of this file.
00001 /*
00002  * OverheadGraspPlanner.cpp
00003  *
00004  *  Created on: Aug 10, 2012
00005  *      Author: jnicho
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         // TODO Auto-generated constructor stub
00034         fetchParameters(true);
00035 
00036 }
00037 
00038 OverheadGraspPlanner::~OverheadGraspPlanner() {
00039         // TODO Auto-generated destructor stub
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         // checking if param containing z vector values exists
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         // updating parameters
00099         fetchParameters(true);
00100 
00101         // checking if grasp to evaluate were passed
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                 // generating candidate poses
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                 // storing all candidate poses
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         // local variables
00136         ros::NodeHandle nh;
00137         std::stringstream stdOut;
00138 
00139         // converting message to pcl type
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         // -------------------------------- resolving cluster reference frame in world coordinates
00147         tf::StampedTransform clusterTf; clusterTf.setIdentity();// will be modified if alternate approach direction is used
00148         tf::StampedTransform clusterTfInWorld; // will remain fixed
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         // -------------------------------- transforming to alternate approach vector
00167         // This transformation will allow finding the highest point relative to the opposite direction of the approach vector
00168         tf::Transform approachTf;approachTf.setIdentity();
00169         if(!_UsingDefaultApproachVector)
00170         {
00171                 // use cros-product and matrix inverse in order to compute transform with modified z-direction (approach vector)
00172                 // use the negative of the modified z-direction vector for all computations.
00173                 tf::Matrix3x3 rotMat;rotMat.setIdentity();
00174                 tf::Vector3 zVec = -_ParamVals.ApproachVector.normalize();
00175 
00176                 // checking that both z vectors have different directions
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()); //z_approach x z_world (0,0,1)
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                         // transforming cluster transform to new coordinates
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         // -------------------------------- transforming to correct reference frame --------------------------------------
00210         Eigen::Affine3d tfEigen;
00211         tf::TransformTFToEigen(clusterTf,tfEigen);
00212         pcl::transformPointCloud(cloud,cloud,Eigen::Affine3f(tfEigen));
00213 
00214         // finding bounding box
00215         pcl::PointXYZ pointMax, pointMin;
00216         pcl::getMinMax3D(cloud,pointMin,pointMax);
00217 
00218         // extracting highest point from bounding box
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         // finding points near or on top plane
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                 // printing found points
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         // projecting filtered points onto top plane
00254         // defining plane coefficients
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         // projecting points
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         // finding all points within a search radius
00277         /*      This search attempts to find points that belong to the same object while removing those that are not part
00278          * of the object of interest but were found to be close enough to the plane.
00279         */
00280         if(_ParamVals.SearchRadius > 0) // skip if search radius <= 0 and use all points instead
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                 // extracting points
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         // finding centroid of projected point cloud (should work well for objects with relative degree of symmetry)
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         // -------------------------------- generating candidate grasps  --------------------------------
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         // creating first grasp pose
00347         tf::Transform graspTf;
00348         tf::Quaternion rot = tf::Quaternion::getIdentity();
00349         rot.setRotation(tf::Vector3(1.0f,0.0f,0.0f),M_PI); // rotates 180 about x in order to invert the direction of the approach vector (z-vector)
00350         graspTf.setOrigin(tf::Vector3(centroid[0],centroid[1],centroid[2]));
00351         graspTf.setRotation(rot);
00352 
00353         if(_UsingDefaultApproachVector)
00354         {
00355                 graspTf = approachTf*graspTf; // transforming to world coordinates
00356         }
00357 
00358         if(!_ParamVals.GraspInWorldCoordinates) // transform to object coordinates
00359         {
00360                 graspTf = clusterTfInWorld.inverse()*graspTf;
00361         }
00362 
00363         // creating grasp array
00364         std::vector<object_manipulation_msgs::Grasp> grasps;
00365 
00366         // computing additional candidate transforms
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                         // rotating by (angle x i) about z axis of initial grasp pose
00375                         rotatedGraspTf.setRotation(rotatedGraspTf.getRotation()*tf::Quaternion(tf::Vector3(0.0f,0.0f,1.0f),angle*i));
00376 
00377                         // filling candidate grasp message data
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                         // adding grasp
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                 // adding grasp
00407                 grasps.push_back(candidateGrasp);
00408         }
00409 
00410 //      object_manipulation_msgs::GraspPlanningErrorCode errorCode;
00411 //      errorCode.value = errorCode.SUCCESS;
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         // converting initial pose to tf
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 //              candidateTf = graspTf;
00433 //              candidateTf.setRotation(tf::Quaternion(_ParamVals.ApproachVector,i*angle));
00434                 geometry_msgs::Pose candidatePose = geometry_msgs::Pose();
00435                 tf::poseTFToMsg(candidateTf,candidatePose);
00436                 poses.push_back(candidatePose);
00437         }
00438 }
00439 
00440 


object_manipulation_tools
Author(s): Jnicho
autogenerated on Mon Oct 6 2014 00:56:17