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  *  Copyright (c) 2015-2016, Jiri Horner.
00007  *  All rights reserved.
00008  *
00009  *  Redistribution and use in source and binary forms, with or without
00010  *  modification, are permitted provided that the following conditions
00011  *  are met:
00012  *
00013  *   * Redistributions of source code must retain the above copyright
00014  *     notice, this list of conditions and the following disclaimer.
00015  *   * Redistributions in binary form must reproduce the above
00016  *     copyright notice, this list of conditions and the following
00017  *     disclaimer in the documentation and/or other materials provided
00018  *     with the distribution.
00019  *   * Neither the name of the Jiri Horner nor the names of its
00020  *     contributors may be used to endorse or promote products derived
00021  *     from this software without specific prior written permission.
00022  *
00023  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00024  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00025  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00026  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00027  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00028  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00029  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00030  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00031  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00033  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00034  *  POSSIBILITY OF SUCH DAMAGE.
00035  *
00036  *********************************************************************/
00037 
00038 #include <explore/explore.h>
00039 #include <explore/explore_frontier.h>
00040 
00041 #include <thread>
00042 
00043 namespace explore {
00044 
00045 Explore::Explore() :
00046   private_nh_("~"),
00047   tf_listener_(ros::Duration(10.0)),
00048   costmap_client_(private_nh_, relative_nh_, &tf_listener_),
00049   planner_("explore_planner", costmap_client_.getCostmap(),
00050     costmap_client_.getGlobalFrameID()),
00051   move_base_client_("move_base"),
00052   explorer_(&costmap_client_, &planner_),
00053   prev_plan_size_(0),
00054   done_exploring_(false)
00055 {
00056   private_nh_.param("planner_frequency", planner_frequency_, 1.0);
00057   private_nh_.param("progress_timeout", progress_timeout_, 30.0);
00058   private_nh_.param("visualize", visualize_, false);
00059   private_nh_.param("potential_scale", potential_scale_, 1e-3);
00060   private_nh_.param("orientation_scale", orientation_scale_, 0.0); // TODO: set this back to 0.318 once getOrientationChange is fixed
00061   private_nh_.param("gain_scale", gain_scale_, 1.0);
00062 
00063   if(visualize_) {
00064     marker_array_publisher_ = private_nh_.advertise<visualization_msgs::MarkerArray>("frontiers", 10);
00065   }
00066 }
00067 
00068 void Explore::publishFrontiers() {
00069   visualization_msgs::MarkerArray markers;
00070   explorer_.getVisualizationMarkers(markers);
00071   ROS_DEBUG("publishing %lu markers", markers.markers.size());
00072   marker_array_publisher_.publish(markers);
00073 }
00074 
00075 void Explore::makePlan() {
00076   // this method may be called from callback
00077   std::lock_guard<std::mutex> lock(planning_mutex_);
00078 
00079   tf::Stamped<tf::Pose> robot_pose;
00080   costmap_client_.getRobotPose(robot_pose);
00081 
00082   std::vector<geometry_msgs::Pose> goals;
00083 
00084   explorer_.getExplorationGoals(robot_pose, goals, potential_scale_, orientation_scale_, gain_scale_);
00085   if (goals.size() == 0) {
00086     done_exploring_ = true;
00087     ROS_DEBUG("no explorations goals found");
00088   } else {
00089     ROS_DEBUG("found %lu explorations goals", goals.size());
00090   }
00091 
00092   // publish frontiers as visualization markers
00093   if (visualize_) {
00094     publishFrontiers();
00095   }
00096 
00097   bool valid_plan = false;
00098   std::vector<geometry_msgs::PoseStamped> plan;
00099   geometry_msgs::PoseStamped goal_pose, robot_pose_msg;
00100   tf::poseStampedTFToMsg(robot_pose, robot_pose_msg);
00101 
00102   goal_pose.header.frame_id = costmap_client_.getGlobalFrameID();
00103   goal_pose.header.stamp = ros::Time::now();
00104   planner_.computePotential(robot_pose_msg.pose.position); // just to be safe, though this should already have been done in explorer_->getExplorationGoals
00105   size_t blacklist_count = 0;
00106   for (auto& goal : goals) {
00107     goal_pose.pose = goal;
00108     if (goalOnBlacklist(goal_pose)) {
00109       ++blacklist_count;
00110       continue;
00111     }
00112 
00113     valid_plan = planner_.getPlanFromPotential(goal_pose, plan) && !plan.empty();
00114     if (valid_plan) {
00115       ROS_DEBUG("got valid plan");
00116       break;
00117     } else {
00118       ROS_DEBUG("got invalid plan");
00119     }
00120   }
00121 
00122   if (valid_plan) {
00123     if (prev_plan_size_ != plan.size()) {
00124       time_since_progress_ = 0.0;
00125     } else {
00126       double dx = prev_goal_.pose.position.x - goal_pose.pose.position.x;
00127       double dy = prev_goal_.pose.position.y - goal_pose.pose.position.y;
00128       double dist = sqrt(dx*dx+dy*dy);
00129       if (dist < 0.01) {
00130         time_since_progress_ += 1.0 / planner_frequency_;
00131       }
00132     }
00133 
00134     // black list goals for which we've made no progress for a long time
00135     if (time_since_progress_ > progress_timeout_) {
00136       frontier_blacklist_.push_back(goal_pose);
00137       ROS_DEBUG("Adding current goal to black list");
00138     }
00139 
00140     prev_plan_size_ = plan.size();
00141     prev_goal_ = goal_pose;
00142 
00143     move_base_msgs::MoveBaseGoal goal;
00144     goal.target_pose = goal_pose;
00145     move_base_client_.sendGoal(goal, boost::bind(&Explore::reachedGoal, this, _1, _2, goal_pose));
00146 
00147   } else {
00148     ROS_WARN("Done exploring with %lu goals left that could not be reached."
00149       "There are %lu goals on our blacklist, and %lu of the frontier goals are too close to"
00150       "them to pursue. The rest had global planning fail to them. \n",
00151       goals.size(), frontier_blacklist_.size(), blacklist_count);
00152     ROS_INFO("Exploration finished. Hooray.");
00153     done_exploring_ = true;
00154   }
00155 }
00156 
00157 bool Explore::goalOnBlacklist(const geometry_msgs::PoseStamped& goal){
00158   costmap_2d::Costmap2D *costmap2d = costmap_client_.getCostmap();
00159 
00160   //check if a goal is on the blacklist for goals that we're pursuing
00161   for(auto& frontier_goal : frontier_blacklist_) {
00162     double x_diff = fabs(goal.pose.position.x - frontier_goal.pose.position.x);
00163     double y_diff = fabs(goal.pose.position.y - frontier_goal.pose.position.y);
00164 
00165     if(x_diff < 2 * costmap2d->getResolution() && y_diff < 2 * costmap2d->getResolution())
00166       return true;
00167   }
00168   return false;
00169 }
00170 
00171 void Explore::reachedGoal(
00172   const actionlib::SimpleClientGoalState& status,
00173   const move_base_msgs::MoveBaseResultConstPtr&,
00174   const geometry_msgs::PoseStamped& frontier_goal){
00175 
00176   ROS_DEBUG("Reached goal");
00177   if(status == actionlib::SimpleClientGoalState::ABORTED){
00178     frontier_blacklist_.push_back(frontier_goal);
00179     ROS_DEBUG("Adding current goal to black list");
00180   }
00181 
00182   //create a plan from the frontiers left and send a new goal to move_base
00183   makePlan();
00184 }
00185 
00186 void Explore::execute() {
00187   while (!move_base_client_.waitForServer(ros::Duration(5,0)))
00188     ROS_WARN("Waiting to connect to move_base server");
00189 
00190   ROS_INFO("Connected to move_base server");
00191 
00192   // This call sends the first goal, and sets up for future callbacks.
00193   makePlan();
00194 
00195   ros::Rate r(planner_frequency_);
00196   while (relative_nh_.ok() && (!done_exploring_)) {
00197     makePlan();
00198     r.sleep();
00199   }
00200 
00201   move_base_client_.cancelAllGoals();
00202 }
00203 
00204 void Explore::spin() {
00205   ros::spinOnce();
00206   std::thread t(&Explore::execute, this);
00207   ros::spin();
00208   if (t.joinable())
00209     t.join();
00210 }
00211 
00212 } // namespace explore
00213 
00214 int main(int argc, char** argv){
00215   ros::init(argc, argv, "explore");
00216 
00217   explore::Explore explore;
00218   explore.spin();
00219 
00220   return(0);
00221 }
00222 


explore
Author(s): Jiri Horner
autogenerated on Sun Mar 26 2017 03:48:06