Skip to main content

Add your description here

Project description

Welcome to BTreeny!

This package is a minimal(ish) implementation of Behavior Trees in Python.

import btreeny

# Generic over blackboards - use dataclasses to get nice type hints!
MyBlackboardType = dict[str, str]

# For the most simple case - define your actions as a function which takes a blackbaord
# and returns a tree
@btreeny.simple_action
def my_failing_action(blackboard: MyBlackboardType):
    # You could modify the blackboard, or take actions here
    return btreeny.TreeStatus.FAILURE

# For more complex actions
@btreeny.action
def my_running_action():
    # Setup
    # ...

    # Yield a tick function
    def _inner(blackboard: MyBlackboardType):
        return btreeny.TreeStatus.RUNNING
    try:
        yield inner
    finally:
        # Teardown
        # ...

# We support many standard control flow nodes - see below for options
root = btreeny.fallback(
    my_failing_action(),
    my_running_action(),
)

# Running the tree can be done manually
blackboard = {}
result = btreeny.TreeStatus.RUNNING
with root as tick_function:
    while result == btreeny.TreeStatus.RUNNING:
        # We expect trees to modify the blackboard in-place
        result = tick_function(blackboard)

Writing an action

In btreeny, an action is specified as a context which returns a callable function to "tick" the action. This allows you to manage the setup and teardown of resources required by that action.

For example, an action which polls a URL until it gets a 200 status code, and will fail after some number of retries, might look like:

@btreeny.action
def poll_url(url: str, retries: int=10):
    # setup a client to allow connection pooling
    client = httpx.Client()
    retry_count = 0
    def tick(blackboard: Any):
        # Since we're assigning to retry_count, we should declare it as nonlocal
        # to this function's scope
        nonlocal retry_count
        if retry_count > retries:
            return btreeny.TreeStatus.FAILURE
        response = client.get(url)
        retry_count += 1
        if response.status_code == 200:
            return btreeny.TreeStatus.SUCCESS
        return btreeny.TreeStatus.RUNNING
    # Use a try... finally block to ensure cleanup is run
    try:
        # yield the tick function
        yield tick
    finally:
        # We can finish the function with our cleanup
        client.close()

Note that in this case it'd have been more ergonomic to use

with httpx.Client() as client:
    def tick(...):
        ...
    yield tick

As the client would have been closed for us!

Controlling flow

Sequential

Accepts multiple children to cycle through. When each child succeeds, move to the next action. If any child fails then the node fails.

Fallback

Accepts multiple children to cycle through. If a child fails, move to the next action. If any child suceeds then the node fails.

Repeat / Retry / Redo

Accepts a factory function and an optional number of retries. If the resulting action matches the specified continue_if value, recreate the action using the factory function and carry on.

  • Retry wraps repeat with continue_if=TreeStatus.FAILURE
  • Redo wraps repeat with continue_if=TreeStatus.SUCCESS

Remap

Map output states to different values - e.g. convert all SUCCESS outputs into FAILURE. Note that this is not reciprocal! You could, for example, use this to convert all outputs to RUNNING.

remap has some utilities

  • swap: Reciprocally map between two states (e.g. Failure <-> Success)
  • remap_to_always: Convert the output of the action to always be this value.

React

Given some condition check which runs on each tick with current blackboard, alternate between two different sub-trees.

Note that this function resumes a sub-tree where it has left off, rather than trying to restart it. This can create undesirable behavior without careful planning.

Failsafe

Given some condition check which runs on each tick with the current blackboard, if the check ever fails move to a failure tree.

Useful when combined with redo to allow failsafe behaviour which can recover to continue normal operations.

This action allows fallback to a charging state on low battery in the waypoint_navigation example script.

Logging and Visualization

Understanding what's going on in your behavior tree is crucial for debugging and triaging issues - btreeny has an (opinionated) set of logging utilities, but lets you access the underlying data to write your own.

The simplest way to log the current tree state is simply to use the

Rich

Rich is a great library for pretty printing in the terminal, to get the current tree state as a rich.Tree renderable, use btree.viz.get_rich_tree().

from rich.print import print
tree = btree.viz.get_rich_tree()
print(tree)

Rerun

Rerun is a great tool for visualizing robotics applications - and we want to make it easy for you to add your btreeny state to each timestep.

import rerun as rr
# fetch the current tree state as a dataclass with Rerun `rr.GraphNodes` and `rr.GraphEdges`
graph = btreeny.viz.rerun_tree_graph()
# Log to Rerun
rr.log("tree", graph.nodes, graph.edges)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

btreeny-0.1.1.tar.gz (70.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

btreeny-0.1.1-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

File details

Details for the file btreeny-0.1.1.tar.gz.

File metadata

  • Download URL: btreeny-0.1.1.tar.gz
  • Upload date:
  • Size: 70.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for btreeny-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0285712b556b042ed3c45ddcb51ad9c85a665f26ded95877bf60dc21daf40691
MD5 3e8c0bd999b7ddca4a6ef696fd216f71
BLAKE2b-256 b6a1592c31cd2cd43a7e729c49b4c8350cbf00bd595fae3c21ecbdd60f26a242

See more details on using hashes here.

Provenance

The following attestation bundles were made for btreeny-0.1.1.tar.gz:

Publisher: publish-to-pypi.yml on tingard/btreeny

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file btreeny-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: btreeny-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 9.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for btreeny-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 ab97b49647b1d217e36a645776c053162a5e9b3ea42377bccdf78d08368c00f0
MD5 016ce859a8676729b3053ac6c77271f5
BLAKE2b-256 a48da7ee1269abd5d1f1a1e53f5f11116c21021dc86f0e11ec1dc11d9948d564

See more details on using hashes here.

Provenance

The following attestation bundles were made for btreeny-0.1.1-py3-none-any.whl:

Publisher: publish-to-pypi.yml on tingard/btreeny

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page