explore.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  *
00003  * Software License Agreement (BSD License)
00004  *
00005  *  Copyright (c) 2008, Robert Bosch LLC.
00006  *  All rights reserved.
00007  *
00008  *  Redistribution and use in source and binary forms, with or without
00009  *  modification, are permitted provided that the following conditions
00010  *  are met:
00011  *
00012  *   * Redistributions of source code must retain the above copyright
00013  *     notice, this list of conditions and the following disclaimer.
00014  *   * Redistributions in binary form must reproduce the above
00015  *     copyright notice, this list of conditions and the following
00016  *     disclaimer in the documentation and/or other materials provided
00017  *     with the distribution.
00018  *   * Neither the name of the Robert Bosch nor the names of its
00019  *     contributors may be used to endorse or promote products derived
00020  *     from this software without specific prior written permission.
00021  *
00022  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  *  POSSIBILITY OF SUCH DAMAGE.
00034  *
00035  *********************************************************************/
00036 
00037 #include <explore_hrl/explore.h>
00038 #include <explore_hrl/explore_frontier.h>
00039 #include <explore_hrl/isDone_srv.h>  // service type
00040 
00041 #include <boost/thread.hpp>
00042 #include <boost/bind.hpp>
00043 
00044 using namespace costmap_2d;
00045 using namespace navfn;
00046 using namespace visualization_msgs;
00047 using namespace geometry_msgs;
00048 using namespace explore_hrl;
00049 
00050 namespace explore {
00051 
00052 double sign(double x){
00053   return x < 0.0 ? -1.0 : 1.0;
00054 }
00055 
00056 Explore::Explore() :
00057   node_(),
00058   tf_(ros::Duration(10.0)),
00059   explore_costmap_ros_(NULL),
00060   move_base_client_("move_base"),
00061   planner_(NULL),
00062   done_exploring_(false),
00063   preempt_(false),
00064   explorer_(NULL),
00065   goal_pose_last_(),
00066   goal_pose_last_defined_(false)
00067 {
00068   ros::NodeHandle private_nh("~");
00069 
00070   // Travis hack -- to replace with actionlib later.
00071   if (! ros::service::exists("~explore_done", false)){  
00072         isDone_srv_ = private_nh.advertiseService("explore_done", &Explore::isDone, this );
00073   }
00074 
00075   marker_publisher_ = node_.advertise<Marker>("visualization_marker",10);
00076   marker_array_publisher_ = node_.advertise<MarkerArray>("visualization_marker_array",10);
00077   map_publisher_ = private_nh.advertise<nav_msgs::OccupancyGrid>("map", 1, true);
00078   map_server_ = private_nh.advertiseService("explore_map", &Explore::mapCallback, this);
00079 
00080   private_nh.param("navfn/robot_base_frame", robot_base_frame_, std::string("base_link"));
00081   private_nh.param("graph_update_frequency", graph_update_frequency_, 1.0);
00082   private_nh.param("visualize", visualize_, 1);
00083   double loop_closure_addition_dist_min;
00084   double loop_closure_loop_dist_min;
00085   double loop_closure_loop_dist_max;
00086   double loop_closure_slam_entropy_max;
00087   private_nh.param("loop_closure_addition_dist_min", loop_closure_addition_dist_min, 2.5);
00088   private_nh.param("loop_closure_loop_dist_min", loop_closure_loop_dist_min, 6.0);
00089   private_nh.param("loop_closure_loop_dist_max", loop_closure_loop_dist_max, 20.0);
00090   private_nh.param("loop_closure_slam_entropy_max", loop_closure_slam_entropy_max, 3.0);
00091   private_nh.param("potential_scale", potential_scale_, 1e-3);
00092   private_nh.param("orientation_scale", orientation_scale_, .318);
00093   private_nh.param("gain_scale", gain_scale_, 1.0);
00094 
00095   explore_costmap_ros_ = new Costmap2DROS(std::string("explore_costmap"), tf_);
00096   explore_costmap_ros_->clearRobotFootprint();
00097 
00098   planner_ = new navfn::NavfnROS(std::string("explore_planner"), explore_costmap_ros_);
00099   explorer_ = new ExploreFrontier();
00100   loop_closure_ = new LoopClosure(loop_closure_addition_dist_min, 
00101                                   loop_closure_loop_dist_min,
00102                                   loop_closure_loop_dist_max,
00103                                   loop_closure_slam_entropy_max,
00104                                   graph_update_frequency_,
00105                                   move_base_client_,
00106                                   *explore_costmap_ros_,
00107                                   client_mutex_,
00108                                   boost::bind(&Explore::makePlan, this));
00109   ROS_INFO( "Done making Explore" );
00110 }
00111 
00112 Explore::~Explore() {
00113 
00114   // Reset markers (if applicable)
00115   if (visualize_ and explorer_ != NULL) {
00116     ROS_INFO("explore: Destroy old markers");
00117     std::vector<Marker> markers;
00118     explorer_->getVisualizationMarkers(markers);
00119     visualization_msgs::MarkerArray marker_array;
00120     marker_array.set_markers_size(markers.size());
00121     for (unsigned int i=0; i < markers.size(); i++){
00122       marker_array.markers[i] = markers[i];
00123       marker_array.markers[i].action = Marker::DELETE;
00124     }
00125     marker_array_publisher_.publish(marker_array);
00126     ROS_INFO("explore: Done.");
00127   }
00128 
00129   if(loop_closure_ != NULL){
00130     ROS_INFO("explore: Action Destroying old explore loop_closure");
00131     delete loop_closure_;
00132     ROS_INFO("Done.");
00133   }
00134 
00135   if(planner_ != NULL) {
00136     ROS_INFO("explore: Action Destroying old explore planner");
00137     delete planner_;
00138     ROS_INFO("Done.");
00139   }
00140 
00141   if(explorer_ != NULL) {
00142     ROS_INFO("explore: Action Destroying old explore explorer");
00143     delete explorer_;
00144     ROS_INFO("Done.");
00145   }
00146 
00147   if(explore_costmap_ros_ != NULL){
00148     ROS_INFO("explore: Action Destroying old explore costmap (WARN: New one might not have probs)");
00149     delete explore_costmap_ros_;
00150     ROS_INFO("Done.");
00151   }
00152 }
00153 
00154 bool Explore::isDone( explore_hrl::isDone_srv::Request &req, explore_hrl::isDone_srv::Response &res ){
00155   res.status = done_exploring_;
00156   return true;
00157 }
00158 
00159 
00160 bool Explore::mapCallback(nav_msgs::GetMap::Request  &req,
00161                           nav_msgs::GetMap::Response &res)
00162 {
00163   ROS_DEBUG("mapCallback");
00164   Costmap2D explore_costmap;
00165   explore_costmap_ros_->getCostmapCopy(explore_costmap);
00166 
00167   res.map.info.width = explore_costmap.getSizeInCellsX();
00168   res.map.info.height = explore_costmap.getSizeInCellsY();
00169   res.map.info.resolution = explore_costmap.getResolution();
00170   res.map.info.origin.position.x = explore_costmap.getOriginX();
00171   res.map.info.origin.position.y = explore_costmap.getOriginY();
00172   res.map.info.origin.position.z = 0;
00173   res.map.info.origin.orientation.x = 0;
00174   res.map.info.origin.orientation.y = 0;
00175   res.map.info.origin.orientation.z = 0;
00176   res.map.info.origin.orientation.w = 1;
00177 
00178   int size = res.map.info.width * res.map.info.height;
00179   const unsigned char* map = explore_costmap.getCharMap();
00180 
00181   res.map.set_data_size(size);
00182   for (int i=0; i<size; i++) {
00183     if (map[i] == NO_INFORMATION)
00184       res.map.data[i] = -1;
00185     else if (map[i] == LETHAL_OBSTACLE)
00186       res.map.data[i] = 100;
00187     else
00188       res.map.data[i] = 0;
00189     // if (map[i] == LETHAL_OBSTACLE )
00190     //   res.map.data[i] = 100;
00191     // else if (map[i] == FREE_SPACE)
00192     //   res.map.data[i] = 0;
00193     // else
00194     //   res.map.data[i] = -1;
00195   }
00196 
00197   return true;
00198 }
00199 
00200 void Explore::publishMap() {
00201   nav_msgs::OccupancyGrid map;
00202   map.header.stamp = ros::Time::now();
00203 
00204   Costmap2D explore_costmap;
00205   explore_costmap_ros_->getCostmapCopy(explore_costmap);
00206 
00207   map.info.width = explore_costmap.getSizeInCellsX();
00208   map.info.height = explore_costmap.getSizeInCellsY();
00209   map.info.resolution = explore_costmap.getResolution();
00210   map.info.origin.position.x = explore_costmap.getOriginX();
00211   map.info.origin.position.y = explore_costmap.getOriginY();
00212   map.info.origin.position.z = 0;
00213   map.info.origin.orientation.x = 0;
00214   map.info.origin.orientation.y = 0;
00215   map.info.origin.orientation.z = 0;
00216   map.info.origin.orientation.w = 1;
00217 
00218   int size = map.info.width * map.info.height;
00219   const unsigned char* char_map = explore_costmap.getCharMap();
00220 
00221   map.set_data_size(size);
00222   for (int i=0; i<size; i++) {
00223     if (char_map[i] == NO_INFORMATION)
00224       map.data[i] = -1;
00225     else if (char_map[i] == LETHAL_OBSTACLE)
00226       map.data[i] = 100;
00227     else
00228       map.data[i] = 0;
00229   }
00230   // for (int i=0; i<size; i++) {
00231   //   if (char_map[i] == NO_INFORMATION)
00232   //     map.data[i] = -1;
00233   //   else if (char_map[i] == LETHAL_OBSTACLE)
00234   //     map.data[i] = 100;
00235   //   else
00236   //     map.data[i] = 0;
00237   // }
00238 
00239   map_publisher_.publish(map);
00240 }
00241 
00242 void Explore::publishGoal(const geometry_msgs::Pose& goal){
00243   visualization_msgs::Marker marker;
00244   marker.header.frame_id = "map";
00245   marker.header.stamp = ros::Time::now();
00246   marker.ns = "explore_goal";
00247   marker.id = 0;
00248   marker.type = visualization_msgs::Marker::ARROW;
00249   marker.pose = goal;
00250   marker.scale.x = 0.5;
00251   marker.scale.y = 1.0;
00252   marker.scale.z = 1.0;
00253   marker.color.a = 1.0;
00254   marker.color.r = 0.0;
00255   marker.color.g = 1.0;
00256   marker.color.b = 0.0;
00257   marker.lifetime = ros::Duration(5);
00258   marker_publisher_.publish(marker);
00259 }
00260 
00261 void Explore::getRobotPose(std::string frame, tf::Stamped<tf::Pose>& pose){
00262   tf::Stamped<tf::Pose> robot_pose;
00263   robot_pose.setIdentity();
00264   robot_pose.frame_id_ = robot_base_frame_;
00265   robot_pose.stamp_ = ros::Time();
00266 
00267   try{
00268     tf_.transformPose(frame, robot_pose, pose);
00269   }
00270   catch(tf::LookupException& ex) {
00271     ROS_ERROR("No Transform available Error: %s\n", ex.what());
00272     return;
00273   }
00274   catch(tf::ConnectivityException& ex) {
00275     ROS_ERROR("Connectivity Error: %s\n", ex.what());
00276     return;
00277   }
00278   catch(tf::ExtrapolationException& ex) {
00279     ROS_ERROR("Extrapolation Error: %s\n", ex.what());
00280   }
00281 }
00282 
00283 
00284 void Explore::makePlan() {
00285   //since this gets called on handle activate
00286   if(explore_costmap_ros_ == NULL)
00287     return;
00288 
00289   //check to see if we have control of the action client
00290   //if(client_mutex_.try_lock()){
00291   if (true){
00292     tf::Stamped<tf::Pose> robot_pose;
00293     getRobotPose(explore_costmap_ros_->getGlobalFrameID(), robot_pose);
00294     geometry_msgs::Point robot_point;
00295     robot_point.x = robot_pose.getOrigin().x();
00296     robot_point.y = robot_pose.getOrigin().y();
00297     robot_point.z = robot_pose.getOrigin().z();
00298 
00299     std::vector<geometry_msgs::Pose> goals;
00300     explore_costmap_ros_->clearRobotFootprint();
00301     explorer_->getExplorationGoals(*explore_costmap_ros_, robot_point, planner_, goals, potential_scale_, orientation_scale_, gain_scale_);
00302 
00303     bool valid_plan = false;
00304     std::vector<geometry_msgs::PoseStamped> plan;
00305     PoseStamped goal_pose, robot_pose_msg;
00306     tf::poseStampedTFToMsg(robot_pose, robot_pose_msg);
00307 
00308     bool found_exploration_goal = false;
00309     goal_pose.header.frame_id = explore_costmap_ros_->getGlobalFrameID();
00310     goal_pose.header.stamp = ros::Time::now();
00311     int blacklist_count = 0;
00312     for (unsigned int i=0; i<goals.size(); i++) {
00313       goal_pose.pose = goals[i];
00314 
00315       valid_plan = ((planner_->makePlan(robot_pose_msg, goal_pose, plan)) &&
00316           (!plan.empty()));
00317 
00318       if (valid_plan) {
00319         if(!goalOnBlacklist(goal_pose)){
00320           found_exploration_goal = true;
00321           break;
00322         }
00323         else{
00324           //just so we can say how many goals are too close to a blacklisted one
00325           blacklist_count++;
00326         }
00327       }
00328     }
00329 
00330     done_exploring_ = !found_exploration_goal;
00331 
00332     // publish visualization markers
00333     if (visualize_) {
00334       std::vector<Marker> markers;
00335       explorer_->getVisualizationMarkers(markers);
00336       visualization_msgs::MarkerArray marker_array;
00337       marker_array.set_markers_size(markers.size());
00338       for (unsigned int i=0; i < markers.size(); i++){
00339         marker_array.markers[i] = markers[i];
00340       }
00341       marker_array_publisher_.publish(marker_array);
00342     }
00343 
00344     if (valid_plan) {
00345       //explore_costmap_ros_->clearRobotFootprint(); // help prevent phantom obstacles, especially at startup
00346       move_base_msgs::MoveBaseGoal goal;
00347       goal.target_pose = goal_pose;
00348 
00349       if (isRepeatGoal( goal.target_pose )){
00350         ROS_INFO("Explore: This appears to be a duplicate goal.");
00351         frontier_blacklist_.push_back( goal.target_pose );
00352         if (!done_exploring_){
00353           makePlan();
00354         }
00355       }
00356       else {
00357         move_base_client_.sendGoal(goal, boost::bind(&Explore::reachedGoal, this, _1, _2, goal_pose));
00358       }
00359 
00360       if (visualize_) {
00361         publishGoal(goal_pose.pose);
00362         publishMap();
00363       }
00364     } else {
00365       ROS_WARN("Done exploring with %d goals left that could not be reached. There are %d goals on our blacklist, and %d of the frontier goals are too close to them to pursue. The rest had global planning fail to them. \n", (int)goals.size(), (int)frontier_blacklist_.size(), blacklist_count);
00366       ROS_INFO("We finsihed exploring the map. Hooray.");
00367     }
00368     
00369 
00370     if (visualize_) {
00371         publishMap();
00372     }
00373     //make sure to unlock the mutex when we're done
00374     client_mutex_.unlock();
00375   }
00376   else {
00377     ROS_WARN("Explore: Mutex acquire failed!");
00378   }
00379 }
00380 
00381 bool Explore::isRepeatGoal(const geometry_msgs::PoseStamped& goal){
00382   // Prevent duplicate goal selections.  This tends to happen at
00383   // startup if there is unknown space that cannot be cleared by LRF
00384   // on PR2.
00385 
00386   // Skip over the first time this is called.
00387   if (!goal_pose_last_defined_){
00388     goal_pose_last_defined_ = true;
00389     goal_pose_last_ = goal;
00390     return false;
00391   }
00392 
00393   double x_diff = fabs(goal.pose.position.x - goal_pose_last_.pose.position.x);
00394   double y_diff = fabs(goal.pose.position.y - goal_pose_last_.pose.position.y);
00395   double ang_diff = tf::getYaw( goal.pose.orientation ) - tf::getYaw( goal_pose_last_.pose.orientation );
00396 
00397   // Update last pose.
00398   goal_pose_last_ = goal;
00399 
00400   while (ang_diff < -3.14159) ang_diff += 2.0 * 3.14159;
00401   while (ang_diff > 3.14159) ang_diff -= 2.0 * 3.14159;
00402 
00403   ROS_INFO( "Differences: %3.2f %3.2f %3.2f", x_diff, y_diff, ang_diff );
00404 
00405   if (x_diff < 2 * explore_costmap_ros_->getResolution() && 
00406       y_diff < 2 * explore_costmap_ros_->getResolution() &&
00407       fabs( ang_diff ) < 5.0 * (3.14158 / 180.0)){
00408     return true;
00409   }
00410   else {
00411     return false;
00412   }
00413 }
00414 
00415 bool Explore::goalOnBlacklist(const geometry_msgs::PoseStamped& goal){
00416   //check if a goal is on the blacklist for goals that we're pursuing
00417   for(unsigned int i = 0; i < frontier_blacklist_.size(); ++i){
00418     double x_diff = fabs(goal.pose.position.x - frontier_blacklist_[i].pose.position.x);
00419     double y_diff = fabs(goal.pose.position.y - frontier_blacklist_[i].pose.position.y);
00420 
00421     if(x_diff < 2 * explore_costmap_ros_->getResolution() && y_diff < 2 * explore_costmap_ros_->getResolution())
00422       return true;
00423   }
00424   return false;
00425 }
00426 
00427 void Explore::reachedGoal(const actionlib::SimpleClientGoalState& status, 
00428     const move_base_msgs::MoveBaseResultConstPtr& result, geometry_msgs::PoseStamped frontier_goal){
00429   if(status == actionlib::SimpleClientGoalState::ABORTED){
00430     frontier_blacklist_.push_back(frontier_goal);
00431     ROS_ERROR("Adding current goal to blacklist");
00432   }
00433 
00434   // if(!done_exploring_){
00435   //   //create a plan from the frontiers left and send a new goal to move_base
00436   //   makePlan();
00437   // }
00438   // else{
00439   //   ROS_INFO("We finsihed exploring the map. Hooray.");
00440   // }
00441 }
00442 
00443 void Explore::execute() {
00444   while (! move_base_client_.waitForServer(ros::Duration(5,0)))
00445     ROS_WARN("Waiting to connect to move_base server");
00446 
00447   ROS_INFO("Connected to move_base server");
00448   goal_pose_last_defined_ = false;
00449 
00450   // This call sends the first goal, and sets up for future callbacks.
00451   ROS_INFO("explore: ONE");
00452   makePlan();
00453   ROS_INFO("explore: TWO");
00454 
00455   //ros::Rate r(graph_update_frequency_);
00456   ros::Rate r(5.0);
00457   ros::Time time_last_moving = ros::Time::now();
00458   tf::Stamped<tf::Pose> robot_pose;
00459   geometry_msgs::PoseStamped new_pose, ref_pose;
00460   
00461   getRobotPose(explore_costmap_ros_->getGlobalFrameID(), robot_pose);
00462   ROS_INFO("explore: THREE");
00463 
00464   // I don't know how to use bullet tf...
00465   tf::poseStampedTFToMsg( robot_pose, new_pose );
00466   ref_pose = new_pose;
00467   ROS_INFO("explore: FOUR");
00468 
00469   while (node_.ok() && (!done_exploring_) && (!preempt_)) {
00470     // Get the current pose and pass it to loop closure
00471     getRobotPose(explore_costmap_ros_->getGlobalFrameID(), robot_pose);
00472     tf::poseStampedTFToMsg( robot_pose, new_pose );
00473     loop_closure_->updateGraph(robot_pose);
00474     //ROS_INFO("explore: FIVE");
00475     
00476     if (move_base_client_.getState() == actionlib::SimpleClientGoalState::ACTIVE){
00477       //ROS_INFO("explore: SIX");
00478       float dx,dy,da;
00479       // Check to see if we've moved...
00480       dx = new_pose.pose.position.x - ref_pose.pose.position.x;
00481       dy = new_pose.pose.position.y - ref_pose.pose.position.y;
00482       da = tf::getYaw(new_pose.pose.orientation) - tf::getYaw(ref_pose.pose.orientation);
00483       if (dx*dx+dy*dy > 0.02 or da*da > 5 * 3.14159 / 180.0){ // Yep, so reset time and new reference position
00484         time_last_moving = ros::Time::now();
00485         ref_pose = new_pose;
00486       }
00487 
00488       // If we haven't moved in 5 seconds, ditch this goal!
00489       if (ros::Time::now() - time_last_moving > ros::Duration( 5 )){
00490         ROS_INFO( "We appear to not be moving... aborting goal." );
00491         move_base_client_.cancelAllGoals();
00492       }
00493     }
00494     else{ // Time to send a new frontier.
00495       makePlan();
00496       ref_pose = new_pose;
00497       time_last_moving = ros::Time::now();
00498     }
00499     r.sleep();
00500   }
00501   move_base_client_.cancelAllGoals();
00502 }
00503 
00504 void Explore::spin() {
00505   ROS_INFO("Entering Explore:spin()");
00506   ros::spinOnce();
00507   boost::thread t(boost::bind( &Explore::execute, this ));
00508   ros::spin();
00509   t.join();
00510 }
00511 
00512 void Explore::setPreemptFlag( bool state ) {
00513   ROS_INFO("Explore preempt state set to %d.", state);
00514   preempt_ = state;
00515 }
00516 
00517 bool Explore::doneExploring( ) {
00518   return done_exploring_;
00519 }
00520 
00521 }
00522 
00523 int main(int argc, char** argv){
00524   ros::init(argc, argv, "explore");
00525 
00526   explore::Explore explore;
00527   explore.spin();
00528 
00529   return(0);
00530 }
00531 


explore_hrl
Author(s): Travis Deyle
autogenerated on Wed Nov 27 2013 11:48:01