Skip to content

Commit 40a67a4

Browse files
committed
Better interop
1 parent f9204a6 commit 40a67a4

File tree

10 files changed

+566
-292
lines changed

10 files changed

+566
-292
lines changed

.pre-commit-config.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v5.0.0
4+
hooks:
5+
- id: check-added-large-files
6+
- id: check-ast
7+
- id: check-builtin-literals
8+
- id: check-byte-order-marker
9+
- id: check-case-conflict
10+
- id: check-docstring-first
11+
- id: check-executables-have-shebangs
12+
- id: check-json
13+
- id: check-merge-conflict
14+
- id: check-symlinks
15+
- id: check-toml
16+
- id: check-vcs-permalinks
17+
- id: check-xml
18+
- id: check-yaml
19+
args: ['--allow-multiple-documents']
20+
- id: debug-statements
21+
- id: destroyed-symlinks
22+
- id: detect-private-key
23+
- id: end-of-file-fixer
24+
- id: fix-byte-order-marker
25+
- id: forbid-new-submodules
26+
- id: mixed-line-ending
27+
- id: name-tests-test
28+
- id: pretty-format-json
29+
args: ['--autofix']
30+
- id: requirements-txt-fixer
31+
- id: sort-simple-yaml
32+
- id: trailing-whitespace
33+
- repo: https://github.com/astral-sh/ruff-pre-commit
34+
rev: v0.6.9
35+
hooks:
36+
- id: ruff
37+
args: ['--output-format=full', '--fix', '--config', 'pyproject.toml']
38+
- id: ruff-format
39+
- repo: https://github.com/pre-commit/mirrors-clang-format
40+
rev: v15.0.7
41+
hooks:
42+
- id: clang-format

CMakeLists.txt

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,47 @@ find_package(pybind11_vendor REQUIRED)
1414
find_package(pybind11 REQUIRED)
1515
find_package(fmt REQUIRED)
1616
find_package(py_binding_tools REQUIRED)
17+
find_package(rclcpp REQUIRED)
1718

18-
ament_python_install_package(behaviortree_py PACKAGE_DIR behaviortree_py)
19-
20-
add_library(${PROJECT_NAME}_headers INTERFACE)
19+
# ##############################################################################
20+
# ############################# Main C++ library ###############################
21+
# ##############################################################################
2122

22-
target_link_libraries(${PROJECT_NAME}_headers
23-
INTERFACE behaviortree_cpp::behaviortree_cpp fmt::fmt)
23+
add_library(${PROJECT_NAME}_lib SHARED src/behaviortree_py.cpp)
24+
target_link_libraries(
25+
${PROJECT_NAME}_lib PUBLIC behaviortree_cpp::behaviortree_cpp fmt::fmt
26+
rclcpp::rclcpp)
27+
ament_target_dependencies(${PROJECT_NAME}_lib pybind11)
2428
target_include_directories(
25-
${PROJECT_NAME}_headers
26-
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
27-
"$<INSTALL_INTERFACE:include>")
29+
${PROJECT_NAME}_lib
30+
PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
31+
"$<INSTALL_INTERFACE:include>")
32+
install(DIRECTORY include/ DESTINATION include/)
33+
install(
34+
TARGETS ${PROJECT_NAME}_lib
35+
EXPORT ${PROJECT_NAME}Targets
36+
ARCHIVE DESTINATION lib
37+
LIBRARY DESTINATION lib
38+
RUNTIME DESTINATION lib
39+
INCLUDES
40+
DESTINATION include)
41+
42+
# ##############################################################################
43+
# ############################# Python bindings ################################
44+
# ##############################################################################
2845

29-
pybind11_add_module(behaviortree_py src/behaviortree_py.cpp)
46+
ament_python_install_package(behaviortree_py PACKAGE_DIR behaviortree_py)
47+
48+
pybind11_add_module(behaviortree_py src/bindings/behaviortree_py.cpp)
3049
target_compile_features(behaviortree_py PRIVATE cxx_std_20)
31-
target_link_libraries(
32-
behaviortree_py
33-
PRIVATE behaviortree_cpp::behaviortree_cpp fmt::fmt ${PROJECT_NAME}_headers
34-
py_binding_tools::py_binding_tools)
50+
target_link_libraries(behaviortree_py PRIVATE ${PROJECT_NAME}_lib)
3551

52+
install(TARGETS behaviortree_py
53+
LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/behaviortree_py)
54+
55+
# ##############################################################################
56+
# ################## Generate and install Python stubs #########################
57+
# ##############################################################################
3658
add_custom_command(
3759
TARGET behaviortree_py
3860
POST_BUILD
@@ -41,23 +63,13 @@ add_custom_command(
4163
WORKING_DIRECTORY $<TARGET_FILE_DIR:behaviortree_py>
4264
USES_TERMINAL)
4365

44-
install(DIRECTORY include/ DESTINATION include/)
45-
install(TARGETS behaviortree_py
46-
LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/behaviortree_py)
47-
48-
install(
49-
TARGETS ${PROJECT_NAME}_headers
50-
EXPORT ${PROJECT_NAME}Targets
51-
ARCHIVE DESTINATION lib
52-
LIBRARY DESTINATION lib
53-
RUNTIME DESTINATION lib
54-
INCLUDES
55-
DESTINATION include)
5666
install(
5767
FILES
5868
$<TARGET_FILE_DIR:behaviortree_py>/$<TARGET_FILE_BASE_NAME:behaviortree_py>.pyi
5969
COMPONENT python
6070
DESTINATION ${PYTHON_INSTALL_DIR}/$<TARGET_FILE_BASE_NAME:behaviortree_py>)
6171

62-
ament_export_targets(${PROJECT_NAME}Targets)
72+
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
73+
ament_export_dependencies(behaviortree_cpp pybind11_vendor fmt py_binding_tools
74+
rclcpp)
6375
ament_package()

README.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,46 @@
1-
#
1+
# BehaviorTree.PY
22

3-
sudo apt install mypy
3+
Minimal python binding to https://github.com/BehaviorTree/BehaviorTree.CPP
4+
5+
## Usage
6+
7+
By default, the library registers the built-in types. To register custom types, you need to create a shared library with the type converters and load it in python.
8+
9+
```cmake
10+
add_library(bt_custom_types SHARED src/custom_types.cpp)
11+
target_compile_features(bt_custom_types PRIVATE cxx_std_20)
12+
target_compile_definitions(bt_custom_types PRIVATE BT_PLUGIN_EXPORT)
13+
target_link_libraries(
14+
bt_custom_types PRIVATE behaviortree_py::behaviortree_py_lib
15+
py_binding_tools::py_binding_tools)
16+
install(
17+
TARGETS bt_custom_types
18+
LIBRARY DESTINATION share/${PROJECT_NAME}/bt_type_converters
19+
ARCHIVE DESTINATION share/${PROJECT_NAME}/bt_type_converters
20+
RUNTIME DESTINATION share/${PROJECT_NAME}/bt_type_converters)
21+
```
22+
23+
```cpp
24+
#include <behaviortree_py/behaviortree_py.hpp>
25+
#include <geometry_msgs/msg/pose_stamped.hpp>
26+
// For the typecasters
27+
#include <py_binding_tools/ros_msg_typecasters.h>
28+
29+
BT_REGISTER_TYPE_CONVERTER(geometry_msgs::msg::PoseStamped,
30+
geometry_msgs::msg::Pose);
31+
```
32+
33+
```python
34+
import behaviortree_py as bt
35+
...
36+
bt.load_type_converters(
37+
get_package_share_directory("my_package")
38+
+ "/bt_type_converters/libbt_custom_types.so"
39+
)
40+
...
41+
```
42+
43+
## Acknowledgements
44+
45+
- A few functions were based on https://github.com/BehaviorTree/BehaviorTree.CPP/pull/634
46+
- The TypeConverter is based on [moveit_task_constructor](https://github.com/moveit/moveit_task_constructor/)'s pybind11 implementation.

behaviortree_py/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
from behaviortree_py.behaviortree_py import *
1+
"""Init."""
2+
3+
from behaviortree_py.behaviortree_py import * # noqa: F403

examples/bt_example.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import behaviortree_py
21
import time
32

3+
import behaviortree_py
4+
45
xml_text = """
56
<root BTCPP_format="4">
67
@@ -31,7 +32,6 @@
3132

3233

3334
class CrossDoor:
34-
3535
def __init__(self):
3636
self.door_open = False
3737
self.door_locked = True
@@ -45,7 +45,8 @@ def register_nodes(self, factory: behaviortree_py.BehaviorTreeFactory):
4545
factory.register_simple_action("SmashDoor", self.smash_door)
4646

4747
def is_door_closed(
48-
self, tree_node: behaviortree_py.TreeNode
48+
self,
49+
tree_node: behaviortree_py.TreeNode,
4950
) -> behaviortree_py.NodeStatus:
5051
time.sleep(0.2)
5152
return (
@@ -55,7 +56,8 @@ def is_door_closed(
5556
)
5657

5758
def pass_through_door(
58-
self, tree_node: behaviortree_py.TreeNode
59+
self,
60+
tree_node: behaviortree_py.TreeNode,
5961
) -> behaviortree_py.NodeStatus:
6062
time.sleep(0.5)
6163
return (
@@ -65,7 +67,8 @@ def pass_through_door(
6567
)
6668

6769
def open_door(
68-
self, tree_node: behaviortree_py.TreeNode
70+
self,
71+
tree_node: behaviortree_py.TreeNode,
6972
) -> behaviortree_py.NodeStatus:
7073
time.sleep(0.5)
7174
if self.door_locked:
@@ -74,7 +77,8 @@ def open_door(
7477
return behaviortree_py.NodeStatus.SUCCESS
7578

7679
def pick_lock(
77-
self, tree_node: behaviortree_py.TreeNode
80+
self,
81+
tree_node: behaviortree_py.TreeNode,
7882
) -> behaviortree_py.NodeStatus:
7983
time.sleep(0.5)
8084
self.pick_attempts += 1

examples/scripting.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import behaviortree_py as bt
2-
from geometry_msgs.msg import Point
3-
from std_msgs.msg import String
4-
from moveit_msgs.msg import OrientedBoundingBox
52

63
xml_text = """
74
<root BTCPP_format="4">
@@ -11,7 +8,6 @@
118
<Script code=" A:=THE_ANSWER; B:=3.14; color:=RED " />
129
<Precondition if="A>B && color != BLUE" else="FAILURE">
1310
<Sequence>
14-
<Vision />
1511
<SetValue value="{double_value}"/>
1612
<SaySomething message="{A}"/>
1713
<SaySomething message="{B}"/>
@@ -45,21 +41,17 @@ def set_value(tree_node: bt.TreeNode) -> bt.NodeStatus:
4541
return bt.NodeStatus.SUCCESS
4642

4743

48-
def vision(tree_node: bt.TreeNode) -> bt.NodeStatus:
49-
if not (result := tree_node.set_output("bbox", OrientedBoundingBox())):
50-
print(f"Failed to set value '{result.error()}'")
51-
return bt.NodeStatus.FAILURE
52-
return bt.NodeStatus.SUCCESS
53-
54-
5544
factory = bt.BehaviorTreeFactory()
56-
factory.register_simple_action("Vision", vision, dict([bt.output_port("bbox")]))
5745
factory.register_simple_action("SetValue", set_value, dict([bt.output_port("value")]))
5846
factory.register_simple_action(
59-
"SaySomething", say_something, dict([bt.input_port("message")])
47+
"SaySomething",
48+
say_something,
49+
dict([bt.input_port("message")]),
6050
)
6151
factory.register_simple_action(
62-
"SaySomethingDouble", say_something_double, dict([bt.input_port("message")])
52+
"SaySomethingDouble",
53+
say_something_double,
54+
dict([bt.input_port("message")]),
6355
)
6456
factory.register_scripting_enum("RED", 1)
6557
factory.register_scripting_enum("BLUE", 2)

0 commit comments

Comments
 (0)