Nav2 Navigation Stack - rolling  main
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 #include "nav2_util/path_utils.hpp"
21 #include "nav2_msgs/msg/tracking_feedback.hpp"
22 #include "nav2_ros_common/node_utils.hpp"
23 
24 namespace nav2_bt_navigator
25 {
26 
27 bool
29  nav2::LifecycleNode::WeakPtr parent_node,
30  std::shared_ptr<nav2_util::OdomSmoother> odom_smoother)
31 {
32  start_time_ = rclcpp::Time(0);
33  auto node = parent_node.lock();
34 
35  goal_blackboard_id_ = node->declare_or_get_parameter(
36  getName() + ".goal_blackboard_id",
37  std::string("goal"));
38  path_blackboard_id_ = node->declare_or_get_parameter(
39  getName() + ".path_blackboard_id",
40  std::string("path"));
41  tracking_feedback_blackboard_id_ = node->declare_or_get_parameter(
42  getName() + ".tracking_feedback_blackboard_id",
43  std::string("tracking_feedback"));
44 
45  search_window_ = node->declare_or_get_parameter(getName() + "search_window", 2.0);
46  transform_staleness_threshold_ = node->declare_or_get_parameter(
47  getName() + ".transform_staleness_threshold", 0.0);
48 
49  // Odometry smoother object for getting current speed
50  odom_smoother_ = odom_smoother;
51 
52  self_client_ = node->create_action_client<ActionT>(getName());
53 
54  goal_sub_ = node->create_subscription<geometry_msgs::msg::PoseStamped>(
55  "goal_pose",
56  std::bind(&NavigateToPoseNavigator::onGoalPoseReceived, this, std::placeholders::_1));
57 
58  bool enable_groot_monitoring =
59  node->declare_or_get_parameter(getName() + ".enable_groot_monitoring", false);
60  int groot_server_port =
61  node->declare_or_get_parameter(getName() + ".groot_server_port", 1669);
62 
63  bt_action_server_->setGrootMonitoring(
64  enable_groot_monitoring,
65  groot_server_port);
66 
67  return true;
68 }
69 
70 std::string
72  nav2::LifecycleNode::WeakPtr parent_node)
73 {
74  auto node = parent_node.lock();
75  std::string pkg_share_dir =
76  nav2::get_package_share_directory("nav2_bt_navigator");
77 
78  auto default_bt_xml_filename = node->declare_or_get_parameter(
79  "default_nav_to_pose_bt_xml",
80  pkg_share_dir +
81  "/behavior_trees/navigate_to_pose_w_replanning_and_recovery.xml");
82 
83  return default_bt_xml_filename;
84 }
85 
86 bool
88 {
89  goal_sub_.reset();
90  self_client_.reset();
91  return true;
92 }
93 
94 bool
95 NavigateToPoseNavigator::goalReceived(ActionT::Goal::ConstSharedPtr goal)
96 {
97  return initializeGoalPose(goal);
98 }
99 
100 void
102  typename ActionT::Result::SharedPtr result,
103  nav2_behavior_tree::BtStatus & /*final_bt_status*/)
104 {
105  if (result->error_code == 0) {
106  if (bt_action_server_->populateInternalError(result)) {
107  RCLCPP_WARN(
108  logger_,
109  "NavigateToPoseNavigator::goalCompleted, internal error %d:%s.",
110  result->error_code,
111  result->error_msg.c_str());
112  }
113  } else {
114  RCLCPP_WARN(
115  logger_, "NavigateToPoseNavigator::goalCompleted error %d:%s.",
116  result->error_code,
117  result->error_msg.c_str());
118  }
119 }
120 
121 void
123 {
124  // action server feedback (pose, duration of task,
125  // number of recoveries, and distance remaining to goal)
126  auto feedback_msg = std::make_shared<ActionT::Feedback>();
127 
128  geometry_msgs::msg::PoseStamped current_pose;
129  if (!nav2_util::getFreshPose(
130  *feedback_utils_.tf, feedback_utils_.global_frame,
131  feedback_utils_.robot_frame, clock_->now(),
132  transform_staleness_threshold_, current_pose))
133  {
134  RCLCPP_ERROR(logger_, "Robot pose is not available.");
135  return;
136  }
137 
138  auto blackboard = bt_action_server_->getBlackboard();
139 
140  // Get current path points
141  nav_msgs::msg::Path current_path;
142  auto res = blackboard->get(path_blackboard_id_, current_path);
143  if (res && current_path.poses.size() > 0u) {
144  // Reset start index if path or goal is updated
145  if (nav2_util::isPathUpdated(current_path, previous_path_) ||
146  nav2_util::isGoalUpdated(current_path, previous_path_) ||
147  previous_path_.poses.size() == 0u)
148  {
149  start_index_ = 0;
150  previous_path_ = current_path;
151  }
152  // Find the closest pose to current pose on global path
153  const auto path_search_result = nav2_util::distance_from_path(
154  current_path, current_pose.pose, start_index_, search_window_);
155 
156  // Calculate distance on the path
157  start_index_ = path_search_result.closest_segment_index;
158  double distance_remaining =
159  nav2_util::geometry_utils::calculate_path_length(current_path, start_index_);
160 
161  // Default value for time remaining
162  rclcpp::Duration estimated_time_remaining = rclcpp::Duration::from_seconds(0.0);
163 
164  // Get current speed
165  geometry_msgs::msg::Twist current_odom = odom_smoother_->getTwist();
166  double current_linear_speed = std::hypot(current_odom.linear.x, current_odom.linear.y);
167 
168  // Calculate estimated time taken to goal if speed is higher than 1cm/s
169  // and at least 10cm to go
170  if ((std::abs(current_linear_speed) > 0.01) && (distance_remaining > 0.1)) {
171  estimated_time_remaining =
172  rclcpp::Duration::from_seconds(distance_remaining / std::abs(current_linear_speed));
173  }
174 
175  feedback_msg->distance_remaining = distance_remaining;
176  feedback_msg->estimated_time_remaining = estimated_time_remaining;
177  }
178 
179  int recovery_count = 0;
180  res = blackboard->get("number_recoveries", recovery_count);
181  feedback_msg->number_of_recoveries = recovery_count;
182  feedback_msg->current_pose = current_pose;
183  feedback_msg->navigation_time = clock_->now() - start_time_;
184  nav2_msgs::msg::TrackingFeedback tracking_feedback;
185  res = blackboard->get(
186  tracking_feedback_blackboard_id_,
187  tracking_feedback);
188  feedback_msg->position_tracking_error = tracking_feedback.position_tracking_error;
189  feedback_msg->heading_tracking_error = tracking_feedback.heading_tracking_error;
190 
191  bt_action_server_->publishFeedback(feedback_msg);
192 }
193 
194 void
195 NavigateToPoseNavigator::onPreempt(ActionT::Goal::ConstSharedPtr goal)
196 {
197  RCLCPP_INFO(logger_, "Received goal preemption request");
198 
199  if (goal->behavior_tree == bt_action_server_->getCurrentBTFilenameOrID() ||
200  (goal->behavior_tree.empty() &&
201  bt_action_server_->getCurrentBTFilenameOrID() == bt_action_server_->getDefaultBTFilenameOrID()))
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 (!initializeGoalPose(bt_action_server_->acceptPendingGoal())) {
207  RCLCPP_WARN(
208  logger_,
209  "Preemption request was rejected since the goal pose could not be "
210  "transformed. For now, continuing to track the last goal until completion.");
211  bt_action_server_->terminatePendingGoal();
212  }
213  } else {
214  RCLCPP_WARN(
215  logger_,
216  "Preemption request was rejected since the requested BT XML file is not the same "
217  "as the one that the current goal is executing. Preemption with a new BT is invalid "
218  "since it would require cancellation of the previous goal instead of true preemption."
219  "\nCancel the current goal and send a new action request if you want to use a "
220  "different BT XML file. For now, continuing to track the last goal until completion.");
221  bt_action_server_->terminatePendingGoal();
222  }
223 }
224 
225 bool
226 NavigateToPoseNavigator::initializeGoalPose(ActionT::Goal::ConstSharedPtr goal)
227 {
228  geometry_msgs::msg::PoseStamped current_pose;
229  if (!nav2_util::getFreshPose(
230  *feedback_utils_.tf, feedback_utils_.global_frame,
231  feedback_utils_.robot_frame, clock_->now(),
232  transform_staleness_threshold_, current_pose))
233  {
234  bt_action_server_->setInternalError(
235  ActionT::Result::TF_ERROR,
236  "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  bt_action_server_->setInternalError(
246  ActionT::Result::TF_ERROR,
247  "Failed to transform a goal pose provided with frame_id '" +
248  goal->pose.header.frame_id +
249  "' to the global frame '" +
250  feedback_utils_.global_frame +
251  "'.");
252  return false;
253  }
254 
255  RCLCPP_INFO(
256  logger_, "Begin navigating from current location (%.2f, %.2f) to (%.2f, %.2f)",
257  current_pose.pose.position.x, current_pose.pose.position.y,
258  goal_pose.pose.position.x, goal_pose.pose.position.y);
259 
260  // Reset state for new action feedback
261  start_time_ = clock_->now();
262  auto blackboard = bt_action_server_->getBlackboard();
263  blackboard->set("number_recoveries", 0); // NOLINT
264  previous_path_ = nav_msgs::msg::Path();
265 
266  // Update the goal pose and path on the blackboard
267  blackboard->set(goal_blackboard_id_, goal_pose);
268  blackboard->set(path_blackboard_id_, nav_msgs::msg::Path());
269 
270  return true;
271 }
272 
273 void
275  const geometry_msgs::msg::PoseStamped::ConstSharedPtr & pose)
276 {
277  ActionT::Goal goal;
278  goal.pose = *pose;
279  self_client_->async_send_goal(goal);
280 }
281 
282 } // namespace nav2_bt_navigator
283 
284 #include "pluginlib/class_list_macros.hpp"
285 PLUGINLIB_EXPORT_CLASS(
A navigator for navigating to a specified pose.
void onPreempt(ActionT::Goal::ConstSharedPtr goal) override
A callback that is called when a preempt is requested.
void goalCompleted(typename ActionT::Result::SharedPtr result, 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...
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(nav2::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(nav2::LifecycleNode::WeakPtr node) override
Get navigator's default BT.
void onGoalPoseReceived(const geometry_msgs::msg::PoseStamped::ConstSharedPtr &pose)
A subscription and callback to handle the topic-based goal published from rviz.
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...