Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
navigate_to_pose.cpp
1 // Copyright (c) 2021 Samsung Research
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <vector>
16 #include <string>
17 #include <set>
18 #include <memory>
19 #include <limits>
20 #include "nav2_bt_navigator/navigators/navigate_to_pose.hpp"
21 
22 namespace nav2_bt_navigator
23 {
24 
25 bool
27  rclcpp_lifecycle::LifecycleNode::WeakPtr parent_node,
28  std::shared_ptr<nav2_util::OdomSmoother> odom_smoother)
29 {
30  start_time_ = rclcpp::Time(0);
31  auto node = parent_node.lock();
32 
33  if (!node->has_parameter("goal_blackboard_id")) {
34  node->declare_parameter("goal_blackboard_id", std::string("goal"));
35  }
36 
37  goal_blackboard_id_ = node->get_parameter("goal_blackboard_id").as_string();
38 
39  if (!node->has_parameter("path_blackboard_id")) {
40  node->declare_parameter("path_blackboard_id", std::string("path"));
41  }
42 
43  path_blackboard_id_ = node->get_parameter("path_blackboard_id").as_string();
44 
45  // Odometry smoother object for getting current speed
46  odom_smoother_ = odom_smoother;
47 
48  self_client_ = rclcpp_action::create_client<ActionT>(node, getName());
49 
50  goal_sub_ = node->create_subscription<geometry_msgs::msg::PoseStamped>(
51  "goal_pose",
52  rclcpp::SystemDefaultsQoS(),
53  std::bind(&NavigateToPoseNavigator::onGoalPoseReceived, this, std::placeholders::_1));
54  return true;
55 }
56 
57 std::string
59  rclcpp_lifecycle::LifecycleNode::WeakPtr parent_node)
60 {
61  std::string default_bt_xml_filename;
62  auto node = parent_node.lock();
63 
64  if (!node->has_parameter("default_nav_to_pose_bt_xml")) {
65  std::string pkg_share_dir =
66  ament_index_cpp::get_package_share_directory("nav2_bt_navigator");
67  node->declare_parameter<std::string>(
68  "default_nav_to_pose_bt_xml",
69  pkg_share_dir +
70  "/behavior_trees/navigate_to_pose_w_replanning_and_recovery.xml");
71  }
72 
73  node->get_parameter("default_nav_to_pose_bt_xml", default_bt_xml_filename);
74 
75  return default_bt_xml_filename;
76 }
77 
78 bool
80 {
81  goal_sub_.reset();
82  self_client_.reset();
83  return true;
84 }
85 
86 bool
87 NavigateToPoseNavigator::goalReceived(ActionT::Goal::ConstSharedPtr goal)
88 {
89  auto bt_xml_filename = goal->behavior_tree;
90 
91  if (!bt_action_server_->loadBehaviorTree(bt_xml_filename)) {
92  RCLCPP_ERROR(
93  logger_, "BT file not found: %s. Navigation canceled.",
94  bt_xml_filename.c_str());
95  return false;
96  }
97 
98  initializeGoalPose(goal);
99 
100  return true;
101 }
102 
103 void
105  typename ActionT::Result::SharedPtr /*result*/,
106  const nav2_behavior_tree::BtStatus /*final_bt_status*/)
107 {
108 }
109 
110 void
112 {
113  // action server feedback (pose, duration of task,
114  // number of recoveries, and distance remaining to goal)
115  auto feedback_msg = std::make_shared<ActionT::Feedback>();
116 
117  geometry_msgs::msg::PoseStamped current_pose;
118  nav2_util::getCurrentPose(
119  current_pose, *feedback_utils_.tf,
120  feedback_utils_.global_frame, feedback_utils_.robot_frame,
121  feedback_utils_.transform_tolerance);
122 
123  auto blackboard = bt_action_server_->getBlackboard();
124 
125  try {
126  // Get current path points
127  nav_msgs::msg::Path current_path;
128  blackboard->get<nav_msgs::msg::Path>(path_blackboard_id_, current_path);
129 
130  // Find the closest pose to current pose on global path
131  auto find_closest_pose_idx =
132  [&current_pose, &current_path]() {
133  size_t closest_pose_idx = 0;
134  double curr_min_dist = std::numeric_limits<double>::max();
135  for (size_t curr_idx = 0; curr_idx < current_path.poses.size(); ++curr_idx) {
136  double curr_dist = nav2_util::geometry_utils::euclidean_distance(
137  current_pose, current_path.poses[curr_idx]);
138  if (curr_dist < curr_min_dist) {
139  curr_min_dist = curr_dist;
140  closest_pose_idx = curr_idx;
141  }
142  }
143  return closest_pose_idx;
144  };
145 
146  // Calculate distance on the path
147  double distance_remaining =
148  nav2_util::geometry_utils::calculate_path_length(current_path, find_closest_pose_idx());
149 
150  // Default value for time remaining
151  rclcpp::Duration estimated_time_remaining = rclcpp::Duration::from_seconds(0.0);
152 
153  // Get current speed
154  geometry_msgs::msg::Twist current_odom = odom_smoother_->getTwist();
155  double current_linear_speed = std::hypot(current_odom.linear.x, current_odom.linear.y);
156 
157  // Calculate estimated time taken to goal if speed is higher than 1cm/s
158  // and at least 10cm to go
159  if ((std::abs(current_linear_speed) > 0.01) && (distance_remaining > 0.1)) {
160  estimated_time_remaining =
161  rclcpp::Duration::from_seconds(distance_remaining / std::abs(current_linear_speed));
162  }
163 
164  feedback_msg->distance_remaining = distance_remaining;
165  feedback_msg->estimated_time_remaining = estimated_time_remaining;
166  } catch (...) {
167  // Ignore
168  }
169 
170  int recovery_count = 0;
171  blackboard->get<int>("number_recoveries", recovery_count);
172  feedback_msg->number_of_recoveries = recovery_count;
173  feedback_msg->current_pose = current_pose;
174  feedback_msg->navigation_time = clock_->now() - start_time_;
175 
176  bt_action_server_->publishFeedback(feedback_msg);
177 }
178 
179 void
180 NavigateToPoseNavigator::onPreempt(ActionT::Goal::ConstSharedPtr goal)
181 {
182  RCLCPP_INFO(logger_, "Received goal preemption request");
183 
184  if (goal->behavior_tree == bt_action_server_->getCurrentBTFilename() ||
185  (goal->behavior_tree.empty() &&
186  bt_action_server_->getCurrentBTFilename() == bt_action_server_->getDefaultBTFilename()))
187  {
188  // if pending goal requests the same BT as the current goal, accept the pending goal
189  // if pending goal has an empty behavior_tree field, it requests the default BT file
190  // accept the pending goal if the current goal is running the default BT file
191  initializeGoalPose(bt_action_server_->acceptPendingGoal());
192  } else {
193  RCLCPP_WARN(
194  logger_,
195  "Preemption request was rejected since the requested BT XML file is not the same "
196  "as the one that the current goal is executing. Preemption with a new BT is invalid "
197  "since it would require cancellation of the previous goal instead of true preemption."
198  "\nCancel the current goal and send a new action request if you want to use a "
199  "different BT XML file. For now, continuing to track the last goal until completion.");
200  bt_action_server_->terminatePendingGoal();
201  }
202 }
203 
204 void
205 NavigateToPoseNavigator::initializeGoalPose(ActionT::Goal::ConstSharedPtr goal)
206 {
207  geometry_msgs::msg::PoseStamped current_pose;
208  nav2_util::getCurrentPose(
209  current_pose, *feedback_utils_.tf,
210  feedback_utils_.global_frame, feedback_utils_.robot_frame,
211  feedback_utils_.transform_tolerance);
212 
213  RCLCPP_INFO(
214  logger_, "Begin navigating from current location (%.2f, %.2f) to (%.2f, %.2f)",
215  current_pose.pose.position.x, current_pose.pose.position.y,
216  goal->pose.pose.position.x, goal->pose.pose.position.y);
217 
218  // Reset state for new action feedback
219  start_time_ = clock_->now();
220  auto blackboard = bt_action_server_->getBlackboard();
221  blackboard->set<int>("number_recoveries", 0); // NOLINT
222 
223  // Update the goal pose on the blackboard
224  blackboard->set<geometry_msgs::msg::PoseStamped>(goal_blackboard_id_, goal->pose);
225 }
226 
227 void
228 NavigateToPoseNavigator::onGoalPoseReceived(const geometry_msgs::msg::PoseStamped::SharedPtr pose)
229 {
230  ActionT::Goal goal;
231  goal.pose = *pose;
232  self_client_->async_send_goal(goal);
233 }
234 
235 } // namespace nav2_bt_navigator
void goalCompleted(typename ActionT::Result::SharedPtr result, const nav2_behavior_tree::BtStatus final_bt_status) override
A callback that is called when a the action is completed, can fill in action result message or indica...
void onGoalPoseReceived(const geometry_msgs::msg::PoseStamped::SharedPtr pose)
A subscription and callback to handle the topic-based goal published from rviz.
void onPreempt(ActionT::Goal::ConstSharedPtr goal) override
A callback that is called when a preempt is requested.
bool goalReceived(ActionT::Goal::ConstSharedPtr goal) override
A callback to be called when a new goal is received by the BT action server Can be used to check if g...
bool configure(rclcpp_lifecycle::LifecycleNode::WeakPtr node, std::shared_ptr< nav2_util::OdomSmoother > odom_smoother) override
A configure state transition to configure navigator's state.
std::string getName() override
Get action name for this navigator.
void initializeGoalPose(ActionT::Goal::ConstSharedPtr goal)
Goal pose initialization on the blackboard.
std::string getDefaultBTFilepath(rclcpp_lifecycle::LifecycleNode::WeakPtr node) override
Get navigator's default BT.
void onLoop() override
A callback that defines execution that happens on one iteration through the BT Can be used to publish...
bool cleanup() override
A cleanup state transition to remove memory allocated.