trajectory_planner.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2008, Willow Garage, Inc.
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 Willow Garage 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 * Author: Eitan Marder-Eppstein
00036 *********************************************************************/
00037 
00038 #include <iri_diff_local_planner/trajectory_planner.h>
00039 
00040 using namespace std;
00041 using namespace costmap_2d;
00042 
00043 namespace iri_diff_local_planner{
00044   void TrajectoryPlanner::reconfigure(IriDiffLocalPlannerConfig &cfg) 
00045   {
00046       iri_diff_local_planner::IriDiffLocalPlannerConfig config(cfg);
00047 
00048       boost::mutex::scoped_lock l(configuration_mutex_);
00049 
00050       acc_lim_x_ = config.acc_lim_x;
00051       acc_lim_y_ = config.acc_lim_y;
00052       acc_lim_theta_ = config.acc_lim_theta;
00053 
00054       *max_vel_x_ = config.max_vel_x;
00055       min_vel_x_ = config.min_vel_x;
00056       max_vel_th_ = config.max_vel_theta;
00057       min_vel_th_ = config.min_vel_theta;
00058       min_in_place_vel_th_ = config.min_in_place_vel_theta;
00059 
00060       sim_time_ = config.sim_time;
00061       sim_granularity_ = config.sim_granularity;
00062 
00063       pdist_scale_ = config.pdist_scale;
00064       gdist_scale_ = config.gdist_scale;
00065       occdist_scale_ = config.occdist_scale;
00066 
00067       oscillation_reset_dist_ = config.oscillation_reset_dist;
00068       escape_reset_dist_ = config.escape_reset_dist;
00069       escape_reset_theta_ = config.escape_reset_theta;
00070 
00071       vx_samples_ = config.vx_samples;
00072       vtheta_samples_ = config.vtheta_samples;
00073 
00074       if (vx_samples_ <= 0) {
00075           config.vx_samples = 1;
00076           vx_samples_ = config.vx_samples;
00077           ROS_WARN("You've specified that you don't want any samples in the x dimension. We'll at least assume that you want to sample one value... so we're going to set vx_samples to 1 instead");
00078       }
00079       if(vtheta_samples_ <= 0) {
00080           config.vtheta_samples = 1;
00081           vtheta_samples_ = config.vtheta_samples;
00082           ROS_WARN("You've specified that you don't want any samples in the theta dimension. We'll at least assume that you want to sample one value... so we're going to set vtheta_samples to 1 instead");
00083       }
00084 
00085       heading_lookahead_ = config.heading_lookahead;
00086 
00087       holonomic_robot_ = config.holonomic_robot;
00088       
00089       backup_vel_ = config.escape_vel;
00090 
00091       dwa_ = config.dwa;
00092 
00093       heading_scoring_ = config.heading_scoring;
00094       heading_scoring_timestep_ = config.heading_scoring_timestep;
00095 
00096       simple_attractor_ = config.simple_attractor;
00097 
00098       angular_sim_granularity_ = config.angular_sim_granularity;
00099 
00100       //y-vels
00101       string y_string = config.y_vels;
00102       vector<string> y_strs;
00103       boost::split(y_strs, y_string, boost::is_any_of(", "), boost::token_compress_on);
00104 
00105       vector<double>y_vels;
00106       for(vector<string>::iterator it=y_strs.begin(); it != y_strs.end(); ++it) {
00107           istringstream iss(*it);
00108           double temp;
00109           iss >> temp;
00110           y_vels.push_back(temp);
00111           //ROS_INFO("Adding y_vel: %e", temp);
00112       }
00113 
00114       y_vels_ = y_vels;
00115       
00116   }
00117 
00118   TrajectoryPlanner::TrajectoryPlanner(WorldModel& world_model, 
00119       const Costmap2D& costmap, 
00120       std::vector<geometry_msgs::Point> footprint_spec,
00121       double acc_lim_x, double acc_lim_y, double acc_lim_theta,
00122       double sim_time, double sim_granularity, 
00123       int vx_samples, int vtheta_samples,
00124       double pdist_scale, double gdist_scale, double occdist_scale,
00125       double heading_lookahead, double oscillation_reset_dist,
00126       double escape_reset_dist, double escape_reset_theta,
00127       bool holonomic_robot,
00128       double *max_vel_x, double min_vel_x,
00129       double max_vel_th, double min_vel_th, double min_in_place_vel_th,
00130       double backup_vel,
00131       bool dwa, bool heading_scoring, double heading_scoring_timestep, bool simple_attractor,
00132       vector<double> y_vels, double stop_time_buffer, double sim_period, double angular_sim_granularity)
00133     : map_(costmap.getSizeInCellsX(), costmap.getSizeInCellsY()), costmap_(costmap), 
00134     world_model_(world_model), footprint_spec_(footprint_spec),
00135     sim_time_(sim_time), sim_granularity_(sim_granularity), angular_sim_granularity_(angular_sim_granularity),
00136     vx_samples_(vx_samples), vtheta_samples_(vtheta_samples),
00137     pdist_scale_(pdist_scale), gdist_scale_(gdist_scale), occdist_scale_(occdist_scale),
00138     acc_lim_x_(acc_lim_x), acc_lim_y_(acc_lim_y), acc_lim_theta_(acc_lim_theta),
00139     prev_x_(0), prev_y_(0), escape_x_(0), escape_y_(0), escape_theta_(0), heading_lookahead_(heading_lookahead), 
00140     oscillation_reset_dist_(oscillation_reset_dist), escape_reset_dist_(escape_reset_dist), 
00141     escape_reset_theta_(escape_reset_theta), holonomic_robot_(holonomic_robot),
00142     min_vel_x_(min_vel_x), 
00143     max_vel_th_(max_vel_th), min_vel_th_(min_vel_th), min_in_place_vel_th_(min_in_place_vel_th),
00144     backup_vel_(backup_vel),
00145     dwa_(dwa), heading_scoring_(heading_scoring), heading_scoring_timestep_(heading_scoring_timestep),
00146     simple_attractor_(simple_attractor), y_vels_(y_vels), stop_time_buffer_(stop_time_buffer), sim_period_(sim_period)
00147   {
00148     this->max_vel_x_=max_vel_x;
00149 
00150     //the robot is not stuck to begin with
00151     stuck_left = false;
00152     stuck_right = false;
00153     stuck_left_strafe = false;
00154     stuck_right_strafe = false;
00155     rotating_left = false;
00156     rotating_right = false;
00157     strafe_left = false;
00158     strafe_right = false;
00159 
00160     escaping_ = false;
00161   }
00162 
00163   TrajectoryPlanner::~TrajectoryPlanner(){}
00164 
00165   bool TrajectoryPlanner::getCellCosts(int cx, int cy, float &path_cost, float &goal_cost, float &occ_cost, float &total_cost) {
00166     MapCell cell = map_(cx, cy);
00167     if (cell.within_robot) {
00168         return false;
00169     }
00170     occ_cost = costmap_.getCost(cx, cy);
00171     if (cell.path_dist >= map_.map_.size() || cell.goal_dist >= map_.map_.size() || occ_cost >= costmap_2d::INSCRIBED_INFLATED_OBSTACLE) {
00172         return false;
00173     }
00174     path_cost = cell.path_dist;
00175     goal_cost = cell.goal_dist;
00176     total_cost = pdist_scale_ * path_cost + gdist_scale_ * goal_cost + occdist_scale_ * occ_cost;
00177     return true;
00178   }
00179 
00180   //create and score a trajectory given the current pose of the robot and selected velocities
00181   void TrajectoryPlanner::generateTrajectory(double x, double y, double theta, double vx, double vy, 
00182       double vtheta, double vx_samp, double vy_samp, double vtheta_samp, 
00183       double acc_x, double acc_y, double acc_theta, double impossible_cost,
00184       Trajectory& traj){
00185 
00186     // make sure the configuration doesn't change mid run
00187     boost::mutex::scoped_lock l(configuration_mutex_);
00188 
00189     double x_i = x;
00190     double y_i = y;
00191     double theta_i = theta;
00192 
00193     double vx_i, vy_i, vtheta_i;
00194 
00195     vx_i = vx;
00196     vy_i = vy;
00197     vtheta_i = vtheta;
00198 
00199     //compute the magnitude of the velocities
00200     double vmag = sqrt(vx_samp * vx_samp + vy_samp * vy_samp);
00201 
00202     //compute the number of steps we must take along this trajectory to be "safe"
00203     int num_steps;
00204     if(!heading_scoring_)
00205       num_steps = int(max((vmag * sim_time_) / sim_granularity_, fabs(vtheta_samp) / angular_sim_granularity_) + 0.5);
00206     else
00207       num_steps = int(sim_time_ / sim_granularity_ + 0.5);
00208 
00209     //we at least want to take one step... even if we won't move, we want to score our current position
00210     if(num_steps == 0)
00211       num_steps = 1;
00212 
00213     double dt = sim_time_ / num_steps;
00214     double time = 0.0;
00215 
00216     //create a potential trajectory
00217     traj.resetPoints();
00218     traj.xv_ = vx_samp; 
00219     traj.yv_ = vy_samp; 
00220     traj.thetav_ = vtheta_samp;
00221     traj.cost_ = -1.0;
00222 
00223     //initialize the costs for the trajectory
00224     double path_dist = 0.0;
00225     double goal_dist = 0.0;
00226     double occ_cost = 0.0;
00227     double heading_diff = 0.0;
00228 
00229     for(int i = 0; i < num_steps; ++i){
00230       //get map coordinates of a point
00231       unsigned int cell_x, cell_y;
00232 
00233       //we don't want a path that goes off the know map
00234       if(!costmap_.worldToMap(x_i, y_i, cell_x, cell_y)){
00235         traj.cost_ = -1.0;
00236         return;
00237       }
00238 
00239       //check the point on the trajectory for legality
00240       double footprint_cost = footprintCost(x_i, y_i, theta_i);
00241 
00242       //if the footprint hits an obstacle this trajectory is invalid
00243       if(footprint_cost < 0){
00244         traj.cost_ = -1.0;
00245         return;
00246         //TODO: Really look at getMaxSpeedToStopInTime... dues to discretization errors and high acceleration limits,
00247         //it can actually cause the robot to hit obstacles. There may be something to be done to fix, but I'll have to 
00248         //come back to it when I have time. Right now, pulling it out as it'll just make the robot a bit more conservative,
00249         //but safe.
00250         /*
00251         double max_vel_x, max_vel_y, max_vel_th;
00252         //we want to compute the max allowable speeds to be able to stop
00253         //to be safe... we'll make sure we can stop some time before we actually hit
00254         getMaxSpeedToStopInTime(time - stop_time_buffer_ - dt, max_vel_x, max_vel_y, max_vel_th);
00255 
00256         //check if we can stop in time
00257         if(fabs(vx_samp) < max_vel_x && fabs(vy_samp) < max_vel_y && fabs(vtheta_samp) < max_vel_th){
00258           ROS_ERROR("v: (%.2f, %.2f, %.2f), m: (%.2f, %.2f, %.2f) t:%.2f, st: %.2f, dt: %.2f", vx_samp, vy_samp, vtheta_samp, max_vel_x, max_vel_y, max_vel_th, time, stop_time_buffer_, dt);
00259           //if we can stop... we'll just break out of the loop here.. no point in checking future points
00260           break;
00261         }
00262         else{
00263           traj.cost_ = -1.0;
00264           return;
00265         }
00266         */
00267       }
00268 
00269       occ_cost = std::max(std::max(occ_cost, footprint_cost), double(costmap_.getCost(cell_x, cell_y)));
00270 
00271       double cell_pdist = map_(cell_x, cell_y).path_dist;
00272       double cell_gdist = map_(cell_x, cell_y).goal_dist;
00273 
00274       //update path and goal distances
00275       if(!heading_scoring_){
00276         path_dist = cell_pdist;
00277         goal_dist = cell_gdist;
00278       }
00279       else if(time >= heading_scoring_timestep_ && time < heading_scoring_timestep_ + dt){
00280         heading_diff = headingDiff(cell_x, cell_y, x_i, y_i, theta_i);
00281         //update path and goal distances
00282         path_dist = cell_pdist;
00283         goal_dist = cell_gdist;
00284       }
00285 
00286       //do we want to follow blindly
00287       if(simple_attractor_){
00288         goal_dist = (x_i - global_plan_[global_plan_.size() -1].pose.position.x) * 
00289           (x_i - global_plan_[global_plan_.size() -1].pose.position.x) + 
00290           (y_i - global_plan_[global_plan_.size() -1].pose.position.y) * 
00291           (y_i - global_plan_[global_plan_.size() -1].pose.position.y);
00292         path_dist = 0.0;
00293       }
00294       else{
00295         //if a point on this trajectory has no clear path to goal it is invalid
00296         if(impossible_cost <= goal_dist || impossible_cost <= path_dist){
00297           ROS_DEBUG("No path to goal with goal distance = %f, path_distance = %f and max cost = %f", 
00298               goal_dist, path_dist, impossible_cost);
00299           traj.cost_ = -2.0;
00300           return;
00301         }
00302       }
00303 
00304 
00305       //the point is legal... add it to the trajectory
00306       traj.addPoint(x_i, y_i, theta_i);
00307 
00308       //calculate velocities
00309       vx_i = computeNewVelocity(vx_samp, vx_i, acc_x, dt);
00310       vy_i = computeNewVelocity(vy_samp, vy_i, acc_y, dt);
00311       vtheta_i = computeNewVelocity(vtheta_samp, vtheta_i, acc_theta, dt);
00312 
00313       //calculate positions
00314       x_i = computeNewXPosition(x_i, vx_i, vy_i, theta_i, dt);
00315       y_i = computeNewYPosition(y_i, vx_i, vy_i, theta_i, dt);
00316       theta_i = computeNewThetaPosition(theta_i, vtheta_i, dt);
00317 
00318       //increment time
00319       time += dt;
00320     }
00321 
00322     //ROS_INFO("OccCost: %f, vx: %.2f, vy: %.2f, vtheta: %.2f", occ_cost, vx_samp, vy_samp, vtheta_samp);
00323     double cost = -1.0;
00324     if(!heading_scoring_)
00325       cost = pdist_scale_ * path_dist + goal_dist * gdist_scale_ + occdist_scale_ * occ_cost;
00326     else
00327       cost = occdist_scale_ * occ_cost + pdist_scale_ * path_dist + 0.3 * heading_diff + goal_dist * gdist_scale_;
00328 
00329     traj.cost_ = cost;
00330   }
00331 
00332   double TrajectoryPlanner::headingDiff(int cell_x, int cell_y, double x, double y, double heading){
00333     double heading_diff = DBL_MAX;
00334     unsigned int goal_cell_x, goal_cell_y;
00335     //find a clear line of sight from the robot's cell to a point on the path
00336     for(int i = global_plan_.size() - 1; i >=0; --i){
00337       if(costmap_.worldToMap(global_plan_[i].pose.position.x, global_plan_[i].pose.position.y, goal_cell_x, goal_cell_y)){
00338         if(lineCost(cell_x, goal_cell_x, cell_y, goal_cell_y) >= 0){
00339           double gx, gy;
00340           costmap_.mapToWorld(goal_cell_x, goal_cell_y, gx, gy);
00341           double v1_x = gx - x;
00342           double v1_y = gy - y;
00343           double v2_x = cos(heading);
00344           double v2_y = sin(heading);
00345 
00346           double perp_dot = v1_x * v2_y - v1_y * v2_x;
00347           double dot = v1_x * v2_x + v1_y * v2_y;
00348 
00349           //get the signed angle
00350           double vector_angle = atan2(perp_dot, dot);
00351 
00352           heading_diff = fabs(vector_angle);
00353           return heading_diff;
00354 
00355         }
00356       }
00357     }
00358     return heading_diff;
00359   }
00360 
00361   //calculate the cost of a ray-traced line
00362   double TrajectoryPlanner::lineCost(int x0, int x1, 
00363       int y0, int y1){
00364     //Bresenham Ray-Tracing
00365     int deltax = abs(x1 - x0);        // The difference between the x's
00366     int deltay = abs(y1 - y0);        // The difference between the y's
00367     int x = x0;                       // Start x off at the first pixel
00368     int y = y0;                       // Start y off at the first pixel
00369 
00370     int xinc1, xinc2, yinc1, yinc2;
00371     int den, num, numadd, numpixels;
00372 
00373     double line_cost = 0.0;
00374     double point_cost = -1.0;
00375 
00376     if (x1 >= x0)                 // The x-values are increasing
00377     {
00378       xinc1 = 1;
00379       xinc2 = 1;
00380     }
00381     else                          // The x-values are decreasing
00382     {
00383       xinc1 = -1;
00384       xinc2 = -1;
00385     }
00386 
00387     if (y1 >= y0)                 // The y-values are increasing
00388     {
00389       yinc1 = 1;
00390       yinc2 = 1;
00391     }
00392     else                          // The y-values are decreasing
00393     {
00394       yinc1 = -1;
00395       yinc2 = -1;
00396     }
00397 
00398     if (deltax >= deltay)         // There is at least one x-value for every y-value
00399     {
00400       xinc1 = 0;                  // Don't change the x when numerator >= denominator
00401       yinc2 = 0;                  // Don't change the y for every iteration
00402       den = deltax;
00403       num = deltax / 2;
00404       numadd = deltay;
00405       numpixels = deltax;         // There are more x-values than y-values
00406     }
00407     else                          // There is at least one y-value for every x-value
00408     {
00409       xinc2 = 0;                  // Don't change the x for every iteration
00410       yinc1 = 0;                  // Don't change the y when numerator >= denominator
00411       den = deltay;
00412       num = deltay / 2;
00413       numadd = deltax;
00414       numpixels = deltay;         // There are more y-values than x-values
00415     }
00416 
00417     for (int curpixel = 0; curpixel <= numpixels; curpixel++)
00418     {
00419       point_cost = pointCost(x, y); //Score the current point
00420 
00421       if(point_cost < 0)
00422         return -1;
00423 
00424       if(line_cost < point_cost)
00425         line_cost = point_cost;
00426 
00427       num += numadd;              // Increase the numerator by the top of the fraction
00428       if (num >= den)             // Check if numerator >= denominator
00429       {
00430         num -= den;               // Calculate the new numerator value
00431         x += xinc1;               // Change the x as appropriate
00432         y += yinc1;               // Change the y as appropriate
00433       }
00434       x += xinc2;                 // Change the x as appropriate
00435       y += yinc2;                 // Change the y as appropriate
00436     }
00437 
00438     return line_cost;
00439   }
00440 
00441   double TrajectoryPlanner::pointCost(int x, int y){
00442     unsigned char cost = costmap_.getCost(x, y);
00443     //if the cell is in an obstacle the path is invalid
00444     if(cost == LETHAL_OBSTACLE || cost == INSCRIBED_INFLATED_OBSTACLE || cost == NO_INFORMATION){
00445       return -1;
00446     }
00447 
00448     return cost;
00449   }
00450 
00451   void TrajectoryPlanner::updatePlan(const vector<geometry_msgs::PoseStamped>& new_plan, bool compute_dists){
00452     global_plan_.resize(new_plan.size());
00453     for(unsigned int i = 0; i < new_plan.size(); ++i){
00454       global_plan_[i] = new_plan[i];
00455     }
00456 
00457     if(compute_dists){
00458       //reset the map for new operations
00459       map_.resetPathDist();
00460 
00461       //make sure that we update our path based on the global plan and compute costs
00462       map_.setPathCells(costmap_, global_plan_);
00463       ROS_DEBUG("Path/Goal distance computed");
00464     }
00465   }
00466 
00467   bool TrajectoryPlanner::checkTrajectory(double x, double y, double theta, double vx, double vy, 
00468       double vtheta, double vx_samp, double vy_samp, double vtheta_samp){
00469     Trajectory t; 
00470 
00471     double cost = scoreTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp);
00472 
00473     //if the trajectory is a legal one... the check passes
00474     if(cost >= 0)
00475       return true;
00476 
00477     //otherwise the check fails
00478     return false;
00479   }
00480 
00481   double TrajectoryPlanner::scoreTrajectory(double x, double y, double theta, double vx, double vy, 
00482       double vtheta, double vx_samp, double vy_samp, double vtheta_samp){
00483     Trajectory t; 
00484     double impossible_cost = map_.map_.size();
00485     generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00486         acc_lim_x_, acc_lim_y_, acc_lim_theta_, impossible_cost, t);
00487 
00488     // return the cost.
00489     return double( t.cost_ );
00490   }
00491 
00492   //create the trajectories we wish to score
00493   Trajectory TrajectoryPlanner::createTrajectories(double x, double y, double theta, 
00494       double vx, double vy, double vtheta,
00495       double acc_x, double acc_y, double acc_theta){
00496     //compute feasible velocity limits in robot space
00497     double max_vel_x, max_vel_theta;
00498     double min_vel_x, min_vel_theta;
00499 
00500     //should we use the dynamic window approach?
00501     if(dwa_){
00502       max_vel_x = max(min(*max_vel_x_, vx + acc_x * sim_period_), min_vel_x_);
00503       min_vel_x = max(min_vel_x_, vx - acc_x * sim_period_);
00504 
00505       max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_period_);
00506       min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_period_);
00507     }
00508     else{
00509       max_vel_x = max(min(*max_vel_x_, vx + acc_x * sim_time_), min_vel_x_);
00510       min_vel_x = max(min_vel_x_, vx - acc_x * sim_time_);
00511 
00512       max_vel_theta = min(max_vel_th_, vtheta + acc_theta * sim_time_);
00513       min_vel_theta = max(min_vel_th_, vtheta - acc_theta * sim_time_);
00514     }
00515 
00516 
00517     //we want to sample the velocity space regularly
00518     double dvx = (max_vel_x - min_vel_x) / (vx_samples_ - 1);
00519     double dvtheta = (max_vel_theta - min_vel_theta) / (vtheta_samples_ - 1);
00520 
00521     double vx_samp = min_vel_x;
00522     double vtheta_samp = min_vel_theta;
00523     double vy_samp = 0.0;
00524 
00525     //keep track of the best trajectory seen so far
00526     Trajectory* best_traj = &traj_one;
00527     best_traj->cost_ = -1.0;
00528 
00529     Trajectory* comp_traj = &traj_two;
00530     comp_traj->cost_ = -1.0;
00531 
00532     Trajectory* swap = NULL;
00533 
00534     //any cell with a cost greater than the size of the map is impossible
00535     double impossible_cost = map_.map_.size();
00536 
00537     //if we're performing an escape we won't allow moving forward
00538     if(!escaping_){
00539       //loop through all x velocities
00540       for(int i = 0; i < vx_samples_; ++i){
00541         vtheta_samp = 0;
00542         //first sample the straight trajectory
00543         generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00544             acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00545 
00546         //if the new trajectory is better... let's take it
00547         if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
00548           swap = best_traj;
00549           best_traj = comp_traj;
00550           comp_traj = swap;
00551         }
00552 
00553         vtheta_samp = min_vel_theta;
00554         //next sample all theta trajectories
00555         for(int j = 0; j < vtheta_samples_ - 1; ++j){
00556           generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00557               acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00558 
00559           //if the new trajectory is better... let's take it
00560           if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
00561             swap = best_traj;
00562             best_traj = comp_traj;
00563             comp_traj = swap;
00564           }
00565           vtheta_samp += dvtheta;
00566         }
00567         vx_samp += dvx;
00568       }
00569 
00570       //only explore y velocities with holonomic robots
00571       if(holonomic_robot_){
00572         //explore trajectories that move forward but also strafe slightly
00573         vx_samp = 0.1;
00574         vy_samp = 0.1;
00575         vtheta_samp = 0.0;
00576         generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00577             acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00578 
00579         //if the new trajectory is better... let's take it
00580         if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
00581           swap = best_traj;
00582           best_traj = comp_traj;
00583           comp_traj = swap;
00584         }
00585 
00586         vx_samp = 0.1;
00587         vy_samp = -0.1;
00588         vtheta_samp = 0.0;
00589         generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00590             acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00591 
00592         //if the new trajectory is better... let's take it
00593         if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
00594           swap = best_traj;
00595           best_traj = comp_traj;
00596           comp_traj = swap;
00597         }
00598       }
00599     }
00600 
00601     //next we want to generate trajectories for rotating in place
00602     vtheta_samp = min_vel_theta;
00603     vx_samp = 0.0;
00604     vy_samp = 0.0;
00605 
00606     //let's try to rotate toward open space
00607     double heading_dist = DBL_MAX;
00608 
00609     for(int i = 0; i < vtheta_samples_; ++i){
00610       //enforce a minimum rotational velocity because the base can't handle small in-place rotations
00611       double vtheta_samp_limited = vtheta_samp > 0 ? max(vtheta_samp, min_in_place_vel_th_) 
00612         : min(vtheta_samp, -1.0 * min_in_place_vel_th_);
00613 
00614       generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp_limited, 
00615           acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00616 
00617       //if the new trajectory is better... let's take it... 
00618       //note if we can legally rotate in place we prefer to do that rather than move with y velocity
00619       if(comp_traj->cost_ >= 0 
00620           && (comp_traj->cost_ <= best_traj->cost_ || best_traj->cost_ < 0 || best_traj->yv_ != 0.0) 
00621           && (vtheta_samp > dvtheta || vtheta_samp < -1 * dvtheta)){
00622         double x_r, y_r, th_r;
00623         comp_traj->getEndpoint(x_r, y_r, th_r);
00624         x_r += heading_lookahead_ * cos(th_r);
00625         y_r += heading_lookahead_ * sin(th_r);
00626         unsigned int cell_x, cell_y;
00627 
00628         //make sure that we'll be looking at a legal cell
00629         if(costmap_.worldToMap(x_r, y_r, cell_x, cell_y)){
00630           double ahead_gdist = map_(cell_x, cell_y).goal_dist;
00631           if(ahead_gdist < heading_dist){
00632             //if we haven't already tried rotating left since we've moved forward
00633             if(vtheta_samp < 0 && !stuck_left){
00634               swap = best_traj;
00635               best_traj = comp_traj;
00636               comp_traj = swap;
00637               heading_dist = ahead_gdist;
00638             }
00639             //if we haven't already tried rotating right since we've moved forward
00640             else if(vtheta_samp > 0 && !stuck_right){
00641               swap = best_traj;
00642               best_traj = comp_traj;
00643               comp_traj = swap;
00644               heading_dist = ahead_gdist;
00645             }
00646           }
00647         }
00648       }
00649 
00650       vtheta_samp += dvtheta;
00651     }
00652 
00653     //do we have a legal trajectory
00654     if(best_traj->cost_ >= 0){
00655       if(!(best_traj->xv_ > 0)){
00656         if(best_traj->thetav_ < 0){
00657           if(rotating_right){
00658             stuck_right = true;
00659           }
00660           rotating_left = true;
00661         }
00662         else if(best_traj->thetav_ > 0){
00663           if(rotating_left){
00664             stuck_left = true;
00665           }
00666           rotating_right = true;
00667         }
00668         else if(best_traj->yv_ > 0){
00669           if(strafe_right){
00670             stuck_right_strafe = true;
00671           }
00672           strafe_left = true;
00673         }
00674         else if(best_traj->yv_ < 0){
00675           if(strafe_left){
00676             stuck_left_strafe = true;
00677           }
00678           strafe_right = true;
00679         }
00680 
00681         //set the position we must move a certain distance away from
00682         prev_x_ = x;
00683         prev_y_ = y;
00684       }
00685 
00686       double dist = sqrt((x - prev_x_) * (x - prev_x_) + (y - prev_y_) * (y - prev_y_));
00687       if(dist > oscillation_reset_dist_){
00688         rotating_left = false;
00689         rotating_right = false;
00690         strafe_left = false;
00691         strafe_right = false;
00692         stuck_left = false;
00693         stuck_right = false;
00694         stuck_left_strafe = false;
00695         stuck_right_strafe = false;
00696       }
00697 
00698       dist = sqrt((x - escape_x_) * (x - escape_x_) + (y - escape_y_) * (y - escape_y_));
00699       if(dist > escape_reset_dist_ || fabs(angles::shortest_angular_distance(escape_theta_, theta)) > escape_reset_theta_){
00700         escaping_ = false;
00701       }
00702 
00703       return *best_traj;
00704     }
00705 
00706 
00707 
00708     //only explore y velocities with holonomic robots
00709     if(holonomic_robot_){
00710       //if we can't rotate in place or move forward... maybe we can move sideways and rotate
00711       vtheta_samp = min_vel_theta;
00712       vx_samp = 0.0;
00713 
00714       //loop through all y velocities
00715       for(unsigned int i = 0; i < y_vels_.size(); ++i){
00716         vtheta_samp = 0;
00717         vy_samp = y_vels_[i];
00718         //sample completely horizontal trajectories
00719         generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00720             acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00721 
00722         //if the new trajectory is better... let's take it
00723         if(comp_traj->cost_ >= 0 && (comp_traj->cost_ <= best_traj->cost_ || best_traj->cost_ < 0)){
00724           double x_r, y_r, th_r;
00725           comp_traj->getEndpoint(x_r, y_r, th_r);
00726           x_r += heading_lookahead_ * cos(th_r);
00727           y_r += heading_lookahead_ * sin(th_r);
00728           unsigned int cell_x, cell_y;
00729 
00730           //make sure that we'll be looking at a legal cell
00731           if(costmap_.worldToMap(x_r, y_r, cell_x, cell_y)){
00732             double ahead_gdist = map_(cell_x, cell_y).goal_dist;
00733             if(ahead_gdist < heading_dist){
00734               //if we haven't already tried strafing left since we've moved forward
00735               if(vy_samp > 0 && !stuck_left_strafe){
00736                 swap = best_traj;
00737                 best_traj = comp_traj;
00738                 comp_traj = swap;
00739                 heading_dist = ahead_gdist;
00740               }
00741               //if we haven't already tried rotating right since we've moved forward
00742               else if(vy_samp < 0 && !stuck_right_strafe){
00743                 swap = best_traj;
00744                 best_traj = comp_traj;
00745                 comp_traj = swap;
00746                 heading_dist = ahead_gdist;
00747               }
00748             }
00749           }
00750         }
00751       }
00752     }
00753 
00754     //do we have a legal trajectory
00755     if(best_traj->cost_ >= 0){
00756       if(!(best_traj->xv_ > 0)){
00757         if(best_traj->thetav_ < 0){
00758           if(rotating_right){
00759             stuck_right = true;
00760           }
00761           rotating_left = true;
00762         }
00763         else if(best_traj->thetav_ > 0){
00764           if(rotating_left){
00765             stuck_left = true;
00766           }
00767           rotating_right = true;
00768         }
00769         else if(best_traj->yv_ > 0){
00770           if(strafe_right){
00771             stuck_right_strafe = true;
00772           }
00773           strafe_left = true;
00774         }
00775         else if(best_traj->yv_ < 0){
00776           if(strafe_left){
00777             stuck_left_strafe = true;
00778           }
00779           strafe_right = true;
00780         }
00781 
00782         //set the position we must move a certain distance away from
00783         prev_x_ = x;
00784         prev_y_ = y;
00785 
00786       }
00787 
00788       double dist = sqrt((x - prev_x_) * (x - prev_x_) + (y - prev_y_) * (y - prev_y_));
00789       if(dist > oscillation_reset_dist_){
00790         rotating_left = false;
00791         rotating_right = false;
00792         strafe_left = false;
00793         strafe_right = false;
00794         stuck_left = false;
00795         stuck_right = false;
00796         stuck_left_strafe = false;
00797         stuck_right_strafe = false;
00798       }
00799 
00800       dist = sqrt((x - escape_x_) * (x - escape_x_) + (y - escape_y_) * (y - escape_y_));
00801       if(dist > escape_reset_dist_ || fabs(angles::shortest_angular_distance(escape_theta_, theta)) > escape_reset_theta_){
00802         escaping_ = false;
00803       }
00804 
00805       return *best_traj;
00806     }
00807 
00808     //and finally, if we can't do anything else, we want to generate trajectories that move backwards slowly
00809     vtheta_samp = 0.0;
00810     vx_samp = backup_vel_;
00811     vy_samp = 0.0;
00812     generateTrajectory(x, y, theta, vx, vy, vtheta, vx_samp, vy_samp, vtheta_samp, 
00813         acc_x, acc_y, acc_theta, impossible_cost, *comp_traj);
00814 
00815     //if the new trajectory is better... let's take it
00816     /*
00817        if(comp_traj->cost_ >= 0 && (comp_traj->cost_ < best_traj->cost_ || best_traj->cost_ < 0)){
00818        swap = best_traj;
00819        best_traj = comp_traj;
00820        comp_traj = swap;
00821        }
00822        */
00823 
00824     //we'll allow moving backwards slowly even when the static map shows it as blocked
00825     swap = best_traj;
00826     best_traj = comp_traj;
00827     comp_traj = swap;
00828     
00829     double dist = sqrt((x - prev_x_) * (x - prev_x_) + (y - prev_y_) * (y - prev_y_));
00830     if(dist > oscillation_reset_dist_){
00831       rotating_left = false;
00832       rotating_right = false;
00833       strafe_left = false;
00834       strafe_right = false;
00835       stuck_left = false;
00836       stuck_right = false;
00837       stuck_left_strafe = false;
00838       stuck_right_strafe = false;
00839     }
00840 
00841     //only enter escape mode when the planner has given a valid goal point
00842     if(!escaping_ && best_traj->cost_ > -2.0){
00843       escape_x_ = x;
00844       escape_y_ = y;
00845       escape_theta_ = theta;
00846       escaping_ = true;
00847     }
00848 
00849     dist = sqrt((x - escape_x_) * (x - escape_x_) + (y - escape_y_) * (y - escape_y_));
00850 
00851     if(dist > escape_reset_dist_ || fabs(angles::shortest_angular_distance(escape_theta_, theta)) > escape_reset_theta_){
00852       escaping_ = false;
00853     }
00854 
00855 
00856     //if the trajectory failed because the footprint hits something, we're still going to back up
00857     if(best_traj->cost_ == -1.0)
00858       best_traj->cost_ = 1.0;
00859 
00860     return *best_traj;
00861 
00862   }
00863 
00864   //given the current state of the robot, find a good trajectory
00865   Trajectory TrajectoryPlanner::findBestPath(tf::Stamped<tf::Pose> global_pose, tf::Stamped<tf::Pose> global_vel, 
00866       tf::Stamped<tf::Pose>& drive_velocities){
00867 
00868     double yaw = tf::getYaw(global_pose.getRotation());
00869     double vel_yaw = tf::getYaw(global_vel.getRotation());
00870 
00871     double x = global_pose.getOrigin().getX();
00872     double y = global_pose.getOrigin().getY();
00873     double theta = yaw;
00874 
00875     double vx = global_vel.getOrigin().getX();
00876     double vy = global_vel.getOrigin().getY();
00877     double vtheta = vel_yaw;
00878 
00879     //reset the map for new operations
00880     map_.resetPathDist();
00881 
00882     //temporarily remove obstacles that are within the footprint of the robot
00883     vector<iri_diff_local_planner::Position2DInt> footprint_list = getFootprintCells(x, y, theta, true);
00884 
00885     //mark cells within the initial footprint of the robot
00886     for(unsigned int i = 0; i < footprint_list.size(); ++i){
00887       map_(footprint_list[i].x, footprint_list[i].y).within_robot = true;
00888     }
00889 
00890     //make sure that we update our path based on the global plan and compute costs
00891     map_.setPathCells(costmap_, global_plan_);
00892     ROS_DEBUG("Path/Goal distance computed");
00893 
00894     //rollout trajectories and find the minimum cost one
00895     Trajectory best = createTrajectories(x, y, theta, 
00896         vx, vy, vtheta, 
00897         acc_lim_x_, acc_lim_y_, acc_lim_theta_);
00898     ROS_DEBUG("Trajectories created");
00899 
00900     /*
00901     //If we want to print a ppm file to draw goal dist
00902     char buf[4096];
00903     sprintf(buf, "iri_diff_local_planner.ppm");
00904     FILE *fp = fopen(buf, "w");
00905     if(fp){
00906       fprintf(fp, "P3\n");
00907       fprintf(fp, "%d %d\n", map_.size_x_, map_.size_y_);
00908       fprintf(fp, "255\n");
00909       for(int j = map_.size_y_ - 1; j >= 0; --j){
00910         for(unsigned int i = 0; i < map_.size_x_; ++i){
00911           int g_dist = 255 - int(map_(i, j).goal_dist);
00912           int p_dist = 255 - int(map_(i, j).path_dist);
00913           if(g_dist < 0)
00914             g_dist = 0;
00915           if(p_dist < 0)
00916             p_dist = 0;
00917           fprintf(fp, "%d 0 %d ", g_dist, 0);
00918         }
00919         fprintf(fp, "\n");
00920       }
00921       fclose(fp);
00922     }
00923     */
00924 
00925     if(best.cost_ < 0){
00926       drive_velocities.setIdentity();
00927     }
00928     else{
00929       tf::Vector3 start(best.xv_, best.yv_, 0);
00930       drive_velocities.setOrigin(start);
00931       tf::Matrix3x3 matrix;
00932       matrix.setRotation(tf::createQuaternionFromYaw(best.thetav_));
00933       drive_velocities.setBasis(matrix);
00934     }
00935 
00936     return best;
00937   }
00938 
00939   //we need to take the footprint of the robot into account when we calculate cost to obstacles
00940   double TrajectoryPlanner::footprintCost(double x_i, double y_i, double theta_i){
00941     //build the oriented footprint
00942     double cos_th = cos(theta_i);
00943     double sin_th = sin(theta_i);
00944     vector<geometry_msgs::Point> oriented_footprint;
00945     for(unsigned int i = 0; i < footprint_spec_.size(); ++i){
00946       geometry_msgs::Point new_pt;
00947       new_pt.x = x_i + (footprint_spec_[i].x * cos_th - footprint_spec_[i].y * sin_th);
00948       new_pt.y = y_i + (footprint_spec_[i].x * sin_th + footprint_spec_[i].y * cos_th);
00949       oriented_footprint.push_back(new_pt);
00950     }
00951 
00952     geometry_msgs::Point robot_position;
00953     robot_position.x = x_i;
00954     robot_position.y = y_i;
00955 
00956     //check if the footprint is legal
00957     double footprint_cost = world_model_.footprintCost(robot_position, oriented_footprint, costmap_.getInscribedRadius(), costmap_.getCircumscribedRadius());
00958 
00959     return footprint_cost;
00960   }
00961 
00962   void TrajectoryPlanner::getLineCells(int x0, int x1, int y0, int y1, vector<iri_diff_local_planner::Position2DInt>& pts){
00963     //Bresenham Ray-Tracing
00964     int deltax = abs(x1 - x0);        // The difference between the x's
00965     int deltay = abs(y1 - y0);        // The difference between the y's
00966     int x = x0;                       // Start x off at the first pixel
00967     int y = y0;                       // Start y off at the first pixel
00968 
00969     int xinc1, xinc2, yinc1, yinc2;
00970     int den, num, numadd, numpixels;
00971 
00972     iri_diff_local_planner::Position2DInt pt;
00973 
00974     if (x1 >= x0)                 // The x-values are increasing
00975     {
00976       xinc1 = 1;
00977       xinc2 = 1;
00978     }
00979     else                          // The x-values are decreasing
00980     {
00981       xinc1 = -1;
00982       xinc2 = -1;
00983     }
00984 
00985     if (y1 >= y0)                 // The y-values are increasing
00986     {
00987       yinc1 = 1;
00988       yinc2 = 1;
00989     }
00990     else                          // The y-values are decreasing
00991     {
00992       yinc1 = -1;
00993       yinc2 = -1;
00994     }
00995 
00996     if (deltax >= deltay)         // There is at least one x-value for every y-value
00997     {
00998       xinc1 = 0;                  // Don't change the x when numerator >= denominator
00999       yinc2 = 0;                  // Don't change the y for every iteration
01000       den = deltax;
01001       num = deltax / 2;
01002       numadd = deltay;
01003       numpixels = deltax;         // There are more x-values than y-values
01004     }
01005     else                          // There is at least one y-value for every x-value
01006     {
01007       xinc2 = 0;                  // Don't change the x for every iteration
01008       yinc1 = 0;                  // Don't change the y when numerator >= denominator
01009       den = deltay;
01010       num = deltay / 2;
01011       numadd = deltax;
01012       numpixels = deltay;         // There are more y-values than x-values
01013     }
01014 
01015     for (int curpixel = 0; curpixel <= numpixels; curpixel++)
01016     {
01017       pt.x = x;      //Draw the current pixel
01018       pt.y = y;
01019       pts.push_back(pt);
01020 
01021       num += numadd;              // Increase the numerator by the top of the fraction
01022       if (num >= den)             // Check if numerator >= denominator
01023       {
01024         num -= den;               // Calculate the new numerator value
01025         x += xinc1;               // Change the x as appropriate
01026         y += yinc1;               // Change the y as appropriate
01027       }
01028       x += xinc2;                 // Change the x as appropriate
01029       y += yinc2;                 // Change the y as appropriate
01030     }
01031   }
01032 
01033   //get the cellsof a footprint at a given position
01034   vector<iri_diff_local_planner::Position2DInt> TrajectoryPlanner::getFootprintCells(double x_i, double y_i, double theta_i, bool fill){
01035     vector<iri_diff_local_planner::Position2DInt> footprint_cells;
01036 
01037     //if we have no footprint... do nothing
01038     if(footprint_spec_.size() <= 1){
01039       unsigned int mx, my;
01040       if(costmap_.worldToMap(x_i, y_i, mx, my)){
01041         Position2DInt center;
01042         center.x = mx;
01043         center.y = my;
01044         footprint_cells.push_back(center);
01045       }
01046       return footprint_cells;
01047     }
01048 
01049     //pre-compute cos and sin values
01050     double cos_th = cos(theta_i);
01051     double sin_th = sin(theta_i);
01052     double new_x, new_y;
01053     unsigned int x0, y0, x1, y1;
01054     unsigned int last_index = footprint_spec_.size() - 1;
01055 
01056     for(unsigned int i = 0; i < last_index; ++i){
01057       //find the cell coordinates of the first segment point
01058       new_x = x_i + (footprint_spec_[i].x * cos_th - footprint_spec_[i].y * sin_th);
01059       new_y = y_i + (footprint_spec_[i].x * sin_th + footprint_spec_[i].y * cos_th);
01060       if(!costmap_.worldToMap(new_x, new_y, x0, y0))
01061         return footprint_cells;
01062 
01063       //find the cell coordinates of the second segment point
01064       new_x = x_i + (footprint_spec_[i + 1].x * cos_th - footprint_spec_[i + 1].y * sin_th);
01065       new_y = y_i + (footprint_spec_[i + 1].x * sin_th + footprint_spec_[i + 1].y * cos_th);
01066       if(!costmap_.worldToMap(new_x, new_y, x1, y1))
01067         return footprint_cells;
01068 
01069       getLineCells(x0, x1, y0, y1, footprint_cells);
01070     }
01071 
01072     //we need to close the loop, so we also have to raytrace from the last pt to first pt
01073     new_x = x_i + (footprint_spec_[last_index].x * cos_th - footprint_spec_[last_index].y * sin_th);
01074     new_y = y_i + (footprint_spec_[last_index].x * sin_th + footprint_spec_[last_index].y * cos_th);
01075     if(!costmap_.worldToMap(new_x, new_y, x0, y0))
01076       return footprint_cells;
01077 
01078     new_x = x_i + (footprint_spec_[0].x * cos_th - footprint_spec_[0].y * sin_th);
01079     new_y = y_i + (footprint_spec_[0].x * sin_th + footprint_spec_[0].y * cos_th);
01080     if(!costmap_.worldToMap(new_x, new_y, x1, y1))
01081       return footprint_cells;
01082 
01083     getLineCells(x0, x1, y0, y1, footprint_cells);
01084 
01085     if(fill)
01086       getFillCells(footprint_cells);
01087 
01088     return footprint_cells;
01089   }
01090 
01091   void TrajectoryPlanner::getFillCells(vector<iri_diff_local_planner::Position2DInt>& footprint){
01092     //quick bubble sort to sort pts by x
01093     iri_diff_local_planner::Position2DInt swap, pt;
01094     unsigned int i = 0;
01095     while(i < footprint.size() - 1){
01096       if(footprint[i].x > footprint[i + 1].x){
01097         swap = footprint[i];
01098         footprint[i] = footprint[i + 1];
01099         footprint[i + 1] = swap;
01100         if(i > 0)
01101           --i;
01102       }
01103       else
01104         ++i;
01105     }
01106 
01107     i = 0;
01108     iri_diff_local_planner::Position2DInt min_pt;
01109     iri_diff_local_planner::Position2DInt max_pt;
01110     unsigned int min_x = footprint[0].x;
01111     unsigned int max_x = footprint[footprint.size() -1].x;
01112     //walk through each column and mark cells inside the footprint
01113     for(unsigned int x = min_x; x <= max_x; ++x){
01114       if(i >= footprint.size() - 1)
01115         break;
01116 
01117       if(footprint[i].y < footprint[i + 1].y){
01118         min_pt = footprint[i];
01119         max_pt = footprint[i + 1];
01120       }
01121       else{
01122         min_pt = footprint[i + 1];
01123         max_pt = footprint[i];
01124       }
01125 
01126       i += 2;
01127       while(i < footprint.size() && footprint[i].x == x){
01128         if(footprint[i].y < min_pt.y)
01129           min_pt = footprint[i];
01130         else if(footprint[i].y > max_pt.y)
01131           max_pt = footprint[i];
01132         ++i;
01133       }
01134 
01135       //loop though cells in the column
01136       for(unsigned int y = min_pt.y; y < max_pt.y; ++y){
01137         pt.x = x;
01138         pt.y = y;
01139         footprint.push_back(pt);
01140       }
01141     }
01142   }
01143 
01144   void TrajectoryPlanner::getLocalGoal(double& x, double& y){
01145     x = map_.goal_x_;
01146     y = map_.goal_y_;
01147   }
01148 
01149 };
01150 
01151 


iri_diff_local_planner
Author(s): Iri Lab
autogenerated on Fri Dec 6 2013 20:32:36