From 6ea7fa9e8509227431e7687fc69fddb8369435ec Mon Sep 17 00:00:00 2001
From: Sergi Hernandez Juan <shernand@iri.upc.edu>
Date: Wed, 10 May 2023 07:54:59 +0200
Subject: [PATCH] Added several basic BT conditions and actions.

---
 include/iri_bt_basic_nodes.h |  29 +++---
 src/iri_bt_basic_nodes.cpp   | 178 ++++++++++++++++++++++++-----------
 2 files changed, 135 insertions(+), 72 deletions(-)

diff --git a/include/iri_bt_basic_nodes.h b/include/iri_bt_basic_nodes.h
index d26eaaa..10bd101 100644
--- a/include/iri_bt_basic_nodes.h
+++ b/include/iri_bt_basic_nodes.h
@@ -2,6 +2,8 @@
 #define IRI_BT_BASIC_NODES_H
 
 #include "iri_bt_factory.h"
+#include <tf2_geometry_msgs/tf2_geometry_msgs.h>
+#include <tf2_ros/transform_listener.h>
 
 class IriBTBasicNodes
 {
@@ -19,12 +21,10 @@ class IriBTBasicNodes
       */
     void init(IriBehaviorTreeFactory &factory);
 
-    void start_tree();
-    void stop_tree();
   private:
     // variables to know whether start or stop have been demanded by the user
-    bool tree_start;
-    bool tree_stop;
+    tf2_ros::Buffer tf2_buffer;
+    tf2_ros::TransformListener tf2_listener;
 
   protected:
     /**
@@ -36,20 +36,13 @@ class IriBTBasicNodes
       * \return a BT:NodeStatus::RUNNING always
       *
       */
-    BT::NodeStatus NOP(void);
-    // functions to check whether start or restart have been demanded by the user
-    BT::NodeStatus is_not_start_tree(void);
-    BT::NodeStatus async_is_start_tree(void);
-    BT::NodeStatus is_not_stop_tree(void);
-    BT::NodeStatus async_is_stop_tree(void);
-
-    // functions set start or restart variables
-    BT::NodeStatus set_start_tree(void);
-    BT::NodeStatus set_stop_tree(void);
-
-    // functions reset start or restart variables
-    BT::NodeStatus reset_start_tree(void);
-    BT::NodeStatus reset_stop_tree(void);
+    BT::NodeStatus RUNNING(void);
+    BT::NodeStatus is_variable_true(BT::TreeNode& self);
+    BT::NodeStatus is_variable_false(BT::TreeNode& self);
+    BT::NodeStatus transform_pose(BT::TreeNode& self);
+    BT::NodeStatus compute_distance(BT::TreeNode& self);
+    BT::NodeStatus compare_bigger(BT::TreeNode& self);
+    BT::NodeStatus compare_smaller(BT::TreeNode& self);    
 };
 
 #endif
diff --git a/src/iri_bt_basic_nodes.cpp b/src/iri_bt_basic_nodes.cpp
index e0efa60..ecf9ca1 100644
--- a/src/iri_bt_basic_nodes.cpp
+++ b/src/iri_bt_basic_nodes.cpp
@@ -1,97 +1,167 @@
 #include "iri_bt_basic_nodes.h"
 #include <ros/ros.h>
 
-IriBTBasicNodes::IriBTBasicNodes()
+IriBTBasicNodes::IriBTBasicNodes() :
+  tf2_listener(tf2_buffer)
 {
-  this->tree_start = false;
-  this->tree_stop = false;
 }
 
 void IriBTBasicNodes::init(IriBehaviorTreeFactory &factory)
 {
-  factory.registerIriAsyncAction("NOP",  std::bind(&IriBTBasicNodes::NOP, this));
-
-  factory.registerSimpleCondition("is_not_start_tree",  std::bind(&IriBTBasicNodes::is_not_start_tree, this));
-  factory.registerIriAsyncAction("async_is_start_tree",  std::bind(&IriBTBasicNodes::async_is_start_tree, this));
-  factory.registerSimpleCondition("is_not_stop_tree",  std::bind(&IriBTBasicNodes::is_not_stop_tree, this));
-  factory.registerIriAsyncAction("async_is_stop_tree",  std::bind(&IriBTBasicNodes::async_is_stop_tree, this));
-
-  factory.registerSimpleAction("set_start_tree",  std::bind(&IriBTBasicNodes::set_start_tree, this));
-  factory.registerSimpleAction("set_stop_tree",  std::bind(&IriBTBasicNodes::set_stop_tree, this));
-  factory.registerSimpleAction("reset_start_tree",  std::bind(&IriBTBasicNodes::reset_start_tree, this));
-  factory.registerSimpleAction("reset_stop_tree",  std::bind(&IriBTBasicNodes::reset_stop_tree, this));
+  BT::PortsList check_variable_ports = {BT::InputPort<bool>("variable")};
+  BT::PortsList transform_pose_ports = {BT::InputPort<geometry_msgs::PoseStamped>("pose_in"),BT::InputPort<std::string>("target_frame"),BT::OutputPort<geometry_msgs::PoseStamped>("pose_out")};
+  BT::PortsList compute_distance_ports = {BT::InputPort<geometry_msgs::PoseStamped>("pose1"),BT::InputPort<geometry_msgs::PoseStamped>("pose2"),BT::OutputPort<double>("distance")};
+  BT::PortsList compare_ports = {BT::InputPort<double>("value1"),BT::InputPort<double>("value2")};
+
+  //Registration of conditions
+  factory.registerSimpleCondition("is_variable_true", std::bind(&IriBTBasicNodes::is_variable_true, this, std::placeholders::_1),check_variable_ports);
+  factory.registerSimpleCondition("is_variable_false", std::bind(&IriBTBasicNodes::is_variable_false, this, std::placeholders::_1),check_variable_ports);
+
+  factory.registerIriAsyncAction("RUNNING",  std::bind(&IriBTBasicNodes::RUNNING, this));
+  factory.registerSimpleAction("transform_pose", std::bind(&IriBTBasicNodes::transform_pose, this, std::placeholders::_1),transform_pose_ports);
+  factory.registerSimpleAction("compute_distance", std::bind(&IriBTBasicNodes::compute_distance, this, std::placeholders::_1),compute_distance_ports);
+  factory.registerSimpleAction("compare_bigger", std::bind(&IriBTBasicNodes::compare_bigger, this, std::placeholders::_1),compare_ports);
+  factory.registerSimpleAction("compare_smaller", std::bind(&IriBTBasicNodes::compare_smaller, this, std::placeholders::_1),compare_ports);
 }
 
-BT::NodeStatus IriBTBasicNodes::NOP()
+BT::NodeStatus IriBTBasicNodes::is_variable_true(BT::TreeNode& self)
 {
-    return BT::NodeStatus::RUNNING;
-}
+  ROS_DEBUG("IriBTBasicNodes::is_variable_true");
+  BT::Optional<bool> variable = self.getInput<bool>("variable");
 
-BT::NodeStatus IriBTBasicNodes::is_not_start_tree(void)
-{
-  if(this->tree_start)
+  if(!variable)
+  {
+    ROS_ERROR("IriBTBasicNodes::is_variable_true-> Incorrect or missing input. It needs the following input ports: variable (bool)");
     return BT::NodeStatus::FAILURE;
-  else
+  }
+  bool variable_value=variable.value();
+  if(variable_value)
     return BT::NodeStatus::SUCCESS;
+  else
+    return BT::NodeStatus::FAILURE;
 }
 
-BT::NodeStatus IriBTBasicNodes::async_is_start_tree(void)
+BT::NodeStatus IriBTBasicNodes::is_variable_false(BT::TreeNode& self)
 {
-  if(this->tree_start)
+  ROS_DEBUG("IriBTBasicNodes::is_variable_false");
+  BT::Optional<bool> variable = self.getInput<bool>("variable");
+
+  if(!variable)
+  {
+    ROS_ERROR("IriBTBasicNodes::is_variable_false-> Incorrect or missing input. It needs the following input ports: variable (bool)");
+    return BT::NodeStatus::FAILURE;
+  }
+  bool variable_value=variable.value();
+  if(!variable_value)
     return BT::NodeStatus::SUCCESS;
   else
-    return BT::NodeStatus::RUNNING;
+    return BT::NodeStatus::FAILURE;
 }
 
-BT::NodeStatus IriBTBasicNodes::is_not_stop_tree(void)
+BT::NodeStatus IriBTBasicNodes::transform_pose(BT::TreeNode& self)
 {
-  if(this->tree_stop)
+  ROS_DEBUG("IriBTBasicNodes::transform_pose");
+  BT::Optional<geometry_msgs::PoseStamped> pose_in = self.getInput<geometry_msgs::PoseStamped>("pose_in");
+  BT::Optional<std::string> target_frame = self.getInput<std::string>("target_frame");
+
+  if((!pose_in) || (!target_frame))
+  {
+    ROS_ERROR("IriBTBasicNodes::transform_pose-> Incorrect or missing input. It needs the following input ports: pose_in (geometry_msgs::PoseStamped) target_frame (std::string) pose_out (geometry_msgs::PoseStamped)");
     return BT::NodeStatus::FAILURE;
+  }
+
+  geometry_msgs::PoseStamped pose_in_value=pose_in.value(),new_pose;
+  std::string target_frame_value=target_frame.value();
+
+  if(target_frame_value!=pose_in_value.header.frame_id)
+  {
+    try{
+      bool tf_exists = this->tf2_buffer.canTransform(target_frame_value, pose_in_value.header.frame_id, pose_in_value.header.stamp, ros::Duration(0.1));
+      if(tf_exists)
+      {
+        this->tf2_buffer.transform(pose_in_value, new_pose,target_frame_value);
+        self.setOutput("pose_out", new_pose);
+        return BT::NodeStatus::SUCCESS;
+      }
+      else
+        return BT::NodeStatus::FAILURE;
+    }catch(tf2::TransformException &ex){
+      return BT::NodeStatus::FAILURE;
+    }
+  }
   else
+  {
+    self.setOutput("pose_out", pose_in_value);
     return BT::NodeStatus::SUCCESS;
+  }
 }
 
-BT::NodeStatus IriBTBasicNodes::async_is_stop_tree(void)
+BT::NodeStatus IriBTBasicNodes::compute_distance(BT::TreeNode& self)
 {
-  if(this->tree_stop)
-    return BT::NodeStatus::SUCCESS;
-  else
-    return BT::NodeStatus::RUNNING;
-}
+  ROS_DEBUG("IriBTBasicNodes::compute_distance");
+  BT::Optional<geometry_msgs::PoseStamped> pose1 = self.getInput<geometry_msgs::PoseStamped>("pose1");
+  BT::Optional<geometry_msgs::PoseStamped> pose2 = self.getInput<geometry_msgs::PoseStamped>("pose2");
 
-BT::NodeStatus IriBTBasicNodes::set_start_tree(void)
-{
-  this->tree_start=true;
-  return BT::NodeStatus::SUCCESS;
-}
+  if((!pose1) || (!pose2))
+  {
+    ROS_ERROR("IriBTBasicNodes::compute_distance-> Incorrect or missing input. It needs the following input ports: pose1 (geometry_msgs::PoseStamped) pose2 (geometry_msgs::PoseStamped) distance (double)");
+    return BT::NodeStatus::FAILURE;
+  }
 
-BT::NodeStatus IriBTBasicNodes::set_stop_tree(void)
-{
-  this->tree_stop=true;
-  return BT::NodeStatus::SUCCESS;
-}
+  geometry_msgs::PoseStamped pose1_value=pose1.value();
+  geometry_msgs::PoseStamped pose2_value=pose2.value();
+  double distance;
 
-BT::NodeStatus IriBTBasicNodes::reset_start_tree(void)
-{
-  this->tree_start=false;
+  distance=sqrt(pow(pose1_value.pose.position.x-pose2_value.pose.position.x,2.0)+pow(pose1_value.pose.position.y-pose2_value.pose.position.y,2.0)+pow(pose1_value.pose.position.z-pose2_value.pose.position.z,2.0));
+
+  self.setOutput("distance",distance);
   return BT::NodeStatus::SUCCESS;
 }
 
-BT::NodeStatus IriBTBasicNodes::reset_stop_tree(void)
+BT::NodeStatus IriBTBasicNodes::compare_bigger(BT::TreeNode& self)
 {
-  this->tree_stop=false;
-  return BT::NodeStatus::SUCCESS;
+  ROS_DEBUG("IriBTBasicNodes::compare_bigger");
+  BT::Optional<double> value1 = self.getInput<double>("value1");
+  BT::Optional<double> value2 = self.getInput<double>("value2");
+
+  if((!value1) || (!value2))
+  {
+    ROS_ERROR("IriBTBasicNodes::compare_bigger-> Incorrect or missing input. It needs the following input ports: value1 (double) value2 (double)");
+    return BT::NodeStatus::FAILURE;
+  }
+
+  double val1=value1.value();
+  double val2=value2.value();
+
+  if(val1>val2)
+    return BT::NodeStatus::SUCCESS;
+  else
+    return BT::NodeStatus::FAILURE;
 }
 
-void IriBTBasicNodes::start_tree(void)
+BT::NodeStatus IriBTBasicNodes::compare_smaller(BT::TreeNode& self)
 {
-  ROS_DEBUG("IriBTBasicNodes: start_tree()");
-  this->tree_start=true;
+  ROS_DEBUG("IriBTBasicNodes::compare_smaller");
+  BT::Optional<double> value1 = self.getInput<double>("value1");
+  BT::Optional<double> value2 = self.getInput<double>("value2");
+
+  if((!value1) || (!value2))
+  {
+    ROS_ERROR("IriBTBasicNodes::compare_smaller-> Incorrect or missing input. It needs the following input ports: value1 (double) value2 (double)");
+    return BT::NodeStatus::FAILURE;
+  }
+
+  double val1=value1.value();
+  double val2=value2.value();
+
+  if(val1<val2)
+    return BT::NodeStatus::SUCCESS;
+  else
+    return BT::NodeStatus::FAILURE;
 }
 
-void IriBTBasicNodes::stop_tree(void)
+BT::NodeStatus IriBTBasicNodes::RUNNING()
 {
-  ROS_DEBUG("IriBTBasicNodes: stop_tree()");
-  this->tree_stop=true;
+    return BT::NodeStatus::RUNNING;
 }
 
-- 
GitLab