objects_database_node_simple.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2009, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Willow Garage nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00035 // Author(s): Matei Ciocarlie
00036 
00039 
00040 #include <vector>
00041 #include <boost/shared_ptr.hpp>
00042 
00043 #include <ros/ros.h>
00044 
00045 #include <actionlib/server/simple_action_server.h>
00046 
00047 #include "moveit/robot_model/robot_model.h"
00048 #include <moveit/robot_model/joint_model_group.h>
00049 #include <moveit/robot_model_loader/robot_model_loader.h>
00050 
00051 #include <visualization_msgs/MarkerArray.h>
00052 
00053 #include <manipulation_msgs/GraspPlanning.h>
00054 #include <manipulation_msgs/GraspPlanningAction.h>
00055 
00056 #include "household_objects_database/objects_database.h"
00057 #include <household_objects_database_msgs/TranslateRecognitionId.h>
00058 #include <household_objects_database_msgs/GetModelList.h>
00059 
00060 #include <tf/transform_datatypes.h>
00061 
00062 const std::string GRASP_PLANNING_SERVICE_NAME = "database_grasp_planning";
00063 const std::string GET_MODELS_SERVICE_NAME = "get_model_list";
00064 const std::string VISUALIZE_GRASPS = "visualize_grasps";
00065 
00066 using namespace household_objects_database_msgs;
00067 using namespace household_objects_database;
00068 using namespace manipulation_msgs;
00069 
00070 geometry_msgs::Vector3 negate(const geometry_msgs::Vector3 &vec)
00071 {
00072   geometry_msgs::Vector3 v;
00073   v.x = - vec.x;
00074   v.y = - vec.y;
00075   v.z = - vec.z;
00076   return v;
00077 }
00078 
00079 bool greaterScaledQuality(const boost::shared_ptr<DatabaseGrasp> &grasp1, 
00080                           const boost::shared_ptr<DatabaseGrasp> &grasp2)
00081 {
00082   if (grasp1->scaled_quality_.data() > grasp2->scaled_quality_.data()) return true;
00083   return false;
00084 }
00085 
00087 
00089 class ObjectsDatabaseNode
00090 {
00091 private:
00093   ros::NodeHandle priv_nh_;
00094 
00096   ros::NodeHandle root_nh_;
00097 
00099   ros::ServiceServer grasp_planning_srv_;
00100 
00101   ros::Publisher visualize_grasps_publisher_;
00102   
00104   ObjectsDatabase *database_;
00105 
00106   std::map<std::string, geometry_msgs::Vector3> approach_direction_;
00107 
00108   std::map<std::string, std::string> database_hand_ids_;
00109   
00111   //  tf::TransformListener listener_;
00112 
00114 
00115   std::string grasp_ordering_method_;
00116 
00117   const robot_model::RobotModelConstPtr &robot_model_;  
00118 
00120   bool getModelsCB(GetModelList::Request &request, 
00121                    GetModelList::Response &response)
00122   {
00123     if (!database_)
00124     {
00125       response.return_code.code = response.return_code.DATABASE_NOT_CONNECTED;
00126       return true;
00127     }
00128     std::vector< boost::shared_ptr<DatabaseScaledModel> > models;
00129     if (!database_->getScaledModelsBySet(models, request.model_set))
00130     {
00131       response.return_code.code = response.return_code.DATABASE_QUERY_ERROR;
00132       return true;
00133     }
00134     for (size_t i=0; i<models.size(); i++)
00135     {
00136       response.model_ids.push_back( models[i]->id_.data() );
00137     }
00138     response.return_code.code = response.return_code.SUCCESS;
00139     return true;
00140   }
00141 
00142   inline std::vector<double> getVectorDoubleParam(const std::string &name)
00143   {
00144     std::vector<double> values;
00145     XmlRpc::XmlRpcValue list;
00146     if (!root_nh_.getParamCached(name, list))
00147     {
00148       ROS_ERROR("Could not find parameter %s", name.c_str());
00149       return values;
00150     }
00151     if (list.getType() != XmlRpc::XmlRpcValue::TypeArray)
00152     {
00153       ROS_ERROR("Bad parameter type %s", name.c_str());
00154       return values;
00155     }
00156     for (int32_t i=0; i<list.size(); i++)
00157     {
00158       if (list[i].getType() != XmlRpc::XmlRpcValue::TypeDouble)
00159       {
00160         ROS_ERROR("Bad parameter %s", name.c_str());
00161       }
00162       values.push_back( static_cast<double>(list[i]) );
00163     }
00164     return values;
00165   }
00166 
00167   inline geometry_msgs::Vector3 approachDirection(const std::string &name, bool &found)
00168   {
00169     geometry_msgs::Vector3 app;
00170     std::vector<double> values = getVectorDoubleParam(name);
00171     if ( values.size() != 3 ) 
00172     {
00173       ROS_ERROR("Bad parameter name %s", name.c_str());
00174       found = false;      
00175       return app;
00176     }
00177     double length = sqrt( values[0]*values[0] + values[1]*values[1] + values[2]*values[2] );
00178     if ( fabs(length) < 1.0e-5 )
00179     {
00180       ROS_ERROR("Bad parameter name %s", name.c_str());
00181       found = false;      
00182       return app;
00183     }
00184     app.x = values[0] / length;
00185     app.y = values[1] / length;
00186     app.z = values[2] / length;
00187     found = true;    
00188     return app;
00189   }
00190 
00191   geometry_msgs::Pose multiplyPoses(const geometry_msgs::Pose &p1, 
00192                                     const geometry_msgs::Pose &p2)
00193   {
00194     tf::Transform t1;
00195     tf::poseMsgToTF(p1, t1);
00196     tf::Transform t2;
00197     tf::poseMsgToTF(p2, t2);
00198     t2 = t1 * t2;        
00199     geometry_msgs::Pose out_pose;
00200     tf::poseTFToMsg(t2, out_pose);
00201     return out_pose;
00202   }
00203 
00204   bool translateIdCB(TranslateRecognitionId::Request &request, TranslateRecognitionId::Response &response)
00205   {
00206     std::vector<boost::shared_ptr<DatabaseScaledModel> > models;
00207     if (!database_)
00208     {
00209       ROS_ERROR("Database not connected");
00210       response.result = response.DATABASE_ERROR;
00211       return true;
00212     }
00213     if (!database_->getScaledModelsByRecognitionId(models, request.recognition_id))
00214     {
00215       ROS_ERROR("Query failed");
00216       response.result = response.DATABASE_ERROR;
00217       return true;
00218     }
00219     if (models.empty())
00220     {
00221       ROS_ERROR("Recognition id %s not found", request.recognition_id.c_str());
00222       response.result = response.ID_NOT_FOUND;
00223       return true;
00224     }
00225     response.household_objects_id = models[0]->id_.data();
00226     if (models.size() > 1) ROS_WARN("Multiple matches found for recognition id %s. Returning the first one.",
00227                                     request.recognition_id.c_str());
00228     response.result = response.SUCCESS;
00229     return true;
00230   }
00231 
00232   void visualizeGrasps(const std::vector<Grasp> &grasps)
00233   {    
00234     visualization_msgs::MarkerArray marker;
00235     for(std::size_t i=0; i < grasps.size(); ++i)
00236     {
00237       visualization_msgs::Marker m;
00238       m.action = m.ADD;      
00239       m.type = m.ARROW;
00240       m.ns = "grasps";
00241       m.id = i;
00242       m.pose = grasps[i].grasp_pose.pose;
00243       m.header = grasps[i].grasp_pose.header;
00244       marker.markers.push_back(m);      
00245 
00246       m.scale.x = 0.05;
00247       m.scale.y = 0.01;
00248       m.scale.z = 0.01;
00249 
00250       m.color.r = 1.0;
00251       m.color.g = 0.0;
00252       m.color.b = 0.0;
00253       m.color.a = 1.0;
00254 
00255       marker.markers.push_back(m);      
00256     }
00257     visualize_grasps_publisher_.publish(marker);
00258   }
00259   
00260   //retrieves all grasps from the database for a given target
00261   bool getGrasps(const GraspableObject &target, const std::string &arm_name, 
00262                  std::vector<Grasp> &grasps, GraspPlanningErrorCode &error_code)
00263   {
00264     if (!database_)
00265     {
00266       ROS_ERROR("Database grasp planning: database not connected");
00267       error_code.value = error_code.OTHER_ERROR;
00268       return false;
00269     }
00270     std::vector< boost::shared_ptr<DatabaseScaledModel> > models;
00271     /*    if (database_->getScaledModelsList(models))
00272     {
00273       for (size_t i=0; i<models.size(); i++)
00274         {
00275           ROS_INFO("Model id: %d", models[i]->id_.data());
00276         }
00277         }*/
00278 
00279     if (target.potential_models.empty())
00280     {
00281       ROS_ERROR("Database grasp planning: no potential model information in grasp planning target");
00282       error_code.value = error_code.OTHER_ERROR;
00283       return false;      
00284     }
00285 
00286     if (target.potential_models.size() > 1)
00287     {
00288       ROS_WARN("Database grasp planning: target has more than one potential models. "
00289                "Returning grasps for first model only");
00290     }
00291 
00292     // get the object id. If the call has specified a new, ORK-style object type, convert that to a
00293     // household_database model_id.
00294     int model_id = target.potential_models[0].model_id;
00295     if (!target.potential_models[0].type.key.empty())
00296     {
00297       if (model_id == 0)
00298       {
00299         TranslateRecognitionId translate;
00300         translate.request.recognition_id = target.potential_models[0].type.key;
00301         translateIdCB(translate.request, translate.response);
00302         if (translate.response.result == translate.response.SUCCESS)
00303         {
00304           model_id = translate.response.household_objects_id;
00305           ROS_DEBUG_STREAM("Grasp planning: translated ORK key " << target.potential_models[0].type.key << 
00306                            " into model_id " << model_id);
00307         }
00308         else
00309         {
00310           ROS_ERROR("Failed to translate ORK key into household model_id");
00311           error_code.value = error_code.OTHER_ERROR;
00312           return false;      
00313         }
00314       }
00315       else
00316       {
00317         ROS_DEBUG("Grasp planning: both model_id and ORK key specified in GraspableObject; using model_id and ignoring ORK key");
00318       }
00319     }
00320     //    HandDescription hd;
00321     std::vector<std::string> hand_ids = robot_model_->getJointModelGroup(arm_name)->getAttachedEndEffectorNames();
00322     
00323     for(std::size_t i=0; i < hand_ids.size(); ++i)
00324     {      
00325       std::map<std::string, std::string>::const_iterator database_hand_id 
00326         = database_hand_ids_.find(hand_ids[i]);
00327 
00328       if(database_hand_id == database_hand_ids_.end())
00329       {        
00330         ROS_INFO("Could not find database hand id for %s", hand_ids[i].c_str());        
00331         return false;
00332       }
00333             
00334       std::map<std::string, geometry_msgs::Vector3>::const_iterator approach_direction = 
00335         approach_direction_.find(hand_ids[i]);
00336       
00337       if(approach_direction == approach_direction_.end())
00338       {        
00339         ROS_INFO("Could not find database hand id for %s", hand_ids[i].c_str());        
00340         return false;
00341       }
00342 
00343       const std::pair<std::string, std::string>& end_effector_parent = 
00344         robot_model_->getEndEffector(hand_ids[i])->getEndEffectorParentGroup();
00345 
00346       //retrieve the raw grasps from the database
00347       std::vector< boost::shared_ptr<DatabaseGrasp> > db_grasps;
00348       if (!database_->getClusterRepGrasps(model_id, database_hand_id->second, db_grasps))
00349       {
00350         ROS_ERROR("Database grasp planning: database query error");
00351         error_code.value = error_code.OTHER_ERROR;
00352         return false;
00353       }
00354       ROS_INFO("Database object node: retrieved %u grasps from database", (unsigned int)db_grasps.size());
00355       
00356       //order grasps based on request
00357       if (grasp_ordering_method_ == "random") 
00358       {
00359         ROS_INFO("Randomizing grasps");
00360         std::random_shuffle(db_grasps.begin(), db_grasps.end());
00361       }
00362       else if (grasp_ordering_method_ == "quality")
00363       {
00364         ROS_INFO("Sorting grasps by scaled quality");
00365         std::sort(db_grasps.begin(), db_grasps.end(), greaterScaledQuality);
00366       }
00367       else
00368       {
00369         ROS_WARN("Unknown grasp ordering method requested -- randomizing grasp order");
00370         std::random_shuffle(db_grasps.begin(), db_grasps.end());
00371       }
00372       
00373       //convert to the Grasp data type
00374       std::vector< boost::shared_ptr<DatabaseGrasp> >::iterator it;
00375       for (it = db_grasps.begin(); it != db_grasps.end(); it++)
00376       {
00377         ROS_ASSERT( (*it)->final_grasp_posture_.get().joint_angles_.size() == 
00378                     (*it)->pre_grasp_posture_.get().joint_angles_.size() );
00379         Grasp grasp;
00380         std::vector<std::string> joint_names = robot_model_->getEndEffector(hand_ids[i])->getJointModelNames();
00381         if (database_hand_id->second != "WILLOW_GRIPPER_2010")
00382         {         
00383           //check that the number of joints in the ROS description of this hand
00384           //matches the number of values we have in the database
00385           if (joint_names.size() != (*it)->final_grasp_posture_.get().joint_angles_.size())
00386           {
00387             ROS_ERROR("Database grasp specification does not match ROS description of hand. "
00388                       "Hand is expected to have %d joints, but database grasp specifies %d values", 
00389                       (int)joint_names.size(), (int)(*it)->final_grasp_posture_.get().joint_angles_.size());
00390             continue;
00391           }
00392           //for now we silently assume that the order of the joints in the ROS description of
00393           //the hand is the same as in the database description
00394           grasp.pre_grasp_posture.name = joint_names;
00395           grasp.grasp_posture.name = joint_names;
00396           grasp.pre_grasp_posture.position = (*it)->pre_grasp_posture_.get().joint_angles_;
00397           grasp.grasp_posture.position = (*it)->final_grasp_posture_.get().joint_angles_;       
00398         }
00399         else
00400         {
00401           //unfortunately we have to hack this, as the grasp is really defined by a single
00402           //DOF, but the urdf for the PR2 gripper is not well set up to do that
00403           /*          if ( joint_names.size() != 4 || (*it)->final_grasp_posture_.get().joint_angles_.size() != 1)
00404           {
00405             ROS_ERROR("PR2 gripper specs and database grasp specs do not match expected values");
00406             continue;
00407             }*/
00408           grasp.pre_grasp_posture.name = joint_names;
00409           grasp.grasp_posture.name = joint_names;
00410           //replicate the single value from the database 4 times
00411           grasp.pre_grasp_posture.position.resize( joint_names.size(), 
00412                                                    (*it)->pre_grasp_posture_.get().joint_angles_.at(0));
00413           grasp.grasp_posture.position.resize( joint_names.size(), 
00414                                                (*it)->final_grasp_posture_.get().joint_angles_.at(0));
00415         }
00416         //for now the effort is not in the database so we hard-code it in here
00417         //this will change at some point
00418         grasp.grasp_posture.effort.resize(joint_names.size(), 50);
00419         grasp.pre_grasp_posture.effort.resize(joint_names.size(), 100);
00420         //approach and retreat directions are based on pre-defined hand characteristics
00421         //note that this silently implies that the same convention was used by whatever planner (or human) stored
00422         //this grasps in the database to begin with
00423         grasp.approach.direction.header.stamp = ros::Time::now();
00424         grasp.approach.direction.header.frame_id = end_effector_parent.second;
00425         grasp.approach.direction.vector = approach_direction->second;
00426         grasp.retreat.direction.header.stamp = ros::Time::now();
00427         grasp.retreat.direction.header.frame_id = end_effector_parent.second;
00428         grasp.retreat.direction.vector = negate( approach_direction->second );      
00429         //min and desired approach distances are the same for all grasps
00430         grasp.approach.desired_distance = 0.10;
00431         grasp.approach.min_distance = 0.05;
00432         grasp.retreat.desired_distance = 0.10;
00433         grasp.retreat.min_distance = 0.05;
00434         //the pose of the grasp
00435         geometry_msgs::Pose grasp_pose = (*it)->final_grasp_pose_.get().pose_;
00436         //convert it to the frame of the detection
00437         grasp_pose = multiplyPoses(target.potential_models[0].pose.pose, grasp_pose);
00438         //store the scaled quality
00439         grasp.grasp_quality = (*it)->scaled_quality_.get();
00440         grasp.grasp_pose.pose = grasp_pose;
00441         grasp.grasp_pose.header.frame_id = target.reference_frame_id;
00442         grasp.grasp_pose.header.stamp = ros::Time::now();
00443         
00444         //insert the new grasp in the list
00445         grasps.push_back(grasp);
00446       }
00447       visualizeGrasps(grasps);      
00448       ROS_INFO("Database grasp planner: returning %u grasps for end-effector %s"
00449                , (unsigned int) grasps.size(), hand_ids[i].c_str());
00450     }    
00451     error_code.value = error_code.SUCCESS;
00452     return true;
00453   }
00454 
00456   bool graspPlanningCB(GraspPlanning::Request &request, GraspPlanning::Response &response)
00457   {
00458     getGrasps(request.target, request.arm_name, response.grasps, response.error_code);
00459     return true;
00460   }
00461 
00462 public:
00463   ObjectsDatabaseNode(const robot_model::RobotModelConstPtr &robot_model) : priv_nh_("~"), root_nh_(""), robot_model_(robot_model)
00464   {
00465     //initialize database connection
00466     ROS_INFO("Starting up");
00467     std::string database_host, database_port, database_user, database_pass, database_name;
00468     root_nh_.param<std::string>("/household_objects_database/database_host", database_host, "");
00469     int port_int;
00470     root_nh_.param<int>("/household_objects_database/database_port", port_int, -1);
00471     std::stringstream ss; ss << port_int; database_port = ss.str();
00472     root_nh_.param<std::string>("/household_objects_database/database_user", database_user, "");
00473     root_nh_.param<std::string>("/household_objects_database/database_pass", database_pass, "");
00474     root_nh_.param<std::string>("/household_objects_database/database_name", database_name, "");
00475 
00476     visualize_grasps_publisher_ = root_nh_.advertise<visualization_msgs::MarkerArray>("visualize_grasps", 20);
00477 
00478     database_ = new ObjectsDatabase(database_host, database_port, database_user, database_pass, database_name);
00479     if (!database_->isConnected())
00480     {
00481       ROS_ERROR("ObjectsDatabaseNode: failed to open model database on host "
00482                 "%s, port %s, user %s with password %s, database %s. Unable to do grasp "
00483                 "planning on database recognized objects. Exiting.",
00484                 database_host.c_str(), database_port.c_str(), 
00485                 database_user.c_str(), database_pass.c_str(), database_name.c_str());
00486       delete database_; database_ = NULL;
00487     }
00488 
00489     //advertise services
00490     grasp_planning_srv_ = root_nh_.advertiseService(GRASP_PLANNING_SERVICE_NAME, 
00491                                                     &ObjectsDatabaseNode::graspPlanningCB, this);
00492 
00493     priv_nh_.param<std::string>("grasp_ordering_method", grasp_ordering_method_, "random");
00494     ROS_INFO("Database connected");
00495     for(std::size_t i=0; i < robot_model_->getJointModelGroupNames().size(); ++i)
00496     {
00497       ROS_INFO("%d %s", (int)i, robot_model_->getJointModelGroupNames()[i].c_str());
00498       geometry_msgs::Vector3 default_approach_direction;
00499       default_approach_direction.x = 1.0; 
00500       default_approach_direction.y = 0.0;
00501       default_approach_direction.z = 0.0;      
00502       const robot_model::JointModelGroup* jmg = robot_model_->getJointModelGroup(robot_model_->getJointModelGroupNames()[i]);
00503       if(jmg && jmg->isEndEffector())
00504       {
00505         std::string database_id;        
00506         root_nh_.param<std::string>(jmg->getEndEffectorName()+"/database_id", database_id, "");
00507         if(!database_id.empty())
00508         {
00509           database_hand_ids_[jmg->getEndEffectorName()] = database_id;        
00510           ROS_INFO("Found database id: %s for %s", database_id.c_str(), jmg->getEndEffectorName().c_str());
00511         }
00512         bool found = false;        
00513         geometry_msgs::Vector3 approach_direction = 
00514           approachDirection(jmg->getEndEffectorName()+"/approach_direction", found);
00515         if(found)
00516         {
00517           approach_direction_[jmg->getEndEffectorName()] = approach_direction;        
00518           ROS_INFO("Found approach directions for %s: %f %f %f", 
00519                    jmg->getEndEffectorName().c_str(),
00520                    approach_direction.x,
00521                    approach_direction.y,
00522                    approach_direction.z);
00523         }
00524         //        else
00525         //          approach_direction_[jmg->getEndEffectorName()] = default_approach_direction;                  
00526       }      
00527     }    
00528   }
00529 
00530   ~ObjectsDatabaseNode()
00531   {
00532     delete database_;
00533   }
00534 };
00535 
00536 int main(int argc, char **argv)
00537 {
00538   ros::init(argc, argv, "objects_database_node");
00539 
00540   ros::AsyncSpinner spinner(1);
00541   spinner.start();
00542   
00543   /* Load the robot model */
00544   robot_model_loader::RobotModelLoader robot_model_loader("robot_description"); 
00545 
00546   ROS_INFO("Loaded robot model");  
00547   /* Get a shared pointer to the model */
00548   robot_model::RobotModelPtr robot_model = robot_model_loader.getModel();    
00549   ROS_INFO("Loaded robot model");  
00550   
00551   ObjectsDatabaseNode node(robot_model);
00552 
00553   ros::waitForShutdown();
00554   return 0;  
00555 }


household_objects_database
Author(s): Matei Ciocarlie, except for source files individually marked otherwise
autogenerated on Thu Aug 27 2015 13:24:24