HighLevelActions.cpp
Go to the documentation of this file.
00001 
00011 #include <boost/algorithm/string.hpp>
00012 #include <carl_dynamixel/LookAtFrame.h>
00013 #include <interactive_world_tools/HighLevelActions.h>
00014 #include <ros/package.h>
00015 #include <std_srvs/Empty.h>
00016 #include <tf2_ros/transform_listener.h>
00017 #include <yaml-cpp/yaml.h>
00018 
00019 using namespace std;
00020 using namespace rail::interactive_world;
00021 
00022 HighLevelActions::HighLevelActions()
00023     : private_node_("~"), nav_ac_("/move_base", true), ac_wait_time_(AC_WAIT_TIME), fixed_frame_("map"),
00024       home_arm_ac_("/carl_moveit_wrapper/common_actions/arm_action", true),
00025       drive_and_search_as_(
00026           private_node_, "drive_and_search", boost::bind(&HighLevelActions::driveAndSearch, this, _1), false
00027       ),
00028       drive_to_surface_as_(
00029           private_node_, "drive_to_surface", boost::bind(&HighLevelActions::driveToSurface, this, _1), false
00030       )
00031 {
00032   // set defaults
00033   recognized_objects_counter_ = 0;
00034   string world_file(ros::package::getPath("interactive_world_tools") + "/config/world.yaml");
00035 
00036   // grab any parameters we need
00037   private_node_.getParam("world_config", world_file);
00038   private_node_.getParam("fixed_frame", fixed_frame_);
00039 
00040   // setup services and topic
00041   look_at_frame_srv_ = node_.serviceClient<carl_dynamixel::LookAtFrame>("/asus_controller/look_at_frame");
00042   segment_srv_ = node_.serviceClient<std_srvs::Empty>("/rail_segmentation/segment");
00043   find_surface_srv_ = private_node_.advertiseService("find_surface", &HighLevelActions::findSurfaceCallback, this);
00044   get_surfaces_srv_ = private_node_.advertiseService("get_surfaces", &HighLevelActions::getSurfacesCallback, this);
00045   transform_to_surface_frame_srv_ = private_node_.advertiseService("transform_to_surface_frame",
00046                                                                    &HighLevelActions::transformToSurfaceFrame, this);
00047   recognized_objects_sub_ = node_.subscribe("/object_recognition_listener/recognized_objects", 1,
00048                                             &HighLevelActions::recognizedObjectsCallback, this);
00049 
00050 // check the YAML version
00051 #ifdef YAMLCPP_GT_0_5_0
00052   // used to gather static TFs
00053   tf2_ros::Buffer tf_buffer;
00054   tf2_ros::TransformListener tf_listener(tf_buffer);
00055   // give the buffer time to fill
00056   sleep(tf_buffer.getCacheLength().sec / 10);
00057 
00058   // parse the world config
00059   YAML::Node world_config = YAML::LoadFile(world_file);
00060   for (size_t i = 0; i < world_config.size(); i++)
00061   {
00062     // starts with rooms
00063     YAML::Node room_config = world_config[i];
00064     Room room(room_config["name"].as<string>(), room_config["frame_id"].as<string>());
00065     // get our TF information
00066     static_tfs_[room.getFrameID()] = tf_buffer.lookupTransform(fixed_frame_, room.getFrameID(), ros::Time(0));
00067 
00068     // get each surface
00069     YAML::Node surfaces_config = room_config["surfaces"];
00070     for (size_t j = 0; j < surfaces_config.size(); j++)
00071     {
00072       YAML::Node surface_config = surfaces_config[j];
00073       // parse the name, frame, and size
00074       Surface surface(surface_config["name"].as<string>(), surface_config["frame_id"].as<string>(),
00075                       surface_config["width"].as<double>(), surface_config["height"].as<double>());
00076       // get our TF information
00077       static_tfs_[surface.getFrameID()] = tf_buffer.lookupTransform(fixed_frame_, surface.getFrameID(), ros::Time(0));
00078 
00079       // get each alias
00080       YAML::Node aliases_config = surface_config["aliases"];
00081       for (size_t k = 0; k < aliases_config.size(); k++)
00082       {
00083         // parse the name
00084         surface.addAlias(aliases_config[k].as<string>());
00085       }
00086 
00087       // get each placement surface
00088       YAML::Node placement_surfaces_config = surface_config["placement_surfaces"];
00089       for (size_t k = 0; k < placement_surfaces_config.size(); k++)
00090       {
00091         // parse the frames
00092         YAML::Node ps_config = placement_surfaces_config[k];
00093         PlacementSurface ps(ps_config["frame_id"].as<string>(), ps_config["nav_frame_id"].as<string>());
00094         surface.addPlacementSurface(ps);
00095         // get our TF information
00096         static_tfs_[ps.getFrameID()] = tf_buffer.lookupTransform(fixed_frame_, ps.getFrameID(), ros::Time(0));
00097         static_tfs_[ps.getNavFrameID()] = tf_buffer.lookupTransform(fixed_frame_, ps.getNavFrameID(), ros::Time(0));
00098       }
00099 
00100       // get each POI
00101       YAML::Node pois_config = surface_config["pois"];
00102       for (size_t k = 0; k < pois_config.size(); k++)
00103       {
00104         // parse the name and frame
00105         YAML::Node poi_config = pois_config[k];
00106         PointOfInterest poi(poi_config["name"].as<string>(), poi_config["frame_id"].as<string>());
00107         // get our TF information
00108         static_tfs_[poi.getFrameID()] = tf_buffer.lookupTransform(fixed_frame_, poi.getFrameID(), ros::Time(0));
00109         surface.addPointOfInterest(poi);
00110       }
00111 
00112       room.addSurface(surface);
00113     }
00114 
00115     // add it to the global world
00116     world_.addRoom(room);
00117   }
00118 
00119   drive_and_search_as_.start();
00120   drive_to_surface_as_.start();
00121   ROS_INFO("High Level Actions Successfully Initialized");
00122   okay_ = true;
00123 #else
00124   ROS_ERROR("Unsupported version of YAML. Config files could not be parsed.");
00125   okay_ = false;
00126 #endif
00127 }
00128 
00129 bool HighLevelActions::okay() const
00130 {
00131   return okay_;
00132 }
00133 
00134 bool HighLevelActions::getSurfacesCallback(interactive_world_msgs::GetSurfaces::Request & req,
00135                                            interactive_world_msgs::GetSurfaces::Response & resp)
00136 {
00137   for (size_t i = 0; i < world_.getNumRooms(); i++)
00138   {
00139     const Room &room = world_.getRoom(i);
00140     for (size_t j = 0; j < room.getNumSurfaces(); j++)
00141     {
00142       resp.surfaces.push_back(room.getSurface(j).getName());
00143     }
00144   }
00145 
00146   return true;
00147 }
00148 
00149 bool HighLevelActions::transformToSurfaceFrame(interactive_world_msgs::TransformToSurfaceFrame::Request &req,
00150     interactive_world_msgs::TransformToSurfaceFrame::Response &resp)
00151 {
00152   // check the fixed frame
00153   tf2::Transform t_pose_world;
00154   if (req.pose.header.frame_id != fixed_frame_)
00155   {
00156     ROS_WARN("Find surface request not in the global fixed frame. Will wait for TF information which can take time...");
00157     tf2_ros::Buffer tf_buffer;
00158     tf2_ros::TransformListener tf_listener(tf_buffer);
00159     // give the buffer time to fill
00160     sleep(tf_buffer.getCacheLength().sec / 10);
00161     geometry_msgs::TransformStamped tf = tf_buffer.lookupTransform(fixed_frame_, req.pose.header.frame_id,
00162                                                                    ros::Time(0));
00163     // transform the pose
00164     tf2::Transform t_req_world = this->tfFromTFMessage(tf.transform);
00165     tf2::Transform t_pose_req = this->tfFromPoseMessage(req.pose.pose);
00166     t_pose_world = t_req_world * t_pose_req;
00167   } else
00168   {
00169     t_pose_world = this->tfFromPoseMessage(req.pose.pose);
00170   }
00171 
00172   // check for surfaces (always use the first)
00173   vector<const Surface *> surfaces = world_.findSurfaces(req.surface);
00174   if (surfaces.empty())
00175   {
00176     ROS_WARN("Could not find surface %s.", req.surface.c_str());
00177     return false;
00178   } else
00179   {
00180     const Surface *surface = surfaces[0];
00181     // get the pose in relation to the surface
00182     tf2::Transform t_surface_world = this->tfFromTFMessage(static_tfs_[surface->getFrameID()].transform);
00183     tf2::Transform t_pose_surface = t_surface_world.inverseTimes(t_pose_world);
00184 
00185     resp.pose.header.frame_id = surface->getFrameID();
00186     resp.pose.pose.position.x = t_pose_surface.getOrigin().x();
00187     resp.pose.pose.position.y = t_pose_surface.getOrigin().y();
00188     resp.pose.pose.position.z = t_pose_surface.getOrigin().z();
00189     resp.pose.pose.orientation.x = t_pose_surface.getRotation().x();
00190     resp.pose.pose.orientation.y = t_pose_surface.getRotation().y();
00191     resp.pose.pose.orientation.z = t_pose_surface.getRotation().z();
00192     resp.pose.pose.orientation.w = t_pose_surface.getRotation().w();
00193 
00194     return true;
00195   }
00196 }
00197 
00198 bool HighLevelActions::findSurfaceCallback(interactive_world_msgs::FindSurface::Request &req,
00199     interactive_world_msgs::FindSurface::Response &resp)
00200 {
00201   // check the fixed frame
00202   tf2::Transform t_pose_world;
00203   if (req.pose.header.frame_id != fixed_frame_)
00204   {
00205     ROS_WARN("Find surface request not in the global fixed frame. Will wait for TF information which can take time...");
00206     tf2_ros::Buffer tf_buffer;
00207     tf2_ros::TransformListener tf_listener(tf_buffer);
00208     // give the buffer time to fill
00209     sleep(tf_buffer.getCacheLength().sec / 10);
00210     geometry_msgs::TransformStamped tf = tf_buffer.lookupTransform(fixed_frame_, req.pose.header.frame_id,
00211                                                                    ros::Time(0));
00212     // transform the pose
00213     tf2::Transform t_req_world = this->tfFromTFMessage(tf.transform);
00214     tf2::Transform t_pose_req = this->tfFromPoseMessage(req.pose.pose);
00215     t_pose_world = t_req_world * t_pose_req;
00216   } else
00217   {
00218     t_pose_world = this->tfFromPoseMessage(req.pose.pose);
00219   }
00220 
00221   // go through each surface
00222   for (size_t i = 0; i < world_.getNumRooms(); i++)
00223   {
00224     const Room &room = world_.getRoom(i);
00225     for (size_t j = 0; j < room.getNumSurfaces(); j++)
00226     {
00227       const Surface &surface = room.getSurface(j);
00228 
00229       // get the pose in relation to the surface
00230       tf2::Transform t_surface_world = this->tfFromTFMessage(static_tfs_[surface.getFrameID()].transform);
00231       tf2::Transform t_pose_surface = t_surface_world.inverseTimes(t_pose_world);
00232 
00233       // first check if we are inside that surface's bounding box
00234       if ((surface.getWidth() / 2.0 >= abs(t_pose_surface.getOrigin().x()))
00235           && (surface.getHeight() / 2.0 >= abs(t_pose_surface.getOrigin().y())))
00236       {
00237         // check each placement surface
00238         double closest = numeric_limits<double>::infinity();
00239         for (size_t k = 0; k < surface.getNumPlacementSurfaces(); k++)
00240         {
00241           const PlacementSurface &ps = surface.getPlacementSurface(k);
00242 
00243           // distance to surface
00244           tf2::Transform t_ps_world = this->tfFromTFMessage(static_tfs_[ps.getFrameID()].transform);
00245           double distance = t_ps_world.getOrigin().distance(t_surface_world.getOrigin());
00246           if (distance < closest)
00247           {
00248             // check if we are within the height of a placement surface
00249 //            if (abs(static_tfs_[ps.getFrameID()].transform.translation.z - t_pose_world.getOrigin().z())
00250 //                <= SURFACE_Z_THRESH)
00251 //            {
00252               closest = distance;
00253               resp.placement_surface_frame_id = ps.getFrameID();
00254 //            }
00255           }
00256         }
00257 
00258         if (closest < numeric_limits<double>::infinity())
00259         {
00260           // found the surface
00261           resp.surface = surface.getName();
00262           resp.surface_frame_id = surface.getFrameID();
00263           return true;
00264         }
00265         // if we are here, we were in the bounding box but no on the surface
00266         ROS_WARN("Point was found to be inside of '%s' but not on the surface itself.", surface.getName().c_str());
00267         return true;
00268       }
00269     }
00270   }
00271 
00272   // if we are here, the point was not on a surface
00273   ROS_WARN("Point was not found to be on any surface.");
00274   return true;
00275 }
00276 
00277 void HighLevelActions::driveAndSearch(const interactive_world_msgs::DriveAndSearchGoalConstPtr &goal)
00278 {
00279   // ignore case
00280   string item_name = boost::to_upper_copy(goal->item);
00281   string surface_name = boost::to_upper_copy(goal->surface);
00282   ROS_INFO("Searching for '%s' on the '%s'...", item_name.c_str(), surface_name.c_str());
00283 
00284   interactive_world_msgs::DriveAndSearchFeedback feedback;
00285   interactive_world_msgs::DriveAndSearchResult result;
00286   // default to false
00287   result.success = false;
00288 
00289   // check each room for the surface
00290   vector<const Surface *> surfaces = world_.findSurfaces(surface_name);
00291   // check if any results were found
00292   if (surfaces.size() == 0)
00293   {
00294     drive_and_search_as_.setSucceeded(result, "Could not locate surface.");
00295     return;
00296   }
00297 
00298   // now check each surface
00299   for (size_t i = 0; i < surfaces.size(); i++)
00300   {
00301     const Surface *cur = surfaces[i];
00302     // go to each placement surface
00303     for (size_t j = 0; j < cur->getNumPlacementSurfaces(); j++)
00304     {
00305       const PlacementSurface &ps = cur->getPlacementSurface(j);
00306 
00307       // retract the arm if we are going to be searching
00308       rail_manipulation_msgs::ArmGoal arm_goal;
00309       arm_goal.action = rail_manipulation_msgs::ArmGoal::RETRACT;
00310       home_arm_ac_.sendGoal(arm_goal);
00311       bool completed = home_arm_ac_.waitForResult(ac_wait_time_);
00312       bool succeeded = (home_arm_ac_.getState() == actionlib::SimpleClientGoalState::SUCCEEDED);
00313       bool success = home_arm_ac_.getResult()->success;
00314       if (!completed || !succeeded || !success)
00315       {
00316         ROS_ERROR("Could not retract arm during search.");
00317         drive_and_search_as_.setSucceeded(result, "Could not retract arm during search.");
00318       }
00319 
00320       // attempt to drive
00321       feedback.message = "Attempting to drive to " + ps.getNavFrameID();
00322       drive_and_search_as_.publishFeedback(feedback);
00323       if (!this->driveHelper(ps.getNavFrameID()))
00324       {
00325         ROS_WARN("Could not drive to %s, will continue the search elsewhere.", ps.getNavFrameID().c_str());
00326         continue;
00327       }
00328 
00329       // look at the surface frame
00330       feedback.message = "Attempting to look at " + ps.getFrameID();
00331       drive_and_search_as_.publishFeedback(feedback);
00332       carl_dynamixel::LookAtFrame look;
00333       look.request.frame = ps.getFrameID();
00334       if (!look_at_frame_srv_.call(look))
00335       {
00336         ROS_WARN("Could not look at %s, will continue the search elsewhere.", ps.getFrameID().c_str());
00337         continue;
00338       }
00339 
00340       // check how many recognized objects we have so far (a segment request will first send an empty list)
00341       uint32_t counter;
00342       {
00343         boost::mutex::scoped_lock lock(recognized_objects_mutex_);
00344         counter = recognized_objects_counter_;
00345       }
00346 
00347       // perform a segmentation request
00348       feedback.message = "Attempting to segment the surface.";
00349       drive_and_search_as_.publishFeedback(feedback);
00350       std_srvs::Empty segment;
00351       if (!segment_srv_.call(segment))
00352       {
00353         ROS_WARN("Could not segment surface, will continue the search elsewhere.");
00354         continue;
00355       }
00356 
00357       // spin and wait
00358       feedback.message = "Waiting for recognition results.";
00359       drive_and_search_as_.publishFeedback(feedback);
00360       bool finished = false;
00361       while (!finished)
00362       {
00363         {
00364           boost::mutex::scoped_lock lock(recognized_objects_mutex_);
00365           finished = recognized_objects_counter_ == (counter + 2);
00366         }
00367       }
00368 
00369       // parse the recognition results
00370       boost::mutex::scoped_lock lock(recognized_objects_mutex_);
00371       for (size_t i = 0; i < recognized_objects_.objects.size(); i++)
00372       {
00373         if (recognized_objects_.objects[i].recognized)
00374         {
00375           string cur_name = boost::to_upper_copy(recognized_objects_.objects[i].name);
00376           // check for a name match
00377           if (cur_name == item_name)
00378           {
00379             // found at least one match
00380             result.success = true;
00381             result.objects.objects.push_back(recognized_objects_.objects[i]);
00382           }
00383         }
00384       }
00385 
00386       // check if we found something in this zone
00387       if (result.success)
00388       {
00389         drive_and_search_as_.setSucceeded(result, "Object located.");
00390         return;
00391       }
00392     }
00393   }
00394 
00395   drive_and_search_as_.setSucceeded(result, "Could not locate object.");
00396 }
00397 
00398 void HighLevelActions::driveToSurface(const interactive_world_msgs::DriveToSurfaceGoalConstPtr &goal)
00399 {
00400   // ignore case
00401   string surface_name = boost::to_upper_copy(goal->surface);
00402   string poi_name = boost::to_upper_copy(goal->poi);
00403   ROS_INFO("Driving to '%s'...", surface_name.c_str());
00404 
00405   interactive_world_msgs::DriveToSurfaceFeedback feedback;
00406   interactive_world_msgs::DriveToSurfaceResult result;
00407   // default to false
00408   result.success = false;
00409 
00410   // search each room
00411   vector<const Surface *> surfaces = world_.findSurfaces(surface_name);
00412   // check if any results were found
00413   if (surfaces.size() == 0)
00414   {
00415     drive_to_surface_as_.setSucceeded(result, "Could not locate surface.");
00416     return;
00417   }
00418 
00419   // check if we can find the POI in any of the surfaces, if not, default to the first
00420   const Surface *surface = surfaces[0];
00421   const PointOfInterest *poi = NULL;
00422   if (poi_name.size() > 0)
00423   {
00424     for (size_t i = 0; i < surfaces.size() && poi == NULL; i++)
00425     {
00426       // search each POI
00427       const Surface *cur = surfaces[i];
00428       for (size_t j = 0; j < cur->getNumPointsOfInterest() && poi == NULL; j++)
00429       {
00430         // check the name
00431         if (boost::to_upper_copy(cur->getPointOfInterest(j).getName()) == poi_name)
00432         {
00433           poi = &(cur->getPointOfInterest(j));
00434           surface = surfaces[i];
00435         }
00436       }
00437     }
00438   }
00439 
00440   // now check which point is closest (use POI over point)
00441   double best_distance = numeric_limits<double>::infinity();
00442   size_t best_index = 0;
00443   if (poi != NULL)
00444   {
00445     tf2::Transform t_poi_world = this->tfFromTFMessage(static_tfs_[poi->getFrameID()].transform);
00446     // check each navigation point
00447     for (size_t i = 0; i < surface->getNumPlacementSurfaces(); i++)
00448     {
00449       // grab the TF for the placement surface
00450       const PlacementSurface &pc = surface->getPlacementSurface(i);
00451       tf2::Transform t_pc_world = this->tfFromTFMessage(static_tfs_[pc.getNavFrameID()].transform);
00452       // now check the distance
00453       double distance = t_pc_world.getOrigin().distance(t_poi_world.getOrigin());
00454       if (distance < best_distance)
00455       {
00456         best_distance = distance;
00457         best_index = i;
00458       }
00459     }
00460   } else
00461   {
00462     // pick the closest point
00463     for (size_t i = 0; i < surface->getNumPlacementSurfaces(); i++)
00464     {
00465       // grab the TF for the placement surface
00466       const PlacementSurface &pc = surface->getPlacementSurface(i);
00467       tf2::Transform t_pc_world = this->tfFromTFMessage(static_tfs_[pc.getNavFrameID()].transform);
00468       tf2::Vector3 point(goal->point.x, goal->point.y, goal->point.z);
00469       // now check the distance
00470       double distance = t_pc_world.getOrigin().distance(point);
00471       if (distance < best_distance)
00472       {
00473         best_distance = distance;
00474         best_index = i;
00475       }
00476     }
00477   }
00478 
00479   // now attempt to drive
00480   feedback.message = "Attempting to drive to " + surface->getPlacementSurface(best_index).getNavFrameID();
00481   drive_to_surface_as_.publishFeedback(feedback);
00482   if (this->driveHelper(surface->getPlacementSurface(best_index).getNavFrameID()))
00483   {
00484     result.success = true;
00485     drive_to_surface_as_.setSucceeded(result, "Success!");
00486   } else
00487   {
00488     drive_to_surface_as_.setSucceeded(result, "Could not navigate correctly.");
00489   }
00490 }
00491 
00492 bool HighLevelActions::driveHelper(const std::string &frame_id)
00493 {
00494   // lock for the client
00495   boost::mutex::scoped_lock lock(nav_mutex_);
00496 
00497   move_base_msgs::MoveBaseGoal nav_goal;
00498   // drive to the nav frame
00499   nav_goal.target_pose.header.frame_id = frame_id;
00500   nav_goal.target_pose.pose.orientation.w = QUATERNION_90_ROTATE;
00501   nav_goal.target_pose.pose.orientation.z = QUATERNION_90_ROTATE;
00502   nav_ac_.sendGoal(nav_goal);
00503   bool completed = nav_ac_.waitForResult(ac_wait_time_);
00504   bool succeeded = (nav_ac_.getState() == actionlib::SimpleClientGoalState::SUCCEEDED);
00505   return completed && succeeded;
00506 }
00507 
00508 tf2::Transform HighLevelActions::tfFromTFMessage(const geometry_msgs::Transform &tf)
00509 {
00510   // construct and return
00511   return tf2::Transform(tf2::Quaternion(tf.rotation.x, tf.rotation.y, tf.rotation.z, tf.rotation.w),
00512                         tf2::Vector3(tf.translation.x, tf.translation.y, tf.translation.z));
00513 }
00514 
00515 tf2::Transform HighLevelActions::tfFromPoseMessage(const geometry_msgs::Pose &pose)
00516 {
00517   // construct and return
00518   return tf2::Transform(tf2::Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w),
00519                         tf2::Vector3(pose.position.x, pose.position.y, pose.position.z));
00520 }
00521 
00522 void HighLevelActions::recognizedObjectsCallback(const rail_manipulation_msgs::SegmentedObjectList &objects)
00523 {
00524   // lock for the list
00525   boost::mutex::scoped_lock lock(recognized_objects_mutex_);
00526 
00527   recognized_objects_ = objects;
00528   recognized_objects_counter_++;
00529 }


interactive_world_tools
Author(s): Russell Toris
autogenerated on Thu Jun 6 2019 21:34:15