Skip to content

Commit adbe0c2

Browse files
committed
Add example mixing C++/Python
1 parent f71807c commit adbe0c2

File tree

5 files changed

+114
-2
lines changed

5 files changed

+114
-2
lines changed

CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ pybind11_add_module(behaviortree_py src/bindings/behaviortree_py.cpp)
4949
target_compile_features(behaviortree_py PRIVATE cxx_std_20)
5050
target_link_libraries(behaviortree_py PRIVATE ${PROJECT_NAME}_lib)
5151

52-
install(TARGETS behaviortree_py
52+
pybind11_add_module(example src/example.cpp)
53+
target_compile_features(example PRIVATE cxx_std_20)
54+
target_compile_definitions(example PRIVATE BT_PLUGIN_EXPORT)
55+
target_link_libraries(example PRIVATE ${PROJECT_NAME}_lib)
56+
57+
install(TARGETS behaviortree_py example
5358
LIBRARY DESTINATION ${PYTHON_INSTALL_DIR}/behaviortree_py)
5459

5560
# ##############################################################################

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ bt.load_type_converters(
4242

4343
## Limitations
4444

45-
- Only `register_simple_action`, `register_simple_condition`, and `register_simple_decorator` are supported.
45+
- Only `register_simple_action`, `register_simple_condition`, and `register_simple_decorator` are currently supported.
4646
- Due to limitations of `BT::Any` only copy-constructible types are supported.
4747
- Due to how `pybind11` handle memory, mixing `std::shared_ptr` and `std::unique_ptr` between python/c++ is not supported.
4848

examples/bt_example_custom.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import time
2+
3+
import behaviortree_py as bt
4+
import behaviortree_py.example as bt_example # Needed to laod the ExampleTask pybind11
5+
6+
xml_text = """
7+
<root BTCPP_format="4">
8+
<BehaviorTree ID="MainTree">
9+
<Sequence>
10+
<SetTaskName task="{task}" />
11+
<ModifyTaskName task="{task}" />
12+
<PrintTaskName task="{task}" />
13+
<PySetTaskName task="{task}" />
14+
<PrintTaskName task="{task}" />
15+
</Sequence>
16+
</BehaviorTree>
17+
</root>
18+
"""
19+
20+
21+
def modify_task_name(tree_node: bt.TreeNode) -> bt.NodeStatus:
22+
task = tree_node.get_input("task")
23+
print(f"Modifying task name: {task.name}")
24+
task.name = "My Task Python"
25+
tree_node.set_output("task", task)
26+
return bt.NodeStatus.SUCCESS
27+
28+
29+
def py_set_task_name(tree_node: bt.TreeNode) -> bt.NodeStatus:
30+
task = bt_example.ExampleTask()
31+
task.name = "New Task Name Python"
32+
tree_node.set_output("task", task)
33+
return bt.NodeStatus.SUCCESS
34+
35+
36+
factory = bt.BehaviorTreeFactory()
37+
factory.register_from_plugin(bt_example.__file__)
38+
bt.load_type_converters(bt_example.__file__)
39+
factory.register_simple_action(
40+
"ModifyTaskName", modify_task_name, dict([bt.bidirectional_port("task")])
41+
)
42+
factory.register_simple_action(
43+
"PySetTaskName", py_set_task_name, dict([bt.output_port("task")])
44+
)
45+
factory.register_behavior_tree_from_text(xml_text)
46+
47+
tree = factory.create_tree("MainTree")
48+
49+
logger = bt.StdCoutLogger(tree)
50+
51+
print(bt.get_tree_recursively(tree.root_node()))
52+
53+
tree.tick_while_running()

src/bindings/behaviortree_py.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ PYBIND11_MODULE(behaviortree_py, m) {
5555
.def("register_behavior_tree_from_text",
5656
&BT::BehaviorTreeFactory::registerBehaviorTreeFromText,
5757
py::arg("xml_text"))
58+
.def("register_from_plugin", &BT::BehaviorTreeFactory::registerFromPlugin, py::arg("file_path"))
5859
.def("register_scripting_enum", &BT::BehaviorTreeFactory::registerScriptingEnum)
5960
.def(
6061
"register_simple_action",
@@ -116,6 +117,7 @@ PYBIND11_MODULE(behaviortree_py, m) {
116117
},
117118
py::arg("root_tree"));
118119

120+
m.def("get_registered_converters", &get_registered_converters);
119121
m.def(
120122
"input_port",
121123
[](BT::StringView name, BT::StringView description) { return BT::InputPort<BT::Any>(name, description); },

src/example.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include <behaviortree_cpp/action_node.h>
2+
#include <behaviortree_cpp/bt_factory.h>
3+
#include <fmt/format.h>
4+
#include <pybind11/pybind11.h>
5+
6+
#include <behaviortree_py/behaviortree_py.hpp>
7+
8+
struct ExampleTask {
9+
std::string name;
10+
};
11+
12+
class SetTaskName : public BT::SyncActionNode {
13+
public:
14+
SetTaskName(const std::string& name, const BT::NodeConfig& config) : SyncActionNode(name, config) {}
15+
16+
static BT::PortsList providedPorts() { return {BT::OutputPort<ExampleTask>("task")}; }
17+
18+
BT::NodeStatus tick() override {
19+
auto task = ExampleTask();
20+
task.name = "My Task C++";
21+
setOutput("task", task);
22+
return BT::NodeStatus::SUCCESS;
23+
}
24+
};
25+
26+
class PrintTaskName : public BT::SyncActionNode {
27+
public:
28+
PrintTaskName(const std::string& name, const BT::NodeConfig& config) : SyncActionNode(name, config) {}
29+
30+
static BT::PortsList providedPorts() { return {BT::InputPort<ExampleTask>("task")}; }
31+
32+
BT::NodeStatus tick() override {
33+
auto task_maybe = getInput<ExampleTask>("task");
34+
if (!task_maybe) {
35+
log_error("Missing required input [task]");
36+
return BT::NodeStatus::FAILURE;
37+
}
38+
log_info(fmt::format("Task name: {}", task_maybe.value().name));
39+
return BT::NodeStatus::SUCCESS;
40+
}
41+
};
42+
43+
namespace py = pybind11;
44+
PYBIND11_MODULE(example, m) {
45+
py::class_<ExampleTask>(m, "ExampleTask").def(py::init<>()).def_readwrite("name", &ExampleTask::name);
46+
}
47+
48+
BT_REGISTER_NODES(factory) {
49+
factory.registerNodeType<SetTaskName>("SetTaskName");
50+
factory.registerNodeType<PrintTaskName>("PrintTaskName");
51+
}
52+
BT_REGISTER_TYPE_CONVERTER(ExampleTask);

0 commit comments

Comments
 (0)