19 #include "nav2_route/goal_intent_extractor.hpp"
20 #include "nav2_ros_common/tf2_factories.hpp"
25 static float EPSILON = 1e-6;
28 nav2::LifecycleNode::SharedPtr node,
30 GraphToIDMap * id_to_graph_map,
31 nav2::TransformBuffer::SharedPtr tf,
32 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber> costmap_subscriber,
33 const std::string & route_frame,
34 const std::string & base_frame)
36 logger_ = node->get_logger();
37 id_to_graph_map_ = id_to_graph_map;
40 costmap_subscriber_ = costmap_subscriber;
41 route_frame_ = route_frame;
42 base_frame_ = base_frame;
43 node_spatial_tree_ = std::make_shared<NodeSpatialTree>();
44 node_spatial_tree_->computeTree(graph);
46 prune_goal_ = node->declare_or_get_parameter(
"prune_goal",
true);
48 max_dist_from_edge_ =
static_cast<float>(
49 node->declare_or_get_parameter(
"max_prune_dist_from_edge", 8.0));
50 min_dist_from_goal_ =
static_cast<float>(
51 node->declare_or_get_parameter(
"min_prune_dist_from_goal", 0.15));
52 min_dist_from_start_ =
static_cast<float>(
53 node->declare_or_get_parameter(
"min_prune_dist_from_start", 0.10));
55 enable_search_ = node->declare_or_get_parameter(
"enable_nn_search",
true);
56 max_nn_search_iterations_ = node->declare_or_get_parameter(
57 "max_nn_search_iterations", 10000);
59 int num_of_nearest_nodes = node->declare_or_get_parameter(
"num_nearest_nodes", 5);
60 node_spatial_tree_->setNumOfNearestNodes(num_of_nearest_nodes);
65 id_to_graph_map_ = id_to_graph_map;
67 node_spatial_tree_->computeTree(graph);
71 geometry_msgs::msg::PoseStamped & pose,
72 const std::string & target_frame)
74 if (pose.header.frame_id != target_frame) {
77 "Request pose in %s frame. Converting to route server frame: %s.",
78 pose.header.frame_id.c_str(), target_frame.c_str());
79 if (!nav2_util::transformPoseInTargetFrame(pose, pose, *tf_, target_frame)) {
92 template<
typename GoalT>
97 if (!goal->use_poses) {
98 unsigned int start_idx = id_to_graph_map_->at(goal->start_id);
99 unsigned int goal_idx = id_to_graph_map_->at(goal->goal_id);
100 const Coordinates & start_coords = graph_->at(start_idx).coords;
101 const Coordinates & goal_coords = graph_->at(goal_idx).coords;
102 start_.pose.position.x = start_coords.x;
103 start_.pose.position.y = start_coords.y;
104 goal_.pose.position.x = goal_coords.x;
105 goal_.pose.position.y = goal_coords.y;
106 return {start_idx, goal_idx};
110 geometry_msgs::msg::PoseStamped start_pose, goal_pose = goal->goal;
111 if (goal->use_start) {
112 start_pose = goal->start;
114 if (!nav2_util::getCurrentPose(start_pose, *tf_, route_frame_, base_frame_)) {
125 std::vector<unsigned int> start_route, end_route;
126 if (!node_spatial_tree_->findNearestGraphNodesToPose(start_, start_route) ||
127 !node_spatial_tree_->findNearestGraphNodesToPose(goal_, end_route))
130 "Could not determine node closest to start or goal pose requested!");
133 unsigned int start_route_loc = start_route.front();
134 unsigned int end_route_loc = end_route.front();
140 std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap =
nullptr;
141 std::string costmap_frame_id;
142 bool enable_search = enable_search_;
145 costmap = costmap_subscriber_->getCostmap();
146 costmap_frame_id = costmap_subscriber_->getFrameID();
147 }
catch (
const std::exception & ex) {
148 enable_search =
false;
151 "Failed to get costmap for goal intent extractor: %s. "
152 "Falling back to closest euclidean route node instead.", ex.what());
156 if (enable_search && start_route.size() > 1u) {
158 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
159 candidate_nodes.reserve(start_route.size());
160 for (
const auto & node : start_route) {
161 auto & node_data = graph_->at(node);
162 geometry_msgs::msg::PoseStamped node_pose;
163 node_pose.pose.position.x = node_data.coords.x;
164 node_pose.pose.position.y = node_data.coords.y;
165 node_pose.header.frame_id = node_data.coords.frame_id;
166 node_pose.header.stamp = start_pose.header.stamp;
167 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
170 auto transformed_start =
transformPose(start_, costmap_frame_id);
173 candidate_nodes.front().pose.position, transformed_start.pose.position))
177 if (bfs.
search(transformed_start, candidate_nodes, max_nn_search_iterations_)) {
184 if (enable_search && end_route.size() > 1u) {
186 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
187 candidate_nodes.reserve(end_route.size());
188 for (
const auto & node : end_route) {
189 auto & node_data = graph_->at(node);
190 geometry_msgs::msg::PoseStamped node_pose;
191 node_pose.pose.position.x = node_data.coords.x;
192 node_pose.pose.position.y = node_data.coords.y;
193 node_pose.header.frame_id = node_data.coords.frame_id;
194 node_pose.header.stamp = goal_pose.header.stamp;
195 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
198 auto transformed_end =
transformPose(goal_, costmap_frame_id);
201 candidate_nodes.front().pose.position, transformed_end.pose.position))
205 if (bfs.
search(transformed_end, candidate_nodes)) {
212 return {start_route_loc, end_route_loc};
215 template<
typename GoalT>
217 const Route & input_route,
218 const std::shared_ptr<const GoalT> goal,
221 Route pruned_route = input_route;
224 EdgePtr last_curr_edge = rerouting_info.curr_edge;
225 rerouting_info.curr_edge =
nullptr;
226 bool first_time = rerouting_info.first_time;
227 rerouting_info.first_time =
false;
230 if (input_route.edges.empty() || (!goal->use_poses && first_time)) {
235 NodePtr first = pruned_route.start_node;
236 NodePtr next = pruned_route.edges[0]->end;
237 float vrx = next->coords.x - first->coords.x;
238 float vry = next->coords.y - first->coords.y;
239 float vpx = start_.pose.position.x - first->coords.x;
240 float vpy = start_.pose.position.y - first->coords.y;
241 float dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
242 Coordinates closest_pt_on_edge = utils::findClosestPoint(start_, first->coords, next->coords);
243 if (dot_prod > EPSILON &&
244 hypotf(vpx, vpy) > min_dist_from_start_ &&
245 utils::distance(closest_pt_on_edge, start_) <= max_dist_from_edge_)
249 if (last_curr_edge && last_curr_edge->edgeid == pruned_route.edges.front()->edgeid) {
250 rerouting_info.closest_pt_on_edge = closest_pt_on_edge;
251 rerouting_info.curr_edge = pruned_route.edges.front();
254 pruned_route.start_node = next;
255 pruned_route.route_cost -= pruned_route.edges.front()->end->search_state.traversal_cost;
256 pruned_route.edges.erase(pruned_route.edges.begin());
260 if (!prune_goal_ || !goal->use_poses || pruned_route.edges.empty()) {
265 next = pruned_route.edges.back()->start;
266 NodePtr last = pruned_route.edges.back()->end;
267 vrx = last->coords.x - next->coords.x;
268 vry = last->coords.y - next->coords.y;
269 vpx = goal_.pose.position.x - last->coords.x;
270 vpy = goal_.pose.position.y - last->coords.y;
272 dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
273 closest_pt_on_edge = utils::findClosestPoint(goal_, next->coords, last->coords);
274 if (dot_prod < -EPSILON &&
275 hypotf(vpx, vpy) > min_dist_from_goal_ &&
276 utils::distance(closest_pt_on_edge, goal_) <= max_dist_from_edge_)
278 pruned_route.route_cost -= pruned_route.edges.back()->end->search_state.traversal_cost;
279 pruned_route.edges.pop_back();
290 template Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
291 const Route & input_route,
292 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal,
295 Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
296 const Route & input_route,
297 const std::shared_ptr<const nav2_msgs::action::ComputeAndTrackRoute::Goal> goal,
299 template NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
300 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal);
302 NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
303 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.