Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
loop_rate.hpp
1 // Copyright (c) 2024 Angsa Robotics
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_BEHAVIOR_TREE__UTILS__LOOP_RATE_HPP_
16 #define NAV2_BEHAVIOR_TREE__UTILS__LOOP_RATE_HPP_
17 
18 #include <memory>
19 #include <utility>
20 
21 #include "rclcpp/rclcpp.hpp"
22 #include "behaviortree_cpp/bt_factory.h"
23 #include "behaviortree_cpp/behavior_tree.h"
24 
25 namespace nav2_behavior_tree
26 {
27 
28 class LoopRate
29 {
30 public:
31  LoopRate(
32  const rclcpp::Duration & period, BT::Tree * tree,
33  rclcpp::Clock::SharedPtr clock)
34  : clock_(std::move(clock)), period_(period),
35  last_interval_(clock_->now()), tree_(tree)
36  {}
37 
38  // Similar to rclcpp::WallRate::sleep() but using tree_->sleep()
39  bool sleep()
40  {
41  // Time coming into sleep
42  auto now = clock_->now();
43  // Time of next interval
44  auto next_interval = last_interval_ + period_;
45  // Detect backwards time flow
46  if (now < last_interval_) {
47  // Best thing to do is to set the next_interval to now + period
48  next_interval = now + period_;
49  }
50  // Update the interval
51  last_interval_ += period_;
52  // If the time_to_sleep is negative or zero, don't sleep
53  if (next_interval <= now) {
54  // If an entire cycle was missed then reset next interval.
55  // This might happen if the loop took more than a cycle.
56  // Or if time jumps forward.
57  if (now > next_interval + period_) {
58  last_interval_ = now + period_;
59  }
60  // Either way do not sleep and return false
61  return false;
62  }
63  // Sleep until the target time, preemptible by emitWakeUpSignal().
64  auto wake_up = tree_->wakeUpSignal();
65  const bool is_sim_time =
66  clock_->get_clock_type() == RCL_ROS_TIME &&
67  clock_->ros_time_is_active();
68  if (is_sim_time) {
69  // Sim time diverges from wall time — poll the target clock in short
70  // intervals while remaining interruptible by emitWakeUpSignal().
71  while (clock_->now() < next_interval) {
72  if (wake_up->waitFor(std::chrono::microseconds(500))) {
73  return true; // preempted
74  }
75  }
76  } else {
77  // Steady/system clock agrees with wall time — sleep for the exact
78  // remaining duration, still interruptible by emitWakeUpSignal().
79  auto remaining = next_interval - clock_->now();
80  wake_up->waitFor(
81  std::chrono::duration_cast<std::chrono::microseconds>(
82  std::chrono::nanoseconds(remaining.nanoseconds())));
83  }
84  return true;
85  }
86 
87  std::chrono::nanoseconds period() const
88  {
89  return std::chrono::nanoseconds(period_.nanoseconds());
90  }
91 
92 private:
93  rclcpp::Clock::SharedPtr clock_;
94  rclcpp::Duration period_;
95  rclcpp::Time last_interval_;
96  BT::Tree * tree_;
97 };
98 
99 } // namespace nav2_behavior_tree
100 
101 #endif // NAV2_BEHAVIOR_TREE__UTILS__LOOP_RATE_HPP_