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