46 #ifndef FASTRACK_PLANNING_GRAPH_DYNAMIC_PLANNER_H 47 #define FASTRACK_PLANNING_GRAPH_DYNAMIC_PLANNER_H 55 #include <unordered_map> 56 #include <unordered_set> 65 static bool CheckTrajectoryEndpoints(
const Trajectory<S>& traj,
const S& start,
67 if (!traj.FirstState().ToVector().isApprox(start.ToVector(),
70 "Planner returned a trajectory with the wrong start state: " 71 << traj.FirstState().ToVector().transpose() <<
" vs. " 72 << start.ToVector().transpose());
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());
95 std_msgs::ColorRGBA operator()(
double t)
const {
96 std_msgs::ColorRGBA color;
99 if (t < t_min_ || t >
t_max_) {
100 ROS_ERROR(
"Colormap: time is out of bounds.");
107 color.b = 1.0 - color.g;
114 void UpdateTimes(
double t) {
126 using dynamics::Dynamics;
128 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
138 virtual bool LoadParameters(
const ros::NodeHandle& n);
139 virtual bool RegisterCallbacks(
const ros::NodeHandle& n);
143 Trajectory<S> Plan(
const S& start,
const S& goal,
144 double start_time = 0.0)
const;
148 virtual Trajectory<S> SubPlan(
const S& start,
const S& goal,
149 double start_time = 0.0)
const = 0;
152 void Visualize()
const;
155 virtual double Cost(
const Trajectory<S>& traj)
const {
156 return traj.Duration();
162 ROS_ERROR(
"%s: Goal node was null.", this->name_.c_str());
166 return (state.ToVector() - goal_node_->state.ToVector()).norm();
174 typedef std::shared_ptr<Node>
Ptr;
183 bool is_viable =
false;
184 bool is_visited =
false;
196 constexpr
double kSmallNumber = 1e-8;
197 return state.ToVector().isApprox(other.
state.ToVector(), kSmallNumber);
211 Trajectory<S> RecursivePlan(
double initial_call_time,
bool outbound)
const;
220 Trajectory<S> ExtractTrajectory()
const;
226 void UpdateDescendants(
const typename Node::Ptr& node)
const;
231 void UpdateAncestorsOnHome(
const typename Node::Ptr& node)
const;
236 void UpdateAncestorsOnGoal(
const typename Node::Ptr& node)
const;
245 mutable std::random_device
rd_;
246 mutable std::default_random_engine
rng_;
256 mutable std::unique_ptr<SearchableSet<Node, S>>
home_set_;
281 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
284 const S& start,
const S& goal,
double start_time)
const {
286 const double initial_call_time = ros::Time::now().toSec();
290 goal_node_ = Node::Create();
291 goal_node_->state = goal;
295 goal_node_->cost_to_goal = 0.0;
296 goal_node_->is_viable =
true;
297 goal_node_->is_visited =
false;
300 if (!goal_node_->state.ToVector().isApprox(goal.ToVector(),
302 throw std::runtime_error(
"Oops. Goal changed.");
314 std::unique_ptr<Trajectory<S>> traj_to_start_node;
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;
328 home_node->is_viable =
true;
329 home_node->is_visited =
true;
330 home_node->time = 0.0;
333 start_node = home_node;
336 colormap_.UpdateTimes(home_node->time);
342 const Trajectory<S> traj = RecursivePlan(initial_call_time,
true);
354 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
357 double initial_call_time,
bool outbound)
const {
359 while (ros::Time::now().toSec() - initial_call_time < this->max_runtime_) {
361 const S sample = S::Sample();
364 if (!this->env_.AreValid(sample.OccupiedPositions(), this->bound_))
368 std::vector<typename Node::Ptr> home_set_neighbors =
369 home_set_->KnnSearch(sample, num_neighbors_);
372 typename Node::Ptr sample_node =
nullptr;
373 for (
const auto& neighboring_parent : home_set_neighbors) {
375 const Trajectory<S> sub_plan =
376 SubPlan(neighboring_parent->state, sample, neighboring_parent->time);
377 if (sub_plan.Size() == 0)
continue;
381 if (!CheckTrajectoryEndpoints(sub_plan, neighboring_parent->state,
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);
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 = {};
399 home_set_->Insert(sample_node);
402 colormap_.UpdateTimes(sample_node->time);
405 neighboring_parent->trajs_to_children.emplace(sample_node, sub_plan);
408 parent = neighboring_parent;
413 if (!parent)
continue;
417 throw std::runtime_error(
"Sample node was null.");
421 std::vector<typename Node::Ptr> neighboring_goals =
422 (outbound) ? std::vector<typename Node::Ptr>({goal_node_})
423 : home_set_->KnnSearch(sample, num_neighbors_);
426 for (
const auto& goal : neighboring_goals) {
430 if (!goal->is_viable) {
432 ROS_ERROR_THROTTLE(1.0,
"%s: Goal was not viable on outbound call.",
433 this->name_.c_str());
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());
447 const Trajectory<S> sub_plan =
448 SubPlan(sample, goal->state, sample_node->time);
449 if (sub_plan.Size() == 0)
continue;
455 if (!CheckTrajectoryEndpoints(sub_plan, sample_node->state, goal->state))
462 goal->parents.push_back(sample_node);
465 sample_node->trajs_to_children.emplace(child, sub_plan);
466 sample_node->is_viable =
true;
469 nodes_to_visit_.emplace(sample_node);
473 if (child ==
nullptr) {
477 const Trajectory<S> ignore = RecursivePlan(initial_call_time,
false);
484 UpdateDescendants(sample_node);
488 sample_node->cost_to_goal =
489 child->cost_to_goal +
490 Cost(sample_node->trajs_to_children.at(child));
492 sample_node->best_goal_child = child;
495 UpdateAncestorsOnGoal(sample_node);
498 sample_node->cost_to_home =
499 child->cost_to_home +
500 Cost(sample_node->trajs_to_children.at(child));
502 sample_node->best_home_child = child;
505 UpdateAncestorsOnHome(sample_node);
513 return ExtractTrajectory();
515 return Trajectory<S>();
521 ROS_ERROR(
"%s: Planner ran out of time.", this->name_.c_str());
524 if (!outbound)
return Trajectory<S>();
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>();
533 ROS_INFO(
"%s: Found a viable loop.", this->name_.c_str());
534 return ExtractTrajectory();
544 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
553 std::list<Trajectory<S>> trajs;
554 std::list<typename Node::Ptr> nodes;
557 double first_traj_time = ros::Time::now().toSec();
563 if (traj_nodes_.empty()) {
564 start_node = home_set_->InitialNode();
565 explore_node_idx_ = 0;
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.");
573 if (iter == traj_node_times_.begin()) {
574 throw std::runtime_error(
"Invalid start time: too early.");
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_);
583 for (
size_t ii = 0; ii <= lo; ii++) {
584 auto& visited_node = traj_nodes_[ii];
587 nodes_to_visit_.erase(visited_node);
590 visited_node->is_visited =
true;
595 start_node = traj_nodes_[hi];
596 first_traj_time = traj_node_times_[lo];
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);
611 std::max(0, static_cast<int>(explore_node_idx_) - static_cast<int>(lo));
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);
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);
642 if (nodes_to_visit_.empty()) {
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());
653 std::uniform_real_distribution<double> unif(0.0, 1.0);
655 (unif(rng_) < epsilon_greedy_)
656 ? nodes_to_visit_.begin()
657 : std::min_element(nodes_to_visit_.begin(), nodes_to_visit_.end(),
660 return Heuristic(node1->state) <
661 Heuristic(node2->state);
663 auto new_node_to_visit = *iter;
664 nodes_to_visit_.erase(iter);
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);
677 trajs.insert(trajs.end(), backward_trajs.begin(), backward_trajs.end());
678 nodes.insert(nodes.end(), backward_nodes.begin(), backward_nodes.end());
681 if (explore_node_idx_ == 0) explore_node_idx_ = nodes.size() - 1;
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);
699 traj_node_times_.clear();
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();
714 if (nodes_iter == nodes.end()) {
715 throw std::runtime_error(
"Expecting one more node.");
718 traj_nodes_.push_back(*nodes_iter);
721 if (nodes_iter != nodes.end())
722 throw std::runtime_error(
"Incorrect number of nodes.");
725 Trajectory<S> traj(trajs);
726 traj.ResetFirstTime(first_traj_time);
731 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
734 const ros::NodeHandle& n) {
737 ros::NodeHandle nl(n);
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);
746 if (!nl.getParam(
"vis/graph", vis_topic_))
return false;
747 if (!nl.getParam(
"frame/fixed", fixed_frame_))
return false;
750 if (!nl.getParam(
"epsilon_greedy", epsilon_greedy_))
return false;
756 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
759 const ros::NodeHandle& n) {
762 ros::NodeHandle nl(n);
764 nl.advertise<visualization_msgs::Marker>(vis_topic_.c_str(), 1,
false);
770 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
773 if (vis_pub_.getNumSubscribers() == 0) {
774 ROS_WARN_THROTTLE(1.0,
"%s: I'm lonely. Please subscribe.",
775 this->name_.c_str());
780 ROS_ERROR_THROTTLE(1.0,
"%s: Tried to visualize without a home set.",
781 this->name_.c_str());
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();
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;
798 visualization_msgs::Marker cubes;
799 cubes.ns =
"viable_nodes";
800 cubes.header.frame_id = fixed_frame_;
801 cubes.header.stamp = ros::Time::now();
803 cubes.type = visualization_msgs::Marker::CUBE_LIST;
804 cubes.action = visualization_msgs::Marker::ADD;
810 visualization_msgs::Marker lines;
812 lines.header.frame_id = fixed_frame_;
813 lines.header.stamp = ros::Time::now();
815 lines.type = visualization_msgs::Marker::LINE_LIST;
816 lines.action = visualization_msgs::Marker::ADD;
820 constexpr
double kProbabilitySkip = 0.0;
821 std::uniform_real_distribution<double> unif(0.0, 1.0);
824 std::unordered_set<typename Node::Ptr> visited_nodes;
825 std::list<typename Node::Ptr> nodes_to_expand({home_set_->InitialNode()});
827 while (!nodes_to_expand.empty()) {
828 const auto current_node = nodes_to_expand.front();
829 nodes_to_expand.pop_front();
832 visited_nodes.emplace(current_node);
835 const bool skip = unif(rng_) < kProbabilitySkip;
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();
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;
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);
855 current_color.a = kTranslucentAlpha;
858 for (
const auto& child_traj_pair : current_node->trajs_to_children) {
859 const auto& child = child_traj_pair.first;
862 if (!visited_nodes.count(child)) nodes_to_expand.push_back(child);
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();
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);
882 vis_pub_.publish(spheres);
883 vis_pub_.publish(cubes);
884 vis_pub_.publish(lines);
891 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
897 std::list<typename Node::Ptr> queue = {node};
899 while (queue.size() > 0) {
901 const typename Node::Ptr current_node = queue.front();
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;
911 traj_to_child.ResetFirstTime(current_node->time);
917 bool new_best_parent = std::isinf(child->cost_to_come);
918 const double our_cost_to_child = Cost(traj_to_child);
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;
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;
932 colormap_.UpdateTimes(child->time);
935 queue.push_back(child);
944 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
950 std::list<typename Node::Ptr> queue = {node};
952 while (queue.size() > 0) {
954 const typename Node::Ptr current_node = queue.front();
959 for (
auto& parent : current_node->parents) {
961 parent->is_viable =
true;
964 if (!parent->is_visited && !nodes_to_visit_.count(parent))
965 nodes_to_visit_.emplace(parent);
968 auto& traj_from_parent = parent->trajs_to_children.at(current_node);
975 bool new_best_home_child = std::isinf(parent->cost_to_home);
976 const double our_cost_from_parent = Cost(traj_from_parent);
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;
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;
990 queue.push_back(parent);
999 template <
typename S,
typename E,
typename D,
typename SD,
typename B,
1005 std::list<typename Node::Ptr> queue = {node};
1007 while (queue.size() > 0) {
1009 const typename Node::Ptr current_node = queue.front();
1014 for (
auto& parent : current_node->parents) {
1016 parent->is_viable =
true;
1019 if (!parent->is_visited && !nodes_to_visit_.count(parent))
1020 nodes_to_visit_.emplace(parent);
1023 auto& traj_from_parent = parent->trajs_to_children.at(current_node);
1030 bool new_best_goal_child = std::isinf(parent->cost_to_goal);
1031 const double our_cost_from_parent = Cost(traj_from_parent);
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;
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;
1045 queue.push_back(parent);
void UpdateAncestorsOnGoal(const typename Node::Ptr &node) const
virtual double Heuristic(const S &state) const
std::shared_ptr< const Node > ConstPtr
std::default_random_engine rng_
static Node::Ptr Create()
bool operator!=(const Node &other) const
std::vector< Node::Ptr > parents
std::vector< double > traj_node_times_
std::unique_ptr< SearchableSet< Node, S > > home_set_
static constexpr double kEpsilon
bool operator==(const Node &other) const
std::vector< typename Node::Ptr > traj_nodes_
virtual bool LoadParameters(const ros::NodeHandle &n)
void UpdateDescendants(const typename Node::Ptr &node) const
Trajectory< S > Plan(const S &start, const S &goal, double start_time=0.0) const
Trajectory< S > ExtractTrajectory() const
std::unordered_set< typename Node::Ptr > nodes_to_visit_
virtual ~GraphDynamicPlanner()
std::unordered_map< Node::Ptr, Trajectory< S > > trajs_to_children
std::shared_ptr< Node > Ptr
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
Trajectory< S > RecursivePlan(double initial_call_time, bool outbound) const