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