path_progress.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2019, DFKI GmbH
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 copyright holder 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 HOLDER 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 #include <mir_dwb_critics/path_progress.h>
00035 #include <nav_grid/coordinate_conversion.h>
00036 #include <pluginlib/class_list_macros.h>
00037 #include <nav_2d_utils/path_ops.h>
00038 #include <vector>
00039 
00040 namespace mir_dwb_critics {
00041 bool PathProgressCritic::prepare(const geometry_msgs::Pose2D &pose, const nav_2d_msgs::Twist2D &vel,
00042                                  const geometry_msgs::Pose2D &goal,
00043                                  const nav_2d_msgs::Path2D &global_plan) {
00044   dwb_critics::MapGridCritic::reset();
00045 
00046   unsigned int local_goal_x, local_goal_y;
00047   if (!getGoalPose(pose, global_plan, local_goal_x, local_goal_y, desired_angle_)) {
00048     return false;
00049   }
00050 
00051   // Enqueue just the last pose
00052   cell_values_.setValue(local_goal_x, local_goal_y, 0.0);
00053   queue_->enqueueCell(local_goal_x, local_goal_y);
00054 
00055   propogateManhattanDistances();
00056 
00057   return true;
00058 }
00059 
00060 void PathProgressCritic::onInit() {
00061   dwb_critics::MapGridCritic::onInit();
00062   critic_nh_.param("xy_local_goal_tolerance", xy_local_goal_tolerance_, 0.20);
00063   critic_nh_.param("angle_threshold", angle_threshold_, M_PI_4);
00064   critic_nh_.param("heading_scale", heading_scale_, 1.0);
00065 
00066   // divide heading scale by position scale because the sum will be multiplied by scale again
00067   heading_scale_ /= getScale();
00068 }
00069 
00070 void PathProgressCritic::reset() {
00071   reached_intermediate_goals_.clear();
00072 }
00073 
00074 double PathProgressCritic::scoreTrajectory(const dwb_msgs::Trajectory2D &traj) {
00075   double position_score = MapGridCritic::scoreTrajectory(traj);
00076   double heading_diff = fabs(remainder(traj.poses.back().theta - desired_angle_, 2 * M_PI));
00077   double heading_score = heading_diff * heading_diff;
00078 
00079   return position_score + heading_scale_ * heading_score;
00080 }
00081 
00082 bool PathProgressCritic::getGoalPose(const geometry_msgs::Pose2D &robot_pose, const nav_2d_msgs::Path2D &global_plan,
00083                                      unsigned int &x, unsigned int &y, double &desired_angle) {
00084   const nav_core2::Costmap &costmap = *costmap_;
00085   const nav_grid::NavGridInfo &info = costmap.getInfo();
00086 
00087   if (global_plan.poses.empty()) {
00088     ROS_ERROR_NAMED("PathProgressCritic", "The global plan was empty.");
00089     return false;
00090   }
00091 
00092   std::vector<geometry_msgs::Pose2D> plan = nav_2d_utils::adjustPlanResolution(global_plan, info.resolution).poses;
00093 
00094   // find the "start pose", i.e. the pose on the plan closest to the robot that is also on the local map
00095   unsigned int start_index = 0;
00096   double distance_to_start = std::numeric_limits<double>::infinity();
00097   bool started_path = false;
00098   for (unsigned int i = 0; i < plan.size(); i++) {
00099     double g_x = plan[i].x;
00100     double g_y = plan[i].y;
00101     unsigned int map_x, map_y;
00102     if (worldToGridBounded(info, g_x, g_y, map_x, map_y)
00103         && costmap(map_x, map_y) != nav_core2::Costmap::NO_INFORMATION) {
00104       // Still on the costmap. Continue.
00105       double distance = nav_2d_utils::poseDistance(plan[i], robot_pose);
00106       if (distance_to_start > distance) {
00107         start_index = i;
00108         distance_to_start = distance;
00109         started_path = true;
00110       } else {
00111         // Plan is going away from the robot again. It's possible that it comes back and we would find a pose that's
00112         // even closer to the robot, but then we would skip over parts of the plan.
00113         break;
00114       }
00115     } else if (started_path) {
00116       // Off the costmap after being on the costmap.
00117       break;
00118     }
00119     // else, we have not yet found a point on the costmap, so we just continue
00120   }
00121 
00122   if (!started_path) {
00123     ROS_ERROR_NAMED("PathProgressCritic", "None of the points of the global plan were in the local costmap.");
00124     return false;
00125   }
00126 
00127   // find the "last valid pose", i.e. the last pose on the plan after the start pose that is still on the local map
00128   unsigned int last_valid_index = start_index;
00129   for (unsigned int i = start_index + 1; i < plan.size(); i++) {
00130     double g_x = plan[i].x;
00131     double g_y = plan[i].y;
00132     unsigned int map_x, map_y;
00133     if (worldToGridBounded(info, g_x, g_y, map_x, map_y)
00134         && costmap(map_x, map_y) != nav_core2::Costmap::NO_INFORMATION) {
00135       // Still on the costmap. Continue.
00136       last_valid_index = i;
00137     } else {
00138       // Off the costmap after being on the costmap.
00139       break;
00140     }
00141   }
00142 
00143   // find the "goal pose" by walking along the plan as long as each step leads far enough away from the starting point,
00144   // i.e. is within angle_threshold_ of the starting direction.
00145   unsigned int goal_index = start_index;
00146 
00147   while (goal_index < last_valid_index) {
00148     goal_index = getGoalIndex(plan, start_index, last_valid_index);
00149 
00150     // check if goal already reached
00151     bool goal_already_reached = false;
00152     for (auto &reached_intermediate_goal : reached_intermediate_goals_) {
00153       double distance = nav_2d_utils::poseDistance(reached_intermediate_goal, plan[goal_index]);
00154       if (distance < xy_local_goal_tolerance_) {
00155         goal_already_reached = true;
00156         // choose a new start_index by walking along the plan until we're outside xy_local_goal_tolerance_ and try again
00157         // (start_index is now > goal_index)
00158         for (start_index = goal_index; start_index <= last_valid_index; ++start_index) {
00159           distance = nav_2d_utils::poseDistance(reached_intermediate_goal, plan[start_index]);
00160           if (distance >= xy_local_goal_tolerance_) {
00161             break;
00162           }
00163         }
00164         break;
00165       }
00166     }
00167     if (!goal_already_reached) {
00168       // new goal not in reached_intermediate_goals_
00169       double distance = nav_2d_utils::poseDistance(plan[goal_index], robot_pose);
00170       if (distance < xy_local_goal_tolerance_) {
00171         // the robot has currently reached the goal
00172         reached_intermediate_goals_.push_back(plan[goal_index]);
00173         ROS_DEBUG_NAMED("PathProgressCritic", "Number of reached_intermediate goals: %zu", reached_intermediate_goals_.size());
00174       } else {
00175         // we've found a new goal!
00176         break;
00177       }
00178     }
00179   }
00180   if (start_index > goal_index)
00181     start_index = goal_index;
00182   ROS_ASSERT(goal_index <= last_valid_index);
00183 
00184   // save goal in x, y
00185   worldToGridBounded(info, plan[goal_index].x, plan[goal_index].y, x, y);
00186   desired_angle = plan[start_index].theta;
00187   return true;
00188 }
00189 
00190 unsigned int PathProgressCritic::getGoalIndex(const std::vector<geometry_msgs::Pose2D> &plan,
00191                                               unsigned int start_index,
00192                                               unsigned int last_valid_index) const {
00193   if (start_index >= last_valid_index)
00194     return last_valid_index;
00195 
00196   unsigned int goal_index = start_index;
00197   const double start_direction_x = plan[start_index + 1].x - plan[start_index].x;
00198   const double start_direction_y = plan[start_index + 1].y - plan[start_index].y;
00199   if (fabs(start_direction_x) > 1e-9 || fabs(start_direction_y) > 1e-9) {  // make sure that atan2 is defined
00200     double start_angle = atan2(start_direction_y, start_direction_x);
00201 
00202     for (unsigned int i = start_index + 1; i <= last_valid_index; ++i) {
00203       const double current_direction_x = plan[i].x - plan[i - 1].x;
00204       const double current_direction_y = plan[i].y - plan[i - 1].y;
00205       if (fabs(current_direction_x) > 1e-9 || fabs(current_direction_y) > 1e-9) {
00206         double current_angle = atan2(current_direction_y, current_direction_x);
00207 
00208         // goal pose is found if plan doesn't point far enough away from the starting point
00209         if (fabs(remainder(start_angle - current_angle, 2 * M_PI)) > angle_threshold_)
00210           break;
00211 
00212         goal_index = i;
00213       }
00214     }
00215   }
00216   return goal_index;
00217 }
00218 
00219 }  // namespace mir_dwb_critics
00220 
00221 PLUGINLIB_EXPORT_CLASS(mir_dwb_critics::PathProgressCritic, dwb_local_planner::TrajectoryCritic)


mir_dwb_critics
Author(s): Martin Günther
autogenerated on Wed May 8 2019 02:53:29