Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
timed_behavior.hpp
1 // Copyright (c) 2018 Intel Corporation
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 #ifndef NAV2_BEHAVIORS__TIMED_BEHAVIOR_HPP_
16 #define NAV2_BEHAVIORS__TIMED_BEHAVIOR_HPP_
17 
18 
19 #include <cstdint>
20 #include <memory>
21 #include <string>
22 #include <cmath>
23 #include <chrono>
24 #include <ctime>
25 #include <thread>
26 #include <utility>
27 
28 #include "rclcpp/rclcpp.hpp"
29 #include "nav2_ros_common/tf2_factories.hpp"
30 #include "geometry_msgs/msg/twist.hpp"
31 #include "nav2_util/robot_utils.hpp"
32 #include "nav2_util/twist_publisher.hpp"
33 #include "nav2_ros_common/simple_action_server.hpp"
34 #include "nav2_ros_common/rate.hpp"
35 #include "nav2_core/behavior.hpp"
36 #pragma GCC diagnostic push
37 #pragma GCC diagnostic ignored "-Wpedantic"
38 #include "tf2/utils.hpp"
39 #pragma GCC diagnostic pop
40 
41 
42 namespace nav2_behaviors
43 {
44 
45 enum class Status : int8_t
46 {
47  SUCCEEDED = 1,
48  FAILED = 2,
49  RUNNING = 3,
50 };
51 
53 {
54  Status status;
55  uint16_t error_code{0};
56  std::string error_msg;
57 };
58 
59 using namespace std::chrono_literals; //NOLINT
60 
65 template<typename ActionT>
67 {
68 public:
70 
75  : action_server_(nullptr),
76  cycle_frequency_(10.0),
77  enabled_(false)
78  {
79  }
80 
81  virtual ~TimedBehavior() = default;
82 
83  // Derived classes can override this method to catch the command and perform some checks
84  // before getting into the main loop. The method will only be called
85  // once and should return SUCCEEDED otherwise behavior will return FAILED.
86  virtual ResultStatus onRun(const std::shared_ptr<const typename ActionT::Goal> command) = 0;
87 
88 
89  // This is the method derived classes should mainly implement
90  // and will be called cyclically while it returns RUNNING.
91  // Implement the behavior such that it runs some unit of work on each call
92  // and provides a status. The Behavior will finish once SUCCEEDED is returned
93  // It's up to the derived class to define the final commanded velocity.
94  virtual ResultStatus onCycleUpdate() = 0;
95 
96  // an opportunity for derived classes to do something on configuration
97  // if they chose
98  virtual void onConfigure()
99  {
100  }
101 
102  // an opportunity for derived classes to do something on cleanup
103  // if they chose
104  virtual void onCleanup()
105  {
106  }
107 
108  // an opportunity for a derived class to do something on action completion
109  virtual void onActionCompletion(std::shared_ptr<typename ActionT::Result>/*result*/)
110  {
111  }
112 
113  // configure the server on lifecycle setup
114  void configure(
115  const nav2::LifecycleNode::WeakPtr & parent,
116  const std::string & name, nav2::TransformBuffer::SharedPtr tf,
117  std::shared_ptr<nav2_costmap_2d::CostmapTopicCollisionChecker> local_collision_checker,
118  std::shared_ptr<nav2_costmap_2d::CostmapTopicCollisionChecker> global_collision_checker)
119  override
120  {
121  node_ = parent;
122  auto node = node_.lock();
123  logger_ = node->get_logger();
124  clock_ = node->get_clock();
125 
126  RCLCPP_INFO(logger_, "Configuring %s", name.c_str());
127 
128  behavior_name_ = name;
129  tf_ = tf;
130 
131  node->get_parameter("cycle_frequency", cycle_frequency_);
132  node->get_parameter("local_frame", local_frame_);
133  node->get_parameter("global_frame", global_frame_);
134  node->get_parameter("robot_base_frame", robot_base_frame_);
135  transform_staleness_threshold_ = node->declare_or_get_parameter(
136  "transform_staleness_threshold", 0.0);
137 
138  action_server_ = node->create_action_server<ActionT>(
139  behavior_name_,
140  std::bind(&TimedBehavior::execute, this), nullptr, nullptr, std::chrono::milliseconds(
141  500), false);
142 
143  local_collision_checker_ = local_collision_checker;
144  global_collision_checker_ = global_collision_checker;
145 
146  vel_pub_ = std::make_unique<nav2_util::TwistPublisher>(node, "cmd_vel");
147 
148  onConfigure();
149  }
150 
151  // Cleanup server on lifecycle transition
152  void cleanup() override
153  {
154  action_server_.reset();
155  vel_pub_.reset();
156  onCleanup();
157  }
158 
159  // Activate server on lifecycle transition
160  void activate() override
161  {
162  RCLCPP_INFO(logger_, "Activating %s", behavior_name_.c_str());
163 
164  vel_pub_->on_activate();
165  action_server_->activate();
166  enabled_ = true;
167  }
168 
169  // Deactivate server on lifecycle transition
170  void deactivate() override
171  {
172  vel_pub_->on_deactivate();
173  action_server_->deactivate();
174  enabled_ = false;
175  }
176 
177 protected:
183  bool getCurrentPoseChecked(geometry_msgs::msg::PoseStamped & pose)
184  {
185  if (!nav2_util::getFreshPose(
186  *tf_, local_frame_, robot_base_frame_, clock_->now(),
187  transform_staleness_threshold_, pose))
188  {
189  return false;
190  }
191  return true;
192  }
193 
194  nav2::LifecycleNode::WeakPtr node_;
195 
196  std::string behavior_name_;
197  std::unique_ptr<nav2_util::TwistPublisher> vel_pub_;
198  typename ActionServer::SharedPtr action_server_;
199  std::shared_ptr<nav2_costmap_2d::CostmapTopicCollisionChecker> local_collision_checker_;
200  std::shared_ptr<nav2_costmap_2d::CostmapTopicCollisionChecker> global_collision_checker_;
201  nav2::TransformBuffer::SharedPtr tf_;
202 
203  double cycle_frequency_;
204  double enabled_;
205  std::string local_frame_;
206  std::string global_frame_;
207  std::string robot_base_frame_;
208  double transform_staleness_threshold_{0.0};
209  rclcpp::Duration elapsed_time_{0, 0};
210 
211  // Clock
212  rclcpp::Clock::SharedPtr clock_;
213 
214  // Logger
215  rclcpp::Logger logger_{rclcpp::get_logger("nav2_behaviors")};
216 
217  // Main execution callbacks for the action server implementation calling the Behavior's
218  // onRun and cycle functions to execute a specific behavior
219  void execute()
220  {
221  RCLCPP_INFO(logger_, "Running %s", behavior_name_.c_str());
222 
223  if (!enabled_) {
224  RCLCPP_WARN(
225  logger_,
226  "Called while inactive, ignoring request.");
227  return;
228  }
229 
230  // Initialize the ActionT result
231  auto result = std::make_shared<typename ActionT::Result>();
232 
233  ResultStatus on_run_result = onRun(action_server_->get_current_goal());
234  if (on_run_result.status != Status::SUCCEEDED) {
235  result->error_code = on_run_result.error_code;
236  result->error_msg = on_run_result.error_msg;
237  RCLCPP_INFO(
238  logger_, "Initial checks failed for %s - %s", behavior_name_.c_str(),
239  on_run_result.error_msg.c_str());
240  action_server_->terminate_current(result);
241  return;
242  }
243 
244  auto start_time = clock_->now();
245  auto node = node_.lock();
246  if (!node) {
247  RCLCPP_ERROR(
248  logger_,
249  "Failed to run %s because the parent node is no longer available.",
250  behavior_name_.c_str());
251  result->error_msg = behavior_name_ + " failed: parent node expired";
252  result->total_elapsed_time = clock_->now() - start_time;
253  onActionCompletion(result);
254  action_server_->terminate_current(result);
255  return;
256  }
257  nav2::Rate loop_rate(node, cycle_frequency_);
258 
259  while (rclcpp::ok()) {
260  elapsed_time_ = clock_->now() - start_time;
261  // TODO(orduno) #868 Enable preempting a Behavior on-the-fly without stopping
262  if (action_server_->is_preempt_requested()) {
263  RCLCPP_ERROR(
264  logger_, "Received a preemption request for %s,"
265  " however feature is currently not implemented. Aborting and stopping.",
266  behavior_name_.c_str());
267  stopRobot();
268  result->total_elapsed_time = clock_->now() - start_time;
269  onActionCompletion(result);
270  action_server_->terminate_current(result);
271  return;
272  }
273 
274  if (action_server_->is_cancel_requested()) {
275  RCLCPP_INFO(logger_, "Canceling %s", behavior_name_.c_str());
276  stopRobot();
277  result->total_elapsed_time = elapsed_time_;
278  onActionCompletion(result);
279  action_server_->terminate_all(result);
280  return;
281  }
282 
283  ResultStatus on_cycle_update_result = onCycleUpdate();
284  switch (on_cycle_update_result.status) {
285  case Status::SUCCEEDED:
286  RCLCPP_INFO(
287  logger_,
288  "%s completed successfully", behavior_name_.c_str());
289  result->total_elapsed_time = clock_->now() - start_time;
290  onActionCompletion(result);
291  action_server_->succeeded_current(result);
292  return;
293 
294  case Status::FAILED:
295  result->error_code = on_cycle_update_result.error_code;
296  result->error_msg = behavior_name_ + " failed:" + on_cycle_update_result.error_msg;
297  RCLCPP_WARN(logger_, "%s", result->error_msg.c_str());
298  result->total_elapsed_time = clock_->now() - start_time;
299  onActionCompletion(result);
300  action_server_->terminate_current(result);
301  return;
302 
303  case Status::RUNNING:
304 
305  default:
306  loop_rate.sleep();
307  break;
308  }
309  }
310  }
311 
312  // Stop the robot with a commanded velocity
313  void stopRobot()
314  {
315  auto cmd_vel = std::make_unique<geometry_msgs::msg::TwistStamped>();
316  cmd_vel->header.frame_id = robot_base_frame_;
317  cmd_vel->header.stamp = clock_->now();
318  cmd_vel->twist.linear.x = 0.0;
319  cmd_vel->twist.linear.y = 0.0;
320  cmd_vel->twist.angular.z = 0.0;
321 
322  vel_pub_->publish(std::move(cmd_vel));
323  }
324 };
325 
326 } // namespace nav2_behaviors
327 
328 #endif // NAV2_BEHAVIORS__TIMED_BEHAVIOR_HPP_
A sim-time-aware rate for Nav2 loops.
Definition: rate.hpp:61
An action server wrapper to make applications simpler using Actions.
TimedBehavior()
A TimedBehavior constructor.
bool getCurrentPoseChecked(geometry_msgs::msg::PoseStamped &pose)
Get one checked robot pose in the local frame, preserving the TF timestamp.
void deactivate() override
Method to deactivate Behavior and any threads involved in execution.
void activate() override
Method to active Behavior and any threads involved in execution.
void configure(const nav2::LifecycleNode::WeakPtr &parent, const std::string &name, nav2::TransformBuffer::SharedPtr tf, std::shared_ptr< nav2_costmap_2d::CostmapTopicCollisionChecker > local_collision_checker, std::shared_ptr< nav2_costmap_2d::CostmapTopicCollisionChecker > global_collision_checker) override
void cleanup() override
Method to cleanup resources used on shutdown.
Abstract interface for behaviors to adhere to with pluginlib.
Definition: behavior.hpp:42