Skip to content

Commit d7890e0

Browse files
Merge pull request #60 from BehaviorTree/bt_server
Action Server to execute trees
2 parents 374edcf + 16baeb5 commit d7890e0

26 files changed

+1197
-19
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This repository contains useful wrappers to use ROS2 and BehaviorTree.CPP togeth
55

66
In particular, it provides a standard way to implement:
77

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

19+
# Documentation
20+
21+
- [ROS Behavior Wrappers](behaviortree_ros2/ros_behavior_wrappers.md)
22+
- [TreeExecutionServer](behaviortree_ros2/tree_execution_server.md)
23+
- [Sample Behaviors](btcpp_ros2_samples/README.md)
24+
1825
Note that this library is compatible **only** with:
1926

2027
- **BT.CPP** 4.1 or newer.

behaviortree_ros2/CMakeLists.txt

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,57 @@ project(behaviortree_ros2)
33

44
set(CMAKE_CXX_STANDARD 17)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6-
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
76

87
set(THIS_PACKAGE_DEPS
98
rclcpp
109
rclcpp_action
1110
ament_index_cpp
12-
behaviortree_cpp)
11+
behaviortree_cpp
12+
btcpp_ros2_interfaces
13+
generate_parameter_library
14+
)
1315

1416
find_package(ament_cmake REQUIRED)
15-
find_package(rclcpp REQUIRED )
16-
find_package(rclcpp_action REQUIRED )
17-
find_package(behaviortree_cpp REQUIRED )
18-
find_package(ament_index_cpp REQUIRED)
19-
20-
# This is compiled only to check if there are errors in the header file
21-
# library will not be exported
22-
include_directories(include)
23-
add_library(${PROJECT_NAME} src/bt_ros2.cpp)
17+
18+
foreach(PACKAGE ${THIS_PACKAGE_DEPS})
19+
find_package(${PACKAGE} REQUIRED )
20+
endforeach()
21+
22+
23+
generate_parameter_library(
24+
bt_executor_parameters
25+
src/bt_executor_parameters.yaml)
26+
27+
add_library(${PROJECT_NAME}
28+
src/bt_ros2.cpp
29+
src/bt_utils.cpp
30+
src/tree_execution_server.cpp )
31+
2432
ament_target_dependencies(${PROJECT_NAME} ${THIS_PACKAGE_DEPS})
2533

34+
target_include_directories(${PROJECT_NAME} PUBLIC
35+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
36+
$<INSTALL_INTERFACE:include>)
37+
38+
target_link_libraries(${PROJECT_NAME} bt_executor_parameters)
39+
40+
2641
######################################################
2742
# INSTALL
2843

2944
install(DIRECTORY include/ DESTINATION include/)
3045

31-
ament_export_include_directories(include)
46+
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
3247
ament_export_dependencies(${THIS_PACKAGE_DEPS})
3348

49+
install(
50+
TARGETS ${PROJECT_NAME} bt_executor_parameters
51+
EXPORT ${PROJECT_NAME}Targets
52+
ARCHIVE DESTINATION lib
53+
LIBRARY DESTINATION lib
54+
RUNTIME DESTINATION lib/${PROJECT_NAME})
55+
56+
ament_export_include_directories(include)
57+
ament_export_libraries(${PROJECT_NAME})
58+
3459
ament_package()
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Bt Server Parameters
2+
3+
Default Config
4+
```yaml
5+
bt_server:
6+
ros__parameters:
7+
action_name: bt_execution
8+
behavior_trees: '{}'
9+
groot2_port: 1667.0
10+
plugins: '{}'
11+
ros_plugins_timeout: 1000.0
12+
tick_frequency: 100.0
13+
14+
```
15+
16+
## action_name
17+
18+
The name the Action Server takes requests from
19+
20+
* Type: `string`
21+
* Default Value: "bt_execution"
22+
* Read only: True
23+
24+
## tick_frequency
25+
26+
Frequency in Hz to tick() the Behavior tree at
27+
28+
* Type: `int`
29+
* Default Value: 100
30+
* Read only: True
31+
32+
*Constraints:*
33+
- parameter must be within bounds 1
34+
35+
## groot2_port
36+
37+
Server port value to publish Groot2 messages on
38+
39+
* Type: `int`
40+
* Default Value: 1667
41+
* Read only: True
42+
43+
*Constraints:*
44+
- parameter must be within bounds 1
45+
46+
## plugins
47+
48+
List of 'package_name/subfolder' containing BehaviorTree plugins to load into the factory
49+
50+
* Type: `string_array`
51+
* Default Value: {}
52+
53+
*Constraints:*
54+
- contains no duplicates
55+
56+
## ros_plugins_timeout
57+
58+
Timeout (ms) used in BT::RosNodeParams
59+
60+
* Type: `int`
61+
* Default Value: 1000
62+
63+
*Constraints:*
64+
- parameter must be within bounds 1
65+
66+
## behavior_trees
67+
68+
List of 'package_name/subfolder' containing SubTrees to load into the BehaviorTree factory
69+
70+
* Type: `string_array`
71+
* Default Value: {}
72+
73+
*Constraints:*
74+
- contains no duplicates

behaviortree_ros2/include/behaviortree_ros2/bt_action_node.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ inline bool RosActionNode<T>::createClient(const std::string& action_name)
316316

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

321326
auto& registry = getRegistry();

behaviortree_ros2/include/behaviortree_ros2/bt_service_node.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ inline bool RosServiceNode<T>::createClient(const std::string& service_name)
282282

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

287292
auto& registry = getRegistry();

behaviortree_ros2/include/behaviortree_ros2/bt_topic_sub_node.hpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,19 @@ inline bool RosTopicSubNode<T>::createSubscriber(const std::string& topic_name)
249249
// find SubscriberInstance in the registry
250250
std::unique_lock lk(registryMutex());
251251

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

256260
auto& registry = getRegistry();
257261
auto it = registry.find(subscriber_key_);
258262
if(it == registry.end() || it->second.expired())
259263
{
260-
sub_instance_ = std::make_shared<SubscriberInstance>(shared_node, topic_name);
264+
sub_instance_ = std::make_shared<SubscriberInstance>(node, topic_name);
261265
registry.insert({ subscriber_key_, sub_instance_ });
262266

263267
RCLCPP_INFO(logger(), "Node [%s] created Subscriber to topic [%s]", name().c_str(),
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2024 Marq Rasmussen
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
6+
// permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright
7+
// notice and this permission notice shall be included in all copies or substantial portions of the Software.
8+
//
9+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
10+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
11+
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
12+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13+
14+
#include <functional>
15+
#include <memory>
16+
#include <thread>
17+
18+
// auto-generated header, created by generate_parameter_library
19+
#include "bt_executor_parameters.hpp"
20+
21+
#include "btcpp_ros2_interfaces/msg/node_status.hpp"
22+
23+
#include "behaviortree_cpp/bt_factory.h"
24+
25+
#include "behaviortree_ros2/plugins.hpp"
26+
#include "behaviortree_ros2/ros_node_params.hpp"
27+
28+
#include "rclcpp/rclcpp.hpp"
29+
#include <ament_index_cpp/get_package_share_directory.hpp>
30+
31+
namespace BT
32+
{
33+
/**
34+
* @brief Convert BT::NodeStatus into Action Server feedback message NodeStatus
35+
*
36+
* @param status Current status of the executing BehaviorTree
37+
* @return NodeStatus used to publish feedback to the Action Client
38+
*/
39+
btcpp_ros2_interfaces::msg::NodeStatus ConvertNodeStatus(BT::NodeStatus& status);
40+
41+
/**
42+
* @brief Function the uses ament_index_cpp to get the package path of the parameter specified by the user
43+
*
44+
* @param parameter_value String containing 'package_name/subfolder' for the directory path to look up
45+
* @return Full path to the directory specified by the parameter_value
46+
*/
47+
std::string GetDirectoryPath(const std::string& parameter_value);
48+
49+
/**
50+
* @brief Function to load BehaviorTree xml files from a specific directory
51+
*
52+
* @param factory BehaviorTreeFactory to register the BehaviorTrees into
53+
* @param directory_path Full path to the directory to search for BehaviorTree definitions
54+
*/
55+
void LoadBehaviorTrees(BT::BehaviorTreeFactory& factory,
56+
const std::string& directory_path);
57+
58+
/**
59+
* @brief Function to load BehaviorTree ROS plugins (or standard BT.CPP plugins) from a specific directory
60+
*
61+
* @param factory BehaviorTreeFactory to register the plugins into
62+
* @param directory_path Full path to the directory to search for BehaviorTree plugins
63+
* @param params parameters passed to the ROS plugins
64+
*/
65+
void LoadRosPlugins(BT::BehaviorTreeFactory& factory, const std::string& directory_path,
66+
BT::RosNodeParams params);
67+
68+
/**
69+
* @brief Function to register all Behaviors and BehaviorTrees from user specified packages
70+
*
71+
* @param params ROS parameters that contain lists of packages to load
72+
* plugins, ros_plugins and BehaviorTrees from
73+
* @param factory BehaviorTreeFactory to register into
74+
* @param node node pointer that is shared with the ROS based Behavior plugins
75+
*/
76+
void RegisterBehaviorTrees(bt_server::Params& params, BT::BehaviorTreeFactory& factory,
77+
rclcpp::Node::SharedPtr node);
78+
79+
} // namespace BT

behaviortree_ros2/include/behaviortree_ros2/plugins.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
#include "behaviortree_cpp/utils/shared_library.h"
2121
#include "behaviortree_ros2/ros_node_params.hpp"
2222

23+
namespace BT
24+
{
25+
constexpr const char* ROS_PLUGIN_SYMBOL = "BT_RegisterRosNodeFromPlugin";
26+
}
27+
2328
// Use this macro to generate a plugin for:
2429
//
2530
// - BT::RosActionNode
@@ -54,6 +59,6 @@ inline void RegisterRosNode(BT::BehaviorTreeFactory& factory,
5459
{
5560
BT::SharedLibrary loader(filepath.generic_string());
5661
typedef void (*Func)(BT::BehaviorTreeFactory&, const BT::RosNodeParams&);
57-
auto func = (Func)loader.getSymbol("BT_RegisterRosNodeFromPlugin");
62+
auto func = (Func)loader.getSymbol(BT::ROS_PLUGIN_SYMBOL);
5863
func(factory, params);
5964
}

0 commit comments

Comments
 (0)