cb_base_navigation
a_star_planner_gpp.cpp
Go to the documentation of this file.
1 #include "a_star_planner_gpp.h"
2 
3 #include <pluginlib/class_list_macros.h>
5 
6 #include <ed_msgs/SimpleQuery.h>
7 
8 #include <ros/console.h>
9 
10 #include <tf2/utils.h>
11 
12 namespace cb_global_planner
13 {
14 
15 //register this planner as a BaseGlobalPlanner plugin
17 
18 // ----------------------------------------------------------------------------------------------------
19 
20 bool AStarPlannerGPP::queryEntityPose(const std::string& id, tf2::Transform& pose)
21 {
22  if (id == "map" || id == "/map")
23  {
24  pose = tf2::Transform::getIdentity();
25  return true;
26  }
27 
28  ed_msgs::SimpleQuery ed_query;
29  ed_query.request.id = id;
30 
31  ROS_DEBUG_STREAM("[A* Planner] Querying ed for entity '" << id << "'");
32 
33  if (!ed_client_.call(ed_query))
34  {
35  ROS_ERROR_STREAM("[A* Planner] Failed to get pose for entity '" << id << "': ED could not be queried");
36  return false;
37  }
38 
39  if (ed_query.response.entities.empty())
40  {
41  ROS_ERROR_STREAM("[A* Planner] Failed to get pose for entity '" << id << "': ED returns 'no such entity'");
42  return false;
43  }
44 
45  const ed_msgs::EntityInfo& e_info = ed_query.response.entities.front();
46 
47  tf2::fromMsg(e_info.pose, pose);
48  return true;
49 }
50 
51 // ----------------------------------------------------------------------------------------------------
52 
53 AStarPlannerGPP::AStarPlannerGPP() : global_costmap_ros_(nullptr), planner_(nullptr), tf_(nullptr)
54 {
55 }
56 
57 void AStarPlannerGPP::initialize(std::string /*name*/, tf2_ros::Buffer* tf, costmap_2d::Costmap2DROS* global_costmap_ros)
58 {
59  // Store a local pointer to the global costmap and the tf_listener
60  global_costmap_ros_ = global_costmap_ros;
61  tf_ = tf;
62 
63  // Create AstarPlanner Object ( initialize with current costmap width and height )
65  initialized_ = true;
66 
67  ros::NodeHandle nh;
68  ed_client_ = nh.serviceClient<ed_msgs::SimpleQuery>("ed/simple_query");
69 
70  ROS_INFO("A* Global planner initialized.");
71 }
72 
74 {
75 }
76 
77 bool AStarPlannerGPP::makePlan(const geometry_msgs::PoseStamped& start, const cb_base_navigation_msgs::PositionConstraint& pc, std::vector<geometry_msgs::PoseStamped>& plan,
78  std::vector<tf2::Vector3>& goal_positions)
79 {
80  if (!initialized_) { ROS_WARN("The global planner is not initialized! It's not possible to create a global plan."); return false; }
81 
82  // Clear the plan and goal positions
83  plan.clear();
84  goal_positions.clear();
85 
86  // If nothing specified, do nothing :)
87  if (pc.frame == "" && pc.constraint == "")
88  return false;
89 
90  unsigned int mx_start, my_start;
91  if(!global_costmap_ros_->getCostmap()->worldToMap(start.pose.position.x, start.pose.position.y, mx_start, my_start)) {
92  ROS_WARN("The robot's start position is off the global costmap. Planning will always fail, are you sure the robot has been properly localized?");
93  ROS_ERROR_STREAM("Received position constraint: " << pc);
94  return false;
95  }
96 
97  // Check whether the constraint has been changed
98  if (constraintChanged(pc))
99  {
101  pc_ = pc;
102  else
103  {
104  ROS_WARN("Failed to update constraint positions in constraint frame.");
105  ROS_ERROR_STREAM("Received position constraint: " << pc);
106  return false;
107  }
108  }
109 
110  // Calculate the area in the map frame which meets the constraints
111  std::vector<unsigned int> mx_goal, my_goal;
112  if (!calculateMapConstraintArea(mx_goal, my_goal, goal_positions))
113  {
114  ROS_ERROR_STREAM("Received position constraint: " << pc);
115  return false;
116  }
117 
118  if(mx_goal.size() == 0) {
119  ROS_ERROR("There is no goal which meets the given constraints. Planning will always fail to this goal constraint.");
120  ROS_ERROR_STREAM("Received position constraint: " << pc);
121  return false;
122  }
123 
124  // Divide goal area in two: with costs above and below a certain threshold
125  // This prevents the planner for planning unnecessarily close to obstacles
126  std::vector<unsigned int> mx_free, my_free, mx_low, my_low, mx_high, my_high;
127  for (unsigned int i = 0; i < mx_goal.size(); i++) {
128 
129  // Check cost
130  double cost = global_costmap_ros_->getCostmap()->getCost(mx_goal[i], my_goal[i]);
131  // ToDo: divided by four is magic number
132  // ToDo: more levels?
133  if (cost == costmap_2d::FREE_SPACE) {
134  mx_free.push_back(mx_goal[i]);
135  my_free.push_back(my_goal[i]);
136  } else if (cost < costmap_2d::INSCRIBED_INFLATED_OBSTACLE/4) {
137  mx_low.push_back(mx_goal[i]);
138  my_low.push_back(my_goal[i]);
139  } else {
140  mx_high.push_back(mx_goal[i]);
141  my_high.push_back(my_goal[i]);
142  }
143  }
144 
145  // Initialize plan
146  std::vector<int> plan_xs, plan_ys;
147 
148  // Resize to current costmap dimensions and set costmap and do some path finding :)
151 
152  //planner_->plan(mx_goal, my_goal, mx_start, my_start, plan_xs, plan_ys);
153  // Try to find a plan with the endgoal in free space
154  planner_->plan(mx_free, my_free, mx_start, my_start, plan_xs, plan_ys);
155 
156  // If no plan, retry with low costs
157  if (plan_xs.empty()) {
158  planner_->plan(mx_low, my_low, mx_start, my_start, plan_xs, plan_ys);
159  }
160 
161  // If still no plan, retry with the remaining goal poses
162  if (plan_xs.empty()) {
163  planner_->plan(mx_high, my_high, mx_start, my_start, plan_xs, plan_ys);
164  }
165 
166 // if (plan_xs.empty()) {
167 
168 // // Try best heuristics path from the other way around
169 // unsigned int mx_start_new = mx_goal[mx_goal.size()/2]; // middlepoint of area
170 // unsigned int my_start_new = my_goal[my_goal.size()/2]; // middlepoint of area
171 
172 // mx_goal.clear(); mx_goal.push_back(mx_start);
173 // my_goal.clear(); my_goal.push_back(my_start);
174 
175 // planner_->plan(mx_goal, my_goal, mx_start_new, my_start_new, plan_xs, plan_ys,true);
176 
177 // // Reverse the plan
178 // std::reverse(plan_xs.begin(),plan_xs.end());
179 // std::reverse(plan_ys.begin(),plan_ys.end());
180 // }
181 
182  // Convert plan to world coordinates
183  planToWorld(plan_xs,plan_ys,plan);
184 
185  // If no plan was found, return false
186  if (plan.empty()) {
187  ROS_ERROR("No connectivity to specified constraint found, sorry :(");
188  } else {
189  ROS_INFO("A* planner succesfully generated plan :)");
190  }
191  return true;
192 }
193 
194 bool AStarPlannerGPP::updateConstraintPositionsInConstraintFrame(const cb_base_navigation_msgs::PositionConstraint& pc)
195 {
196  ROS_INFO("Position constraint has been changed, updating positions in constraint frame.");
197 
198  // Clear the constraint positions in constraint world
200 
201  // Request the constraint frame transform from map
202  tf2::Transform world_to_constraint_tf;
203  if (!queryEntityPose(pc.frame, world_to_constraint_tf))
204  return false;
205 
206  tf2::Transform constraint_to_world_tf = world_to_constraint_tf.inverse();
207 
208 // try {
209 // tf_->lookupTransform(pc.frame, global_costmap_ros_->getGlobalFrameID(), ros::Time(0), constraint_to_world_tf);
210 // } catch(tf::TransformException& ex) {
211 // ROS_ERROR_STREAM( "Transform error calculating constraint positions in global planner: " << ex.what());
212 // return false;
213 // }
214 
216 
217  if (!ce.init(pc.constraint)) {
218  ROS_ERROR("Could not setup goal constraints...");
219  return false;
220  }
221 
222  // Iterate over all costmap cells and evaluate the constraint in the constraint frame
223  for (unsigned int i = 0; i < global_costmap_ros_->getCostmap()->getSizeInCellsX(); ++i) {
224  for (unsigned int j = 0; j < global_costmap_ros_->getCostmap()->getSizeInCellsY(); ++j) {
225 
226  double wx,wy;
228  tf2::Vector3 pc(wx,wy,0);
229  pc = constraint_to_world_tf*pc;
230 
231  if(ce.evaluate(pc.x(),pc.y())) {
232  ROS_DEBUG_STREAM("Pushing back in constraint frame point: (" << pc.x() << ", " << pc.y() << ")");
234  }
235  }
236  }
237  return true;
238 }
239 
241 {
242  ROS_DEBUG("Calculating map constraint area");
243 
244  // Request the constraint frame transform from map
245  tf2::Stamped<tf2::Transform> world_to_constraint_tf;
246  if (!queryEntityPose(pc_.frame, world_to_constraint_tf))
247  return false;
248 
249 // try {
250 // tf_->lookupTransform(global_costmap_ros_->getGlobalFrameID(), pc_.frame, ros::Time(0), world_to_constraint_tf);
251 // } catch(tf::TransformException& ex) {
252 // ROS_ERROR_STREAM( "Transform error calculating constraint positions in global planner: " << ex.what());
253 // return false;
254 // }
255 
256  // Loop over the positions in the constraint frame and convert these to map points
258  {
259  tf2::Vector3 pw = world_to_constraint_tf * *it;
260  unsigned int x,y;
261  if (global_costmap_ros_->getCostmap()->worldToMap(pw.x(),pw.y(),x,y)) { // This should guarantee that we do not go off map, however the a* crashes sometimes
262 
263  // Check whether this point is blocked by an obstacle
264  unsigned char goal_cell_cost = global_costmap_ros_->getCostmap()->getCost(x, y);
265  if (goal_cell_cost == costmap_2d::INSCRIBED_INFLATED_OBSTACLE || goal_cell_cost == costmap_2d::LETHAL_OBSTACLE) {
266  continue;
267  }
268 
269  goal_positions.push_back(pw);
270  mx.push_back(x); my.push_back(y);
271  ROS_DEBUG_STREAM("Pushing back in map point: (" << x << ", " << y << ")");
272  }
273  }
274 
275  return true;
276 }
277 
279 {
280  ros::Time plan_time = ros::Time::now();
282 
283  plan.resize(plan_xs.size());
284  for(unsigned int i = 0; i < plan_xs.size(); ++i) {
285  double world_x, world_y;
286  global_costmap_ros_->getCostmap()->mapToWorld(plan_xs[i], plan_ys[i], world_x, world_y);
287 
288  plan[i].header.stamp = plan_time;
289  plan[i].header.frame_id = global_frame;
290  plan[i].pose.position.x = world_x;
291  plan[i].pose.position.y = world_y;
292  plan[i].pose.position.z = 0;
293 
294  unsigned int size = 5;
295 
296  if (i+size < plan_xs.size())
297  {
298  global_costmap_ros_->getCostmap()->mapToWorld(plan_xs[i+size], plan_ys[i+size], world_x, world_y);
299  double yaw = atan2(world_y - plan[i].pose.position.y, world_x - plan[i].pose.position.x);
300  tf2::Quaternion quat_tf;
301  quat_tf.setRPY(0, 0, yaw);
302  plan[i].pose.orientation = tf2::toMsg(quat_tf);
303  }
304  else if (plan_xs.size() > size)
305  {
306  plan[i].pose.orientation = plan[i-1].pose.orientation;
307  }
308  }
309 }
310 
312 {
313  unsigned int mx,my;
314  unsigned char cost;
315  for (std::vector<geometry_msgs::PoseStamped>::const_iterator it = plan.begin(); it != plan.end(); ++it)
316  {
317  geometry_msgs::PoseStamped p;
318  if (global_costmap_ros_->getCostmap()->worldToMap(it->pose.position.x,it->pose.position.y,mx,my)) {
319  cost = global_costmap_ros_->getCostmap()->getCost(mx, my);
320 
321  if (cost == costmap_2d::INSCRIBED_INFLATED_OBSTACLE || cost == costmap_2d::LETHAL_OBSTACLE) { // This also has to be removed and replaced by a proper implementation
322  return false;
323  }
324  }
325  }
326  return true;
327 }
328 
329 } // end namespace
330 
std::vector::resize
T resize(T... args)
cb_global_planner::AStarPlannerGPP::ed_client_
ros::ServiceClient ed_client_
World model client.
Definition: a_star_planner_gpp.h:95
std::string
cb_global_planner::AStarPlannerGPP::tf_
tf2_ros::Buffer * tf_
Definition: a_star_planner_gpp.h:81
costmap_2d::Costmap2D::worldToMap
bool worldToMap(double wx, double wy, unsigned int &mx, unsigned int &my) const
std::vector< geometry_msgs::PoseStamped >
std::vector::size
T size(T... args)
cb_global_planner::AStarPlannerGPP::AStarPlannerGPP
AStarPlannerGPP()
Constructor for the AStarPlannerGPP object.
Definition: a_star_planner_gpp.cpp:53
costmap_2d::INSCRIBED_INFLATED_OBSTACLE
static const unsigned char INSCRIBED_INFLATED_OBSTACLE
a_star_planner_gpp.h
cb_global_planner::AStarPlannerGPP::checkPlan
bool checkPlan(const std::vector< geometry_msgs::PoseStamped > &plan)
Check if plan is valid.
Definition: a_star_planner_gpp.cpp:311
cb_global_planner::GlobalPlannerPlugin
Provides an interface for global planners used in navigation.
Definition: global_planner_plugin.h:32
cb_global_planner::AStarPlannerGPP::~AStarPlannerGPP
~AStarPlannerGPP()
Definition: a_star_planner_gpp.cpp:73
costmap_2d::Costmap2D::mapToWorld
void mapToWorld(unsigned int mx, unsigned int my, double &wx, double &wy) const
costmap_2d::Costmap2D::getCharMap
unsigned char * getCharMap() const
cost_values.h
cb_global_planner::ConstraintEvaluator::init
bool init(const std::string &constraint)
Init function which initializes the constraint that has to be evaluated.
Definition: constraint_evaluator.cpp:7
std::vector::clear
T clear(T... args)
cb_global_planner::AStarPlannerGPP::constraintChanged
bool constraintChanged(const cb_base_navigation_msgs::PositionConstraint &pc)
Definition: a_star_planner_gpp.h:84
std::vector::push_back
T push_back(T... args)
cb_global_planner::AStarPlannerGPP::global_costmap_ros_
costmap_2d::Costmap2DROS * global_costmap_ros_
Definition: a_star_planner_gpp.h:79
cb_global_planner::ConstraintEvaluator::evaluate
bool evaluate(const double &x, const double &y)
Evaluates the constraint for an x,y value.
Definition: constraint_evaluator.cpp:26
cb_global_planner::AStarPlannerGPP::goal_positions_in_constraint_frame_
std::vector< tf2::Vector3 > goal_positions_in_constraint_frame_
Definition: a_star_planner_gpp.h:92
cb_global_planner::AStarPlannerGPP::updateConstraintPositionsInConstraintFrame
bool updateConstraintPositionsInConstraintFrame(const cb_base_navigation_msgs::PositionConstraint &pc)
Definition: a_star_planner_gpp.cpp:194
cb_global_planner::AStarPlannerGPP::initialized_
bool initialized_
Definition: a_star_planner_gpp.h:82
cb_global_planner::AStarPlannerGPP
Definition: a_star_planner_gpp.h:41
cb_global_planner::AStarPlannerGPP::pc_
cb_base_navigation_msgs::PositionConstraint pc_
Definition: a_star_planner_gpp.h:91
costmap_2d::Costmap2D::getSizeInCellsY
unsigned int getSizeInCellsY() const
costmap_2d::LETHAL_OBSTACLE
static const unsigned char LETHAL_OBSTACLE
cb_global_planner::AStarPlanner
Definition: a_star_planner.h:27
cb_global_planner::AStarPlannerGPP::makePlan
bool makePlan(const geometry_msgs::PoseStamped &start, const cb_base_navigation_msgs::PositionConstraint &pc, std::vector< geometry_msgs::PoseStamped > &plan, std::vector< tf2::Vector3 > &goal_positions)
Given a set of goal constraints, compute a plan.
Definition: a_star_planner_gpp.cpp:77
cb_global_planner::AStarPlannerGPP::planToWorld
void planToWorld(const std::vector< int > &plan_xs, const std::vector< int > &plan_ys, std::vector< geometry_msgs::PoseStamped > &plan)
Definition: a_star_planner_gpp.cpp:278
cb_global_planner::AStarPlannerGPP::initialize
void initialize(std::string name, tf2_ros::Buffer *tf, costmap_2d::Costmap2DROS *global_costmap_ros)
Initialization function for the AStarPlannerGPP object.
Definition: a_star_planner_gpp.cpp:57
cb_global_planner::AStarPlannerGPP::queryEntityPose
bool queryEntityPose(const std::string &id, tf2::Transform &pose)
Definition: a_star_planner_gpp.cpp:20
cb_global_planner::AStarPlannerGPP::calculateMapConstraintArea
bool calculateMapConstraintArea(std::vector< unsigned int > &mx, std::vector< unsigned int > &my, std::vector< tf2::Vector3 > &goal_positions)
Definition: a_star_planner_gpp.cpp:240
std::vector::begin
T begin(T... args)
std
std::vector::empty
T empty(T... args)
cb_global_planner
Definition: a_star_planner.cpp:5
std::vector::end
T end(T... args)
costmap_2d::FREE_SPACE
static const unsigned char FREE_SPACE
costmap_2d::Costmap2DROS::getCostmap
Costmap2D * getCostmap() const
costmap_2d::Costmap2D::getSizeInCellsX
unsigned int getSizeInCellsX() const
costmap_2d::Costmap2DROS::getGlobalFrameID
const std::string & getGlobalFrameID() const noexcept
tf_
tf2_ros::Buffer * tf_
std::unique_ptr
costmap_2d::Costmap2D::getCost
unsigned char getCost(unsigned int mx, unsigned int my) const
cb_global_planner::AStarPlannerGPP::planner_
std::unique_ptr< AStarPlanner > planner_
Definition: a_star_planner_gpp.h:80
cb_global_planner::ConstraintEvaluator
Definition: constraint_evaluator.h:27
costmap_2d::Costmap2DROS