From b08160c126b1b84ba11df25bfbd18a1a6f982bcd Mon Sep 17 00:00:00 2001
From: Mohammad Haghighipanah <39755151+mhpanah@users.noreply.github.com>
Date: Tue, 18 Jun 2019 14:48:33 -0700
Subject: [PATCH] Adding RecoveryNode to Behavior Tree (#857)

* added recovery node
---
 nav2_bt_navigator/CMakeLists.txt              |   1 +
 .../navigate_w_replanning_and_recovery.xml    |  36 +++---
 .../nav2_bt_navigator/recovery_node.hpp       |  57 ++++++++++
 .../src/navigate_to_pose_behavior_tree.cpp    |   4 +
 nav2_bt_navigator/src/recovery_node.cpp       | 107 ++++++++++++++++++
 5 files changed, 185 insertions(+), 20 deletions(-)
 create mode 100644 nav2_bt_navigator/include/nav2_bt_navigator/recovery_node.hpp
 create mode 100644 nav2_bt_navigator/src/recovery_node.cpp

diff --git a/nav2_bt_navigator/CMakeLists.txt b/nav2_bt_navigator/CMakeLists.txt
index 31fb3c91..5d4fbbcf 100644
--- a/nav2_bt_navigator/CMakeLists.txt
+++ b/nav2_bt_navigator/CMakeLists.txt
@@ -33,6 +33,7 @@ set(library_name ${executable_name}_core)
 add_library(${library_name} SHARED
   src/bt_navigator.cpp
   src/navigate_to_pose_behavior_tree.cpp
+  src/recovery_node.cpp
 )
 
 set(dependencies
diff --git a/nav2_bt_navigator/behavior_trees/navigate_w_replanning_and_recovery.xml b/nav2_bt_navigator/behavior_trees/navigate_w_replanning_and_recovery.xml
index 6a4b0bdd..4691193b 100644
--- a/nav2_bt_navigator/behavior_trees/navigate_w_replanning_and_recovery.xml
+++ b/nav2_bt_navigator/behavior_trees/navigate_w_replanning_and_recovery.xml
@@ -4,25 +4,21 @@
 -->
 <root main_tree_to_execute="MainTree">
   <BehaviorTree ID="MainTree">
-    <RetryUntilSuccesful name="retry_navigate" num_attempts="6">
-      <Fallback>
-        <Sequence>
-          <RateController hz="1.0">
-            <Fallback>
-              <GoalReached/>
-              <ComputePathToPose goal="${goal}" path="${path}"/>
-            </Fallback>
-          </RateController>
-          <FollowPath path="${path}"/>
-        </Sequence>
-        <ForceFailure>
-          <SequenceStar name="recovery">
-            <clearEntirelyCostmapServiceRequest service_name="/local_costmap/clear_entirely_local_costmap"/>
-            <clearEntirelyCostmapServiceRequest service_name="/global_costmap/clear_entirely_global_costmap"/>
-            <Spin/>
-          </SequenceStar>
-        </ForceFailure>
-      </Fallback>
-    </RetryUntilSuccesful>
+    <RecoveryNode number_of_retries="6">
+      <Sequence name="NavigateWithReplanning">
+        <RateController hz="1.0">
+          <Fallback>
+            <GoalReached/>
+             <ComputePathToPose goal="${goal}" path="${path}"/>
+          </Fallback>
+        </RateController>
+        <FollowPath path="${path}"/>
+      </Sequence>
+      <SequenceStar name="RecoveryActions">
+        <clearEntirelyCostmapServiceRequest service_name="/local_costmap/clear_entirely_local_costmap"/>
+        <clearEntirelyCostmapServiceRequest service_name="/global_costmap/clear_entirely_global_costmap"/>
+        <Spin/>
+      </SequenceStar>
+    </RecoveryNode>
   </BehaviorTree>
 </root>
diff --git a/nav2_bt_navigator/include/nav2_bt_navigator/recovery_node.hpp b/nav2_bt_navigator/include/nav2_bt_navigator/recovery_node.hpp
new file mode 100644
index 00000000..76f26eac
--- /dev/null
+++ b/nav2_bt_navigator/include/nav2_bt_navigator/recovery_node.hpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NAV2_BT_NAVIGATOR__RECOVERY_NODE_HPP_
+#define NAV2_BT_NAVIGATOR__RECOVERY_NODE_HPP_
+
+#include <string>
+#include "behaviortree_cpp/control_node.h"
+
+namespace nav2_bt_navigator
+{
+/**
+ * @brief The RecoveryNode has only two children and returns SUCCESS if and only if the first child
+ * returns SUCCESS.
+ *
+ * - If the first child returns FAILURE, the second child will be executed.  After that the first
+ * child is executed again if the second child returns SUCCESS.
+ *
+ * - If the first or second child returns RUNNING, this node returns RUNNING.
+ *
+ * - If the second child returns FAILURE, this control node will stop the loop and returns FAILURE.
+ *
+ */
+class RecoveryNode : public BT::ControlNode
+{
+public:
+  RecoveryNode(const std::string & name, const BT::NodeParameters & params);
+
+  ~RecoveryNode() override = default;
+
+  // Any BT node that accepts parameters must provide a requiredNodeParameters method
+  static const BT::NodeParameters & requiredNodeParameters()
+  {
+    static BT::NodeParameters params = {{"number_of_retries", "1"}};
+    return params;
+  }
+
+private:
+  unsigned int current_child_idx_;
+  unsigned int number_of_retries_;
+  unsigned int retry_count_;
+  BT::NodeStatus tick() override;
+};
+}  // namespace nav2_bt_navigator
+
+#endif  // NAV2_BT_NAVIGATOR__RECOVERY_NODE_HPP_
diff --git a/nav2_bt_navigator/src/navigate_to_pose_behavior_tree.cpp b/nav2_bt_navigator/src/navigate_to_pose_behavior_tree.cpp
index e25fc0c0..5fd5bc9b 100644
--- a/nav2_bt_navigator/src/navigate_to_pose_behavior_tree.cpp
+++ b/nav2_bt_navigator/src/navigate_to_pose_behavior_tree.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "nav2_bt_navigator/navigate_to_pose_behavior_tree.hpp"
+#include "nav2_bt_navigator/recovery_node.hpp"
 
 #include <memory>
 #include <string>
@@ -52,6 +53,9 @@ NavigateToPoseBehaviorTree::NavigateToPoseBehaviorTree()
   // Register our custom decorator nodes
   factory_.registerNodeType<nav2_tasks::RateController>("RateController");
 
+  // Register our custom control nodes
+  factory_.registerNodeType<nav2_bt_navigator::RecoveryNode>("RecoveryNode");
+
   // Register our simple action nodes
   factory_.registerSimpleAction("globalLocalizationServiceRequest",
     std::bind(&NavigateToPoseBehaviorTree::globalLocalizationServiceRequest, this));
diff --git a/nav2_bt_navigator/src/recovery_node.cpp b/nav2_bt_navigator/src/recovery_node.cpp
new file mode 100644
index 00000000..ae9cec3f
--- /dev/null
+++ b/nav2_bt_navigator/src/recovery_node.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include "nav2_bt_navigator/recovery_node.hpp"
+
+namespace nav2_bt_navigator
+{
+RecoveryNode::RecoveryNode(const std::string & name, const BT::NodeParameters & params)
+: BT::ControlNode::ControlNode(name, params), current_child_idx_(0), retry_count_(0)
+{
+  getParam<unsigned int>("number_of_retries", number_of_retries_);
+}
+
+BT::NodeStatus RecoveryNode::tick()
+{
+  const unsigned children_count = children_nodes_.size();
+
+  if (children_count != 2) {
+    throw BT::BehaviorTreeException("Recovery Node '" + name() + "' must only have 2 children.");
+  }
+
+  setStatus(BT::NodeStatus::RUNNING);
+
+  while (current_child_idx_ < children_count && retry_count_ < number_of_retries_) {
+    TreeNode * child_node = children_nodes_[current_child_idx_];
+    const BT::NodeStatus child_status = child_node->executeTick();
+
+    if (current_child_idx_ == 0) {
+      switch (child_status) {
+        case BT::NodeStatus::SUCCESS:
+          {
+            retry_count_ = 0;
+            return BT::NodeStatus::SUCCESS;
+          }
+          break;
+
+        case BT::NodeStatus::FAILURE:
+          {
+            // tick second child
+            if (retry_count_ <= number_of_retries_) {
+              current_child_idx_++;
+              break;
+            } else {
+              haltChildren(0);
+              return BT::NodeStatus::FAILURE;
+            }
+          }
+          break;
+
+        case BT::NodeStatus::RUNNING:
+          {
+            return BT::NodeStatus::RUNNING;
+          }
+          break;
+
+        default:
+          {
+          }
+      }  // end switch
+
+    } else if (current_child_idx_ == 1) {
+      switch (child_status) {
+        case BT::NodeStatus::SUCCESS:
+          {
+            retry_count_++;
+            current_child_idx_--;
+            haltChildren(1);
+          }
+          break;
+
+        case BT::NodeStatus::FAILURE:
+          {
+            current_child_idx_--;
+            retry_count_ = 0;
+            return BT::NodeStatus::FAILURE;
+          }
+          break;
+
+        case BT::NodeStatus::RUNNING:
+          {
+            return BT::NodeStatus::RUNNING;
+          }
+          break;
+
+        default:
+          {
+          }
+      }  // end switch
+    }
+  }  // end while loop
+  retry_count_ = 0;
+  return BT::NodeStatus::FAILURE;
+}
+
+}  // namespace nav2_bt_navigator
-- 
GitLab