planner_core.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  *
3  * Software License Agreement (BSD License)
4  *
5  * Copyright (c) 2008, 2013, Willow Garage, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * * Neither the name of Willow Garage, Inc. nor the names of its
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * Author: Eitan Marder-Eppstein
36  * David V. Lu!!
37  *********************************************************************/
40 #include <tf/transform_listener.h>
41 #include <costmap_2d/cost_values.h>
42 #include <costmap_2d/costmap_2d.h>
43 
45 #include <global_planner/astar.h>
49 
50 //register this planner as a BaseGlobalPlanner plugin
52 
53 namespace global_planner {
54 
55 void GlobalPlanner::outlineMap(unsigned char* costarr, int nx, int ny, unsigned char value) {
56  unsigned char* pc = costarr;
57  for (int i = 0; i < nx; i++)
58  *pc++ = value;
59  pc = costarr + (ny - 1) * nx;
60  for (int i = 0; i < nx; i++)
61  *pc++ = value;
62  pc = costarr;
63  for (int i = 0; i < ny; i++, pc += nx)
64  *pc = value;
65  pc = costarr + nx - 1;
66  for (int i = 0; i < ny; i++, pc += nx)
67  *pc = value;
68 }
69 
70 GlobalPlanner::GlobalPlanner() :
71  costmap_(NULL), initialized_(false), allow_unknown_(true),
72  p_calc_(NULL), planner_(NULL), path_maker_(NULL), orientation_filter_(NULL),
73  potential_array_(NULL) {
74 }
75 
76 GlobalPlanner::GlobalPlanner(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id) :
77  costmap_(NULL), initialized_(false), allow_unknown_(true),
78  p_calc_(NULL), planner_(NULL), path_maker_(NULL), orientation_filter_(NULL),
79  potential_array_(NULL) {
80  //initialize the planner
81  initialize(name, costmap, frame_id);
82 }
83 
85  if (p_calc_)
86  delete p_calc_;
87  if (planner_)
88  delete planner_;
89  if (path_maker_)
90  delete path_maker_;
91  if (dsrv_)
92  delete dsrv_;
93 }
94 
95 void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2DROS* costmap_ros) {
96  initialize(name, costmap_ros->getCostmap(), costmap_ros->getGlobalFrameID());
97 }
98 
99 void GlobalPlanner::initialize(std::string name, costmap_2d::Costmap2D* costmap, std::string frame_id) {
100  if (!initialized_) {
101  ros::NodeHandle private_nh("~/" + name);
102  costmap_ = costmap;
103  frame_id_ = frame_id;
104 
105  unsigned int cx = costmap->getSizeInCellsX(), cy = costmap->getSizeInCellsY();
106 
107  private_nh.param("old_navfn_behavior", old_navfn_behavior_, false);
109  convert_offset_ = 0.5;
110  else
111  convert_offset_ = 0.0;
112 
113  bool use_quadratic;
114  private_nh.param("use_quadratic", use_quadratic, true);
115  if (use_quadratic)
116  p_calc_ = new QuadraticCalculator(cx, cy);
117  else
118  p_calc_ = new PotentialCalculator(cx, cy);
119 
120  bool use_dijkstra;
121  private_nh.param("use_dijkstra", use_dijkstra, true);
122  if (use_dijkstra)
123  {
124  DijkstraExpansion* de = new DijkstraExpansion(p_calc_, cx, cy);
126  de->setPreciseStart(true);
127  planner_ = de;
128  }
129  else
130  planner_ = new AStarExpansion(p_calc_, cx, cy);
131 
132  bool use_grid_path;
133  private_nh.param("use_grid_path", use_grid_path, false);
134  if (use_grid_path)
136  else
138 
140 
141  plan_pub_ = private_nh.advertise<nav_msgs::Path>("plan", 1);
142  potential_pub_ = private_nh.advertise<nav_msgs::OccupancyGrid>("potential", 1);
143 
144  private_nh.param("allow_unknown", allow_unknown_, true);
146  private_nh.param("planner_window_x", planner_window_x_, 0.0);
147  private_nh.param("planner_window_y", planner_window_y_, 0.0);
148  private_nh.param("default_tolerance", default_tolerance_, 0.0);
149  private_nh.param("publish_scale", publish_scale_, 100);
150 
151  //get the tf prefix
152  ros::NodeHandle prefix_nh;
153  tf_prefix_ = tf::getPrefixParam(prefix_nh);
154 
155  make_plan_srv_ = private_nh.advertiseService("make_plan", &GlobalPlanner::makePlanService, this);
156 
157  dsrv_ = new dynamic_reconfigure::Server<global_planner::GlobalPlannerConfig>(ros::NodeHandle("~/" + name));
158  dynamic_reconfigure::Server<global_planner::GlobalPlannerConfig>::CallbackType cb = boost::bind(
159  &GlobalPlanner::reconfigureCB, this, _1, _2);
160  dsrv_->setCallback(cb);
161 
162  initialized_ = true;
163  } else
164  ROS_WARN("This planner has already been initialized, you can't call it twice, doing nothing");
165 
166 }
167 
168 void GlobalPlanner::reconfigureCB(global_planner::GlobalPlannerConfig& config, uint32_t level) {
169  planner_->setLethalCost(config.lethal_cost);
170  path_maker_->setLethalCost(config.lethal_cost);
171  planner_->setNeutralCost(config.neutral_cost);
172  planner_->setFactor(config.cost_factor);
173  publish_potential_ = config.publish_potential;
174  orientation_filter_->setMode(config.orientation_mode);
175  orientation_filter_->setWindowSize(config.orientation_window_size);
176 }
177 
178 void GlobalPlanner::clearRobotCell(const tf::Stamped<tf::Pose>& global_pose, unsigned int mx, unsigned int my) {
179  if (!initialized_) {
180  ROS_ERROR(
181  "This planner has not been initialized yet, but it is being used, please call initialize() before use");
182  return;
183  }
184 
185  //set the associated costs in the cost map to be free
187 }
188 
189 bool GlobalPlanner::makePlanService(nav_msgs::GetPlan::Request& req, nav_msgs::GetPlan::Response& resp) {
190  makePlan(req.start, req.goal, resp.plan.poses);
191 
192  resp.plan.header.stamp = ros::Time::now();
193  resp.plan.header.frame_id = frame_id_;
194 
195  return true;
196 }
197 
198 void GlobalPlanner::mapToWorld(double mx, double my, double& wx, double& wy) {
201 }
202 
203 bool GlobalPlanner::worldToMap(double wx, double wy, double& mx, double& my) {
204  double origin_x = costmap_->getOriginX(), origin_y = costmap_->getOriginY();
205  double resolution = costmap_->getResolution();
206 
207  if (wx < origin_x || wy < origin_y)
208  return false;
209 
210  mx = (wx - origin_x) / resolution - convert_offset_;
211  my = (wy - origin_y) / resolution - convert_offset_;
212 
213  if (mx < costmap_->getSizeInCellsX() && my < costmap_->getSizeInCellsY())
214  return true;
215 
216  return false;
217 }
218 
219 bool GlobalPlanner::makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
220  std::vector<geometry_msgs::PoseStamped>& plan) {
221  return makePlan(start, goal, default_tolerance_, plan);
222 }
223 
224 bool GlobalPlanner::makePlan(const geometry_msgs::PoseStamped& start, const geometry_msgs::PoseStamped& goal,
225  double tolerance, std::vector<geometry_msgs::PoseStamped>& plan) {
226  boost::mutex::scoped_lock lock(mutex_);
227  if (!initialized_) {
228  ROS_ERROR(
229  "This planner has not been initialized yet, but it is being used, please call initialize() before use");
230  return false;
231  }
232 
233  //clear the plan, just in case
234  plan.clear();
235 
236  ros::NodeHandle n;
237  std::string global_frame = frame_id_;
238 
239  //until tf can handle transforming things that are way in the past... we'll require the goal to be in our global frame
240  if (tf::resolve(tf_prefix_, goal.header.frame_id) != tf::resolve(tf_prefix_, global_frame)) {
241  ROS_ERROR(
242  "The goal pose passed to this planner must be in the %s frame. It is instead in the %s frame.", tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, goal.header.frame_id).c_str());
243  return false;
244  }
245 
246  if (tf::resolve(tf_prefix_, start.header.frame_id) != tf::resolve(tf_prefix_, global_frame)) {
247  ROS_ERROR(
248  "The start pose passed to this planner must be in the %s frame. It is instead in the %s frame.", tf::resolve(tf_prefix_, global_frame).c_str(), tf::resolve(tf_prefix_, start.header.frame_id).c_str());
249  return false;
250  }
251 
252  double wx = start.pose.position.x;
253  double wy = start.pose.position.y;
254 
255  unsigned int start_x_i, start_y_i, goal_x_i, goal_y_i;
256  double start_x, start_y, goal_x, goal_y;
257 
258  if (!costmap_->worldToMap(wx, wy, start_x_i, start_y_i)) {
259  ROS_WARN(
260  "The robot's start position is off the global costmap. Planning will always fail, are you sure the robot has been properly localized?");
261  return false;
262  }
264  start_x = start_x_i;
265  start_y = start_y_i;
266  }else{
267  worldToMap(wx, wy, start_x, start_y);
268  }
269 
270  wx = goal.pose.position.x;
271  wy = goal.pose.position.y;
272 
273  if (!costmap_->worldToMap(wx, wy, goal_x_i, goal_y_i)) {
274  ROS_WARN_THROTTLE(1.0,
275  "The goal sent to the global planner is off the global costmap. Planning will always fail to this goal.");
276  return false;
277  }
279  goal_x = goal_x_i;
280  goal_y = goal_y_i;
281  }else{
282  worldToMap(wx, wy, goal_x, goal_y);
283  }
284 
285  //clear the starting cell within the costmap because we know it can't be an obstacle
286  tf::Stamped<tf::Pose> start_pose;
287  tf::poseStampedMsgToTF(start, start_pose);
288  clearRobotCell(start_pose, start_x_i, start_y_i);
289 
290  int nx = costmap_->getSizeInCellsX(), ny = costmap_->getSizeInCellsY();
291 
292  //make sure to resize the underlying array that Navfn uses
293  p_calc_->setSize(nx, ny);
294  planner_->setSize(nx, ny);
295  path_maker_->setSize(nx, ny);
296  potential_array_ = new float[nx * ny];
297 
299 
300  bool found_legal = planner_->calculatePotentials(costmap_->getCharMap(), start_x, start_y, goal_x, goal_y,
301  nx * ny * 2, potential_array_);
302 
304  planner_->clearEndpoint(costmap_->getCharMap(), potential_array_, goal_x_i, goal_y_i, 2);
307 
308  if (found_legal) {
309  //extract the plan
310  if (getPlanFromPotential(start_x, start_y, goal_x, goal_y, goal, plan)) {
311  //make sure the goal we push on has the same timestamp as the rest of the plan
312  geometry_msgs::PoseStamped goal_copy = goal;
313  goal_copy.header.stamp = ros::Time::now();
314  plan.push_back(goal_copy);
315  } else {
316  ROS_ERROR("Failed to get a plan from potential when a legal potential was found. This shouldn't happen.");
317  }
318  }else{
319  ROS_ERROR("Failed to get a plan.");
320  }
321 
322  // add orientations if needed
323  orientation_filter_->processPath(start, plan);
324 
325  //publish the plan for visualization purposes
326  publishPlan(plan);
327  delete[] potential_array_;
328  return !plan.empty();
329 }
330 
331 void GlobalPlanner::publishPlan(const std::vector<geometry_msgs::PoseStamped>& path) {
332  if (!initialized_) {
333  ROS_ERROR(
334  "This planner has not been initialized yet, but it is being used, please call initialize() before use");
335  return;
336  }
337 
338  //create a message for the plan
339  nav_msgs::Path gui_path;
340  gui_path.poses.resize(path.size());
341 
342  gui_path.header.frame_id = frame_id_;
343  gui_path.header.stamp = ros::Time::now();
344 
345  // Extract the plan in world co-ordinates, we assume the path is all in the same frame
346  for (unsigned int i = 0; i < path.size(); i++) {
347  gui_path.poses[i] = path[i];
348  }
349 
350  plan_pub_.publish(gui_path);
351 }
352 
353 bool GlobalPlanner::getPlanFromPotential(double start_x, double start_y, double goal_x, double goal_y,
354  const geometry_msgs::PoseStamped& goal,
355  std::vector<geometry_msgs::PoseStamped>& plan) {
356  if (!initialized_) {
357  ROS_ERROR(
358  "This planner has not been initialized yet, but it is being used, please call initialize() before use");
359  return false;
360  }
361 
362  std::string global_frame = frame_id_;
363 
364  //clear the plan, just in case
365  plan.clear();
366 
367  std::vector<std::pair<float, float> > path;
368 
369  if (!path_maker_->getPath(potential_array_, start_x, start_y, goal_x, goal_y, path)) {
370  ROS_ERROR("NO PATH!");
371  return false;
372  }
373 
374  ros::Time plan_time = ros::Time::now();
375  for (int i = path.size() -1; i>=0; i--) {
376  std::pair<float, float> point = path[i];
377  //convert the plan to world coordinates
378  double world_x, world_y;
379  mapToWorld(point.first, point.second, world_x, world_y);
380 
381  geometry_msgs::PoseStamped pose;
382  pose.header.stamp = plan_time;
383  pose.header.frame_id = global_frame;
384  pose.pose.position.x = world_x;
385  pose.pose.position.y = world_y;
386  pose.pose.position.z = 0.0;
387  pose.pose.orientation.x = 0.0;
388  pose.pose.orientation.y = 0.0;
389  pose.pose.orientation.z = 0.0;
390  pose.pose.orientation.w = 1.0;
391  plan.push_back(pose);
392  }
394  plan.push_back(goal);
395  }
396  return !plan.empty();
397 }
398 
399 void GlobalPlanner::publishPotential(float* potential)
400 {
401  int nx = costmap_->getSizeInCellsX(), ny = costmap_->getSizeInCellsY();
402  double resolution = costmap_->getResolution();
403  nav_msgs::OccupancyGrid grid;
404  // Publish Whole Grid
405  grid.header.frame_id = frame_id_;
406  grid.header.stamp = ros::Time::now();
407  grid.info.resolution = resolution;
408 
409  grid.info.width = nx;
410  grid.info.height = ny;
411 
412  double wx, wy;
413  costmap_->mapToWorld(0, 0, wx, wy);
414  grid.info.origin.position.x = wx - resolution / 2;
415  grid.info.origin.position.y = wy - resolution / 2;
416  grid.info.origin.position.z = 0.0;
417  grid.info.origin.orientation.w = 1.0;
418 
419  grid.data.resize(nx * ny);
420 
421  float max = 0.0;
422  for (unsigned int i = 0; i < grid.data.size(); i++) {
423  float potential = potential_array_[i];
424  if (potential < POT_HIGH) {
425  if (potential > max) {
426  max = potential;
427  }
428  }
429  }
430 
431  for (unsigned int i = 0; i < grid.data.size(); i++) {
432  if (potential_array_[i] >= POT_HIGH) {
433  grid.data[i] = -1;
434  } else
435  grid.data[i] = potential_array_[i] * publish_scale_ / max;
436  }
437  potential_pub_.publish(grid);
438 }
439 
440 } //end namespace global_planner
void setCost(unsigned int mx, unsigned int my, unsigned char cost)
void clearRobotCell(const tf::Stamped< tf::Pose > &global_pose, unsigned int mx, unsigned int my)
void publishPotential(float *potential)
~GlobalPlanner()
Default deconstructor for the PlannerCore object.
bool makePlan(const geometry_msgs::PoseStamped &start, const geometry_msgs::PoseStamped &goal, std::vector< geometry_msgs::PoseStamped > &plan)
Given a goal pose in the world, compute a plan.
void mapToWorld(double mx, double my, double &wx, double &wy)
#define ROS_WARN_THROTTLE(rate,...)
void publish(const boost::shared_ptr< M > &message) const
std::string getPrefixParam(ros::NodeHandle &nh)
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
virtual void processPath(const geometry_msgs::PoseStamped &start, std::vector< geometry_msgs::PoseStamped > &path)
void setLethalCost(unsigned char lethal_cost)
Definition: expander.h:64
void outlineMap(unsigned char *costarr, int nx, int ny, unsigned char value)
virtual void setSize(int nx, int ny)
Sets or resets the size of the map.
Definition: expander.h:59
void initialize(std::string name, costmap_2d::Costmap2DROS *costmap_ros)
Initialization function for the PlannerCore object.
std::string getGlobalFrameID()
PLUGINLIB_EXPORT_CLASS(BAGReader, nodelet::Nodelet)
double getOriginX() const
costmap_2d::Costmap2D * costmap_
Store a copy of the current costmap in costmap. Called by makePlan.
Definition: planner_core.h:172
virtual bool getPath(float *potential, double start_x, double start_y, double end_x, double end_y, std::vector< std::pair< float, float > > &path)=0
ServiceServer advertiseService(const std::string &service, bool(T::*srv_func)(MReq &, MRes &), T *obj)
void reconfigureCB(global_planner::GlobalPlannerConfig &config, uint32_t level)
static const unsigned char FREE_SPACE
#define ROS_WARN(...)
PotentialCalculator * p_calc_
Definition: planner_core.h:188
unsigned char * getCharMap() const
#define POT_HIGH
Definition: planner_core.h:40
void clearEndpoint(unsigned char *costs, float *potential, int gx, int gy, int s)
Definition: expander.h:77
std::string resolve(const std::string &prefix, const std::string &frame_name)
void publishPlan(const std::vector< geometry_msgs::PoseStamped > &path)
Publish a path for visualization purposes.
bool getPlanFromPotential(double start_x, double start_y, double end_x, double end_y, const geometry_msgs::PoseStamped &goal, std::vector< geometry_msgs::PoseStamped > &plan)
Compute a plan to a goal after the potential for a start point has already been computed (Note: You s...
void setPreciseStart(bool precise)
Definition: dijkstra.h:75
void setLethalCost(unsigned char lethal_cost)
Definition: traceback.h:57
bool param(const std::string &param_name, T &param_val, const T &default_val) const
bool worldToMap(double wx, double wy, double &mx, double &my)
double getOriginY() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
unsigned int getSizeInCellsY() const
GlobalPlanner()
Default constructor for the PlannerCore object.
virtual void setSize(int xs, int ys)
Definition: traceback.h:50
OrientationFilter * orientation_filter_
Definition: planner_core.h:191
void setHasUnknown(bool unknown)
Definition: expander.h:73
unsigned int getSizeInCellsX() const
bool makePlanService(nav_msgs::GetPlan::Request &req, nav_msgs::GetPlan::Response &resp)
ros::ServiceServer make_plan_srv_
Definition: planner_core.h:186
static const unsigned char LETHAL_OBSTACLE
static Time now()
virtual bool calculatePotentials(unsigned char *costs, double start_x, double start_y, double end_x, double end_y, int cycles, float *potential)=0
void setMode(OrientationMode new_mode)
static void poseStampedMsgToTF(const geometry_msgs::PoseStamped &msg, Stamped< Pose > &bt)
void setNeutralCost(unsigned char neutral_cost)
Definition: expander.h:67
double getResolution() const
Costmap2D * getCostmap()
double max(double a, double b)
#define ROS_ERROR(...)
void setWindowSize(size_t window_size)
void setFactor(float factor)
Definition: expander.h:70
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
dynamic_reconfigure::Server< global_planner::GlobalPlannerConfig > * dsrv_
Definition: planner_core.h:205
virtual void setSize(int nx, int ny)
Sets or resets the size of the map.


global_planner
Author(s): David Lu!!
autogenerated on Thu Jan 21 2021 04:06:07