Giter Club home page Giter Club logo

behaviortree.ros2's People

Contributors

cihataltiparmak avatar emersonknapp avatar facontidavide avatar marip8 avatar marj3220 avatar marqrazz avatar mnumanuyar avatar stevemacenski avatar syllogismrxs avatar tony-p avatar vicmassy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

behaviortree.ros2's Issues

Unit Testing

Below I have attached my cpp file for which I wish to write a unit test, specifically for the onTick method. This node is supposed to run at the top of the tree inside a reactive fallback to control the current state of the tree eg, localized, e stop, hw failure, sw failure etc. Since it is an essential node in the tree, I wanted to write a unit test for it. The code is from the sample subscriber wrapper provided in the package

`

#include "my_behavior_tree/ok_status.hpp"

using namespace BT;

OKStatus::OKStatus(const std::string& name,
                            const NodeConfig& conf,
                            const RosNodeParams& params)
: RosTopicSubNode<std_msgs::msg::String>(name, conf, params)
{

}

BT::PortsList OKStatus::providedPorts()
{
    return{InputPort<std::string>("topic_name")};
}

// Check if status is okay or not, if not return failure. Used to control state of the tree
NodeStatus OKStatus::onTick(const std::shared_ptr<std_msgs::msg::String>& last_msg)
  {    // TODO - Add multiple state checks

    if (last_msg)
    {
      status = last_msg->data;

      if(status =="OK") 
    {
      // RCLCPP_INFO(logger(), "Not in error"); 
      c = 0;
      return NodeStatus::SUCCESS; 
    }
    else
    {
      c =1;
      // RCLCPP_INFO(logger(), "In error");
      return NodeStatus::FAILURE; 
    }

    }

    else
    {
      
      if(c ==0) 
    {
      return NodeStatus::SUCCESS; 
    }
    else
    {
        return NodeStatus::FAILURE; 
    }
       

    } 
    
    
  }

`

This is the corresponding header file

`

#include "behaviortree_ros2/bt_topic_sub_node.hpp"
#include <std_msgs/msg/string.hpp>

using namespace BT;

class OKStatus: public RosTopicSubNode<std_msgs::msg::String>
{
public:
  OKStatus(const std::string& name,
                const NodeConfig& conf,
                const RosNodeParams& params);

  static BT::PortsList providedPorts();

  NodeStatus onTick(const std::shared_ptr<std_msgs::msg::String>& last_msg) override;

  std::string status;
  int c;
};

`

This is the Test method I have written

`

#include <gtest/gtest.h>
#include <ament_index_cpp/get_package_share_directory.hpp>
#include <ament_index_cpp/get_package_prefix.hpp>
#include <rclcpp/rclcpp.hpp>
#include <vector>
#include <string>
#include <fstream>
#include <memory>
#include <utility>
#include <boost/filesystem.hpp>
#include <my_behavior_tree/ok_status.hpp>

using namespace BT;


std_msgs::msg::String createString(const std::string& name)
{
    std_msgs::msg::String sample;
    sample.data = name;
    return sample;
}

class BehaviorTreeTestFixture : public ::testing::Test
{

protected:
    std::shared_ptr<BehaviorTreeHandler> bt_handler;
    std_msgs::msg::String a;

public:
    void SetUp() override
    {
        bt_handler = std::make_shared<BehaviorTreeHandler>();
        a = createString("OK");
        
    }

    void TearDown() override
    {
        bt_handler.reset();
    }

};

TEST_F(BehaviorTreeTestFixture, TestSuccess)
{

rclcpp::init(0, nullptr);


auto nh = std::make_shared<rclcpp::Node>("tree_test_node");

RosNodeParams params;
params.nh = nh;
params.default_port_value = "test";

factory.registerNodeType<OKStatus>("OKStatus", params);

OKStatus ok_status("ok_status",NodeConfig(),RosNodeParams());

auto message_ptr = std::make_shared<std_msgs::msg::String>(a);

auto status = ok_status.onTick(message_ptr);
EXPECT_EQ(status, BT::NodeStatus::SUCCESS);

rclcpp::shutdown();

}
`

I am getting the following error when I am running this code

unknown file: Failure C++ exception with description "Both [topic_name] in the InputPort and the RosNodeParams are empty." thrown in the test body.

My idea is to write a unit test specifically for the onTick method only without involving any of the ROS components, but since the OKStatus class is inheriting from the RosTopicSubNode class, I believe that will not be possible without completely re writing the class and separating out the ROS dependency and onTick method.

So I wanted to check if someone has faced a similar problem or has some better suggestions to write a unit test for this method?

I have also tried to load the xml file and also add a tickOnce/tickWhileRunning method but even then I get the same error, however with those cases, the subscriber is actually created but the code eventually crashes at line 181 https://github.com/BehaviorTree/BehaviorTree.ROS2/blob/humble/behaviortree_ros2/include/behaviortree_ros2/bt_topic_sub_node.hpp

Thank you

Having a Service node and Action node within the same ReactiveSequence

I'm currently refactoring our behavior trees to use these base classes in our merge to BTV4. Currently I've reached an issue where we have this ReactiveSequence

             <Sequence>
                <SubTree ID="DisableStopOnObstacle" _autoremap="true"/>
                <ReactiveSequence>
                    <Sequence>
                        <SubTree ID="CheckEStop" _autoremap="true"/>
                        <SubTree ID="CheckContinueMissionDocking" _autoremap="true"/>
                    </Sequence>
                    <DockingAction
                        action_name="/docking_action_server"
                        distance="-0.1"
                        linear_speed="0.03"
                        stop_at_charger="false"
                        stop_at_mag_marker="false"
                        target_id="{docking_id}"
                        allowed_time="30"
                        failure_if_no_success = "true"
                        level="{level}"
                        message="{message}"
                    />
                </ReactiveSequence>
            </Sequence>

Both the Subtrees CheckEStop and CheckContinueMissionDocking are service node calls.
In our BT3.8 implementation this sequence would work because the service node calls would wait until the service had responded and then only return SUCCESS or FAILURE. However with this Service Node implementation a service node can now return RUNNING when waiting for a response. This is meaning that we cannot have a Service node and Action node within the same Reactive Sequence.

Is this expected behavior? If so, is there a way to have a service node and action node within the same Reactive Sequence?

Cancellation of unkown goal not detectable

Hi, I wanted to note that it can be desirable to throw rclcpp_action::exceptions::UnknownGoalHandleError or at least any kind of exception on cancelGoal() if the goal response has not arrived yet. In fact, I am relying on such a case in my application, because I want to make sure that the goal is canceled either way, so I wait until it has arrived and proceed with the cancellation. After merging #53, there is no way for me to detect if haltTree() ran into a problem or not. Before I did like below:

try {
  tree.haltTree();
} catch (const rclcpp_action::exceptions::UnknownGoalHandleError& e) {
    // If tree is halted directly after an action started, the goal response might not have been
    // received yet. In this case we assume the goal is about to arrive and asynchronously retry to HALT
}

I have a seperate ROS2 node that tries to halt the tree and doesn't know about when a RosActionNode published goal requests, so it is mandatory for this case to be detectable during halting the tree.

I am falling back to a previous commit, until this issue has been resolved.

Originally posted by @robin-mueller in #53 (comment)

Overrided registerNodesIntoFactory doesn't work

Thank you for adding the new and exciting feature, BT::TreeExecutionServer.

I attempted to create my own class by inheriting from BT::TreeExecutionServer, but it seems that the registerNodesIntoFactory I inherited is not functioning properly. When attempting to load a behavior tree containing the Node added within registerNodesIntoFactory, the following error occurs:

[ERROR] [1715003079.255998664] [bt_action_server]: Failed to load BehaviorTree: tree.xml 
 Error at line 7: -> Node not recognized: MyNode

This issue seems to be caused by two reasons:

  • The registerNodesIntoFactory called within the constructor of BT::TreeExecutionServer is its own rather than that of the inheriting class.
  • RegisterBehaviorTrees() is called before registerNodesIntoFactory() is invoked.

TreeExecutionServer::TreeExecutionServer(const rclcpp::NodeOptions& options)
: p_(new Pimpl(options))
{
// create the action server
const auto action_name = p_->params.action_name;
RCLCPP_INFO(kLogger, "Starting Action Server: %s", action_name.c_str());
p_->action_server = rclcpp_action::create_server<ExecuteTree>(
p_->node, action_name,
[this](const rclcpp_action::GoalUUID& uuid,
std::shared_ptr<const ExecuteTree::Goal> goal) {
return handle_goal(uuid, std::move(goal));
},
[this](const std::shared_ptr<GoalHandleExecuteTree> goal_handle) {
return handle_cancel(std::move(goal_handle));
},
[this](const std::shared_ptr<GoalHandleExecuteTree> goal_handle) {
handle_accepted(std::move(goal_handle));
});
// register the users Plugins and BehaviorTree.xml files into the factory
RegisterBehaviorTrees(p_->params, p_->factory, p_->node);
registerNodesIntoFactory(p_->factory);
}

void RegisterBehaviorTrees(bt_server::Params& params, BT::BehaviorTreeFactory& factory,
rclcpp::Node::SharedPtr node)
{
// clear the factory and load/reload it with the Behaviors and Trees specified by the user in their [bt_action_server] config yaml
factory.clearRegisteredBehaviorTrees();
BT::RosNodeParams ros_params;
ros_params.nh = node;
ros_params.server_timeout = std::chrono::milliseconds(params.ros_plugins_timeout);
ros_params.wait_for_server_timeout = ros_params.server_timeout;
for(const auto& plugin : params.plugins)
{
const auto plugin_directory = GetDirectoryPath(plugin);
// skip invalid plugins directories
if(plugin_directory.empty())
{
continue;
}
LoadRosPlugins(factory, plugin_directory, ros_params);
}
for(const auto& tree_dir : params.behavior_trees)
{
const auto tree_directory = GetDirectoryPath(tree_dir);
// skip invalid subtree directories
if(tree_directory.empty())
continue;
LoadBehaviorTrees(factory, tree_directory);
}
}

Late subscribtions to topics using a dynamic 'topic_name' (blackboardpointer) output 'nullptr' until next message is published.

When using the RosTopicSubNode object and providing the topic_name port using a blackboard pointer, the subscriber is created on the first tick. When using topics with a transient local setting I would expect the last message on the topic to be provided as the last_msg argument on that same first tick. Instead we keep getting nullptr's.

I've been playing around with multiple settings on either subscription as publisher side, but haven't been able to resolve my problem.
The problem only occurs on the very first tick or ticks until the next message is published on the topics.

Is there a way to fix this?
I heard from my colleagues this behavior also occurs for action/service nodes using blackboard pointers. for action_name and service_name. We haven't been testing publisher nodes, but perhaps the issue is present there as well.

Reproducability
I can reproduce my issue as follows:

  1. Start publishing (transient local) messages
    ros2 topic pub --qos-durability=transient_local --qos-history=keep_last --qos-depth=1 test_topic std_msgs/msg/Bool "{data: False}"
  2. Start behaviortree

Used behaviorTree

<root BTCPP_format="4" main_tree_to_execute="MainTree">
    <BehaviorTree ID="test_tree">
        <GetDigitalSignal topic_name="{test_topic_name}" value="{output}"/>
    </BehaviorTree>

    <BehaviorTree ID="MainTree">
        <Sequence>
	    <Script code="my_topic:='test_topic'"/>
            <Delay delay_msec="10000"> <!-- Added Delay to allow some time connecting to Groot-->
                <KeepRunningUntilFailure>
                    <ForceSuccess>
                        <Delay delay_msec="3000"> <!-- Delay added to see in Groot what happens easier-->
                            <ParallelAll>
                                <GetDigitalSignal topic_name="test_topic" value="{my_output_static}"/>
                                <GetDigitalSignal topic_name="{my_topic}" value="{my_output_scripted}"/>
                                <SubTree ID="test_tree" test_topic_name="{my_topic}" output="{my_ouput_subtree}"/>
                            </ParallelAll>
                        </Delay>
                    </ForceSuccess>
                </KeepRunningUntilFailure>
            </Delay>
        </Sequence>
    </BehaviorTree>
</root>

GetDigitalSignal Implementation

static BT::PortsList providedPorts()
{
    return providedBasicPorts({
        BT::OutputPort<std_msgs::msg::Bool::_data_type>("value", "Current value of the digital signal"),
    });
}

BT::NodeStatus onTick(const std_msgs::msg::Bool::SharedPtr &msg)
{
    if (msg != nullptr)
    {
        setOutput("value", msg->data);
        return BT::NodeStatus::SUCCESS;
    }

    return BT::NodeStatus::FAILURE;
}

Groot Outcome
image

Why doesn't TreeExecutionServer directly expose rclcpp::Node::SharedPtr?

When I implemented the override of void registerNodesIntoFactory(BT::BehaviorTreeFactory& factory), I wrote it like this:

BT::RosNodeParams params;
params.nh = std::dynamic_pointer_cast<rclcpp::Node>(nodeBaseInterface());

This caused the following exceptions during tree construction:

[ERROR] [1715013001.175801122] [bt_action_server]: Behavior Tree exception:bad_weak_ptr
[ERROR] [1715013002.693655469] [bt_action_server]: Behavior Tree exception:The ROS node went out of scope. RosNodeParams doesn't take the ownership of the node.

Instead, when I changed the API to directly assign rclcpp::Node::SharedPtr to nh, it worked perfectly.
Why doesn't TreeExecutionServer directly expose rclcpp::Node::SharedPtr?
If there are no issues, I would like to add an API to directly exposerclcpp::Node::SharedPtr.

ERROR loading library [libdummy_sleep_action_bt_node.so]: can't find symbol [BT_RegisterNodesFromPlugin]

Hi all,

I'm facing an issue using my own created ROS2 Action BT Plugin.

I'm basically using the same header and cpp file as the sample sleep_action in the btcpp_ros2_samples package. I register the Action Server using the macro CreateRosNodePlugin(ros_behavior_tree::DummySleepAction, "DummySleepAction"); from behaviortree_ros2/plugins.hpp in the cpp file of the plugin.

Opposing to BehaviorTree/BehaviorTree.CPP#300 , I'm also adding the compile definitions BT_EXPORT_PLUGIN in my CMakeLists.txt:

add_library(dummy_sleep_action_bt_node SHARED plugins/action/DummySleepAction.cpp)
list(APPEND plugin_libs dummy_sleep_action_bt_node)

foreach(bt_plugin ${plugin_libs})
  ament_target_dependencies(${bt_plugin} ${dependencies})
  target_compile_definitions(${bt_plugin} PRIVATE BT_PLUGIN_EXPORT)
endforeach()

The plugin is being loaded using the BT::SharedLibrary loader with factory_.registerFromPlugin(loader.getOSName(p)); (p being "dummy_sleep_action_bt_node").

Does anyone have an idea what the issue might be? Also please let me know if I can provide any more information.

Best regards,
Tom

EDIT: In the meantime I found out, that naively "mixing" the provided functions by this package and functionalities that were part of the navigation2 behavior tree package could be the cause of the problem as I'm not registering the action plugins as described in the "sleep_client" sample of this package. Will update, but still happy for some feedback.

Defining default_port_value for ROS2 plugins

TL:DR How can the default_port_value of a ROS2 BT Node be set when using the new plugin architecture?

Context

Normally a ROS2 BT node would be set this way:

// in main()
  BehaviorTreeFactory factory;

  auto node = std::make_shared<rclcpp::Node>("fibonacci_action_client");
  // provide the ROS node and the name of the action service
  RosNodeParams params; 
  params.nh = node;
  params.default_port_value = "fibonacci";
  factory.registerNodeType<FibonacciAction>("Fibonacci", params);

Problem when using macro from BT.CPP:

Using the bt_factory.h (BT.CPP) for creating a non-ROS plugin is straightforward, but when adding ROS2 necessities the ROS node goes out of scope.

Code:

image

Result:

image

Problem when using macro from BT.ROS2:

Inversly, when using the plugins.hpp (BT.ROS2) macro, I can't find how to set the params:

Code:

image

Result:

image

So how should a ROS2 plugin be set with its params?

Thank you in advance!

Feature request: generic sub nodes and ports

Currently to use topics in BT I wirte up a simple sub node and expose needed members as ports. This works, however after a while there are a lot of similiar code.

Is it possible to make this generic?

Also it would be nice to have generic port types for topic types. To this, to_yaml functions could be used. For example

    multi_uav_bt_nodes::Pose msg;
    msg.position.x = 10;
    msg.position.y = 10;
    msg.position.z = 10;
    EXPECT_EQ( geometry_msgs::msg::to_yaml(msg,true), "{position: {x: 10.0000, y: 10.0000, z: 10.0000}, orientation: {x: 0.00000, y: 0.00000, z: 0.00000, w: 1.00000}}" );

couldnt find how to parse it but probobly some function rosidl will work.

Also for reference my sub nodes looks like this:

using Pose = geometry_msgs::msg::Pose;

class ListenPoseAction : public BT::RosTopicSubNode<Pose>
{
public:
  ListenPoseAction(
    const std::string & name,
    const BT::NodeConfig & conf,
    const BT::RosNodeParams & params)
  : BT::RosTopicSubNode<Pose>(name, conf, params) {}

  static BT::PortsList providedPorts();

  BT::NodeStatus onTick(const typename Pose::SharedPtr & last_msg) override;

  virtual ~ListenPoseAction();
};
BT::PortsList ListenPoseAction::providedPorts()
{
  return providedBasicPorts(
    {
      BT::OutputPort<double>("X"),
      BT::OutputPort<double>("Y"),
      BT::OutputPort<double>("Z")});
}

BT::NodeStatus ListenPoseAction::onTick(const typename Pose::SharedPtr & last_msg)
{
  if (!last_msg) {
    return BT::NodeStatus::FAILURE;
  }
  setOutput("X", last_msg->position.x);
  setOutput("Y", last_msg->position.y);
  setOutput("Z", last_msg->position.z);
  return BT::NodeStatus::SUCCESS;
}

ListenPoseAction::~ListenPoseAction()
{
}

note that ony the first line of the hpp (which could be a template variable) and code related to ports (which would be the generic port) change for different ros topics

BT Not Running / Failed When Running Inside Docker Container

Hi,

I am trying to dockerized my behavior tree calling several ros2 actions server. The action servers are running inside another container.

Everything works well if I run the behavior tree outside the docker.

When running inside the docker, it keeps telling that the action server is unreachable and kills itself. When I tried to enter the docker container terminal, I could list all the action servers using the ros2 action list command.

Could it be caused by it needs some time to recognize / discover the action server?

Thank you in advance

Error when canceling action during halt()

I'm running in a weird behavior when trying to halting an action node.

Setup:
BehaviorTree.ROS2 branch humble bb0d510
BehaviorTree.CPP branch master acbc707

Structure:
ws
|- BehaviorTree.ROS2
|- BehaviorTree.CPP
|- test
|- CMakeLists.txt
|- package.xml
|- src
|- bt_act_test.cpp

CMakeLists.txt:

cmake_minimum_required(VERSION 3.8)
project(bt_act_test)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED )
find_package(rclcpp_action REQUIRED )
find_package(behaviortree_cpp REQUIRED )
find_package(behaviortree_ros2 REQUIRED )
find_package(example_interfaces REQUIRED)

add_executable(bt_act_test src/bt_act_test.cpp)
target_include_directories(bt_act_test PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
ament_target_dependencies(bt_act_test
    rclcpp
    rclcpp_action
    behaviortree_cpp
    behaviortree_ros2
    example_interfaces
)
    

if(BUILD_TESTING)
    find_package(ament_lint_auto REQUIRED)
    # the following line skips the linter which checks for copyrights
    # comment the line when a copyright and license is added to all source files
    set(ament_cmake_copyright_FOUND TRUE)
    # the following line skips cpplint (only works in a git repo)
    # comment the line when this package is in a git repo and when
    # a copyright and license is added to all source files
    set(ament_cmake_cpplint_FOUND TRUE)
    ament_lint_auto_find_test_dependencies()
endif()

install(TARGETS 
    bt_act_test
DESTINATION lib/${PROJECT_NAME})    

ament_package()

package.xml:

...
    <buildtool_depend>ament_cmake</buildtool_depend>

    <depend>rclcpp</depend>
    <depend>rclcpp_action</depend>
    <depend>behaviortree_ros2</depend>
    <depend>behaviortree_cpp</depend>
    <depend>example_interfaces</depend>

    <test_depend>ament_lint_auto</test_depend>
    <test_depend>ament_lint_common</test_depend>

    <export>
      <build_type>ament_cmake</build_type>
    </export>
...

bt_act_test.cpp:

#include <stdio.h>
#include <string>
#include <chrono>
#include <thread>
#include <memory>
#include <filesystem>
#include <vector>

#include <example_interfaces/action/fibonacci.hpp>
#include <rclcpp/rclcpp.hpp>
#include <rclcpp_action/rclcpp_action.hpp>

#include <behaviortree_ros2/bt_action_node.hpp>

using vec_int = std::vector<int32_t>;
using Fibonacci = example_interfaces::action::Fibonacci;
using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;



void sleep(int ms=10){
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}


class FibonacciActionServer : public rclcpp::Node {  

public:

    explicit FibonacciActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
    : Node("server_node", options)
    {
        using namespace std::placeholders;

        this->action_server_ = rclcpp_action::create_server<Fibonacci>(
            this,
            "fibonacci",
            std::bind(&FibonacciActionServer::handle_goal, this, _1, _2),
            std::bind(&FibonacciActionServer::handle_cancel, this, _1),
            std::bind(&FibonacciActionServer::handle_accepted, this, _1)    
        );
    }

private:
    rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;

    rclcpp_action::GoalResponse handle_goal(
        const rclcpp_action::GoalUUID & uuid,
        std::shared_ptr<const Fibonacci::Goal> goal)
    {
        RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order);
        (void)uuid;
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
    }

    rclcpp_action::CancelResponse handle_cancel(
        const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
        RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
    }

    void handle_accepted(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
        using namespace std::placeholders;
        // this needs to return quickly to avoid blocking the executor, so spin up a new thread
        std::thread{std::bind(&FibonacciActionServer::execute, this, _1), goal_handle}.detach();
    }

    void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
        RCLCPP_INFO(this->get_logger(), "Executing goal");
        rclcpp::Rate loop_rate(1);
        const auto goal = goal_handle->get_goal();
        auto feedback = std::make_shared<Fibonacci::Feedback>();
        auto & sequence = feedback->sequence;
        sequence.push_back(0);
        sequence.push_back(1);
        auto result = std::make_shared<Fibonacci::Result>();

        for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
            // Check if there is a cancel request
            if (goal_handle->is_canceling()) {
                result->sequence = sequence;
                goal_handle->canceled(result);
                RCLCPP_INFO(this->get_logger(), "Goal canceled");
                return;
            }
            // Update sequence
            sequence.push_back(sequence[i] + sequence[i - 1]);
            // Publish feedback
            goal_handle->publish_feedback(feedback);
            RCLCPP_INFO(this->get_logger(), "Publish feedback");

            loop_rate.sleep();
        }

        // Check if goal is done
        if (rclcpp::ok()) {
            result->sequence = sequence;
            goal_handle->succeed(result);
            RCLCPP_INFO(this->get_logger(), "Goal succeeded");
        }
    }
};

using namespace BT;

class FibonacciAction: public RosActionNode<Fibonacci>
{
public:
  FibonacciAction(const std::string& name,
                  const NodeConfig& conf,
                  const RosNodeParams& params)
    : RosActionNode<Fibonacci>(name, conf, params)
  {}

  // The specific ports of this Derived class
  // should be merged with the ports of the base class,
  // using RosActionNode::providedBasicPorts()
  static PortsList providedPorts()
  {
    return providedBasicPorts({InputPort<unsigned>("order"), OutputPort<vec_int>("result")});
  }

  // This is called when the TreeNode is ticked and it should
  // send the request to the action server
  bool setGoal(RosActionNode::Goal& goal) override 
  {
    // get "order" from the Input port
    getInput("order", goal.order);
    // return true, if we were able to set the goal correctly.
    return true;
  }
  
  // Callback executed when the reply is received.
  // Based on the reply you may decide to return SUCCESS or FAILURE.
  NodeStatus onResultReceived(const WrappedResult& wr) override
  {
    std::stringstream ss;
    ss << "Result received: ";
    for (auto number : wr.result->sequence) {
      ss << number << " ";
    }
    RCLCPP_INFO(node_->get_logger(), ss.str().c_str());
    this->setOutput<vec_int>("result", wr.result->sequence);
    return NodeStatus::SUCCESS;
  }

  // Callback invoked when there was an error at the level
  // of the communication between client and server.
  // This will set the status of the TreeNode to either SUCCESS or FAILURE,
  // based on the return value.
  // If not overridden, it will return FAILURE by default.
  virtual NodeStatus onFailure(ActionNodeErrorCode error) override
  {
    RCLCPP_ERROR(node_->get_logger(), "Error: %d", error);
    return NodeStatus::FAILURE;
  }

  // we also support a callback for the feedback, as in
  // the original tutorial.
  // Usually, this callback should return RUNNING, but you
  // might decide, based on the value of the feedback, to abort
  // the action, and consider the TreeNode completed.
  // In that case, return SUCCESS or FAILURE.
  // The Cancel request will be send automatically to the server.
  NodeStatus onFeedback(const std::shared_ptr<const Feedback> feedback)
  {
    std::stringstream ss;
    ss << "Next number in sequence received: ";
    for (auto number : feedback->sequence) {
      ss << number << " ";
    }
    RCLCPP_INFO(node_->get_logger(), ss.str().c_str());
    this->setOutput<vec_int>("result", feedback->sequence);
    return NodeStatus::RUNNING;
  }
};

template<typename T>
class GetBBvalueNode : public BT::SyncActionNode{
public:
    GetBBvalueNode (
        const std::string& name, const BT::NodeConfig& config, 
        std::function<BT::NodeStatus(T)> callback,
        std::function<BT::NodeStatus(BT::Expected<T>)> no_value_callback = [](BT::Expected<T> input){
            throw BT::RuntimeError("missing required input [input]: ", input.error() );
            return BT::NodeStatus::FAILURE; //needed to pass contructor checks
        }
    ) : 
    BT::SyncActionNode(name, config),
    _callback(callback),
    _no_value_callback(no_value_callback) {}

    BT::NodeStatus tick() override{
        auto input = getInput<T>("input"); //get value from port "input"

        // Check if expected is valid. If not, throw its error
        if (!input) return this->_no_value_callback(input);
        else return this->_callback(input.value());
    }

    /**
     * @brief set port input as <T> input
    */
    static BT::PortsList providedPorts(){
        return{ BT::InputPort<T>("input", "read value")};
    }

private:
    std::function<BT::NodeStatus(T)> _callback;
    std::function<BT::NodeStatus(BT::Expected<T>)> _no_value_callback;
};

int main(int argc, char *argv[]){
    rclcpp::init(argc, argv);

    static const char* xml_text = R"(
    <root BTCPP_format="4" >
        <BehaviorTree ID="MainTree">
            <ReactiveSequence>
                <GetVec input="{res}"/>
                <Fibonacci  order="5"
                            result="{res}"/>
            </ReactiveSequence>
        </BehaviorTree>
    </root>
    )";
    
    std::vector<vec_int> res_vecs;

    auto get_vec = [&res_vecs](vec_int input){
        if ( std::find(res_vecs.begin(), res_vecs.end(), input) == res_vecs.end() ){ 
            res_vecs.push_back(input);
            for(auto el : res_vecs.back()) std::cout << el << "; ";
            std::cout << std::endl;
        } 
        if (res_vecs.size() == 2) return BT::NodeStatus::FAILURE;
        else return BT::NodeStatus::SUCCESS;
    };
    auto on_err = [](BT::Expected<vec_int> input){
        (void)input; 
        return BT::NodeStatus::SUCCESS;
    };


    BehaviorTreeFactory factory;
    // provide the ROS node and the name of the action service
    RosNodeParams params; 
    auto node = std::make_shared<rclcpp::Node>("fibonacci_action_client");
    params.nh = node;
    params.default_port_value = "fibonacci";
    factory.registerNodeType<FibonacciAction>("Fibonacci", params);
    factory.registerNodeType<GetBBvalueNode<vec_int>>("GetVec", get_vec, on_err);

    
    auto server_node = std::make_shared<FibonacciActionServer>();
    auto spin_callback = [](rclcpp::Node::SharedPtr node){rclcpp::spin(node);};
    std::thread{spin_callback, server_node}.detach();
    sleep();

    // tick the tree
    auto tree = factory.createTreeFromText(xml_text);
    tree.tickWhileRunning();

    rclcpp::shutdown();

    return 0;
}

The code is essentially copied-pasted from the tutorials BT ros2 integration and ROS2 action server.

When I try to run the executable I get the following:

root@f1e379732b0f:~/projects# ros2 run bt_act_test bt_act_test 
[INFO] [1687792324.400457200] [server_node]: Received goal request with order 5
[INFO] [1687792324.401037500] [server_node]: Executing goal
[INFO] [1687792324.401250200] [server_node]: Publish feedback
[INFO] [1687792324.421203700] [fibonacci_action_client]: Goal accepted by server, waiting for result
[INFO] [1687792325.401689100] [server_node]: Publish feedback
[INFO] [1687792325.407532400] [fibonacci_action_client]: Next number in sequence received: 0 1 1 2 
0; 1; 1; 2; 
[INFO] [1687792326.401572200] [server_node]: Publish feedback
[INFO] [1687792326.405681700] [fibonacci_action_client]: Next number in sequence received: 0 1 1 2 3 
0; 1; 1; 2; 3; 
[INFO] [1687792326.406163200] [server_node]: Received request to cancel goal
terminate called after throwing an instance of 'std::runtime_error'
  what():  Asked to publish result for goal that does not exist
[ros2run]: Aborted

Apologies for the long message, but I hope it will make the error reproduction easier.
Thanks in advance for the support

rclcpp::Node as a shared pointer may cause circular dependency

It's common practice to place all logic inside a ROS node, with the main method simply creating and spinning the node. However, registering BT nodes from within an rclcpp::Node child class can lead to a circular dependency:

	RosNodeParams params;
    params.nh = this->shared_from_this();
    params.default_port_value = "btcpp_string";
    factory_->registerNodeType<ReceiveString>("ReceiveString", params);

This is due to passing this->shared_from_this() as a rclcpp node pointer, creating a situation where the main ROS node holds a reference to a BT node and vice versa, leading to a circular dependency. One potential solution is to use weak pointers for BT nodes. It worked for me in a simple example similar to subscriber_test. Not sure if there are any obvious drawbacks of this approach. What are your thoughts on this?

Also, what's your opinion on creating and ticking BT tree inside ROS node e.g. using ROS timer? Currently, I use ROS parameters to load BT nodes via plugins, and moving this outside the node might get complicated.

Subscriber node is unable to register due to undefined symbol

I have been trying to create some basic examples of each of the node types, all work fine apart from the subscriber.
You say to register the node using RegisterRosAction<DerivedClass>(factory, params); However nowhere in either the Behavortree.cpp or the Behavortree.ros2 does RegisterRosAction exist. I'm assuming this comment was taken from your Ros 1 implementation.
I have been using the below in order to register my plugins which has worked for actions, services and publishers however does not work for subscribers.
RegisterRosNode(factory, "install/bt_ros_tests/lib/liblistening_plugin.so", params);
It builds, however returns a runtime error terminate called after throwing an instance of 'BT::RuntimeError' what(): Could not load library: install/bt_ros_tests/lib/liblistening_plugin.so: undefined symbol: _ZN11ListenerSub6onTickERKSt10shared_ptrIN8std_msgs3msg7String_ISaIvEEEE
Any help as to why would be much appreciated, all my code is base very closely to your examples and am happy to share part if needed

Getting rcutils_set_error_state for the given examples

Hi,

First of all, thank you for providing such a wrapper and examples on how to use it.

I was testing the sleep_client and sleep_server example and I am getting the following error

Error for the sleep_client

>>> [rcutils|error_handling.c:108] rcutils_set_error_state()
This error state is being overwritten:

  'Service type support not from this implementation. Got:
    Handle's typesupport identifier (rosidl_typesupport_cpp) is not supported by this library, at ./src/type_support_dispatch.hpp:111
    Could not load library libbehaviortree_ros2__rosidl_typesupport_introspection_cpp.so: dlopen error: libbehaviortree_ros2__rosidl_typesupport_introspection_cpp.so: cannot open shared object file: No such file or directory, at ./src/shared_library.c:99, at ./src/type_support_dispatch.hpp:76
while fetching it, at ./src/rmw_node.cpp:4503'

with this new error message:

  'type_support is null, at ./src/rmw_node.cpp:4573'

rcutils_reset_error() should be called after error handling to avoid this.
<<<
terminate called after throwing an instance of 'rclcpp::exceptions::RCLError'
  what():  could not initialize rcl action client: type_support is null, at ./src/rmw_node.cpp:4573, at ./src/rcl/client.c:113
[1]    149016 IOT instruction (core dumped)  ./install/behaviortree_ros2/bin/sleep_client

Error for the sleep_server

>>> [rcutils|error_handling.c:108] rcutils_set_error_state()
This error state is being overwritten:

  'Service type support not from this implementation. Got:
    Handle's typesupport identifier (rosidl_typesupport_cpp) is not supported by this library, at ./src/type_support_dispatch.hpp:111
    Could not load library libbehaviortree_ros2__rosidl_typesupport_introspection_cpp.so: dlopen error: libbehaviortree_ros2__rosidl_typesupport_introspection_cpp.so: cannot open shared object file: No such file or directory, at ./src/shared_library.c:99, at ./src/type_support_dispatch.hpp:76
while fetching it, at ./src/rmw_node.cpp:4503'

with this new error message:

  'type_support is null, at ./src/rmw_node.cpp:4573'

rcutils_reset_error() should be called after error handling to avoid this.
<<<
terminate called after throwing an instance of 'rclcpp::exceptions::RCLError'
  what():  type_support is null, at ./src/rmw_node.cpp:4573, at ./src/rcl/service.c:124
[1]    149048 IOT instruction (core dumped)  ./install/behaviortree_ros2/bin/sleep_server

I tested by building the package using ROS Humble and tested the executables by calling them directly from the build directory as ./build/behaviortree_ros2/sleep_server

Am I missing something to correctly build/use this package?

onFeedback for ROS

Hey guys, anyone know if there could be a similar onFeedback function as in ROS2 OR I need to abandon BehaviorTree.ROS and implement Action(goal result feedback ,all by myself) by using BehaviorTree.CPP .
Thanks for any answer.

Constant timeout in RosServiceNode

I stumbled over a use-case where a service call is taking longer than 1 second. I do not want to globally increase the service timeout over the BT::RosNodeParams parameter as this would affect all plugins I am using.

Is there any concern about removing the const declaration of service_timeout_ variable for the ROS2 service timeout here? This way one could overwrite the value in the setRequest() function (and get the timeout value eventually over an input port).

Let me know and i will file a PR.

Bug Not To Handle ResultCallback While Canceling Goal

Hello @facontidavide ,
Firstly, i want to thank for your great work. I have found a bug in cancelGoal function in bt_action_node.hpp . When it's canceled goal, it's not waited that the goal gets result after finishing. This bug gives rise to exist the possibility of sending new goal without handling result_callback of corresponding goal. If you debug by putting following lines to according places, you understand the bug.

RCLCPP_INFO(node_->get_logger(), "RESULT_GOAL ID : %s", rclcpp_action::to_string(result.goal_id).c_str()); to line 332

RCLCPP_INFO(node_->get_logger(), "CANCEL_GOAL ID : %s", rclcpp_action::to_string(goal_handle_->get_goal_id()).c_str()); to line 431

I tried with your btcpp_ros2_samples. I run ros2 run btcpp_ros2_samples sleep_client for my fixed version and your current version of this repo.

Your output:

PrintValue: start
[INFO] [1695293776.715275411] [sleep_client]: RESULT_GOAL ID : ef75e3cbdd7c226bb1c6e2cd36a0c236
[INFO] [1695293776.715445058] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695293778.268966187] [sleep_client]: CANCEL_GOAL ID : dd28f1f6ab96854d84f85a2fdde2a2
[INFO] [1695293778.269641275] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695293780.270740751] [sleep_client]: RESULT_GOAL ID : 1dcb1f40d8421bd4f824346bfb878b
[INFO] [1695293780.270829274] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695293780.271580174] [sleep_client]: RESULT_GOAL ID : dd28f1f6ab96854d84f85a2fdde2a2
[INFO] [1695293781.773432366] [sleep_client]: CANCEL_GOAL ID : 2160659ffbfa6596de53aabe02fa9e5
[INFO] [1695293781.774070320] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695293783.775155203] [sleep_client]: RESULT_GOAL ID : 956327c886842cca4da9a83ba9a375df
[INFO] [1695293783.775239360] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695293783.775899932] [sleep_client]: RESULT_GOAL ID : 2160659ffbfa6596de53aabe02fa9e5
[INFO] [1695293785.275925308] [sleep_client]: CANCEL_GOAL ID : 45126ab2219b8e3861537d843c991
[INFO] [1695293785.276538092] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695293787.277526354] [sleep_client]: RESULT_GOAL ID : dd451ef6592bf2b73c6935dc4cf70d6
[INFO] [1695293787.277612585] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695293787.278468184] [sleep_client]: RESULT_GOAL ID : 45126ab2219b8e3861537d843c991
[INFO] [1695293788.783090360] [sleep_client]: CANCEL_GOAL ID : acbe325acc152b348c40c6a96178fd8c
[INFO] [1695293788.783685525] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695293790.784665696] [sleep_client]: RESULT_GOAL ID : 3a38ae54fb365a81490d7737b49a8
[INFO] [1695293790.784733127] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695293790.785492102] [sleep_client]: RESULT_GOAL ID : acbe325acc152b348c40c6a96178fd8c
[INFO] [1695293792.286093402] [sleep_client]: CANCEL_GOAL ID : 8688086cee0346310fbdc595213e
[INFO] [1695293792.286707982] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted

My Output:

PrintValue: start
[INFO] [1695295344.142244774] [sleep_client]: RESULT_GOAL ID : f9f6d789aa5114b51857d44126bc662
[INFO] [1695295344.142411571] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695295345.643276985] [sleep_client]: CANCEL_GOAL ID : e5261931333755dc253252dfb2e861d9
[INFO] [1695295345.743568867] [sleep_client]: RESULT_GOAL ID : e5261931333755dc253252dfb2e861d9
[INFO] [1695295345.743714297] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695295347.744981916] [sleep_client]: RESULT_GOAL ID : c2baf96cb5b79a7674a94352a087383f
[INFO] [1695295347.745074847] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695295349.264379159] [sleep_client]: CANCEL_GOAL ID : 5454cc3c188455c138a998b5d9280
[INFO] [1695295349.346670567] [sleep_client]: RESULT_GOAL ID : 5454cc3c188455c138a998b5d9280
[INFO] [1695295349.346815698] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695295351.348284469] [sleep_client]: RESULT_GOAL ID : 1ab7cf57e54120eaf0fceeddd65562c4
[INFO] [1695295351.348366768] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695295352.868317555] [sleep_client]: CANCEL_GOAL ID : f276a469da4f1365db2cd3bc79961469
[INFO] [1695295352.949544723] [sleep_client]: RESULT_GOAL ID : f276a469da4f1365db2cd3bc79961469
[INFO] [1695295352.949629849] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695295354.950950643] [sleep_client]: RESULT_GOAL ID : e99b9b5b9c75a854f22234197362ff9
[INFO] [1695295354.951044802] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695295356.471232107] [sleep_client]: CANCEL_GOAL ID : 69b57badefdb26a65bafeaca7b2a4537
[INFO] [1695295356.552313938] [sleep_client]: RESULT_GOAL ID : 69b57badefdb26a65bafeaca7b2a4537
[INFO] [1695295356.552409254] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted
PrintValue: start
[INFO] [1695295358.553869406] [sleep_client]: RESULT_GOAL ID : eee66aede6adfc71a2326b49d98fd677
[INFO] [1695295358.553942439] [sleep_client]: sleepA: onResultReceived. Done = true
PrintValue: sleep completed
[INFO] [1695295360.058143973] [sleep_client]: CANCEL_GOAL ID : 6cafc4d991f0ff796b338252e123618
[INFO] [1695295360.155102960] [sleep_client]: RESULT_GOAL ID : 6cafc4d991f0ff796b338252e123618
[INFO] [1695295360.155213032] [sleep_client]: sleepB: onHalt
PrintValue: sleep aborted

Shortly, Without waiting result, it cancels goal with new id, but runs result_callback with previous id.

In addition, i have several questions for you:

  • Why do you use emitWakeUpSignal in result_callback and feedback? I think you use for preventing race condition etc. But i would love to hear from you as well.
  • Do you think it is necessary to remove controlling if goal_handle_.goal_id == result.goal_id after this fix. I haven't been sure. Could you inform me about it? Because it looks like navigation2 stack is little bit sufferer by this issue.

If all of it is problem or bug, i will pull request to ros-planning/navigation2 stack too.

Thank you in advance.

[Docker] failing build due to emitStateChanged() not being declared in the BehaviorTree.ROS2 package?

Hi all,
I have a docker image with ros2 and Im trying to build behaviortree and groot together to start making some cool stuff. I was wondering if you guys have any idea why my build is failing:

The Dockerfile that does the behaviortree part of the build (using ubuntu 20.04 and ros2 foxy):

RUN mkdir -p $HOME/$EBOTS_ARMS_WORKSPACE/behaviortree_cpp && \
    git clone \
    https://github.com/BehaviorTree/BehaviorTree.CPP.git \
    $HOME/$EBOTS_ARMS_WORKSPACE/behaviortree_cpp && \
    cd $HOME/$EBOTS_ARMS_WORKSPACE/behaviortree_cpp && \
    mkdir build && \
    cd build && \
    cmake .. && \
    make && \
    make install

RUN mkdir -p $HOME/$EBOTS_ARMS_WORKSPACE/behaviortree_ros && \
    git clone \
    https://github.com/BehaviorTree/BehaviorTree.ROS2.git \
    $HOME/$EBOTS_ARMS_WORKSPACE/behaviortree_ros

# I don't think the submodules are necessary if we do the rosdep install? *unsure*
RUN mkdir -p $HOME/$EBOTS_ARMS_WORKSPACE/groot && \
    git clone \
    https://github.com/BehaviorTree/Groot \
    $HOME/$EBOTS_ARMS_WORKSPACE/groot && \
    cd $HOME/$EBOTS_ARMS_WORKSPACE/groot && \
    git submodule update --init --recursive && \
    mkdir build && \
    cd build && \
    cmake .. && \
    make

# Install dependencies of all packages
RUN cd $HOME/$EBOTS_ARMS_WORKSPACE && \
    rosdep install -r \
    --from-paths . -y

The error:

--- stderr: behaviortree_ros2
In file included from /home/ebots/workspace/behaviortree_ros/test/test_client.cpp:1:
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp: In lambda function:
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:217:7: error: there are no arguments to ‘emitStateChanged’ that depend on a template parameter, so a declaration of ‘emitStateChanged’ must be available [-fpermissive]
  217 |       emitStateChanged();
      |       ^~~~~~~~~~~~~~~~
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:217:7: note: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp: In lambda function:
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:225:7: error: there are no arguments to ‘emitStateChanged’ that depend on a template parameter, so a declaration of ‘emitStateChanged’ must be available [-fpermissive]
  225 |       emitStateChanged();
      |       ^~~~~~~~~~~~~~~~
/home/ebots/workspace/behaviortree_ros/test/test_client.cpp: In function ‘int main(int, char**)’:
/home/ebots/workspace/behaviortree_ros/test/test_client.cpp:112:10: error: ‘class BT::Tree’ has no member named ‘sleep’
  112 |     tree.sleep(std::chrono::milliseconds(100));
      |          ^~~~~
In file included from /home/ebots/workspace/behaviortree_ros/test/test_client.cpp:1:
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp: In instantiation of ‘BT::NodeStatus BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]’:
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:177:14:   required from here
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:217:23: error: ‘emitStateChanged’ was not declared in this scope
  217 |       emitStateChanged();
      |       ~~~~~~~~~~~~~~~~^~
/home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:225:23: error: ‘emitStateChanged’ was not declared in this scope
  225 |       emitStateChanged();
      |       ~~~~~~~~~~~~~~~~^~
In file included from /usr/include/c++/9/future:48,
                 from /opt/ros/foxy/include/rclcpp/executors.hpp:18,
                 from /home/ebots/workspace/behaviortree_ros/include/behaviortree_ros2/bt_action_node.hpp:6,
                 from /home/ebots/workspace/behaviortree_ros/test/test_client.cpp:1:
/usr/include/c++/9/bits/std_function.h: At global scope:
/usr/include/c++/9/bits/std_function.h:523:2: error: ‘std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> std::function<_Res(_ArgTypes ...)>::operator=(_Functor&&) [with _Functor = BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep>::SharedPtr, std::shared_ptr<const behaviortree_ros2::action::Sleep_Feedback_<std::allocator<void> > >)>; _Res = void; _ArgTypes = {std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> >, std::shared_ptr<const behaviortree_ros2::action::Sleep_Feedback_<std::allocator<void> > >}; std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> = std::function<void(std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> >, std::shared_ptr<const behaviortree_ros2::action::Sleep_Feedback_<std::allocator<void> > >)>&]’, declared using local type ‘BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep>::SharedPtr, std::shared_ptr<const behaviortree_ros2::action::Sleep_Feedback_<std::allocator<void> > >)>’, is used but never defined [-fpermissive]
  523 |  operator=(_Functor&& __f)
      |  ^~~~~~~~
/usr/include/c++/9/bits/std_function.h:523:2: error: ‘std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> std::function<_Res(_ArgTypes ...)>::operator=(_Functor&&) [with _Functor = BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(const WrappedResult&)>; _Res = void; _ArgTypes = {const rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep>::WrappedResult&}; std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> = std::function<void(const rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep>::WrappedResult&)>&]’, declared using local type ‘BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(const WrappedResult&)>’, is used but never defined [-fpermissive]
/usr/include/c++/9/bits/std_function.h:523:2: error: ‘std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> std::function<_Res(_ArgTypes ...)>::operator=(_Functor&&) [with _Functor = BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(std::shared_future<std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> > >)>; _Res = void; _ArgTypes = {std::shared_future<std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> > >}; std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_Callable<typename std::decay<_Functor>::type>, std::function<_Res(_ArgTypes ...)>&> = std::function<void(std::shared_future<std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> > >)>&]’, declared using local type ‘BT::RosActionNode<ActionT>::tick() [with ActionT = behaviortree_ros2::action::Sleep]::<lambda(std::shared_future<std::shared_ptr<rclcpp_action::ClientGoalHandle<behaviortree_ros2::action::Sleep> > >)>’, is used but never defined [-fpermissive]
make[2]: *** [CMakeFiles/test_client.dir/build.make:63: CMakeFiles/test_client.dir/test/test_client.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:126: CMakeFiles/test_client.dir/all] Error 2
make: *** [Makefile:141: all] Error 2
---
Failed   <<< behaviortree_ros2 [31.0s, exited with code 2]
Aborted  <<< behaviortree_cpp [1min 50s]

Summary: 5 packages finished [1min 51s]
  1 package failed: behaviortree_ros2
  6 packages aborted: behaviortree_cpp control_msgs controller_manager_msgs groot industrial_msgs rviz_visual_tools
  2 packages had stderr output: behaviortree_cpp behaviortree_ros2
  10 packages not processed
The command '/bin/sh -c cd $HOME/$EBOTS_ARMS_WORKSPACE &&     . /opt/ros/$ROS_DISTRO/setup.sh &&     colcon build --allow-overriding control_msgs      controller_interface hardware_interface     joint_state_publisher joint_state_publisher_gui     controller_manager ros2_control_test_assets --cmake-args -DCMAKE_BUILD_TYPE=Release' returned a non-zero code: 2

High CPU Usage with ROS Action BT Nodes

Bug Report

Required Info

  • Docker version:
    24.0.6
  • Installation:
    • ROS2 docker container from base image: osrf/ros:humble-desktop
    • Ubuntu 20.04
  • Version or commit hash:
    b8ee381
  • DDS Implementation:
    FastDDS (default)
  • ROS2 Distribution:
    Humble
  • Processor info
    • Processor: Intel® Core™ i7-9700T CPU @ 2.00GHZ
    • CPU(s): 8
    • Socket(s): 1
    • Core(s) per socket: 1

Steps to reproduce issue

Run sleep_client node from the btcpp_ros2_samples/ package. Also, sleep_server node must be running in parallel.
We identified three scenarios of BT diagrams for our tests, which we document below. In these scenarios, we must differentiate between

  1. the SleepAction BT node, which is a ROS Action Client node as written in the sleep_action.cpp module, communicating with the ROS Action Server running in the sleep_server node.

and

  1. the SleepNormal BT node, which is one of the default BT nodes in the BT.CPP library, written in pure cpp, with source code found in sleep_node.cpp.

Scenario 1: SleepAction + SleepNormal

Tree.xml:

<root BTCPP_format="4">
    <BehaviorTree>
        <Sequence>
            <PrintValue message="start sleepAction"/>
            <SleepAction name="sleepA" msec="5000"/>
            <PrintValue message="sleepAction completed"/>
            <PrintValue message="start sleepNormal"/>
            <Sleep msec="6000"/>
            <PrintValue message="sleepNormal completed"/>
        </Sequence>
    </BehaviorTree>
</root>

Expected Behavior: While the tree executes the sleepAction node, the CPU should spike a little, as communication between nodes (action client and server nodes) will be happening, but afterwards when the sleepAction node succeeds, the sleepNormal node should execute and the CPU usage should be back to low values, as there is no ROS communication happening.

Actual Behavior: When the tree is executing the sleepAction node, the CPU consumption rises up to 44%. When the sleepNormal node is being executed, the CPU consumption reaches 101%, and remains high until the sleepNormal succeeds causing the tree to succeed and the client.cpp script to exit.

Scenario 2: SleepAction

Tree.xml:

<root BTCPP_format="4">
    <BehaviorTree>
        <Sequence>
            <PrintValue message="start sleepAction"/>
            <SleepAction name="sleepA" msec="5000"/>
            <PrintValue message="sleepAction completed"/>
        </Sequence>
    </BehaviorTree>
</root>

Expected Behavior: While the tree executes the sleepAction node, the CPU should spike a little, as communication between nodes (action client and server nodes) will be happening, but afterwards when the sleepAction node succeeds, the sleepNormal node should execute and the CPU usage should be back to low values.

Actual Behavior: When the tree is executing the sleepAction node, the CPU consumption rises up to 39% until the node succeeds causing the tree to succeed and the client.cpp script to exit.

Scenario 3: SleepNormal

Tree.xml:

<root BTCPP_format="4">
    <BehaviorTree>
        <Sequence>
            <PrintValue message="start sleepNormal"/>
            <Sleep msec="6000"/>
            <PrintValue message="sleepNormal completed"/>
        </Sequence>
    </BehaviorTree>
</root>

Expected Behavior: While the tree executes the sleepNormal node, the CPU should remain small, as there is no communication through ROS.

Actual Behavior: When the sleepNormal node is being executed, the CPU consumption reaches 5%.

Additional Feedback

Scenario 1 reveals that there is an issue with BT Nodes acting as ROS Action Clients, especially when we note that we tested the same tree, but replaced the sleepAction with an addTwoIntsService BT Node acting as a ROS Service Client (not featured here or in the original source code of BT.ROS2) , and the CPU usage was very low during the execution of the subsequent sleepNormal node.

Scenario 3 compared to Scenario 1 shows what the baseline CPU usage should be while a sleepNormal BT node is executed, namely it should be around 5% as in Scenario 3, but instead in Scenario 1, following (not before) the execution of a sleepAction node (or any ROS Action based BT Node for that matter), the CPU hits 100%, and stays there, until another ROS Action BT Node is executed, at which point CPU consumption drops to around 40%, before shooting back up to 100% when not executing a ROS Action BT Node.

Fail to compile sleep_client.cpp with latest master of BT.CPP

The changes introduced by BehaviorTree/BehaviorTree.CPP@77c0571 broke this package.

While compiling I get the following error

Starting >>> behaviortree_ros2
--- stderr: behaviortree_ros2
/usr/bin/ld: CMakeFiles/sleep_client.dir/test/sleep_client.cpp.o: in function `std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, BT::PortInfo> BT::InputPort<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string_view<char, std::char_traits<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string_view<char, std::char_traits<char> >)':
sleep_client.cpp:(.text._ZN2BT9InputPortINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEESt4pairIS6_NS_8PortInfoEESt17basic_string_viewIcS4_ERKT_SB_[_ZN2BT9InputPortINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEESt4pairIS6_NS_8PortInfoEESt17basic_string_viewIcS4_ERKT_SB_]+0x7d): undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > BT::toStr<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
gmake[2]: *** [CMakeFiles/sleep_client.dir/build.make:172: sleep_client] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:711: CMakeFiles/sleep_client.dir/all] Error 2
gmake: *** [Makefile:146: all] Error 2
---
Failed   <<< behaviortree_ros2 [2.97s, exited with code 2]

From what I can see, it seems that the way the providedBasicPorts was generated is not valid anymore.

If I change the InputPort definition like this:

-      InputPort<std::string>("action_name", "__default__placeholder__", "Action server name")
+      InputPort<std::string>("action_name", "Action server name")

the issue is fixed and no side effects are present.

foxy support

in README.md it says this is supported after galactic. I assume there is no planned support for foxy, I am just curious what is the main reason for this

Tree calling other trees design question

I am facing a situation of the following form: There exist one central pc, one robot of type A, and one robot of type B. The pc is designed to be running a BT. That BT needs, on occasion, among other things, to make robot A and robot B perform certain actions, e.g. check self diagnostics, initialise navigation goals, make them navigate somewhere, etc.

At this point I have only bundled up all actions that pertain to each robot type to separate BTs, i.e. there are 2 BTs: BT_A and BT_B. My question is: how would it be best in your opinion to implement BT_pc so that it calls functionalities (tree nodes) of BT_A and BT_B? Is something like this even feasible? Additionally, is it feasible given that all trees run on different machines?

I understand that the answer to this question is part ROS-2-implementation-specific and part Behavior-tree-related. If you believe that it is best suited for the main BehaviorTree repository, or elsewhere, please point it out to me. Thank you.

goal rejected exception in bt_action_node

Currently, the bt_action_node throws an exception when the action server rejects the goal, see here.

I encountered some use cases where a variable is generated from a node, written through an output to the blackboard, passed as an input to an action node and then used in the goal request. This runtime generation of the goal request can lead to situations where the action server rejects it. A goal rejection could however be resolved in the behavior tree through a fallback etc. if the bt_action_node would not throw but return NodeStatus::Failure instead.

Let me know if you agree so i can file a PR. thanks

rosdep cannot locate behaviortree_cpp dependecy.

Hello!
I am struggeling to integrate behaviortree_cpp version 4.0.1 into my ROS2 package. I cannot even pass the first step, which would be including the dependencies in CMakeLists.txt and package.xml.

With "rosdep install -i --from-path src --rosdistro humble -y" I am always getting the error:
ERROR: the following packages/stacks could not have their rosdep keys resolved to system dependencies: behavior_tree_example: Cannot locate rosdep definition for [behaviortree_cpp]

If I add the behaviortree_cpp_v3 dependencies, there is no problem at all.
Is it somehow not integrated, i.e. do I have to integrate it manually?

Thanks in advance,
best,
hans

sample_bt_executor.yaml contains missing plugin path

This directory appears not to exist:

With BehaviorTree.CPP installed viasudo apt install ros-humble-behaviortree-cpp on Ubuntu 22.04.

Running the launchfile results in the following output, and I couldn't find any reference to bt_plugins in any BehaviorTree.CPP CMakeLists.txt:

$ ros2 launch btcpp_ros2_samples sample_bt_executor.launch.xml
[INFO] [launch]: All log files can be found below /home/ros/.ros/log/2024-05-15-11-04-28-365715-ros-desktop-72895
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [sample_bt_executor-1]: process started with pid [72896]
[INFO] [sleep_server-2]: process started with pid [72898]
[sample_bt_executor-1] [INFO] [1715796268.500210508] [bt_action_server]: Starting Action Server: behavior_server
[sample_bt_executor-1] terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
[sample_bt_executor-1]   what():  filesystem error: directory iterator cannot open directory: No such file or directory [/opt/ros/humble/share/behaviortree_cpp/bt_plugins]
[ERROR] [sample_bt_executor-1]: process has died [pid 72896, exit code -6, cmd '/home/ros/prebuilt_ws/install/btcpp_ros2_samples/lib/btcpp_ros2_samples/sample_bt_executor --ros-args --params-file /home/ros/prebuilt_ws/install/btcpp_ros2_samples/share/btcpp_ros2_samples/config/sample_bt_executor.yaml'].
^C[WARNING] [launch]: user interrupted with ctrl-c (SIGINT)
[sleep_server-2] [INFO] [1715796270.557774044] [rclcpp]: signal_handler(signum=2)
[INFO] [sleep_server-2]: process has finished cleanly [pid 72898]

Segmentation Fault when ticking the tree

Hi everyone.
I'm having some issues with my first behavior tree implementation.

I created a simple action client, that inherits from the RosActionNode class, in order to send target poses to a 6DOF manipulator but as soon as this client is ticked, I get a segmentation fault.

The main node, i.e. the one that "builds" and ticks the behavior tree, is the following:

#include "segmentation_bt_client.hpp"
#include "move_bt_action.hpp"

#include "behaviortree_cpp/bt_factory.h"
#include "behaviortree_cpp/loggers/bt_cout_logger.h"
#include "behaviortree_ros2/plugins.hpp"

using namespace BT;


int main(int argc, char** argv)
{

    const std::string bt_xml_path = "/home/diruscio/ros2_proj/src/parcel_pkg/config/bt_xml.xml";
    rclcpp::init(argc,argv);

    auto node = std::make_shared<rclcpp::Node>("bt_main");
    node->declare_parameter("bt_xml", rclcpp::ParameterValue(bt_xml_path));
    std::string bt_xml;
    node->get_parameter("bt_xml", bt_xml);

    
    RosNodeParams params_action; 
    params_action.nh = node;
    params_action.default_port_value="move_action_server";


    RosNodeParams params_service;
    params_service.nh = node;
    params_service.default_port_value = "cloud_segmentation_server";

    BehaviorTreeFactory factory;
    factory.registerNodeType<MoveToTargetAction>("MoveToTarget", params_action);    
    factory.registerNodeType<SegmentationClient>("SegmentationClient", params_service);
    
    auto tree = factory.createTreeFromFile(bt_xml);
    tree.tickWhileRunning();
    std::this_thread::sleep_for(std::chrono::milliseconds(10000));

    return 0;
}

And the XML file is:

<root BTCPP_format="4" >
    <BehaviorTree ID="MainTree">
        <Sequence name="root_sequence">
            <MoveToTarget     move_type="home" target_frame="{target_frame}" />
            <SegmentationClient target_frame="{target_frame}"/>
            <Repeat num_cycles="3">
                <Sequence>
                    <MoveToTarget     move_type="pick"    target_frame="{target_frame}"/>
                    <MoveToTarget     move_type="away"    target_frame="{target_frame}"/>
                    <MoveToTarget     move_type="release"    target_frame="{target_frame}"/>            
                    <SegmentationClient target_frame="{target_frame}"/>
                </Sequence>
            </Repeat>
        </Sequence>
    </BehaviorTree>
</root>

When running the main node, I get the following output:
[INFO] [1693418921.313392475] [move_client_logger]: Reading fixed poses from file: /home/diruscio/ros2_proj/src/parcel_pkg/config/fixed_positions.yaml
[INFO] [1693418921.313670412] [move_client_logger]: Fixed poses read correctly
[INFO] [1693418921.848795157] [segmentation_client_logger]: Segmentation client initialized
[INFO] [1693418921.851096244] [move_client_logger]: Reading fixed poses from file: /home/diruscio/ros2_proj/src/parcel_pkg/config/fixed_positions.yaml
[INFO] [1693418921.851268127] [move_client_logger]: Fixed poses read correctly
[INFO] [1693418921.872170030] [move_client_logger]: Reading fixed poses from file: /home/diruscio/ros2_proj/src/parcel_pkg/config/fixed_positions.yaml
[INFO] [1693418921.872526930] [move_client_logger]: Fixed poses read correctly
[INFO] [1693418921.883179841] [move_client_logger]: Reading fixed poses from file: /home/diruscio/ros2_proj/src/parcel_pkg/config/fixed_positions.yaml
[INFO] [1693418921.884537858] [move_client_logger]: Fixed poses read correctly
[INFO] [1693418921.888328717] [segmentation_client_logger]: Segmentation client initialized
[INFO] [1693418921.888423013] [move_client_logger]: Move to home position.
[INFO] [1693418921.888430763] [move_client_logger]: Move type: PTP
[INFO] [1693418921.888433997] [move_client_logger]: Speed: 1.000
[INFO] [1693418921.888438358] [move_client_logger]: Target x: -0.027
[INFO] [1693418921.888441878] [move_client_logger]: Target y: 0.744
[INFO] [1693418921.888444680] [move_client_logger]: Target z: 0.659
[INFO] [1693418921.888447384] [move_client_logger]: Target qx: -0.707
[INFO] [1693418921.888450183] [move_client_logger]: Target qy: -0.674
[INFO] [1693418921.888452922] [move_client_logger]: Target qz: 0.177
[INFO] [1693418921.888455738] [move_client_logger]: Target qw: -0.120
[ros2run]: Segmentation fault

The target pose is printed before the "setGoal" function returns "true".
The action server does not receive any request.
I sure I'm facing a trivial bug, but I have not been able to find it.

Additional information:
OS: Ubuntu 22.04
ROS Version: ROS 2 Humble
BehaviorTree.ROS2 version: 0.2.0

Allow override of QoS for RosTopicSubNode and RosTopicPubNode

Users should have a mechanism to override QoS when subscribing / publishing for topics.
@facontidavide would you prefer this to be done over the blackboard, at compilation time in the derived class, or allowing both (like for the topic name ?)

I have a local version at compile time, but since the subscriber / publisher are created in the constructor, I had to move the initialization part in a public function that take an rclcpp::QoS parameter. This add a bit of boilerplate, as the end user has to call this function in the constructor of it's derived class. If this seems sufficient to you, I can create a PR.

Thanks for this amazing library,
Cheers

How to combine `StatefulActionNode` and `RosServiceNode`?

This issue relates to issue #41.

Assume a tree consisting of a single Sequence of StatefulActionNode nodes, all belonging to class
class TickRemoteTree : public BT::StatefulActionNode:

<root BTCPP_format="4">
  <BehaviorTree ID="tree">

    <Sequence>
      <TickRemoteTree robot_name="A" var="1"/>
      <TickRemoteTree robot_name="B" var="2"/>

      <TickRemoteTree robot_name="A" var="10"/>
      <TickRemoteTree robot_name="B" var="20"/> 
    
      <TickRemoteTree robot_name="A" var="100"/>
      <TickRemoteTree robot_name="B" var="200"/>
    </Sequence>

  </BehaviorTree>
</root>

What I am trying to achieve is execute each TickRemoteTree action sequentially, that is, make robot A sleep for 1 sec, then wake up, then make robot B sleep for 2 sec, then wake up, then make robot A sleep again for 10 sec, and so on. (Essentially the same thing as BehaviorTree/BehaviorTree.CPP#242 (comment))

The way this must be achieved is by sending variable var to robots A and B via a call to a service that they provide. Assume that var specifies the amount of time each robot should sleep. Since TickRemoteTree is a StatefulActionNode, each TickRemoteTree node returns RUNNING after being ticked, and execution should not continue further than each node, since all TickRemoteTree are wrapped inside a Sequence. After waking up, each robot calls a service called tockFromRemoteTree that TickRemoteTree provides, making the node able to escape from a RUNNING state and into a SUCCESS or FAILURE state.

However, it is clear I am misunderstanding some things because what happens in practice is that

  • all six constructors get called sequentially
  • when the first robot stops sleeping and calls the tockFromRemoteTree service with a SUCCESS state, the callbacks of all six tockFromRemoteTree services of all six nodes are called, one after another, and
  • the tree stops running with a SUCCESS state, having only made robot A sleep for 1 sec

It is as though all six nodes are pointers to a single changing object, but I would like to have six different objects. What is perhaps needed is some mix between RosServiceNode and StatefulActionNode, which seem to be complementary because the latter cannot deal with services on its own (at least to my understanding and programming level). Someone would say this is why the RosActionNode exists. But what if one needed to avoid actions altogether? (actions are unbridgeable between ROS 1 and ROS2).

ros2 remapping support

is it possible to applly remappings to nodes in bt.ROS2?
currently i am launching bt codes using ExecuteProcess froM roslaunnch file

Tips to implement "long running or infinite time actions"

Hi
Thank you for providing such great library.
I am currently implemented to behaviors in ros2 and now i designed a behavior tree using back-chaining method. My problem is that some actions in my behavior tree should run for an infinite time until a certain condition changed. For example suppose I have a behavior tree like below :

image

All "Scripts" in above behavior tree are implemented as action servers and all "ScriptConditions" are implemented as SubTopics in ROS2.

The challenge I am currently facing is that as long as no one is visible for the robot, it should search for a person in the room. It means that it should run a long running or infinite action of searching until "Is Any Person Visible" condition is met and it will automatically "halt" the search action and start another infinite action of approaching that person until another condition of "Can High Five To Person" is met. After that it will "High Five and Shut Down".

I can easily implement the behaviors such that they never return success and keep running forever until they fail for some reason (for example missing the person in case of approaching that person action). The problem is that the behavior-tree send "Goal Timeout" after some time and change the action node state to failure resulting in running that action again and again.

One could argue that the action should return "success" whenever the condition is met but isn't the success condition of behavior or action already defined in behavior tree ? for example for the action "Search For A Person In The Room" to be successful, "Is Any Person Visible?" should return success.

I would be very thankful if anyone could help me with this.

sleep_server crashes on start

When running sleep_server from examples (on windows), the node crashes immediately with

$ sleep_server
[rcutils|error_handling.c:66] an error string (message, file name, or formatted message) will 
be truncated

Branch main fails for Galactic

At the time of 70aa7c9, bt_service_node.hpp fails to build for galactic due to line 267,

future_response_ = service_client_->async_send_request(request).share();

In galactic, async_send_request() returns a shared_future, so further calling .share() invokes an error (reference).

Removing the call to .share() resolved the error.

onFeedback for bt_action_node is not receiving feedback

When creating a custom RosActionNode and overriding the onFeedback method, the method is never called. Here is an example:
The .hpp file

#pragma once

#include <behaviortree_ros2/bt_action_node.hpp>
#include <iostream>

#include "behaviortree_cpp/action_node.h"
#include "dictator_behavior_tree/pose_to_string.hpp"
#include "geometry_msgs/msg/pose.hpp"
#include "nav2_msgs/action/navigate_to_pose.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"

using Nav2 = nav2_msgs::action::NavigateToPose;

namespace BT
{
class NavigateToPose : public RosActionNode<Nav2>
{
 public:
   NavigateToPose(const std::string &name, const NodeConfig &conf, const RosNodeParams &params)
       : RosActionNode<Nav2>(name, conf, params)
   {
   }

   static PortsList providedPorts()
   {
       return providedBasicPorts({InputPort<Position2D>("goal_pose")});
   }

   bool setGoal(RosActionNode::Goal &goal) override;

   NodeStatus onResultReceived(const WrappedResult &wr) override;

   NodeStatus onFailure(ActionNodeErrorCode error) override;

   NodeStatus onFeedback(const std::shared_ptr<const Feedback> feedback) override;
};
} // namespace BT

the implemented method:

NodeStatus NavigateToPose::onFeedback(const std::shared_ptr<const Feedback> feedback)
{
    std::cout << "FEEDBACK" << std::endl;
    std::stringstream ss;
    ss << "Estimated remaining time: " << feedback->estimated_time_remaining.sec << " ";
    std::cout << ss.str() << std::endl;
    return NodeStatus::RUNNING;
}

With this example, I never get an output to the terminal for the received feedback. I also validated that the action does provide a feedback by listening to the action feedback topic ros2 topic echo /navigate_to_pose/_action/feedback

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.