graph_dynamic_planner.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018, The Regents of the University of California (Regents).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following
14  * disclaimer in the documentation and/or other materials provided
15  * with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Please contact the author(s) of this library if you have any questions.
34  * Authors: David Fridovich-Keil ( dfk@eecs.berkeley.edu )
35  * Jaime F. Fisac ( jfisac@eecs.berkeley.edu )
36  */
37 
39 //
40 // Base class for all graph-based dynamic planners. These planners are all
41 // guaranteed to generate recursively feasible trajectories constructed
42 // using sampling-based logic.
43 //
45 
46 #ifndef FASTRACK_PLANNING_GRAPH_DYNAMIC_PLANNER_H
47 #define FASTRACK_PLANNING_GRAPH_DYNAMIC_PLANNER_H
48 
53 #include <fastrack/utils/types.h>
54 
55 #include <unordered_map>
56 #include <unordered_set>
57 
58 namespace fastrack {
59 namespace planning {
60 
61 namespace {
62 // Return false if the given trajectory does not start/end at the specified
63 // states.
64 template <typename S>
65 static bool CheckTrajectoryEndpoints(const Trajectory<S>& traj, const S& start,
66  const S& end) {
67  if (!traj.FirstState().ToVector().isApprox(start.ToVector(),
69  ROS_ERROR_STREAM(
70  "Planner returned a trajectory with the wrong start state: "
71  << traj.FirstState().ToVector().transpose() << " vs. "
72  << start.ToVector().transpose());
73  return false;
74  }
75 
76  if (!traj.LastState().ToVector().isApprox(end.ToVector(),
78  ROS_ERROR_STREAM("Planner returned a trajectory with the wrong end state: "
79  << traj.LastState().ToVector().transpose() << " vs. "
80  << end.ToVector().transpose());
81  return false;
82  }
83 
84  return true;
85 }
86 
87 // Colormap class. Keeps track of earliest and latest time stamps and provides
88 // a functor for mapping time to color.
89 class Colormap {
90  public:
91  ~Colormap() {}
92  Colormap() : t_min_(0.0), t_max_(0.0) {}
93 
94  // Map a time to a ROS ColorRGBA.
95  std_msgs::ColorRGBA operator()(double t) const {
96  std_msgs::ColorRGBA color;
97  color.a = 1.0;
98 
99  if (t < t_min_ || t > t_max_) {
100  ROS_ERROR("Colormap: time is out of bounds.");
101  color.r = 0.5;
102  color.g = 0.5;
103  color.b = 0.5;
104  } else {
105  color.r = 0.5;
106  color.g = (t - t_min_) / std::max(constants::kEpsilon, (t_max_ - t_min_));
107  color.b = 1.0 - color.g;
108  }
109 
110  return color;
111  }
112 
113  // Update t_min and t_max.
114  void UpdateTimes(double t) {
115  t_min_ = std::min(t_min_, t);
116  t_max_ = std::max(t_max_, t);
117  }
118 
119  private:
120  // Min/max timestamps.
121  double t_min_, t_max_;
122 }; //\class Colormap
123 
124 } //\namespace
125 
126 using dynamics::Dynamics;
127 
128 template <typename S, typename E, typename D, typename SD, typename B,
129  typename SB>
130 class GraphDynamicPlanner : public Planner<S, E, D, SD, B, SB> {
131  public:
132  virtual ~GraphDynamicPlanner() {}
133 
134  protected:
135  explicit GraphDynamicPlanner() : Planner<S, E, D, SD, B, SB>(), rng_(rd_()) {}
136 
137  // Load parameters.
138  virtual bool LoadParameters(const ros::NodeHandle& n);
139  virtual bool RegisterCallbacks(const ros::NodeHandle& n);
140 
141  // Plan a trajectory from the given start to goal states starting
142  // at the given time.
143  Trajectory<S> Plan(const S& start, const S& goal,
144  double start_time = 0.0) const;
145 
146  // Generate a sub-plan that connects two states and is dynamically feasible
147  // (but not necessarily recursively feasible).
148  virtual Trajectory<S> SubPlan(const S& start, const S& goal,
149  double start_time = 0.0) const = 0;
150 
151  // Visualize the graph.
152  void Visualize() const;
153 
154  // Cost functional. Defaults to time, but can be overridden.
155  virtual double Cost(const Trajectory<S>& traj) const {
156  return traj.Duration();
157  }
158 
159  // Heuristic function. Defaults to distance between state and goal.
160  virtual double Heuristic(const S& state) const {
161  if (!goal_node_) {
162  ROS_ERROR("%s: Goal node was null.", this->name_.c_str());
163  return constants::kInfinity;
164  }
165 
166  return (state.ToVector() - goal_node_->state.ToVector()).norm();
167  }
168 
169  // Node in implicit planning graph, templated on state type.
170  // NOTE! To avoid memory leaks, Nodes are constructed using a static
171  // factory method that returns a shared pointer.
172  struct Node {
173  // Typedefs.
174  typedef std::shared_ptr<Node> Ptr;
175  typedef std::shared_ptr<const Node> ConstPtr;
176 
177  // Member variables.
178  S state;
179  double time = constants::kInfinity;
180  double cost_to_come = constants::kInfinity;
181  double cost_to_home = constants::kInfinity;
182  double cost_to_goal = constants::kInfinity;
183  bool is_viable = false;
184  bool is_visited = false;
185  Node::Ptr best_parent = nullptr;
186  Node::Ptr best_home_child = nullptr;
187  Node::Ptr best_goal_child = nullptr;
188  std::vector<Node::Ptr> parents;
189  std::unordered_map<Node::Ptr, Trajectory<S>> trajs_to_children;
190 
191  // Factory methods.
192  static Node::Ptr Create() { return Node::Ptr(new Node()); }
193 
194  // Operators for equality checking.
195  bool operator==(const Node& other) const {
196  constexpr double kSmallNumber = 1e-8;
197  return state.ToVector().isApprox(other.state.ToVector(), kSmallNumber);
198  }
199 
200  bool operator!=(const Node& other) const { return !(*this == other); }
201 
202  private:
203  Node() {}
204  }; //\struct Node
205 
206  // Recursive version of Plan() that plans outbound and return trajectories.
207  // High level recursive feasibility logic is here. Keep track of the
208  // graph of explored states, a set of goal states, the start time,
209  // whether or not this is an outbound or return trip, and whether
210  // or not to extract a trajectory at the end (if not, returns an empty one.)
211  Trajectory<S> RecursivePlan(double initial_call_time, bool outbound) const;
212 
213  // Extract a trajectory including the given start time, which either
214  // loops back home or goes to the goal (if such a trajectory exists).
215  // Returns empty trajectory if none exists.
216  // NOTE: this function updates 'traj_nodes_' and 'traj_node_times_'.
217  // These variables are needed to keep track of the most recently delivered
218  // trajectory to the planner manager; it is used here to determine which
219  // node the new trajectory should start from.
220  Trajectory<S> ExtractTrajectory() const;
221 
222  // Update cost to come, best parent, time, and all traj_to_child times
223  // recursively.
224  // NOTE: this will never get into an infinite loop because eventually
225  // every node will know its best option and reject further updates.
226  void UpdateDescendants(const typename Node::Ptr& node) const;
227 
228  // Update cost to home and best home child recursively.
229  // NOTE: this will never get into an infinite loop because eventually
230  // every node will know its best option and reject further updates.
231  void UpdateAncestorsOnHome(const typename Node::Ptr& node) const;
232 
233  // Update cost to goal and best goal child recursively.
234  // NOTE: this will never get into an infinite loop because eventually
235  // every node will know its best option and reject further updates.
236  void UpdateAncestorsOnGoal(const typename Node::Ptr& node) const;
237 
238  // Number of neighbors and radius to use for nearest neighbor searches.
241 
242  // Epsilon greedy exploration parameter. This is the probability of sampling
243  // a random (viable!) state to visit.
245  mutable std::random_device rd_;
246  mutable std::default_random_engine rng_;
247 
248  // Unordered set storing nodes that we have not yet visited.
249  mutable std::unordered_set<typename Node::Ptr> nodes_to_visit_;
250 
251  // Goal node. This will be set on the first planning invocation.
252  mutable typename Node::Ptr goal_node_;
253 
254  // Backward-reachable set of the "home" state.
255  // This is the initial state of the planner which we assume is viable.
256  mutable std::unique_ptr<SearchableSet<Node, S>> home_set_;
257 
258  // Parallel lists of nodes and times.
259  // These correspond to the most recently output trajectory and are used
260  // for quickly identifying query start states on the graph.
261  // NOTE: these times are absolute ROS times, not relative to 0.0.
262  // NOTE: we also store the index of the node we selected for exploration,
263  // since
264  // we want to make sure we always get there if we replan.
265  mutable std::vector<typename Node::Ptr> traj_nodes_;
266  mutable std::vector<double> traj_node_times_;
267  mutable size_t explore_node_idx_;
268 
269  // Publisher and topic for visualization. Also fixed frame name and colormap.
270  ros::Publisher vis_pub_;
271  std::string vis_topic_;
272  std::string fixed_frame_;
273 
274  mutable Colormap colormap_;
275 }; //\class GraphDynamicPlanner
276 
277 // ----------------------------- IMPLEMENTATION ----------------------------- //
278 
279 // Plan a trajectory from the given start to goal states starting
280 // at the given time.o
281 template <typename S, typename E, typename D, typename SD, typename B,
282  typename SB>
284  const S& start, const S& goal, double start_time) const {
285  // Keep track of initial time.
286  const double initial_call_time = ros::Time::now().toSec();
287 
288  // Set up goal node.
289  if (!goal_node_) {
290  goal_node_ = Node::Create();
291  goal_node_->state = goal;
292  goal_node_->time = constants::kInfinity;
293  goal_node_->cost_to_come = constants::kInfinity;
294  goal_node_->cost_to_home = constants::kInfinity;
295  goal_node_->cost_to_goal = 0.0;
296  goal_node_->is_viable = true;
297  goal_node_->is_visited = false;
298  } else {
299  // Make sure the goal has not changed.
300  if (!goal_node_->state.ToVector().isApprox(goal.ToVector(),
302  throw std::runtime_error("Oops. Goal changed.");
303  }
304  }
305 
306  // Set a start node as null. This will end up either being:
307  // (1) the home node, if we don't have one yet, OR
308  // (2) the node in traj_nodes immediately after the start_time.
309  typename Node::Ptr start_node;
310 
311  // If we have a start node that is not the home node, we will also have
312  // a trajectory from the previous node to the start node that will have to
313  // be concatenated to the trajectory extracted from the graph.
314  std::unique_ptr<Trajectory<S>> traj_to_start_node;
315 
316  // Check if we have a home_set. If not, create one from this start state.
317  // NOTE: assign home node a time of 0.0, since it will persist over multiple
318  // planning invocations.
319  // NOTE: if we don't yet have a home set, we also will not have a previous
320  // node trajectory.
321  if (!home_set_) {
322  typename Node::Ptr home_node = Node::Create();
323  home_node->state = start;
324  home_node->time = start_time;
325  home_node->cost_to_come = 0.0;
326  home_node->cost_to_home = 0.0;
327  home_node->cost_to_goal = constants::kInfinity;
328  home_node->is_viable = true;
329  home_node->is_visited = true;
330  home_node->time = 0.0;
331 
332  home_set_.reset(new SearchableSet<Node, S>(home_node));
333  start_node = home_node;
334 
335  // Update colormap.
336  colormap_.UpdateTimes(home_node->time);
337  }
338 
339  // Generate trajectory. This trajectory will originate from the start node and
340  // either terminate within the goal set OR at the start_node and pass through
341  // the home node.
342  const Trajectory<S> traj = RecursivePlan(initial_call_time, true);
343 
344  // Visualize the new graph.
345  Visualize();
346 
347  // NOTE! Don't need to sleep until max runtime is exceeded because we're going
348  // to include the trajectory segment the planner is already on.
349  return traj;
350 }
351 
352 // Recursive version of Plan() that plans outbound and return trajectories.
353 // High level recursive feasibility logic is here.
354 template <typename S, typename E, typename D, typename SD, typename B,
355  typename SB>
357  double initial_call_time, bool outbound) const {
358  // Loop until we run out of time.
359  while (ros::Time::now().toSec() - initial_call_time < this->max_runtime_) {
360  // (1) Sample a new point.
361  const S sample = S::Sample();
362 
363  // Reject this sample if it's not in known free space.
364  if (!this->env_.AreValid(sample.OccupiedPositions(), this->bound_))
365  continue;
366 
367  // Check the home set for nearest neighbors and connect.
368  std::vector<typename Node::Ptr> home_set_neighbors =
369  home_set_->KnnSearch(sample, num_neighbors_);
370 
371  typename Node::Ptr parent = nullptr;
372  typename Node::Ptr sample_node = nullptr;
373  for (const auto& neighboring_parent : home_set_neighbors) {
374  // (2) Plan a sub-path from the start to the sampled state.
375  const Trajectory<S> sub_plan =
376  SubPlan(neighboring_parent->state, sample, neighboring_parent->time);
377  if (sub_plan.Size() == 0) continue;
378 
379  // If somehow the planner returned a plan that does not terminate at the
380  // desired goal, or begin at the desired start, then discard.
381  if (!CheckTrajectoryEndpoints(sub_plan, neighboring_parent->state,
382  sample))
383  continue;
384 
385  // Add to graph.
386  sample_node = Node::Create();
387  sample_node->state = sample;
388  sample_node->time = neighboring_parent->time + sub_plan.Duration();
389  sample_node->cost_to_come =
390  neighboring_parent->cost_to_come + Cost(sub_plan);
391  sample_node->cost_to_home = constants::kInfinity;
392  sample_node->is_viable = false;
393  sample_node->is_visited = false;
394  sample_node->best_parent = neighboring_parent;
395  sample_node->best_home_child = nullptr;
396  sample_node->parents = {neighboring_parent};
397  sample_node->trajs_to_children = {};
398 
399  home_set_->Insert(sample_node);
400 
401  // Update colormap.
402  colormap_.UpdateTimes(sample_node->time);
403 
404  // Update parent.
405  neighboring_parent->trajs_to_children.emplace(sample_node, sub_plan);
406 
407  // Set parent to neighboring parent and get of here.
408  parent = neighboring_parent;
409  break;
410  }
411 
412  // Skip this sample if we didn't find a parent.
413  if (!parent) continue;
414 
415  // Sample node had better not be null.
416  if (!sample_node) {
417  throw std::runtime_error("Sample node was null.");
418  }
419 
420  // (3) Connect to one of the k nearest goal states if possible.
421  std::vector<typename Node::Ptr> neighboring_goals =
422  (outbound) ? std::vector<typename Node::Ptr>({goal_node_})
423  : home_set_->KnnSearch(sample, num_neighbors_);
424 
425  typename Node::Ptr child = nullptr;
426  for (const auto& goal : neighboring_goals) {
427  // Check if this is a viable node.
428  // NOTE: inbound recursive calls may encounter nodes which are goals (i.e.
429  // in the home set) but NOT viable yet.
430  if (!goal->is_viable) {
431  if (outbound) {
432  ROS_ERROR_THROTTLE(1.0, "%s: Goal was not viable on outbound call.",
433  this->name_.c_str());
434  }
435 
436  continue;
437  }
438 
439  // Check if goal is in known free space.
440  if (!this->env_.AreValid(goal->state.OccupiedPositions(), this->bound_)) {
441  ROS_INFO_THROTTLE(1.0, "%s: Goal was not in known free space.",
442  this->name_.c_str());
443  continue;
444  }
445 
446  // Try to connect.
447  const Trajectory<S> sub_plan =
448  SubPlan(sample, goal->state, sample_node->time);
449  if (sub_plan.Size() == 0) continue;
450 
451  // Upon success, set child to point to goal and update sample node to
452  // include child node and corresponding trajectory.
453  // If somehow the planner returned a plan that does not terminate at the
454  // desired goal, or begin at the desired start, then discard.
455  if (!CheckTrajectoryEndpoints(sub_plan, sample_node->state, goal->state))
456  continue;
457 
458  // Set child to goal, since we have a valid trajectory to get there.
459  child = goal;
460 
461  // Add ourselves as a parent of the goal.
462  goal->parents.push_back(sample_node);
463 
464  // Update sample node to point to child.
465  sample_node->trajs_to_children.emplace(child, sub_plan);
466  sample_node->is_viable = true;
467 
468  // Add this guy to 'nodes_to_visit_'.
469  nodes_to_visit_.emplace(sample_node);
470  break;
471  }
472 
473  if (child == nullptr) {
474  // (5) If outbound, make a recursive call. We can ignore the returned
475  // trajectory since we'll generate one later once we're all done.
476  if (outbound) {
477  const Trajectory<S> ignore = RecursivePlan(initial_call_time, false);
478  }
479  } else {
480  // Reached the goal. This means that 'sample_node' now has exactly one
481  // viable child. We need to update all sample node's descendants to
482  // reflect the potential change in best parent of sample_node's child.
483  // Also, sample node is now viable, so mark all ancestors as viable.
484  UpdateDescendants(sample_node);
485 
486  if (outbound) {
487  // Update sample node's cost to goal.
488  sample_node->cost_to_goal =
489  child->cost_to_goal +
490  Cost(sample_node->trajs_to_children.at(child));
491 
492  sample_node->best_goal_child = child;
493 
494  // Propagate backward to all ancestors.
495  UpdateAncestorsOnGoal(sample_node);
496  } else {
497  // Update sample node's cost to home.
498  sample_node->cost_to_home =
499  child->cost_to_home +
500  Cost(sample_node->trajs_to_children.at(child));
501 
502  sample_node->best_home_child = child;
503 
504  // Propagate backward to all ancestors.
505  UpdateAncestorsOnHome(sample_node);
506  }
507 
508  // Extract trajectory. Always walk backward from the initial node of the
509  // goal set to the start node. Be sure to set the correct start time.
510  // NOTE: this will automatically set traj_nodes and traj_node_times.
511  // Else, return a dummy trajectory since it will be ignored anyway.
512  if (outbound) {
513  return ExtractTrajectory();
514  } else {
515  return Trajectory<S>();
516  }
517  }
518  }
519 
520  // Ran out of time.
521  ROS_ERROR("%s: Planner ran out of time.", this->name_.c_str());
522 
523  // Don't return a trajectory if not outbound.
524  if (!outbound) return Trajectory<S>();
525 
526  // Return a viable loop if we found one.
527  const typename Node::Ptr home_node = home_set_->InitialNode();
528  if (home_node->parents.empty()) {
529  ROS_ERROR("%s: No viable loops available.", this->name_.c_str());
530  return Trajectory<S>();
531  }
532 
533  ROS_INFO("%s: Found a viable loop.", this->name_.c_str());
534  return ExtractTrajectory();
535 }
536 
537 // Extract a trajectory including the given start time, which either
538 // loops back home or goes to the goal (if such a trajectory exists).
539 // Returns empty trajectory if none exists.
540 // NOTE: this function updates 'traj_nodes_' and 'traj_node_times_'.
541 // These variables are needed to keep track of the most recently delivered
542 // trajectory to the planner manager; it is used here to determine which
543 // node the new trajectory should start from.
544 template <typename S, typename E, typename D, typename SD, typename B,
545  typename SB>
547  const {
548  // Start node for graph traversal. This will either be the home node, or
549  // the next node along our previous plan.
550  typename Node::Ptr start_node = nullptr;
551 
552  // Accumulate trajectories and nodes in a list.
553  std::list<Trajectory<S>> trajs;
554  std::list<typename Node::Ptr> nodes;
555 
556  // Time to use for first node in trajectory.
557  double first_traj_time = ros::Time::now().toSec();
558 
559  // If we have traj_nodes and traj_node_times, interpolate at the current time
560  // and reset to include only the node/time preceding the current_time.
561  // The node/time succeeding the current time will be added later in this
562  // method.
563  if (traj_nodes_.empty()) {
564  start_node = home_set_->InitialNode();
565  explore_node_idx_ = 0;
566  } else {
567  const auto iter = std::lower_bound(traj_node_times_.begin(),
568  traj_node_times_.end(), first_traj_time);
569  if (iter == traj_node_times_.end()) {
570  throw std::runtime_error("Invalid start time: too late.");
571  }
572 
573  if (iter == traj_node_times_.begin()) {
574  throw std::runtime_error("Invalid start time: too early.");
575  }
576 
577  size_t hi = std::distance(traj_node_times_.begin(), iter);
578  const size_t lo = hi - 1;
579  hi = std::max(hi, explore_node_idx_);
580 
581  // Remove all nodes up to and including previous node from nodes to visit,
582  // and also be sure to mark them as visited.
583  for (size_t ii = 0; ii <= lo; ii++) {
584  auto& visited_node = traj_nodes_[ii];
585 
586  // This will erase only if it was there to start with.
587  nodes_to_visit_.erase(visited_node);
588 
589  // Mark as visited.
590  visited_node->is_visited = true;
591  }
592 
593  // Extract nodes and times.
594  // const auto& previous_node = traj_nodes_[lo];
595  start_node = traj_nodes_[hi];
596  first_traj_time = traj_node_times_[lo];
597 
598  // Extract trajectory from previous node to start node and ensure
599  // that it begins at the right time.
600  for (size_t ii = lo; ii < hi; ii++) {
601  const auto& node = traj_nodes_[ii];
602  const auto& next_node = traj_nodes_[ii + 1];
603  const auto& traj = node->trajs_to_children.at(next_node);
604  trajs.push_back(traj);
605  nodes.push_back(node);
606  }
607 
608  // Update 'explore_node_idx_' to account for the nodes we've removed from
609  // 'traj_nodes_'.
610  explore_node_idx_ =
611  std::max(0, static_cast<int>(explore_node_idx_) - static_cast<int>(lo));
612  }
613 
614  // If we are connected to the goal (i.e. we have a best goal child),
615  // then just follow best goal child all the way there!
616  if (start_node->best_goal_child) {
617  nodes.push_back(start_node);
618  for (auto node = start_node; node->cost_to_goal > 0.0;
619  node = node->best_goal_child) {
620  trajs.push_back(node->trajs_to_children.at(node->best_goal_child));
621  nodes.push_back(node->best_goal_child);
622  }
623  } else {
624  // We're going home.
625  // (1) Follow best home child till we get home.
626  // (2) Pick a optimistic node from 'nodes_to_visit_'.
627  // (3) Backtrack from that node all the way home via best parent.
628  // (4) From that node, follow best home child all the way home.
629  // (5) Stitch these three sub-trajectories together in the right order:
630  // { start_node -> home -> optimistic new node -> home }.
631 
632  // (1) Follow best home child till we get home.
633  nodes.push_back(start_node);
634  for (auto node = start_node; node->cost_to_home > 0.0;
635  node = node->best_home_child) {
636  trajs.push_back(node->trajs_to_children.at(node->best_home_child));
637  nodes.push_back(node->best_home_child);
638  }
639 
640  // (2) Pick a optimistic node from 'nodes_to_visit_'.
641  // Choose the one with best heuristic value.
642  if (nodes_to_visit_.empty()) {
643  // We've explored the entire space and there is no way to the goal.
644  // So, just return home and give up.
645  ROS_WARN("%s: There is no recursively feasible trajectory to the goal",
646  this->name_.c_str());
647  ROS_WARN("%s: The safely reachable space has been fully explored.",
648  this->name_.c_str());
649  ROS_WARN("%s: Returning home.", this->name_.c_str());
650  } else {
651  // Take a uniform random draw from [0, 1] and if it is below
652  // 'epsilon_greedy_' choose a random element. Otherwise choose at random.
653  std::uniform_real_distribution<double> unif(0.0, 1.0);
654  auto iter =
655  (unif(rng_) < epsilon_greedy_)
656  ? nodes_to_visit_.begin()
657  : std::min_element(nodes_to_visit_.begin(), nodes_to_visit_.end(),
658  [this](const typename Node::Ptr& node1,
659  const typename Node::Ptr& node2) {
660  return Heuristic(node1->state) <
661  Heuristic(node2->state);
662  });
663  auto new_node_to_visit = *iter;
664  nodes_to_visit_.erase(iter);
665 
666  // (3) Backtrack from that node all the way home via best parent.
667  std::list<typename Node::Ptr> backward_nodes;
668  std::list<Trajectory<S>> backward_trajs;
669  for (auto node = new_node_to_visit; node->cost_to_come > 0.0;
670  node = node->best_parent) {
671  backward_trajs.push_front(
672  node->best_parent->trajs_to_children.at(node));
673  backward_nodes.push_front(node);
674  }
675 
676  // Append these nodes and trajs to the main lists.
677  trajs.insert(trajs.end(), backward_trajs.begin(), backward_trajs.end());
678  nodes.insert(nodes.end(), backward_nodes.begin(), backward_nodes.end());
679 
680  // Only update 'explore_node_idx_' if we've reached it.
681  if (explore_node_idx_ == 0) explore_node_idx_ = nodes.size() - 1;
682 
683  // (4) From that node, follow best home child all the way home.
684  for (auto node = new_node_to_visit; node->cost_to_home > 0.0;
685  node = node->best_home_child) {
686  trajs.push_back(node->trajs_to_children.at(node->best_home_child));
687  nodes.push_back(node->best_home_child);
688  }
689 
690  // (5) Stitch these three sub-trajectories together in the right order:
691  // { start_node -> home -> optimistic new node -> home }.
692  // Already done. Oh man.
693  }
694  }
695 
696  // Reinitialize traj nodes/times with previous node/time. Start node/time
697  // will be added later.
698  traj_nodes_.clear();
699  traj_node_times_.clear();
700 
701  // Add all nodes and corresponding times to traj_nodes and traj_node_times.
702  double node_time = first_traj_time;
703  auto trajs_iter = trajs.begin();
704  auto nodes_iter = nodes.begin();
705  while (trajs_iter != trajs.end() && nodes_iter != nodes.end()) {
706  traj_nodes_.push_back(*nodes_iter);
707  traj_node_times_.push_back(node_time);
708  node_time += trajs_iter->Duration();
709 
710  trajs_iter++;
711  nodes_iter++;
712  }
713 
714  if (nodes_iter == nodes.end()) {
715  throw std::runtime_error("Expecting one more node.");
716  }
717 
718  traj_nodes_.push_back(*nodes_iter);
719  nodes_iter++;
720 
721  if (nodes_iter != nodes.end())
722  throw std::runtime_error("Incorrect number of nodes.");
723 
724  // Concatenate into a single trajectory and set initial time.
725  Trajectory<S> traj(trajs);
726  traj.ResetFirstTime(first_traj_time);
727  return traj;
728 }
729 
730 // Load parameters.
731 template <typename S, typename E, typename D, typename SD, typename B,
732  typename SB>
734  const ros::NodeHandle& n) {
735  if (!Planner<S, E, D, SD, B, SB>::LoadParameters(n)) return false;
736 
737  ros::NodeHandle nl(n);
738 
739  // Search parameters.
740  int k;
741  if (!nl.getParam("search_radius", search_radius_)) return false;
742  if (!nl.getParam("num_neighbors", k)) return false;
743  num_neighbors_ = static_cast<size_t>(k);
744 
745  // Visualization parameters.
746  if (!nl.getParam("vis/graph", vis_topic_)) return false;
747  if (!nl.getParam("frame/fixed", fixed_frame_)) return false;
748 
749  // Epsilon for epsilon-greedy exploration.
750  if (!nl.getParam("epsilon_greedy", epsilon_greedy_)) return false;
751 
752  return true;
753 }
754 
755 // Register callbacks.
756 template <typename S, typename E, typename D, typename SD, typename B,
757  typename SB>
759  const ros::NodeHandle& n) {
761 
762  ros::NodeHandle nl(n);
763  vis_pub_ =
764  nl.advertise<visualization_msgs::Marker>(vis_topic_.c_str(), 1, false);
765 
766  return true;
767 }
768 
769 // Visualize the graph.
770 template <typename S, typename E, typename D, typename SD, typename B,
771  typename SB>
773  if (vis_pub_.getNumSubscribers() == 0) {
774  ROS_WARN_THROTTLE(1.0, "%s: I'm lonely. Please subscribe.",
775  this->name_.c_str());
776  return;
777  }
778 
779  if (!home_set_) {
780  ROS_ERROR_THROTTLE(1.0, "%s: Tried to visualize without a home set.",
781  this->name_.c_str());
782  return;
783  }
784 
785  // Set up spheres marker. This will be used to mark non-viable nodes.
786  visualization_msgs::Marker spheres;
787  spheres.ns = "non_viable_nodes";
788  spheres.header.frame_id = fixed_frame_;
789  spheres.header.stamp = ros::Time::now();
790  spheres.id = 0;
791  spheres.type = visualization_msgs::Marker::SPHERE_LIST;
792  spheres.action = visualization_msgs::Marker::ADD;
793  spheres.scale.x = 0.2;
794  spheres.scale.y = 0.2;
795  spheres.scale.z = 0.2;
796 
797  // Set up cubes marker. This will be used to mark viable nodes.
798  visualization_msgs::Marker cubes;
799  cubes.ns = "viable_nodes";
800  cubes.header.frame_id = fixed_frame_;
801  cubes.header.stamp = ros::Time::now();
802  cubes.id = 0;
803  cubes.type = visualization_msgs::Marker::CUBE_LIST;
804  cubes.action = visualization_msgs::Marker::ADD;
805  cubes.scale.x = 0.2;
806  cubes.scale.y = 0.2;
807  cubes.scale.z = 0.2;
808 
809  // Set up line list marker. This will be used to mark all edges in the graph.
810  visualization_msgs::Marker lines;
811  lines.ns = "edges";
812  lines.header.frame_id = fixed_frame_;
813  lines.header.stamp = ros::Time::now();
814  lines.id = 0;
815  lines.type = visualization_msgs::Marker::LINE_LIST;
816  lines.action = visualization_msgs::Marker::ADD;
817  lines.scale.x = 0.1;
818 
819  // Randomly downsample the graph.
820  constexpr double kProbabilitySkip = 0.0;
821  std::uniform_real_distribution<double> unif(0.0, 1.0);
822 
823  // Walk the graph via breadth-first search.
824  std::unordered_set<typename Node::Ptr> visited_nodes;
825  std::list<typename Node::Ptr> nodes_to_expand({home_set_->InitialNode()});
826 
827  while (!nodes_to_expand.empty()) {
828  const auto current_node = nodes_to_expand.front();
829  nodes_to_expand.pop_front();
830 
831  // Add to visited list.
832  visited_nodes.emplace(current_node);
833 
834  // Randomly don't visualize this node.
835  const bool skip = unif(rng_) < kProbabilitySkip;
836 
837  // Add to sphere marker.
838  geometry_msgs::Point current_position;
839  current_position.x = current_node->state.X();
840  current_position.y = current_node->state.Y();
841  current_position.z = current_node->state.Z();
842 
843  constexpr double kTranslucentAlpha = 0.2;
844  auto current_color = colormap_(current_node->time);
845  if (!current_node->is_viable)
846  current_color.a = kTranslucentAlpha;
847 
848  if (!skip) {
849  auto& node_marker = (current_node->is_viable) ? cubes : spheres;
850  node_marker.points.push_back(current_position);
851  node_marker.colors.push_back(current_color);
852  }
853 
854  // Lower alpha value for color in order to make lines show up translucent.
855  current_color.a = kTranslucentAlpha;
856 
857  // Expand this node.
858  for (const auto& child_traj_pair : current_node->trajs_to_children) {
859  const auto& child = child_traj_pair.first;
860 
861  // Add to queue if unvisited.
862  if (!visited_nodes.count(child)) nodes_to_expand.push_back(child);
863 
864  // Update lines marker only.
865  if (!skip) {
866  geometry_msgs::Point child_position;
867  child_position.x = child->state.X();
868  child_position.y = child->state.Y();
869  child_position.z = child->state.Z();
870 
871  auto child_color = colormap_(child->time);
872  child_color.a = kTranslucentAlpha;
873  lines.points.push_back(current_position);
874  lines.points.push_back(child_position);
875  lines.colors.push_back(current_color);
876  lines.colors.push_back(child_color);
877  }
878  }
879  }
880 
881  // Publish.
882  vis_pub_.publish(spheres);
883  vis_pub_.publish(cubes);
884  vis_pub_.publish(lines);
885 }
886 
887 // Update cost to come, best parent, time, and all traj_to_child times
888 // recursively.
889 // NOTE: this will never get into an infinite loop because eventually
890 // every node will know its best option and reject further updates.
891 template <typename S, typename E, typename D, typename SD, typename B,
892  typename SB>
894  const typename Node::Ptr& node) const {
895  // Run breadth-first search.
896  // Initialize a queue with 'node' inside.
897  std::list<typename Node::Ptr> queue = {node};
898 
899  while (queue.size() > 0) {
900  // Pop oldest node.
901  const typename Node::Ptr current_node = queue.front();
902  queue.pop_front();
903 
904  // Loop over all children and add to queue if this node is their
905  // NEW best parent.
906  for (auto& child_traj_pair : current_node->trajs_to_children) {
907  auto& child = child_traj_pair.first;
908  auto& traj_to_child = child_traj_pair.second;
909 
910  // Update trajectory to child.
911  traj_to_child.ResetFirstTime(current_node->time);
912 
913  // Maybe update child's best parent to be the current node.
914  // If so, also update time and cost to come.
915  // NOTE: if child's cost to come is infinite, it is DEFINTELY a goal.
916  // Everybody else will already have a best parent, except home.
917  bool new_best_parent = std::isinf(child->cost_to_come);
918  const double our_cost_to_child = Cost(traj_to_child);
919 
920  if (!new_best_parent) {
921  if (child->cost_to_come >
922  current_node->cost_to_come + our_cost_to_child)
923  new_best_parent = true;
924  }
925 
926  if (new_best_parent) {
927  child->best_parent = current_node;
928  child->time = current_node->time + traj_to_child.Duration();
929  child->cost_to_come = current_node->cost_to_come + our_cost_to_child;
930 
931  // Update colormap.
932  colormap_.UpdateTimes(child->time);
933 
934  // Push child onto the queue.
935  queue.push_back(child);
936  }
937  }
938  }
939 }
940 
941 // Update cost to home and best home child recursively.
942 // NOTE: this will never get into an infinite loop because eventually
943 // every node will know its best option and reject further updates.
944 template <typename S, typename E, typename D, typename SD, typename B,
945  typename SB>
947  const typename Node::Ptr& node) const {
948  // Run breadth-first search.
949  // Initialize a queue with 'node' inside.
950  std::list<typename Node::Ptr> queue = {node};
951 
952  while (queue.size() > 0) {
953  // Pop oldest node.
954  const typename Node::Ptr current_node = queue.front();
955  queue.pop_front();
956 
957  // Loop over all parents and add to queue if this node is their
958  // NEW best home child.
959  for (auto& parent : current_node->parents) {
960  // Parent is DEFINITELY viable.
961  parent->is_viable = true;
962 
963  // If parent has not been visited yet, then add to 'nodes_to_visit_'.
964  if (!parent->is_visited && !nodes_to_visit_.count(parent))
965  nodes_to_visit_.emplace(parent);
966 
967  // Extract trajectory from parent to this node.
968  auto& traj_from_parent = parent->trajs_to_children.at(current_node);
969 
970  // Maybe update parent's best home child to be the current node.
971  // If so, also update its viability and cost to home.
972  // NOTE: if parent's cost to home is infinite, we are definitely its
973  // best
974  // (and only) option as best home child.
975  bool new_best_home_child = std::isinf(parent->cost_to_home);
976  const double our_cost_from_parent = Cost(traj_from_parent);
977 
978  if (!new_best_home_child) {
979  if (parent->cost_to_home >
980  current_node->cost_to_home + our_cost_from_parent)
981  new_best_home_child = true;
982  }
983 
984  if (new_best_home_child) {
985  parent->best_home_child = current_node;
986  parent->cost_to_home =
987  current_node->cost_to_home + our_cost_from_parent;
988 
989  // Push child onto the queue.
990  queue.push_back(parent);
991  }
992  }
993  }
994 }
995 
996 // Update cost to goal and best goal child recursively.
997 // NOTE: this will never get into an infinite loop because eventually
998 // every node will know its best option and reject further updates.
999 template <typename S, typename E, typename D, typename SD, typename B,
1000  typename SB>
1002  const typename Node::Ptr& node) const {
1003  // Run breadth-first search.
1004  // Initialize a queue with 'node' inside.
1005  std::list<typename Node::Ptr> queue = {node};
1006 
1007  while (queue.size() > 0) {
1008  // Pop oldest node.
1009  const typename Node::Ptr current_node = queue.front();
1010  queue.pop_front();
1011 
1012  // Loop over all parents and add to queue if this node is their
1013  // NEW best goal child.
1014  for (auto& parent : current_node->parents) {
1015  // Parent is DEFINITELY viable.
1016  parent->is_viable = true;
1017 
1018  // If parent has not been visited yet, then add to 'nodes_to_visit_'.
1019  if (!parent->is_visited && !nodes_to_visit_.count(parent))
1020  nodes_to_visit_.emplace(parent);
1021 
1022  // Extract trajectory from parent to this node.
1023  auto& traj_from_parent = parent->trajs_to_children.at(current_node);
1024 
1025  // Maybe update parent's best goal child to be the current node.
1026  // If so, also update its viability and cost to goal.
1027  // NOTE: if parent's cost to goal is infinite, we are definitely its
1028  // best
1029  // (and only) option as best goal child.
1030  bool new_best_goal_child = std::isinf(parent->cost_to_goal);
1031  const double our_cost_from_parent = Cost(traj_from_parent);
1032 
1033  if (!new_best_goal_child) {
1034  if (parent->cost_to_goal >
1035  current_node->cost_to_goal + our_cost_from_parent)
1036  new_best_goal_child = true;
1037  }
1038 
1039  if (new_best_goal_child) {
1040  parent->best_goal_child = current_node;
1041  parent->cost_to_goal =
1042  current_node->cost_to_goal + our_cost_from_parent;
1043 
1044  // Push child onto the queue.
1045  queue.push_back(parent);
1046  }
1047  }
1048  }
1049 }
1050 
1051 } // namespace planning
1052 } // namespace fastrack
1053 
1054 #endif
void UpdateAncestorsOnGoal(const typename Node::Ptr &node) const
virtual double Heuristic(const S &state) const
double t_min_
std::unique_ptr< SearchableSet< Node, S > > home_set_
static constexpr double kEpsilon
Definition: types.h:70
std::vector< typename Node::Ptr > traj_nodes_
Definition: box.h:53
virtual bool LoadParameters(const ros::NodeHandle &n)
double t_max_
void UpdateDescendants(const typename Node::Ptr &node) const
Trajectory< S > Plan(const S &start, const S &goal, double start_time=0.0) const
std::unordered_set< typename Node::Ptr > nodes_to_visit_
std::unordered_map< Node::Ptr, Trajectory< S > > trajs_to_children
virtual double Cost(const Trajectory< S > &traj) const
void UpdateAncestorsOnHome(const typename Node::Ptr &node) const
virtual bool RegisterCallbacks(const ros::NodeHandle &n)
static constexpr double kInfinity
Definition: types.h:73
Trajectory< S > RecursivePlan(double initial_call_time, bool outbound) const


fastrack
Author(s): David Fridovich-Keil
autogenerated on Mon Aug 3 2020 21:28:37