Skip to content

Behavior Tree Base

The Vultron behavior tree base module defines the basic building blocks of any behavior tree. This page covers the basic Tree and Blackboard classes, as well as the basic error classes. Detailed descriptions of Node types are provided in Behavior Tree Basic Node Types.

vultron.bt.base

This package implements a basic Behavior Tree library.

vultron.bt.base.bt

This module defines a Behavior Tree object.

BehaviorTree

BehaviorTree is the base class for all bt trees. It is responsible for setting up the tree and running the root node.

Attributes:

Name Type Description
root BtNode

the root node of the tree

bb Blackboard

the blackboard object

Source code in vultron/bt/base/bt.py
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
class BehaviorTree:
    """BehaviorTree is the base class for all bt trees.
    It is responsible for setting up the tree and running the root node.

    Attributes:
        root: the root node of the tree
        bb: the blackboard object
    """

    bbclass = Blackboard

    def __init__(self, root: BtNode = None, bbclass: Type[Blackboard] = None):
        """
        Initialize the BehaviorTree object.

        Args:
            root: the root node of the tree
            bbclass: the blackboard class to use
        """
        self.root: BtNode = root
        if bbclass is not None:
            self.bbclass = bbclass

        self.bb: Blackboard = self.bbclass()
        self.status: NodeStatus = None

        # track whether we've done the pre-tick setup stuff
        self._setup: bool = False

    # runtime context
    def __enter__(self) -> "BehaviorTree":
        """
        Runtime context for the BehaviorTree object.

        Returns:
            self: the BehaviorTree object
        """
        self._ensure_setup()
        return self

    def __exit__(
        self, exc_type: Exception, exc_val: str, exc_tb: list
    ) -> bool:
        """
        Runtime context for the BehaviorTree object.

        Args:
            exc_type: the exception type
            exc_val: the exception value
            exc_tb: the exception traceback

        Returns:
            bool: True if the exception was handled, False otherwise
        """
        if exc_type is not None:
            # where were we in the tree?
            import inspect

            frm = inspect.trace()[-1]
            obj = frm[0].f_locals["self"]
            logger.debug(f"Exception in {obj.name}")
            print(obj.name)

            while obj.parent is not None:
                obj = obj.parent
                print(obj.name)
                logger.debug(f"parent: {obj.name}")

            # print(self.root.graph())
            return False

    def add_root(self, node: BtNode) -> None:
        """Adds a root node to the tree.

        Args:
            node: the root node to add

        Returns:
            None
        """
        self.root = node
        self.root.bb = self.bb

    def _ensure_setup(self) -> None:
        """Ensures that the tree has been set up.

        Args:
            None

        Returns:
            None

        Raises:
            BehaviorTreeError: if setup() fails
        """
        if self._setup:
            return

        self.setup()

        if not self._setup:
            raise BehaviorTreeError(f"{self.__class__.__name__} setup failed")

    def _pre_tick(self) -> None:
        """_pre_tick() is called before the root node's tick() method.

        Args:
            None

        Returns:
            None
        """
        self._ensure_setup()

    def tick(self) -> NodeStatus:
        """tick() is the main entry point for running the bt tree.
        It calls the root node's tick() method.
        Two callbacks are provided for subclasses to override:
        _pre_tick() and _post_tick().

        Returns:
            NodeStatus: the status of the root node after the tick

        """
        self._pre_tick()
        status = self.root.tick(depth=0)
        self._post_tick()
        self.status = status
        return status

    def _post_tick(self) -> None:
        """_post_tick() is called after the root node's tick() method.

        Returns:
            None
        """

    # def graph(self) -> :
    #     return self.root.graph()

    def setup(self) -> None:
        """Recursively calls the setup() method on all nodes in the tree starting at the root.

        Returns:
            None
        """
        self.root.bb = self.bb
        self.root.setup()
        self._setup = True

__enter__()

Runtime context for the BehaviorTree object.

Returns:

Name Type Description
self BehaviorTree

the BehaviorTree object

Source code in vultron/bt/base/bt.py
61
62
63
64
65
66
67
68
69
def __enter__(self) -> "BehaviorTree":
    """
    Runtime context for the BehaviorTree object.

    Returns:
        self: the BehaviorTree object
    """
    self._ensure_setup()
    return self

__exit__(exc_type, exc_val, exc_tb)

Runtime context for the BehaviorTree object.

Parameters:

Name Type Description Default
exc_type Exception

the exception type

required
exc_val str

the exception value

required
exc_tb list

the exception traceback

required

Returns:

Name Type Description
bool bool

True if the exception was handled, False otherwise

Source code in vultron/bt/base/bt.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
def __exit__(
    self, exc_type: Exception, exc_val: str, exc_tb: list
) -> bool:
    """
    Runtime context for the BehaviorTree object.

    Args:
        exc_type: the exception type
        exc_val: the exception value
        exc_tb: the exception traceback

    Returns:
        bool: True if the exception was handled, False otherwise
    """
    if exc_type is not None:
        # where were we in the tree?
        import inspect

        frm = inspect.trace()[-1]
        obj = frm[0].f_locals["self"]
        logger.debug(f"Exception in {obj.name}")
        print(obj.name)

        while obj.parent is not None:
            obj = obj.parent
            print(obj.name)
            logger.debug(f"parent: {obj.name}")

        # print(self.root.graph())
        return False

__init__(root=None, bbclass=None)

Initialize the BehaviorTree object.

Parameters:

Name Type Description Default
root BtNode

the root node of the tree

None
bbclass Type[Blackboard]

the blackboard class to use

None
Source code in vultron/bt/base/bt.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def __init__(self, root: BtNode = None, bbclass: Type[Blackboard] = None):
    """
    Initialize the BehaviorTree object.

    Args:
        root: the root node of the tree
        bbclass: the blackboard class to use
    """
    self.root: BtNode = root
    if bbclass is not None:
        self.bbclass = bbclass

    self.bb: Blackboard = self.bbclass()
    self.status: NodeStatus = None

    # track whether we've done the pre-tick setup stuff
    self._setup: bool = False

add_root(node)

Adds a root node to the tree.

Parameters:

Name Type Description Default
node BtNode

the root node to add

required

Returns:

Type Description
None

None

Source code in vultron/bt/base/bt.py
102
103
104
105
106
107
108
109
110
111
112
def add_root(self, node: BtNode) -> None:
    """Adds a root node to the tree.

    Args:
        node: the root node to add

    Returns:
        None
    """
    self.root = node
    self.root.bb = self.bb

setup()

Recursively calls the setup() method on all nodes in the tree starting at the root.

Returns:

Type Description
None

None

Source code in vultron/bt/base/bt.py
171
172
173
174
175
176
177
178
179
def setup(self) -> None:
    """Recursively calls the setup() method on all nodes in the tree starting at the root.

    Returns:
        None
    """
    self.root.bb = self.bb
    self.root.setup()
    self._setup = True

tick()

tick() is the main entry point for running the bt tree. It calls the root node's tick() method. Two callbacks are provided for subclasses to override: _pre_tick() and _post_tick().

Returns:

Name Type Description
NodeStatus NodeStatus

the status of the root node after the tick

Source code in vultron/bt/base/bt.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def tick(self) -> NodeStatus:
    """tick() is the main entry point for running the bt tree.
    It calls the root node's tick() method.
    Two callbacks are provided for subclasses to override:
    _pre_tick() and _post_tick().

    Returns:
        NodeStatus: the status of the root node after the tick

    """
    self._pre_tick()
    status = self.root.tick(depth=0)
    self._post_tick()
    self.status = status
    return status

vultron.bt.base.blackboard

This module defines Blackboard class for sharing data between nodes in the tree.

The default Blackboard class is a dict, which is good enough if you just want to run a single tree in a single process.

If you want multiple trees in a single process with shared state, just use the same Blackboard object instance for each tree.

If you want to run multiple trees in a single process without shared state, you'll need to use a different Blackboard object instance for each tree. You can still do that with the default Blackboard class, but you'll need to create a new Blackboard object instance for each tree.

If you want to run multiple trees in multiple processes with shared state, you'll need to use a Blackboard object that can communicate with some external data store, such as a key-value store on a server. The default Blackboard class is not designed for that use case, but you can subclass it to use any object that implements the __getitem__ and __setitem__ methods to provide a python dict-like interface. For example, mongodict and redis-dict provide such an interface for MongoDb and Redis, respectively.

Blackboard dataclass

Provides a blackboard object for sharing data between nodes in the tree. To use a custom blackboard object, subclass this class and set the BehaviorTree's bbclass attribute to your subclass.

Source code in vultron/bt/base/blackboard.py
41
42
43
44
45
46
47
@dataclass(kw_only=True)
class Blackboard:
    """
    Provides a blackboard object for sharing data between nodes in the tree.
    To use a custom blackboard object, subclass this class and set
    the BehaviorTree's bbclass attribute to your subclass.
    """

vultron.bt.base.errors

This module defines error classes for the vultron.bt.base module

ActionNodeError

Bases: LeafNodeError

Raised when an action node encounters an error

Source code in vultron/bt/base/errors.py
32
33
class ActionNodeError(LeafNodeError):
    """Raised when an action node encounters an error"""

BehaviorTreeError

Bases: VultronError

Raised when a BehaviorTree encounters an error

Source code in vultron/bt/base/errors.py
20
21
class BehaviorTreeError(VultronError):
    """Raised when a BehaviorTree encounters an error"""

BehaviorTreeFuzzerError

Bases: BehaviorTreeError

Raised when a BehaviorTreeFuzzer encounters an error

Source code in vultron/bt/base/errors.py
24
25
class BehaviorTreeFuzzerError(BehaviorTreeError):
    """Raised when a BehaviorTreeFuzzer encounters an error"""

ConditionCheckError

Bases: LeafNodeError

Raised when a condition check encounters an error

Source code in vultron/bt/base/errors.py
36
37
class ConditionCheckError(LeafNodeError):
    """Raised when a condition check encounters an error"""

LeafNodeError

Bases: BehaviorTreeError

Raised when a leaf node encounters an error

Source code in vultron/bt/base/errors.py
28
29
class LeafNodeError(BehaviorTreeError):
    """Raised when a leaf node encounters an error"""