Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Action Server to execute trees #60

Merged
merged 12 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This repository contains useful wrappers to use ROS2 and BehaviorTree.CPP togeth

In particular, it provides a standard way to implement:

- Behavior Tree Executor with ROS Action interface.
- Action clients.
- Service Clients.
- Topic Subscribers.
Expand All @@ -15,6 +16,12 @@ Our main goals are:
- to minimize the amount of boilerplate.
- to make asynchronous Actions non-blocking.

# Documentation

- [ROS Behavior Wrappers](behaviortree_ros2/ros_behavior_wrappers.md)
- [TreeExecutionServer](behaviortree_ros2/tree_execution_server.md)
- [Sample Behaviors](btcpp_ros2_samples/README.md)

Note that this library is compatible **only** with:

- **BT.CPP** 4.1 or newer.
Expand Down
49 changes: 37 additions & 12 deletions behaviortree_ros2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,57 @@ project(behaviortree_ros2)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

set(THIS_PACKAGE_DEPS
rclcpp
rclcpp_action
ament_index_cpp
behaviortree_cpp)
behaviortree_cpp
btcpp_ros2_interfaces
generate_parameter_library
)

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED )
find_package(rclcpp_action REQUIRED )
find_package(behaviortree_cpp REQUIRED )
find_package(ament_index_cpp REQUIRED)

# This is compiled only to check if there are errors in the header file
# library will not be exported
include_directories(include)
add_library(${PROJECT_NAME} src/bt_ros2.cpp)

foreach(PACKAGE ${THIS_PACKAGE_DEPS})
find_package(${PACKAGE} REQUIRED )
endforeach()


generate_parameter_library(
bt_executor_parameters
src/bt_executor_parameters.yaml)

add_library(${PROJECT_NAME}
src/bt_ros2.cpp
src/bt_utils.cpp
src/tree_execution_server.cpp )

ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_DEPS})

target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)

target_link_libraries(${PROJECT_NAME} bt_executor_parameters)


######################################################
# INSTALL

install(DIRECTORY include/ DESTINATION include/)

ament_export_include_directories(include)
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
ament_export_dependencies(${THIS_PACKAGE_DEPS})

install(
TARGETS ${PROJECT_NAME} bt_executor_parameters
EXPORT ${PROJECT_NAME}Targets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION lib/${PROJECT_NAME})

ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME})

ament_package()
74 changes: 74 additions & 0 deletions behaviortree_ros2/bt_executor_parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Bt Server Parameters

Default Config
```yaml
bt_server:
ros__parameters:
action_name: bt_execution
behavior_trees: '{}'
groot2_port: 1667.0
plugins: '{}'
ros_plugins_timeout: 1000.0
tick_frequency: 100.0

```

## action_name

The name the Action Server takes requests from

* Type: `string`
* Default Value: "bt_execution"
* Read only: True

## tick_frequency

Frequency in Hz to tick() the Behavior tree at

* Type: `int`
* Default Value: 100
* Read only: True

*Constraints:*
- parameter must be within bounds 1

## groot2_port

Server port value to publish Groot2 messages on

* Type: `int`
* Default Value: 1667
* Read only: True

*Constraints:*
- parameter must be within bounds 1

## plugins

List of 'package_name/subfolder' containing BehaviorTree plugins to load into the factory

* Type: `string_array`
* Default Value: {}

*Constraints:*
- contains no duplicates

## ros_plugins_timeout

Timeout (ms) used in BT::RosNodeParams

* Type: `int`
* Default Value: 1000

*Constraints:*
- parameter must be within bounds 1

## behavior_trees

List of 'package_name/subfolder' containing SubTrees to load into the BehaviorTree factory

* Type: `string_array`
* Default Value: {}

*Constraints:*
- contains no duplicates
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,11 @@ inline bool RosActionNode<T>::createClient(const std::string& action_name)

std::unique_lock lk(getMutex());
auto node = node_.lock();
if(!node)
{
throw RuntimeError("The ROS node went out of scope. RosNodeParams doesn't take the "
"ownership of the node.");
}
action_client_key_ = std::string(node->get_fully_qualified_name()) + "/" + action_name;

auto& registry = getRegistry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ inline bool RosServiceNode<T>::createClient(const std::string& service_name)

std::unique_lock lk(getMutex());
auto node = node_.lock();
if(!node)
{
throw RuntimeError("The ROS node went out of scope. RosNodeParams doesn't take the "
"ownership of the node.");
}
auto client_key = std::string(node->get_fully_qualified_name()) + "/" + service_name;

auto& registry = getRegistry();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,19 @@ inline bool RosTopicSubNode<T>::createSubscriber(const std::string& topic_name)
// find SubscriberInstance in the registry
std::unique_lock lk(registryMutex());

auto shared_node = node_.lock();
subscriber_key_ =
std::string(shared_node->get_fully_qualified_name()) + "/" + topic_name;
auto node = node_.lock();
if(!node)
{
throw RuntimeError("The ROS node went out of scope. RosNodeParams doesn't take the "
"ownership of the node.");
}
subscriber_key_ = std::string(node->get_fully_qualified_name()) + "/" + topic_name;

auto& registry = getRegistry();
auto it = registry.find(subscriber_key_);
if(it == registry.end() || it->second.expired())
{
sub_instance_ = std::make_shared<SubscriberInstance>(shared_node, topic_name);
sub_instance_ = std::make_shared<SubscriberInstance>(node, topic_name);
registry.insert({ subscriber_key_, sub_instance_ });

RCLCPP_INFO(logger(), "Node [%s] created Subscriber to topic [%s]", name().c_str(),
Expand Down
79 changes: 79 additions & 0 deletions behaviortree_ros2/include/behaviortree_ros2/bt_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2024 Marq Rasmussen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright
// notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <functional>
#include <memory>
#include <thread>

// auto-generated header, created by generate_parameter_library
#include "bt_executor_parameters.hpp"

#include "btcpp_ros2_interfaces/msg/node_status.hpp"

#include "behaviortree_cpp/bt_factory.h"

#include "behaviortree_ros2/plugins.hpp"
#include "behaviortree_ros2/ros_node_params.hpp"

#include "rclcpp/rclcpp.hpp"
#include <ament_index_cpp/get_package_share_directory.hpp>

namespace BT
{
/**
* @brief Convert BT::NodeStatus into Action Server feedback message NodeStatus
*
* @param status Current status of the executing BehaviorTree
* @return NodeStatus used to publish feedback to the Action Client
*/
btcpp_ros2_interfaces::msg::NodeStatus ConvertNodeStatus(BT::NodeStatus& status);

/**
* @brief Function the uses ament_index_cpp to get the package path of the parameter specified by the user
*
* @param parameter_value String containing 'package_name/subfolder' for the directory path to look up
* @return Full path to the directory specified by the parameter_value
*/
std::string GetDirectoryPath(const std::string& parameter_value);

/**
* @brief Function to load BehaviorTree xml files from a specific directory
*
* @param factory BehaviorTreeFactory to register the BehaviorTrees into
* @param directory_path Full path to the directory to search for BehaviorTree definitions
*/
void LoadBehaviorTrees(BT::BehaviorTreeFactory& factory,
const std::string& directory_path);

/**
* @brief Function to load BehaviorTree ROS plugins (or standard BT.CPP plugins) from a specific directory
*
* @param factory BehaviorTreeFactory to register the plugins into
* @param directory_path Full path to the directory to search for BehaviorTree plugins
* @param params parameters passed to the ROS plugins
*/
void LoadRosPlugins(BT::BehaviorTreeFactory& factory, const std::string& directory_path,
BT::RosNodeParams params);

/**
* @brief Function to register all Behaviors and BehaviorTrees from user specified packages
*
* @param params ROS parameters that contain lists of packages to load
* plugins, ros_plugins and BehaviorTrees from
* @param factory BehaviorTreeFactory to register into
* @param node node pointer that is shared with the ROS based Behavior plugins
*/
void RegisterBehaviorTrees(bt_server::Params& params, BT::BehaviorTreeFactory& factory,
rclcpp::Node::SharedPtr node);

} // namespace BT
7 changes: 6 additions & 1 deletion behaviortree_ros2/include/behaviortree_ros2/plugins.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
#include "behaviortree_cpp/utils/shared_library.h"
#include "behaviortree_ros2/ros_node_params.hpp"

namespace BT
{
constexpr const char* ROS_PLUGIN_SYMBOL = "BT_RegisterRosNodeFromPlugin";
}

// Use this macro to generate a plugin for:
//
// - BT::RosActionNode
Expand Down Expand Up @@ -54,6 +59,6 @@ inline void RegisterRosNode(BT::BehaviorTreeFactory& factory,
{
BT::SharedLibrary loader(filepath.generic_string());
typedef void (*Func)(BT::BehaviorTreeFactory&, const BT::RosNodeParams&);
auto func = (Func)loader.getSymbol("BT_RegisterRosNodeFromPlugin");
auto func = (Func)loader.getSymbol(BT::ROS_PLUGIN_SYMBOL);
func(factory, params);
}
Loading
Loading