gradient_path.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2018, Locus Robotics
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 
00035 #include <dlux_plugins/gradient_path.h>
00036 #include <nav_grid/coordinate_conversion.h>
00037 #include <nav_core2/exceptions.h>
00038 #include <pluginlib/class_list_macros.h>
00039 #include <math.h>
00040 #include <string>
00041 
00042 PLUGINLIB_EXPORT_CLASS(dlux_plugins::GradientPath, dlux_global_planner::Traceback)
00043 
00044 using dlux_global_planner::HIGH_POTENTIAL;
00045 
00046 namespace dlux_plugins
00047 {
00048 void GradientPath::initialize(ros::NodeHandle& private_nh, dlux_global_planner::CostInterpreter::Ptr cost_interpreter)
00049 {
00050   cost_interpreter_ = cost_interpreter;
00051   private_nh.param("step_size", step_size_, 0.5);
00052   private_nh.param("lethal_cost", lethal_cost_, 250.0);
00053 
00054   // As a safeguard against infinitely iterating, we limit the number of steps we take, relative to the size of the
00055   // costmap/potential grid. The maximum number of iterations is width * height * iteration_factor.
00056   private_nh.param("iteration_factor", iteration_factor_, 4.0);
00057 
00058   // NavFn would not attempt to calculate a gradient if one of the neighboring cells had not been initialized.
00059   // Setting this to true will replicate that behavior
00060   private_nh.param("grid_step_near_high", grid_step_near_high_, false);
00061 }
00062 
00063 nav_2d_msgs::Path2D GradientPath::getPath(const dlux_global_planner::PotentialGrid& potential_grid,
00064                                           const geometry_msgs::Pose2D& start, const geometry_msgs::Pose2D& goal,
00065                                           double& path_cost)
00066 {
00067   const nav_grid::NavGridInfo& info = potential_grid.getInfo();
00068   if (gradx_.getInfo() != info)
00069   {
00070     gradx_.setInfo(info);
00071     grady_.setInfo(info);
00072   }
00073   gradx_.reset();
00074   grady_.reset();
00075 
00076   nav_2d_msgs::Path2D path;
00077   path_cost = 0.0;
00078 
00079   double map_x, map_y;
00080   worldToGrid(info, start.x, start.y, map_x, map_y);
00081   // Running Variables
00082   //   index is the current index
00083   //   dx and dy represent the precise floating point coordinates within the cell [0, 1)
00084   double x_int_part, y_int_part;
00085   float dx = modf(map_x, &x_int_part),
00086         dy = modf(map_y, &y_int_part);
00087   nav_grid::Index index(static_cast<unsigned int>(x_int_part), static_cast<unsigned int>(y_int_part));
00088 
00089   // Variables to check for loop completion
00090   worldToGrid(info, goal.x, goal.y, map_x, map_y);
00091   double finish_x = floor(map_x),
00092          finish_y = floor(map_y);
00093   nav_grid::Index finish_index(static_cast<unsigned int>(finish_x), static_cast<unsigned int>(finish_y));
00094 
00095   unsigned int c = 0, max_iterations = static_cast<unsigned int>(potential_grid.size() * iteration_factor_);
00096   geometry_msgs::Pose2D current;
00097 
00098   while (c++ < max_iterations)
00099   {
00100     current.x = index.x + dx;
00101     current.y = index.y + dy;
00102     path.poses.push_back(current);
00103     path_cost += step_size_ * cost_interpreter_->getCost(index.x, index.y);
00104 
00105     // check if near goal
00106     if (index == finish_index || (fabs(current.x - finish_x) <= step_size_ && fabs(current.y - finish_y) <= step_size_))
00107     {
00108       nav_2d_msgs::Path2D world_path = mapPathToWorldPath(path, potential_grid.getInfo());
00109       world_path.poses.push_back(goal);
00110       return world_path;
00111     }
00112 
00113     ROS_DEBUG_NAMED("GradientPath", "xy: %.2f %.2f (%d %.2f, %d %.2f)", current.x, current.y,
00114                     index.x, dx, index.y, dy);
00115 
00116     // Check if the most recent pose, and two before the most recent pose are the same
00117     bool oscillation_detected = false;
00118     int npath = path.poses.size();
00119     if (npath > 2 && path.poses[npath - 1].x == path.poses[npath - 3].x
00120                   && path.poses[npath - 1].y == path.poses[npath - 3].y)
00121     {
00122       oscillation_detected = true;
00123     }
00124 
00125     /* We need to examine eight different neighboring cells.
00126      * +----------+----------+----------+
00127      * | index_nw | index_n  | index_ne |
00128      * +----------+----------+----------+
00129      * | index_w  | index    | index_e  |
00130      * +----------+----------+----------+
00131      * | index_sw | index_s  | index_se |
00132      * +----------+----------+----------+
00133      *
00134      * The original implementors of navfn unrolled the loops for interating over all the cells.
00135      * We copy their technique here.
00136      */
00137     if (shouldGridStep(potential_grid, index) || oscillation_detected)
00138     {
00139       std::string error;
00140       if (oscillation_detected)
00141         error = "Oscillation detected.";
00142       else
00143         error = "High Potential Nearby.";
00144       ROS_DEBUG_NAMED("GradientPath", "%s Following grid.", error.c_str());
00145       index = gridStep(potential_grid, index);
00146       dx = 0.0;
00147       dy = 0.0;
00148 
00149       ROS_DEBUG_NAMED("GradientPath", "Potential: %0.1f  position: %0.1f,%0.1f",
00150                       potential_grid(index), path.poses[npath - 1].x, path.poses[npath - 1].y);
00151     }
00152     // have a good gradient here
00153     else
00154     {
00155       /* Calculate the gradient of four cells
00156        * +----------+----------+----------+
00157        * |          | index_n  | index_ne |
00158        * +----------o----------o----------+
00159        * |          | index    | index_e  |
00160        * +----------o----------o----------+
00161        * |          |          |          |
00162        * +----------+----------+----------+
00163        * Our position is somewhere in the index cell, relative to dx and dy
00164        * The gradient we calculate is relative to the lower left corner of each cell (as marked by 'o's above)
00165        * Thus, the precise graident we will follow is two-dimensional linear combination of four gradients
00166        * surrounding our current cell.
00167        */
00168       nav_grid::Index  index_e(index.x + 1, index.y),
00169                        index_n(index.x,     index.y + 1),
00170                       index_ne(index.x + 1, index.y + 1);
00171       calculateGradient(potential_grid, index);
00172       calculateGradient(potential_grid, index_e);
00173       calculateGradient(potential_grid, index_n);
00174       calculateGradient(potential_grid, index_ne);
00175 
00176       // get interpolated gradient
00177       float x1 = (1.0 - dx) * gradx_(index) + dx * gradx_(index_e);
00178       float x2 = (1.0 - dx) * gradx_(index_n) + dx * gradx_(index_ne);
00179       float x = (1.0 - dy) * x1 + dy * x2;  // interpolated x
00180       float y1 = (1.0 - dx) * grady_(index) + dx * grady_(index_e);
00181       float y2 = (1.0 - dx) * grady_(index_n) + dx * grady_(index_ne);
00182       float y = (1.0 - dy) * y1 + dy * y2;  // interpolated y
00183 
00184       // show gradients
00185       ROS_DEBUG_NAMED("GradientPath", "%0.2f,%0.2f  %0.2f,%0.2f  %0.2f,%0.2f  %0.2f,%0.2f; final x=%.3f, y=%.3f",
00186                       gradx_(index), grady_(index), gradx_(index_e), grady_(index_e),
00187                       gradx_(index_n), grady_(index_n), gradx_(index_ne), grady_(index_ne), x, y);
00188 
00189       // check for zero gradient
00190       if (x == 0.0 && y == 0.0)
00191       {
00192         // This generally shouldn't happen, but can if using a not well-formed potential function
00193         // If there's no gradient, move along the grid.
00194         index = gridStep(potential_grid, index);
00195         dx = 0.0;
00196         dy = 0.0;
00197         continue;
00198       }
00199 
00200       // move in the right direction
00201       float ss = step_size_ / hypot(x, y);
00202       dx += x * ss;
00203       dy += y * ss;
00204 
00205       // check if we have moved out of the current cell
00206       if (dx >= 1.0)
00207       {
00208         index.x++;
00209         dx -= 1.0;
00210       }
00211       if (dx < 0.0)
00212       {
00213         index.x--;
00214         dx += 1.0;
00215       }
00216       if (dy >= 1.0)
00217       {
00218         index.y++;
00219         dy -= 1.0;
00220       }
00221       if (dy < 0.0)
00222       {
00223         index.y--;
00224         dy += 1.0;
00225       }
00226     }
00227 
00228     ROS_DEBUG_NAMED("GradientPath", "Potential: %0.1f  gradient: %0.1f,%0.1f  position: %0.1f,%0.1f\n",
00229                     potential_grid(index), dx, dy, path.poses[npath - 1].x, path.poses[npath - 1].y);
00230   }
00231 
00232   return path;
00233 }
00234 
00235 bool GradientPath::shouldGridStep(const dlux_global_planner::PotentialGrid& potential_grid,
00236                                   const nav_grid::Index& index)
00237 {
00238   bool near_edge = index.x == 0 || index.x >= potential_grid.getWidth() - 1 ||
00239                    index.y == 0 || index.y >= potential_grid.getHeight() - 1;
00240   if (near_edge || !grid_step_near_high_)
00241     return near_edge;
00242 
00243   // check for potentials at eight positions near cell
00244   return potential_grid(index) >= HIGH_POTENTIAL ||
00245       potential_grid(index.x + 1, index.y)     >= HIGH_POTENTIAL ||
00246       potential_grid(index.x - 1, index.y)     >= HIGH_POTENTIAL ||
00247       potential_grid(index.x,     index.y + 1) >= HIGH_POTENTIAL ||
00248       potential_grid(index.x,     index.y - 1) >= HIGH_POTENTIAL ||
00249       potential_grid(index.x + 1, index.y + 1) >= HIGH_POTENTIAL ||
00250       potential_grid(index.x + 1, index.y - 1) >= HIGH_POTENTIAL ||
00251       potential_grid(index.x - 1, index.y + 1) >= HIGH_POTENTIAL ||
00252       potential_grid(index.x - 1, index.y - 1) >= HIGH_POTENTIAL;
00253 }
00254 
00255 nav_grid::Index GradientPath::gridStep(const dlux_global_planner::PotentialGrid& potential_grid,
00256                                        const nav_grid::Index& index)
00257 {
00258   // check eight neighbors to find the lowest
00259   nav_grid::Index min_index = index;
00260   float min_potential = potential_grid(index);
00261 
00262   // Unrolled loop
00263 
00264   // Check the south
00265   if (index.y > 0)
00266   {
00267     nav_grid::Index index_s(index.x, index.y - 1);
00268     if (potential_grid(index_s) < min_potential)
00269     {
00270       min_potential = potential_grid(index_s);
00271       min_index = index_s;
00272     }
00273 
00274     // check the south west
00275     if (index.x > 0)
00276     {
00277       nav_grid::Index index_sw(index.x - 1, index.y - 1);
00278       if (potential_grid(index_sw) < min_potential)
00279       {
00280         min_potential = potential_grid(index_sw);
00281         min_index = index_sw;
00282       }
00283     }
00284 
00285     // check the south east
00286     if (index.x < potential_grid.getWidth() - 1)
00287     {
00288       nav_grid::Index index_se(index.x + 1, index.y - 1);
00289       if (potential_grid(index_se) < min_potential)
00290       {
00291         min_potential = potential_grid(index_se);
00292         min_index = index_se;
00293       }
00294     }
00295   }
00296 
00297   // check the west
00298   if (index.x > 0)
00299   {
00300     nav_grid::Index index_w(index.x - 1, index.y);
00301     if (potential_grid(index_w) < min_potential)
00302     {
00303       min_potential = potential_grid(index_w);
00304       min_index = index_w;
00305     }
00306   }
00307   // check the east
00308   if (index.x < potential_grid.getWidth() - 1)
00309   {
00310     nav_grid::Index index_e(index.x + 1, index.y);
00311     if (potential_grid(index_e) < min_potential)
00312     {
00313       min_potential = potential_grid(index_e);
00314       min_index = index_e;
00315     }
00316   }
00317 
00318   // Check the north
00319   if (index.y < potential_grid.getHeight() - 1)
00320   {
00321     nav_grid::Index index_n(index.x, index.y + 1);
00322     if (potential_grid(index_n) < min_potential)
00323     {
00324       min_potential = potential_grid(index_n);
00325       min_index = index_n;
00326     }
00327 
00328     // check the north west
00329     if (index.x > 0)
00330     {
00331       nav_grid::Index index_nw(index.x - 1, index.y + 1);
00332       if (potential_grid(index_nw) < min_potential)
00333       {
00334         min_potential = potential_grid(index_nw);
00335         min_index = index_nw;
00336       }
00337     }
00338 
00339     // check the north east
00340     if (index.x < potential_grid.getWidth() - 1)
00341     {
00342       nav_grid::Index index_ne(index.x + 1, index.y + 1);
00343       if (potential_grid(index_ne) < min_potential)
00344       {
00345         min_potential = potential_grid(index_ne);
00346         min_index = index_ne;
00347       }
00348     }
00349   }
00350   if (min_index == index)
00351   {
00352     throw nav_core2::PlannerException("No path found. Local min.");
00353   }
00354   else if (potential_grid(min_index) >= HIGH_POTENTIAL)
00355   {
00356     throw nav_core2::PlannerException("No path found, high potential");
00357   }
00358   return min_index;
00359 }
00360 
00361 void GradientPath::calculateGradient(const dlux_global_planner::PotentialGrid& potential_grid,
00362                                      const nav_grid::Index& index)
00363 {
00364   // If non-zero, gradient already calculated, skip
00365   if (gradx_(index) + grady_(index) > 0.0)
00366     return;
00367 
00368   float cv = potential_grid(index);
00369   float dx = 0.0;
00370   float dy = 0.0;
00371 
00372   float south_p = index.y > 0                              ? potential_grid(index.x, index.y - 1) : HIGH_POTENTIAL;
00373   float north_p = index.y < potential_grid.getHeight() - 1 ? potential_grid(index.x, index.y + 1) : HIGH_POTENTIAL;
00374   float west_p =  index.x > 0                              ? potential_grid(index.x - 1, index.y) : HIGH_POTENTIAL;
00375   float east_p =  index.x < potential_grid.getWidth() - 1  ? potential_grid(index.x + 1, index.y) : HIGH_POTENTIAL;
00376 
00377   // check if in an obstacle
00378   if (cv >= HIGH_POTENTIAL)
00379   {
00380     if (west_p < HIGH_POTENTIAL)
00381       dx = -lethal_cost_;
00382     else if (east_p < HIGH_POTENTIAL)
00383       dx = lethal_cost_;
00384 
00385     if (south_p < HIGH_POTENTIAL)
00386       dy = -lethal_cost_;
00387     else if (north_p < HIGH_POTENTIAL)
00388       dy = lethal_cost_;
00389   }
00390   else  // not in an obstacle
00391   {
00392     // dx calc, average to sides
00393     if (west_p < HIGH_POTENTIAL)
00394       dx += west_p - cv;
00395     if (east_p < HIGH_POTENTIAL)
00396       dx += cv - east_p;
00397 
00398     // dy calc, average to sides
00399     if (south_p < HIGH_POTENTIAL)
00400       dy += south_p - cv;
00401     if (north_p < HIGH_POTENTIAL)
00402       dy += cv - north_p;
00403   }
00404 
00405   // normalize
00406   float norm = hypot(dx, dy);
00407   if (norm > 0)
00408   {
00409     norm = 1.0 / norm;
00410     gradx_.setValue(index, norm * dx);
00411     grady_.setValue(index, norm * dy);
00412   }
00413 }
00414 
00415 }  // namespace dlux_plugins


dlux_plugins
Author(s):
autogenerated on Wed Jun 26 2019 20:09:55