19 #include "nav2_route/goal_intent_extractor.hpp"
24 static float EPSILON = 1e-6;
27 nav2::LifecycleNode::SharedPtr node,
29 GraphToIDMap * id_to_graph_map,
30 std::shared_ptr<tf2_ros::Buffer> tf,
31 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber> costmap_subscriber,
32 const std::string & route_frame,
33 const std::string & base_frame)
35 logger_ = node->get_logger();
36 id_to_graph_map_ = id_to_graph_map;
39 costmap_subscriber_ = costmap_subscriber;
40 route_frame_ = route_frame;
41 base_frame_ = base_frame;
42 node_spatial_tree_ = std::make_shared<NodeSpatialTree>();
43 node_spatial_tree_->computeTree(graph);
45 nav2::declare_parameter_if_not_declared(
46 node,
"prune_goal", rclcpp::ParameterValue(
true));
47 prune_goal_ = node->get_parameter(
"prune_goal").as_bool();
49 nav2::declare_parameter_if_not_declared(
50 node,
"max_prune_dist_from_edge", rclcpp::ParameterValue(8.0));
51 max_dist_from_edge_ =
static_cast<float>(
52 node->get_parameter(
"max_prune_dist_from_edge").as_double());
53 nav2::declare_parameter_if_not_declared(
54 node,
"min_prune_dist_from_goal", rclcpp::ParameterValue(0.15));
55 min_dist_from_goal_ =
static_cast<float>(
56 node->get_parameter(
"min_prune_dist_from_goal").as_double());
57 nav2::declare_parameter_if_not_declared(
58 node,
"min_prune_dist_from_start", rclcpp::ParameterValue(0.10));
59 min_dist_from_start_ =
static_cast<float>(
60 node->get_parameter(
"min_prune_dist_from_start").as_double());
62 nav2::declare_parameter_if_not_declared(
63 node,
"enable_nn_search", rclcpp::ParameterValue(
true));
64 enable_search_ = node->get_parameter(
"enable_nn_search").as_bool();
65 nav2::declare_parameter_if_not_declared(
66 node,
"max_nn_search_iterations", rclcpp::ParameterValue(10000));
67 max_nn_search_iterations_ = node->get_parameter(
"max_nn_search_iterations").as_int();
69 nav2::declare_parameter_if_not_declared(
70 node,
"num_nearest_nodes", rclcpp::ParameterValue(5));
71 int num_of_nearest_nodes = node->get_parameter(
"num_nearest_nodes").as_int();
72 node_spatial_tree_->setNumOfNearestNodes(num_of_nearest_nodes);
77 id_to_graph_map_ = id_to_graph_map;
79 node_spatial_tree_->computeTree(graph);
83 geometry_msgs::msg::PoseStamped & pose,
84 const std::string & target_frame)
86 if (pose.header.frame_id != target_frame) {
89 "Request pose in %s frame. Converting to route server frame: %s.",
90 pose.header.frame_id.c_str(), target_frame.c_str());
91 if (!nav2_util::transformPoseInTargetFrame(pose, pose, *tf_, target_frame)) {
104 template<
typename GoalT>
109 if (!goal->use_poses) {
110 unsigned int start_idx = id_to_graph_map_->at(goal->start_id);
111 unsigned int goal_idx = id_to_graph_map_->at(goal->goal_id);
112 const Coordinates & start_coords = graph_->at(start_idx).coords;
113 const Coordinates & goal_coords = graph_->at(goal_idx).coords;
114 start_.pose.position.x = start_coords.x;
115 start_.pose.position.y = start_coords.y;
116 goal_.pose.position.x = goal_coords.x;
117 goal_.pose.position.y = goal_coords.y;
118 return {start_idx, goal_idx};
122 geometry_msgs::msg::PoseStamped start_pose, goal_pose = goal->goal;
123 if (goal->use_start) {
124 start_pose = goal->start;
126 if (!nav2_util::getCurrentPose(start_pose, *tf_, route_frame_, base_frame_)) {
137 std::vector<unsigned int> start_route, end_route;
138 if (!node_spatial_tree_->findNearestGraphNodesToPose(start_, start_route) ||
139 !node_spatial_tree_->findNearestGraphNodesToPose(goal_, end_route))
142 "Could not determine node closest to start or goal pose requested!");
145 unsigned int start_route_loc = start_route.front();
146 unsigned int end_route_loc = end_route.front();
152 std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap =
nullptr;
153 std::string costmap_frame_id;
154 bool enable_search = enable_search_;
157 costmap = costmap_subscriber_->getCostmap();
158 costmap_frame_id = costmap_subscriber_->getFrameID();
159 }
catch (
const std::exception & ex) {
160 enable_search =
false;
163 "Failed to get costmap for goal intent extractor: %s. "
164 "Falling back to closest euclidean route node instead.", ex.what());
168 if (enable_search && start_route.size() > 1u) {
170 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
171 candidate_nodes.reserve(start_route.size());
172 for (
const auto & node : start_route) {
173 auto & node_data = graph_->at(node);
174 geometry_msgs::msg::PoseStamped node_pose;
175 node_pose.pose.position.x = node_data.coords.x;
176 node_pose.pose.position.y = node_data.coords.y;
177 node_pose.header.frame_id = node_data.coords.frame_id;
178 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
181 auto transformed_start =
transformPose(start_, costmap_frame_id);
184 candidate_nodes.front().pose.position, transformed_start.pose.position))
188 if (bfs.
search(transformed_start, candidate_nodes, max_nn_search_iterations_)) {
195 if (enable_search && end_route.size() > 1u) {
197 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
198 candidate_nodes.reserve(end_route.size());
199 for (
const auto & node : end_route) {
200 auto & node_data = graph_->at(node);
201 geometry_msgs::msg::PoseStamped node_pose;
202 node_pose.pose.position.x = node_data.coords.x;
203 node_pose.pose.position.y = node_data.coords.y;
204 node_pose.header.frame_id = node_data.coords.frame_id;
205 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
208 auto transformed_end =
transformPose(goal_, costmap_frame_id);
211 candidate_nodes.front().pose.position, transformed_end.pose.position))
215 if (bfs.
search(transformed_end, candidate_nodes)) {
222 return {start_route_loc, end_route_loc};
225 template<
typename GoalT>
227 const Route & input_route,
228 const std::shared_ptr<const GoalT> goal,
231 Route pruned_route = input_route;
234 EdgePtr last_curr_edge = rerouting_info.curr_edge;
235 rerouting_info.curr_edge =
nullptr;
236 bool first_time = rerouting_info.first_time;
237 rerouting_info.first_time =
false;
240 if (input_route.edges.empty() || (!goal->use_poses && first_time)) {
245 NodePtr first = pruned_route.start_node;
246 NodePtr next = pruned_route.edges[0]->end;
247 float vrx = next->coords.x - first->coords.x;
248 float vry = next->coords.y - first->coords.y;
249 float vpx = start_.pose.position.x - first->coords.x;
250 float vpy = start_.pose.position.y - first->coords.y;
251 float dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
252 Coordinates closest_pt_on_edge = utils::findClosestPoint(start_, first->coords, next->coords);
253 if (dot_prod > EPSILON &&
254 hypotf(vpx, vpy) > min_dist_from_start_ &&
255 utils::distance(closest_pt_on_edge, start_) <= max_dist_from_edge_)
259 if (last_curr_edge && last_curr_edge->edgeid == pruned_route.edges.front()->edgeid) {
260 rerouting_info.closest_pt_on_edge = closest_pt_on_edge;
261 rerouting_info.curr_edge = pruned_route.edges.front();
264 pruned_route.start_node = next;
265 pruned_route.route_cost -= pruned_route.edges.front()->end->search_state.traversal_cost;
266 pruned_route.edges.erase(pruned_route.edges.begin());
270 if (!prune_goal_ || !goal->use_poses || pruned_route.edges.empty()) {
275 next = pruned_route.edges.back()->start;
276 NodePtr last = pruned_route.edges.back()->end;
277 vrx = last->coords.x - next->coords.x;
278 vry = last->coords.y - next->coords.y;
279 vpx = goal_.pose.position.x - last->coords.x;
280 vpy = goal_.pose.position.y - last->coords.y;
282 dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
283 closest_pt_on_edge = utils::findClosestPoint(goal_, next->coords, last->coords);
284 if (dot_prod < -EPSILON &&
285 hypotf(vpx, vpy) > min_dist_from_goal_ &&
286 utils::distance(closest_pt_on_edge, goal_) <= max_dist_from_edge_)
288 pruned_route.route_cost -= pruned_route.edges.back()->end->search_state.traversal_cost;
289 pruned_route.edges.pop_back();
300 template Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
301 const Route & input_route,
302 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal,
305 Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
306 const Route & input_route,
307 const std::shared_ptr<const nav2_msgs::action::ComputeAndTrackRoute::Goal> goal,
309 template NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
310 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal);
312 NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
313 const std::shared_ptr<const nav2_msgs::action::ComputeAndTrackRoute::Goal> goal);
bool search(const geometry_msgs::msg::PoseStamped &reference_node, const std::vector< geometry_msgs::msg::PoseStamped > &candidate_nodes, const int max_iterations=std::numeric_limits< int >::max())
Search for the closest node to the given reference node.
unsigned int getClosestNodeIdx()
Get the output closest node in candidate indices.
bool isInCollision()
Check if the line segment is in collision with the costmap.
bool worldToMap(const geometry_msgs::msg::Point &start, const geometry_msgs::msg::Point &end)
Find the line segment in cosmap frame.
An object to store Node coordinates in different frames.
An object representing edges between nodes.
An object to store the nodes in the graph file.
State shared to objects to communicate important rerouting data to avoid rerouting over blocked edges...
An ordered set of nodes and edges corresponding to the planned route.