Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
navigate_through_poses.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 <stdexcept>
21 #include "nav2_bt_navigator/navigators/navigate_through_poses.hpp"
22 
23 namespace nav2_bt_navigator
24 {
25 
26 bool
28  rclcpp_lifecycle::LifecycleNode::WeakPtr parent_node,
29  std::shared_ptr<nav2_util::OdomSmoother> odom_smoother)
30 {
31  start_time_ = rclcpp::Time(0);
32  auto node = parent_node.lock();
33 
34  if (!node->has_parameter("goals_blackboard_id")) {
35  node->declare_parameter("goals_blackboard_id", std::string("goals"));
36  }
37 
38  goals_blackboard_id_ = node->get_parameter("goals_blackboard_id").as_string();
39 
40  if (!node->has_parameter("path_blackboard_id")) {
41  node->declare_parameter("path_blackboard_id", std::string("path"));
42  }
43 
44  path_blackboard_id_ = node->get_parameter("path_blackboard_id").as_string();
45 
46  // Odometry smoother object for getting current speed
47  odom_smoother_ = odom_smoother;
48 
49  if (!node->has_parameter(getName() + ".enable_groot_monitoring")) {
50  node->declare_parameter(getName() + ".enable_groot_monitoring", false);
51  }
52 
53  if (!node->has_parameter(getName() + ".groot_server_port")) {
54  node->declare_parameter(getName() + ".groot_server_port", 1669);
55  }
56 
57  bt_action_server_->setGrootMonitoring(
58  node->get_parameter(getName() + ".enable_groot_monitoring").as_bool(),
59  node->get_parameter(getName() + ".groot_server_port").as_int());
60 
61  return true;
62 }
63 
64 std::string
66  rclcpp_lifecycle::LifecycleNode::WeakPtr parent_node)
67 {
68  std::string default_bt_xml_filename;
69  auto node = parent_node.lock();
70 
71  if (!node->has_parameter("default_nav_through_poses_bt_xml")) {
72  std::string pkg_share_dir =
73  ament_index_cpp::get_package_share_directory("nav2_bt_navigator");
74  node->declare_parameter<std::string>(
75  "default_nav_through_poses_bt_xml",
76  pkg_share_dir +
77  "/behavior_trees/navigate_through_poses_w_replanning_and_recovery.xml");
78  }
79 
80  node->get_parameter("default_nav_through_poses_bt_xml", default_bt_xml_filename);
81 
82  return default_bt_xml_filename;
83 }
84 
85 bool
86 NavigateThroughPosesNavigator::goalReceived(ActionT::Goal::ConstSharedPtr goal)
87 {
88  auto bt_xml_filename = goal->behavior_tree;
89 
90  if (!bt_action_server_->loadBehaviorTree(bt_xml_filename)) {
91  RCLCPP_ERROR(
92  logger_, "Error loading XML file: %s. Navigation canceled.",
93  bt_xml_filename.c_str());
94  return false;
95  }
96 
97  return initializeGoalPoses(goal);
98 }
99 
100 void
102  typename ActionT::Result::SharedPtr /*result*/,
103  const nav2_behavior_tree::BtStatus /*final_bt_status*/)
104 {
105 }
106 
107 void
109 {
110  using namespace nav2_util::geometry_utils; // NOLINT
111 
112  // action server feedback (pose, duration of task,
113  // number of recoveries, and distance remaining to goal, etc)
114  auto feedback_msg = std::make_shared<ActionT::Feedback>();
115 
116  auto blackboard = bt_action_server_->getBlackboard();
117 
118  Goals goal_poses;
119  [[maybe_unused]] auto res = blackboard->get(goals_blackboard_id_, goal_poses);
120 
121  if (goal_poses.size() == 0) {
122  bt_action_server_->publishFeedback(feedback_msg);
123  return;
124  }
125 
126  geometry_msgs::msg::PoseStamped current_pose;
127  if (!nav2_util::getCurrentPose(
128  current_pose, *feedback_utils_.tf,
129  feedback_utils_.global_frame, feedback_utils_.robot_frame,
130  feedback_utils_.transform_tolerance))
131  {
132  RCLCPP_ERROR(logger_, "Robot pose is not available.");
133  return;
134  }
135 
136  try {
137  // Get current path points
138  nav_msgs::msg::Path current_path;
139  if (!blackboard->get(path_blackboard_id_, current_path) || current_path.poses.size() == 0u) {
140  // If no path set yet or not meaningful, can't compute ETA or dist remaining yet.
141  throw std::exception();
142  }
143 
144  // Find the closest pose to current pose on global path
145  auto find_closest_pose_idx =
146  [&current_pose, &current_path]() {
147  size_t closest_pose_idx = 0;
148  double curr_min_dist = std::numeric_limits<double>::max();
149  for (size_t curr_idx = 0; curr_idx < current_path.poses.size(); ++curr_idx) {
150  double curr_dist = nav2_util::geometry_utils::euclidean_distance(
151  current_pose, current_path.poses[curr_idx]);
152  if (curr_dist < curr_min_dist) {
153  curr_min_dist = curr_dist;
154  closest_pose_idx = curr_idx;
155  }
156  }
157  return closest_pose_idx;
158  };
159 
160  // Calculate distance on the path
161  double distance_remaining =
162  nav2_util::geometry_utils::calculate_path_length(current_path, find_closest_pose_idx());
163 
164  // Default value for time remaining
165  rclcpp::Duration estimated_time_remaining = rclcpp::Duration::from_seconds(0.0);
166 
167  // Get current speed
168  geometry_msgs::msg::Twist current_odom = odom_smoother_->getTwist();
169  double current_linear_speed = std::hypot(current_odom.linear.x, current_odom.linear.y);
170 
171  // Calculate estimated time taken to goal if speed is higher than 1cm/s
172  // and at least 10cm to go
173  if ((std::abs(current_linear_speed) > 0.01) && (distance_remaining > 0.1)) {
174  estimated_time_remaining =
175  rclcpp::Duration::from_seconds(distance_remaining / std::abs(current_linear_speed));
176  }
177 
178  feedback_msg->distance_remaining = distance_remaining;
179  feedback_msg->estimated_time_remaining = estimated_time_remaining;
180  } catch (...) {
181  // Ignore
182  }
183 
184  int recovery_count = 0;
185  res = blackboard->get("number_recoveries", recovery_count);
186  feedback_msg->number_of_recoveries = recovery_count;
187  feedback_msg->current_pose = current_pose;
188  feedback_msg->navigation_time = clock_->now() - start_time_;
189  feedback_msg->number_of_poses_remaining = goal_poses.size();
190 
191  bt_action_server_->publishFeedback(feedback_msg);
192 }
193 
194 void
195 NavigateThroughPosesNavigator::onPreempt(ActionT::Goal::ConstSharedPtr goal)
196 {
197  RCLCPP_INFO(logger_, "Received goal preemption request");
198 
199  if (goal->behavior_tree == bt_action_server_->getCurrentBTFilename() ||
200  (goal->behavior_tree.empty() &&
201  bt_action_server_->getCurrentBTFilename() == bt_action_server_->getDefaultBTFilename()))
202  {
203  // if pending goal requests the same BT as the current goal, accept the pending goal
204  // if pending goal has an empty behavior_tree field, it requests the default BT file
205  // accept the pending goal if the current goal is running the default BT file
206  if (!initializeGoalPoses(bt_action_server_->acceptPendingGoal())) {
207  throw std::runtime_error(
208  "Preemption request was rejected since the goal poses could not be "
209  "transformed.");
210  }
211  } else {
212  RCLCPP_WARN(
213  logger_,
214  "Preemption request was rejected since the requested BT XML file is not the same "
215  "as the one that the current goal is executing. Preemption with a new BT is invalid "
216  "since it would require cancellation of the previous goal instead of true preemption."
217  "\nCancel the current goal and send a new action request if you want to use a "
218  "different BT XML file. For now, continuing to track the last goal until completion.");
219  bt_action_server_->terminatePendingGoal();
220  }
221 }
222 
223 bool
224 NavigateThroughPosesNavigator::initializeGoalPoses(ActionT::Goal::ConstSharedPtr goal)
225 {
226  Goals goal_poses = goal->poses;
227  for (auto & goal_pose : goal_poses) {
228  if (!nav2_util::transformPoseInTargetFrame(
229  goal_pose, goal_pose, *feedback_utils_.tf, feedback_utils_.global_frame,
230  feedback_utils_.transform_tolerance))
231  {
232  RCLCPP_ERROR(
233  logger_,
234  "Failed to transform a goal pose provided with frame_id '%s' to the global frame '%s'.",
235  goal_pose.header.frame_id.c_str(), feedback_utils_.global_frame.c_str());
236  return false;
237  }
238  }
239 
240  if (goal_poses.size() > 0) {
241  RCLCPP_INFO(
242  logger_, "Begin navigating from current location through %zu poses to (%.2f, %.2f)",
243  goal_poses.size(), goal_poses.back().pose.position.x, goal_poses.back().pose.position.y);
244  }
245 
246  // Reset state for new action feedback
247  start_time_ = clock_->now();
248  auto blackboard = bt_action_server_->getBlackboard();
249  blackboard->set("number_recoveries", 0); // NOLINT
250 
251  // Update the goal pose on the blackboard
252  blackboard->set<Goals>(goals_blackboard_id_, std::move(goal_poses));
253 
254  return true;
255 }
256 
257 } // namespace nav2_bt_navigator
258 
259 #include "pluginlib/class_list_macros.hpp"
260 PLUGINLIB_EXPORT_CLASS(
A navigator for navigating to a a bunch of intermediary poses.
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.
void onPreempt(ActionT::Goal::ConstSharedPtr goal) override
A callback that is called when a preempt is requested.
std::string getName() override
Get action name for this navigator.
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...
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...
std::string getDefaultBTFilepath(rclcpp_lifecycle::LifecycleNode::WeakPtr node) override
Get navigator's default BT.
bool initializeGoalPoses(ActionT::Goal::ConstSharedPtr goal)
Goal pose initialization on the blackboard.
void onLoop() override
A callback that defines execution that happens on one iteration through the BT Can be used to publish...
Navigator interface to allow navigators to be stored in a vector and accessed via pluginlib due to te...