20 #include "nav2_route/goal_intent_extractor.hpp"
21 #include "nav2_ros_common/tf2_factories.hpp"
26 static float EPSILON = 1e-6;
29 nav2::LifecycleNode::SharedPtr node,
31 GraphToIDMap * id_to_graph_map,
32 nav2::TransformBuffer::SharedPtr tf,
33 std::shared_ptr<nav2_costmap_2d::CostmapSubscriber> costmap_subscriber,
34 const std::string & route_frame,
35 const std::string & base_frame)
37 logger_ = node->get_logger();
38 id_to_graph_map_ = id_to_graph_map;
41 costmap_subscriber_ = costmap_subscriber;
42 route_frame_ = route_frame;
43 base_frame_ = base_frame;
44 node_spatial_tree_ = std::make_shared<NodeSpatialTree>();
45 node_spatial_tree_->computeTree(graph);
47 prune_goal_ = node->declare_or_get_parameter(
"prune_goal",
true);
49 max_dist_from_edge_ =
static_cast<float>(
50 node->declare_or_get_parameter(
"max_prune_dist_from_edge", 8.0));
51 min_dist_from_goal_ =
static_cast<float>(
52 node->declare_or_get_parameter(
"min_prune_dist_from_goal", 0.15));
53 min_dist_from_start_ =
static_cast<float>(
54 node->declare_or_get_parameter(
"min_prune_dist_from_start", 0.10));
56 enable_search_ = node->declare_or_get_parameter(
"enable_nn_search",
true);
57 max_nn_search_iterations_ = node->declare_or_get_parameter(
58 "max_nn_search_iterations", 10000);
60 int num_of_nearest_nodes = node->declare_or_get_parameter(
"num_nearest_nodes", 5);
61 node_spatial_tree_->setNumOfNearestNodes(num_of_nearest_nodes);
66 id_to_graph_map_ = id_to_graph_map;
68 node_spatial_tree_->computeTree(graph);
72 geometry_msgs::msg::PoseStamped & pose,
73 const std::string & target_frame)
75 if (pose.header.frame_id != target_frame) {
78 "Request pose in %s frame. Converting to route server frame: %s.",
79 pose.header.frame_id.c_str(), target_frame.c_str());
80 if (!nav2_util::transformPoseInTargetFrame(pose, pose, *tf_, target_frame)) {
93 template<
typename GoalT>
98 if (!goal->use_poses) {
99 unsigned int start_idx = id_to_graph_map_->at(goal->start_id);
100 unsigned int goal_idx = id_to_graph_map_->at(goal->goal_id);
101 const Coordinates & start_coords = graph_->at(start_idx).coords;
102 const Coordinates & goal_coords = graph_->at(goal_idx).coords;
103 start_.pose.position.x = start_coords.x;
104 start_.pose.position.y = start_coords.y;
105 goal_.pose.position.x = goal_coords.x;
106 goal_.pose.position.y = goal_coords.y;
107 return {start_idx, goal_idx};
111 geometry_msgs::msg::PoseStamped start_pose, goal_pose = goal->goal;
112 if (goal->use_start) {
113 start_pose = goal->start;
115 if (!nav2_util::getCurrentPose(start_pose, *tf_, route_frame_, base_frame_)) {
126 std::vector<unsigned int> start_route, end_route;
127 if (!node_spatial_tree_->findNearestGraphNodesToPose(start_, start_route) ||
128 !node_spatial_tree_->findNearestGraphNodesToPose(goal_, end_route))
131 "Could not determine node closest to start or goal pose requested!");
134 unsigned int start_route_loc = start_route.front();
135 unsigned int end_route_loc = end_route.front();
141 std::shared_ptr<nav2_costmap_2d::Costmap2D> costmap =
nullptr;
142 std::string costmap_frame_id;
143 bool enable_search = enable_search_;
146 costmap = costmap_subscriber_->getCostmap();
147 costmap_frame_id = costmap_subscriber_->getFrameID();
148 }
catch (
const std::exception & ex) {
149 enable_search =
false;
152 "Failed to get costmap for goal intent extractor: %s. "
153 "Falling back to closest euclidean route node instead.", ex.what());
157 if (enable_search && start_route.size() > 1u) {
159 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
160 candidate_nodes.reserve(start_route.size());
161 for (
const auto & node : start_route) {
162 auto & node_data = graph_->at(node);
163 geometry_msgs::msg::PoseStamped node_pose;
164 node_pose.pose.position.x = node_data.coords.x;
165 node_pose.pose.position.y = node_data.coords.y;
166 node_pose.header.frame_id = node_data.coords.frame_id;
167 node_pose.header.stamp = start_pose.header.stamp;
168 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
171 auto transformed_start =
transformPose(start_, costmap_frame_id);
172 std::lock_guard<nav2_costmap_2d::Costmap2D::mutex_t> lock(*costmap->getMutex());
175 candidate_nodes.front().pose.position, transformed_start.pose.position))
179 if (bfs.
search(transformed_start, candidate_nodes, max_nn_search_iterations_)) {
186 if (enable_search && end_route.size() > 1u) {
188 std::vector<geometry_msgs::msg::PoseStamped> candidate_nodes;
189 candidate_nodes.reserve(end_route.size());
190 for (
const auto & node : end_route) {
191 auto & node_data = graph_->at(node);
192 geometry_msgs::msg::PoseStamped node_pose;
193 node_pose.pose.position.x = node_data.coords.x;
194 node_pose.pose.position.y = node_data.coords.y;
195 node_pose.header.frame_id = node_data.coords.frame_id;
196 node_pose.header.stamp = goal_pose.header.stamp;
197 candidate_nodes.push_back(
transformPose(node_pose, costmap_frame_id));
200 auto transformed_end =
transformPose(goal_, costmap_frame_id);
201 std::lock_guard<nav2_costmap_2d::Costmap2D::mutex_t> lock(*costmap->getMutex());
204 candidate_nodes.front().pose.position, transformed_end.pose.position))
208 if (bfs.
search(transformed_end, candidate_nodes)) {
215 return {start_route_loc, end_route_loc};
218 template<
typename GoalT>
220 const Route & input_route,
221 const std::shared_ptr<const GoalT> goal,
224 Route pruned_route = input_route;
227 EdgePtr last_curr_edge = rerouting_info.curr_edge;
228 rerouting_info.curr_edge =
nullptr;
229 bool first_time = rerouting_info.first_time;
230 rerouting_info.first_time =
false;
233 if (input_route.edges.empty() || (!goal->use_poses && first_time)) {
238 NodePtr first = pruned_route.start_node;
239 NodePtr next = pruned_route.edges[0]->end;
240 float vrx = next->coords.x - first->coords.x;
241 float vry = next->coords.y - first->coords.y;
242 float vpx = start_.pose.position.x - first->coords.x;
243 float vpy = start_.pose.position.y - first->coords.y;
244 float dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
245 Coordinates closest_pt_on_edge = utils::findClosestPoint(start_, first->coords, next->coords);
246 if (dot_prod > EPSILON &&
247 hypotf(vpx, vpy) > min_dist_from_start_ &&
248 utils::distance(closest_pt_on_edge, start_) <= max_dist_from_edge_)
252 if (last_curr_edge && last_curr_edge->edgeid == pruned_route.edges.front()->edgeid) {
253 rerouting_info.closest_pt_on_edge = closest_pt_on_edge;
254 rerouting_info.curr_edge = pruned_route.edges.front();
257 pruned_route.start_node = next;
258 pruned_route.route_cost -= pruned_route.edges.front()->end->search_state.traversal_cost;
259 pruned_route.edges.erase(pruned_route.edges.begin());
263 if (!prune_goal_ || !goal->use_poses || pruned_route.edges.empty()) {
268 next = pruned_route.edges.back()->start;
269 NodePtr last = pruned_route.edges.back()->end;
270 vrx = last->coords.x - next->coords.x;
271 vry = last->coords.y - next->coords.y;
272 vpx = goal_.pose.position.x - last->coords.x;
273 vpy = goal_.pose.position.y - last->coords.y;
275 dot_prod = utils::normalizedDot(vrx, vry, vpx, vpy);
276 closest_pt_on_edge = utils::findClosestPoint(goal_, next->coords, last->coords);
277 if (dot_prod < -EPSILON &&
278 hypotf(vpx, vpy) > min_dist_from_goal_ &&
279 utils::distance(closest_pt_on_edge, goal_) <= max_dist_from_edge_)
281 pruned_route.route_cost -= pruned_route.edges.back()->end->search_state.traversal_cost;
282 pruned_route.edges.pop_back();
293 template Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
294 const Route & input_route,
295 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal,
298 Route GoalIntentExtractor::pruneStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
299 const Route & input_route,
300 const std::shared_ptr<const nav2_msgs::action::ComputeAndTrackRoute::Goal> goal,
302 template NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeRoute::Goal>(
303 const std::shared_ptr<const nav2_msgs::action::ComputeRoute::Goal> goal);
305 NodeExtents GoalIntentExtractor::findStartandGoal<nav2_msgs::action::ComputeAndTrackRoute::Goal>(
306 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.