Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
is_stuck_condition.cpp
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 #include <string>
16 #include <chrono>
17 
18 #include "nav2_behavior_tree/plugins/condition/is_stuck_condition.hpp"
19 
20 using namespace std::chrono_literals; // NOLINT
21 
22 namespace nav2_behavior_tree
23 {
24 
25 IsStuckCondition::IsStuckCondition(
26  const std::string & condition_name,
27  const BT::NodeConfiguration & conf)
28 : BT::ConditionNode(condition_name, conf),
29  is_stuck_(false),
30  odom_history_size_(10),
31  current_accel_(0.0),
32  brake_accel_limit_(-10.0)
33 {
34  node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
35  callback_group_ = node_->create_callback_group(
36  rclcpp::CallbackGroupType::MutuallyExclusive,
37  false);
38  callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());
39  callback_group_executor_thread = std::thread([this]() {callback_group_executor_.spin();});
40 
41  rclcpp::SubscriptionOptions sub_option;
42  sub_option.callback_group = callback_group_;
43  odom_sub_ = node_->create_subscription<nav_msgs::msg::Odometry>(
44  "odom",
45  rclcpp::SystemDefaultsQoS(),
46  std::bind(&IsStuckCondition::onOdomReceived, this, std::placeholders::_1),
47  sub_option);
48 
49  RCLCPP_DEBUG(node_->get_logger(), "Initialized an IsStuckCondition BT node");
50 
51  RCLCPP_INFO_ONCE(node_->get_logger(), "Waiting on odometry");
52 }
53 
55 {
56  RCLCPP_DEBUG(node_->get_logger(), "Shutting down IsStuckCondition BT node");
57  callback_group_executor_.cancel();
58  callback_group_executor_thread.join();
59 }
60 
61 void IsStuckCondition::onOdomReceived(const typename nav_msgs::msg::Odometry::SharedPtr msg)
62 {
63  RCLCPP_INFO_ONCE(node_->get_logger(), "Got odometry");
64 
65  while (odom_history_.size() >= odom_history_size_) {
66  odom_history_.pop_front();
67  }
68 
69  odom_history_.push_back(*msg);
70 
71  // TODO(orduno) #383 Move the state calculation and is stuck to robot class
72  updateStates();
73 }
74 
75 BT::NodeStatus IsStuckCondition::tick()
76 {
77  // TODO(orduno) #383 Once check for is stuck and state calculations are moved to robot class
78  // this becomes
79  // if (robot_state_.isStuck()) {
80 
81  if (is_stuck_) {
82  logStuck("Robot got stuck!");
83  return BT::NodeStatus::SUCCESS; // Successfully detected a stuck condition
84  }
85 
86  logStuck("Robot is free");
87  return BT::NodeStatus::FAILURE; // Failed to detected a stuck condition
88 }
89 
90 void IsStuckCondition::logStuck(const std::string & msg) const
91 {
92  static std::string prev_msg;
93 
94  if (msg == prev_msg) {
95  return;
96  }
97 
98  RCLCPP_INFO(node_->get_logger(), "%s", msg.c_str());
99  prev_msg = msg;
100 }
101 
103 {
104  // Approximate acceleration
105  // TODO(orduno) #400 Smooth out velocity history for better accel approx.
106  if (odom_history_.size() > 2) {
107  auto curr_odom = odom_history_.end()[-1];
108  double curr_time = static_cast<double>(curr_odom.header.stamp.sec);
109  curr_time += (static_cast<double>(curr_odom.header.stamp.nanosec)) * 1e-9;
110 
111  auto prev_odom = odom_history_.end()[-2];
112  double prev_time = static_cast<double>(prev_odom.header.stamp.sec);
113  prev_time += (static_cast<double>(prev_odom.header.stamp.nanosec)) * 1e-9;
114 
115  double dt = curr_time - prev_time;
116  double vel_diff = static_cast<double>(
117  curr_odom.twist.twist.linear.x - prev_odom.twist.twist.linear.x);
118  current_accel_ = vel_diff / dt;
119  }
120 
121  is_stuck_ = isStuck();
122 }
123 
125 {
126  // TODO(orduno) #400 The robot getting stuck can result on different types of motion
127  // depending on the state prior to getting stuck (sudden change in accel, not moving at all,
128  // random oscillations, etc). For now, we only address the case where there is a sudden
129  // harsh deceleration. A better approach to capture all situations would be to do a forward
130  // simulation of the robot motion and compare it with the actual one.
131 
132  // Detect if robot bumped into something by checking for abnormal deceleration
133  if (current_accel_ < brake_accel_limit_) {
134  RCLCPP_DEBUG(
135  node_->get_logger(), "Current deceleration is beyond brake limit."
136  " brake limit: %.2f, current accel: %.2f", brake_accel_limit_, current_accel_);
137 
138  return true;
139  }
140 
141  return false;
142 }
143 
144 } // namespace nav2_behavior_tree
145 
146 #include "behaviortree_cpp_v3/bt_factory.h"
147 BT_REGISTER_NODES(factory)
148 {
149  factory.registerNodeType<nav2_behavior_tree::IsStuckCondition>("IsStuck");
150 }
A BT::ConditionNode that tracks robot odometry and returns SUCCESS if robot is stuck somewhere and FA...
void updateStates()
Function to approximate acceleration from the odom history.
bool isStuck()
Detect if robot bumped into something by checking for abnormal deceleration.
void onOdomReceived(const typename nav_msgs::msg::Odometry::SharedPtr msg)
Callback function for odom topic.
~IsStuckCondition() override
A destructor for nav2_behavior_tree::IsStuckCondition.
BT::NodeStatus tick() override
The main override required by a BT action.
void logStuck(const std::string &msg) const
Function to log status when robot is stuck/free.