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

Implement Python bindings #634

Open
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

kylc
Copy link

@kylc kylc commented Aug 15, 2023

This PR implements Python bindings to the core BehaviorTree.CPP APIs enabling:

  1. Implementation of new behaviors in Python code
  2. Executing behavior trees from Python code
  3. Seamless interoperation of C++ and Python nodes
  4. Integration with the Python ecosystem (e.g. pip install BehaviorTree from PyPI, use NumPy and SciPy, etc.)

Progress

Core API

  • BehaviorTreeFactory
  • Tree (minimal API)

Node Types

  • StatefulActionNode
  • SyncActionNode
  • CoroActionNode (implemented as AsyncActionNode using Python generators)
  • ConditionNode (needed?)
  • ControlNode (needed?)
  • DecoratorNode (needed?)
  • ThreadedAction (won't do, tricky, maybe not the most useful in Python)

Interop

  • py::object -> C++ types (via JsonExporter::fromJson)
  • C++ types (BT::Any) -> py::object (via JsonExporter::toJson)

Examples

To run the examples:

  1. Create a Python virtualenv in the root directory: python3 -m venv venv && source venv/bin/activate
  2. Build and install the BehaviorTree Python package: pip install -v .
  3. Run an example, e.g. python3 python_examples/ex01_sample.py

See all of the examples under the python_examples/ directory.

from btpy import BehaviorTreeFactory, SyncActionNode, NodeStatus, ports


xml_text = """
 <root BTCPP_format="4" >

     <BehaviorTree ID="MainTree">
        <Sequence name="root">
            <AlwaysSuccess/>
            <SaySomething   message="this works too" />
            <ThinkWhatToSay text="{the_answer}"/>
            <SaySomething   message="{the_answer}" />
        </Sequence>
     </BehaviorTree>

 </root>
"""


@ports(inputs=["message"])
class SaySomething(SyncActionNode):
    def tick(self):
        msg = self.get_input("message")
        print(msg)
        return NodeStatus.SUCCESS


@ports(outputs=["text"])
class ThinkWhatToSay(SyncActionNode):
    def tick(self):
        self.set_output("text", "The answer is 42")
        return NodeStatus.SUCCESS


factory = BehaviorTreeFactory()
factory.register(SaySomething)
factory.register(ThinkWhatToSay)

tree = factory.create_tree_from_text(xml_text)
tree.tick_while_running()

Next Steps

  • Submit package to PyPI to enable pip install BehaviorTree
  • Provide more complete bindings to the core API
  • Proper Python API documentation (generated via Sphinx, for example)

Open Questions

  • Does this belong in the core BehaviorTree.CPP repository? This is certainly easiest because the Python <-> C++ type conversion code must somehow exist in the TreeNode::getInput method.
  • Is round-tripping type conversions through JSON sufficiently user-friendly/performant? It means that users must think ahead and define the JSON conversion functions for their C++ types or else they will face runtime errors when they try to send their types across the language barrier.
  • What should the Python module be called? For now it's btpy because that's easy to type.

@facontidavide
Copy link
Collaborator

witchcraft

This is insanely cool!

Shall we create a BehaviorTree.PY repo specifically for this?
Which limitation do we have in terms of the type being passed to the blackboard?

src/python_bindings.cpp Outdated Show resolved Hide resolved
@kylc
Copy link
Author

kylc commented Aug 20, 2023

Shall we create a BehaviorTree.PY repo specifically for this?

That might be possible, but with the current implementation of C++/Python type interop there are some required additions to the TreeNode::getInput method to handle the additional type conversions. Maybe there is a way to move it external, but I'm not sure yet.

For now, I've at least tried to put everything behind a #ifdef BTCPP_PYTHON gate.

Which limitation do we have in terms of the type being passed to the blackboard?

In my latest commits, I've added generalized conversions between C++/Python types via the JSONExporter functionality. It would be possible to pass types statically if all of the possible generic type combinations were registered with pybind in the user's code, but that turns into a giant mess of if statements because of the runtime type logic in BT::Any. For now, requiring that the user implement the conversion to/from JSON in exchange for full Python interop seems OK to me.

See an example here of passing objects between C++ and Python: https://github.com/BehaviorTree/BehaviorTree.CPP/blob/21d450e880e772c4e3e8a11d865a19d07ce5270d/python_examples/ex05_type_interop.py

@kylc
Copy link
Author

kylc commented Sep 6, 2023

@facontidavide I'm marking this as ready for review, but I've left some "open questions" in the original PR description. The Python bindings are in a pretty usable state at this point. The main missing item is some README-like documentation.

@kylc kylc marked this pull request as ready for review September 6, 2023 18:13
@kylc kylc changed the title WIP: Implement Python bindings Implement Python bindings Sep 6, 2023
@corot
Copy link

corot commented Nov 9, 2023

Hi @kylc, this is really amazing. Thanks a lot for the effort. I'm trying to run the examples. Build work fine, but I always get this error when running any example:

$ python3 python_examples/ex01_sample.py
Traceback (most recent call last):
  File "python_examples/ex01_sample.py", line 7, in <module>
    from btpy import BehaviorTreeFactory, SyncActionNode, NodeStatus, ports
  File "/home/jorge/.local/lib/python3.8/site-packages/btpy/__init__.py", line 8, in <module>
    from btpy_cpp import (
ImportError: /home/jorge/.local/lib/python3.8/site-packages/btpy_cpp.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZN2BT14toPythonObjectERKNS_3AnyERN8pybind116objectE

I checked that python/types.cpp gets compiled and linked to libbehaviortree_cpp.so, so no clue why this happens

  [40/56] Building CXX object CMakeFiles/behaviortree_cpp.dir/src/python/types.cpp.o
  

@vamsikalagaturu
Copy link

Hi, can we have any estimation on when this PR will be accepted or are there any other plans for the Python support?

@kylc
Copy link
Author

kylc commented Nov 27, 2023

Hi @kylc, this is really amazing. Thanks a lot for the effort. I'm trying to run the examples. Build work fine, but I always get this error when running any example:

$ python3 python_examples/ex01_sample.py
Traceback (most recent call last):
  File "python_examples/ex01_sample.py", line 7, in <module>
    from btpy import BehaviorTreeFactory, SyncActionNode, NodeStatus, ports
  File "/home/jorge/.local/lib/python3.8/site-packages/btpy/__init__.py", line 8, in <module>
    from btpy_cpp import (
ImportError: /home/jorge/.local/lib/python3.8/site-packages/btpy_cpp.cpython-38-x86_64-linux-gnu.so: undefined symbol: _ZN2BT14toPythonObjectERKNS_3AnyERN8pybind116objectE

I checked that python/types.cpp gets compiled and linked to libbehaviortree_cpp.so, so no clue why this happens

  [40/56] Building CXX object CMakeFiles/behaviortree_cpp.dir/src/python/types.cpp.o
  

That's odd. I just tried installing in a fresh ubuntu-22.04 container and get the same error, but only when compiling with GCC. If I use Clang then the examples work fine. Maybe something odd going on with the linker/LTO?

@corot
Copy link

corot commented Nov 27, 2023

That's odd. I just tried installing in a fresh ubuntu-22.04 container and get the same error, but only when compiling with GCC. If I use Clang then the examples work fine. Maybe something odd going on with the linker/LTO?

Odd indeed...
I use gcc, but on Ubuntu 20.04.6 LTS

@facontidavide
Copy link
Collaborator

Any update on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants