Module API

py_trees

This is the top-level namespace of the py_trees package.

py_trees.behaviour

The core behaviour template for all py_tree behaviours.

class py_trees.behaviour.Behaviour(name: str)[source]

Bases: ABC

A parent class for all user definable tree behaviours.

Parameters

name (str) – the behaviour name, defaults to auto-generating from the class name

Raises

TypeError – if the provided name is not a string

Variables
  • ~py_trees.behaviours.Behaviour.id (uuid.UUID) – automagically generated unique identifier for the behaviour

  • ~py_trees.behaviours.Behaviour.name (str) – the behaviour name

  • ~py_trees.behaviours.Behaviour.blackboards (List[py_trees.blackboard.Client]) – collection of attached blackboard clients

  • ~py_trees.behaviours.Behaviour.status (Status) – the behaviour status (INVALID, RUNNING, FAILURE, SUCCESS)

  • ~py_trees.behaviours.Behaviour.parent (Behaviour) – a Composite instance if nested in a tree, otherwise None

  • ~py_trees.behaviours.Behaviour.children ([Behaviour]) – empty for regular behaviours, populated for composites

  • ~py_trees.behaviours.Behaviour.logger (logging.Logger) – a simple logging mechanism

  • ~py_trees.behaviours.Behaviour.feedback_message (str) – improve debugging with a simple message

  • ~py_trees.behaviours.Behaviour.blackbox_level (BlackBoxLevel) – a helper variable for dot graphs and runtime gui’s to collapse/explode entire subtrees dependent upon the blackbox level.

attach_blackboard_client(name: Optional[str] = None, namespace: Optional[str] = None) Client[source]

Create and attach a blackboard to this behaviour.

Parameters
  • name (Optional[str]) – human-readable (not necessarily unique) name for the client

  • namespace (Optional[str]) – sandbox the client to variables behind this namespace

Returns

a handle to the attached blackboard client

Return type

Client

has_parent_with_instance_type(instance_type: Type[Behaviour]) bool[source]

Search this behaviour’s ancestors for one of the specified type.

Parameters
  • match (instance type of the parent to) –

  • instance_type (Type[Behaviour]) –

Returns

whether a parent was found or not

Return type

bool

has_parent_with_name(name: str) bool[source]

Search this behaviour’s ancestors for one with the specified name.

Parameters

name (str) – name of the parent to match, can be a regular expression

Returns

whether a parent was found or not

Return type

bool

initialise() None[source]

Execute user specified instructions prior to commencement of a new round of activity.

Users should override this method to perform any necessary initialising/clearing/resetting of variables prior to a new round of activity for the behaviour.

This method is automatically called via the py_trees.behaviour.Behaviour.tick() method whenever the behaviour is not RUNNING.

… note:: This method can be called more than once in the lifetime of a tree!

Return type

None

iterate(direct_descendants: bool = False) Iterator[Behaviour][source]

Iterate over this child and it’s children.

This utilises python generators for looping. To traverse the entire tree:

for node in my_behaviour.iterate():
    print("Name: {0}".format(node.name))
Parameters

direct_descendants (bool) – only yield children one step away from this behaviour.

Yields

Behaviour – one of it’s children

Return type

Iterator[Behaviour]

setup(**kwargs: Any) None[source]

Set up and verify infrastructure (middleware connections, etc) is available.

Users should override this method for any configuration and/or validation that is necessary prior to ticking the tree. Such construction is best done here rather than in __init__ since there is no guarantee at __init__ that the infrastructure is ready or even available (e.g. you may be just rendering dot graphs of the trees, no robot around).

Examples

  • establishing a middleware connection to a sensor or driver

  • ensuring a sensor or driver is in a ‘ready’ state

This method will typically be called before a tree’s first tick as this gives the application time to check and verify that everything is in a ready state before executing. This is especially important given that a tree does not always tick every behaviour and if not checked up-front, it may be some time before discovering a behaviour was in a broken state.

Tip

When to use __init__(), setup() and when to use initialise()?

Use __init__() for configuration of non-runtime dependencies (e.g. no middleware).

Use setup() for one-offs or to get early signal that everything (e.g. middleware) is ready to go.

Use initialise() for just-in-time configurations and/or checks.

There are times when it makes sense to do all three. For example, pythonic variable configuration in __init__(), middleware service client creation / server existence checks in setup() and a just-in-time check to ensure the server is still available in initialise().

Tip

Faults are notified to the user of the behaviour via exceptions. Choice of exception to use is left to the user.

Warning

The kwargs argument is for distributing objects at runtime to behaviours before ticking. For example, a simulator instance with which behaviours can interact with the simulator’s python api, a ros2 node for setting up communications. Use sparingly, as this is not proof against keyword conflicts amongst disparate libraries of behaviours.

Parameters

**kwargs (Any) – distribute arguments to this behaviour and in turn, all of it’s children

Raises

Exception – if this behaviour has a fault in construction or configuration

Return type

None

setup_with_descendants() None[source]

Call setup on this child, it’s children (it’s children’s children, ).

Return type

None

shutdown() None[source]

Destroy setup infrastructure (the antithesis of setup).

Users should override this method for any custom destruction of infrastructure usually brought into being in setup().

Raises

Exception – of whatever flavour the child raises when errors occur on destruction

Return type

None

stop(new_status: Status) None[source]

Stop the behaviour with the specified status.

Parameters

new_status (Status) – the behaviour is transitioning to this new status

Return type

None

This is called to bring the current round of activity for the behaviour to completion, typically resulting in a final status of SUCCESS, FAILURE or INVALID.

Warning

Users should not override this method to provide custom termination behaviour. The terminate() method has been provided for that purpose.

terminate(new_status: Status) None[source]

Execute user specified instructions when the behaviour is stopped.

Users should override this method to clean up. It will be triggered when a behaviour either finishes execution (switching from RUNNING to FAILURE || SUCCESS) or it got interrupted by a higher priority branch (switching to INVALID). Remember that the initialise() method will handle resetting of variables before re-entry, so this method is about disabling resources until this behaviour’s next tick. This could be a indeterminably long time. e.g.

  • cancel an external action that got started

  • shut down any temporary communication handles

Parameters

new_status (Status) – the behaviour is transitioning to this new status

Return type

None

Warning

Do not set self.status = new_status here, that is automatically handled by the stop() method. Use the argument purely for introspection purposes (e.g. comparing the current state in self.status with the state it will transition to in new_status.

tick() Iterator[Behaviour][source]

Tick the behaviour.

This function is a generator that can be used by an iterator on an entire behaviour tree. It handles the logic for deciding when to call the user’s initialise() and terminate() methods as well as making the actual call to the user’s update() method that determines the behaviour’s new status once the tick has finished. Once done, it will then yield itself (generator mechanism) so that it can be used as part of an iterator for the entire tree.

for node in my_behaviour.tick():
    print("Do something")

Note

This is a generator function, you must use this with yield. If you need a direct call, prefer tick_once() instead.

Yields

a reference to itself

Return type

Iterator[Behaviour]

Warning

Users should not override this method to provide custom tick behaviour. The update() method has been provided for that purpose.

tick_once() None[source]

Tick the object without iterating step-by-step over the children (i.e. without generators).

Return type

None

tip() Optional[Behaviour][source]

Get the tip of this behaviour’s subtree (if it has one).

This corresponds to the the deepest node that was running before the subtree traversal reversed direction and headed back to this node.

Returns

The deepest node (behaviour) that was running before subtree traversal reversed direction, or None if this behaviour’s status is INVALID.

Return type

Optional[Behaviour]

abstract update() Status[source]

Execute user specified instructions when the behaviour is ticked.

Users should override this method to perform any logic required to arrive at a decision on the behaviour’s new status. It is the primary worker function called by the tick() mechanism.

Returns

the behaviour’s new status Status

Return type

Status

Tip

This method should be almost instantaneous and non-blocking

visit(visitor: Any) None[source]

Introspect on this behaviour with a visitor.

This is functionality that enables external introspection into the behaviour. It gets used by the tree manager classes to collect information as ticking traverses a tree.

Parameters

visitor (Any) – the visiting class, must have a run(Behaviour) method.

Return type

None

py_trees.behaviours

A library of fundamental behaviours for use.

class py_trees.behaviours.BlackboardToStatus(name: str, variable_name: str)[source]

Bases: Behaviour

Reflects a Status stored in a blackboard variable.

This behaviour reverse engineers the StatusToBlackboard decorator. Used in conjuction with that decorator, this behaviour can be used to reflect the status of a decision elsewhere in the tree.

Note

A word of caution. The consequences of a behaviour’s status should be discernable upon inspection of the tree graph. If using StatusToBlackboard and BlackboardToStatus to reflect a behaviour’s status across a tree, this is no longer true. The graph of the tree communicates the local consequences, but not the reflected consequences at the point BlackboardToStatus is used. A recommendation, use this class only where other options are infeasible or impractical.

Parameters
  • variable_name (str) – name of the variable look for, may be nested, e.g. battery.percentage

  • name (str) – name of the behaviour

Raises
update() Status[source]

Check for existence.

Returns

SUCCESS if key found, FAILURE otherwise.

Return type

Status

class py_trees.behaviours.CheckBlackboardVariableExists(name: str, variable_name: str)[source]

Bases: Behaviour

A non-blocking check for the existence of a blackboard variable.

Check the blackboard to verify if a specific variable (key-value pair) exists. This is non-blocking, so will always tick with status FAILURE SUCCESS.

See also

WaitForBlackboardVariable for the blocking counterpart to this behaviour.

Parameters
  • variable_name (str) – name of the variable look for, may be nested, e.g. battery.percentage

  • name (str) – name of the behaviour

update() Status[source]

Check for existence.

Returns

SUCCESS if key found, FAILURE otherwise.

Return type

Status

class py_trees.behaviours.CheckBlackboardVariableValue(name: str, check: ComparisonExpression)[source]

Bases: Behaviour

Non-blocking check to determine if a blackboard variable matches a given value/expression.

Inspect a blackboard variable and if it exists, check that it meets the specified criteria (given by operation type and expected value). This is non-blocking, so it will always tick with SUCCESS or FAILURE.

Parameters
  • name (str) – name of the behaviour

  • check (ComparisonExpression) – a comparison expression to check against

Note

If the variable does not yet exist on the blackboard, the behaviour will return with status FAILURE.

Tip

The python operator module includes many useful comparison operations.

update() Status[source]

Check for existence, or the appropriate match on the expected value.

Returns

FAILURE

if not matched, SUCCESS otherwise.

Return type

Status

class py_trees.behaviours.CheckBlackboardVariableValues(name: str, checks: List[ComparisonExpression], operator: Callable[[bool, bool], bool], namespace: Optional[str] = None)[source]

Bases: Behaviour

Apply a logical operation across a set of blackboard variable checks.

This is non-blocking, so will always tick with status FAILURE or SUCCESS.

Parameters
  • checks (List[ComparisonExpression]) – a list of comparison checks to apply to blackboard variables

  • logical_operator – a logical check to apply across the results of the blackboard variable checks

  • name (str) – name of the behaviour

  • namespace (Optional[str]) – optionally store results of the checks (boolean) under this namespace

  • operator (Callable[[bool, bool], bool]) –

Tip

The python operator module includes many useful logical operators, e.g. operator.xor.

Raises

ValueError if less than two variable checks are specified (insufficient for logical operations)

Parameters
update() Status[source]

Apply comparison checks on each and a logical check across all variables.

Returns

FAILURE if key retrieval or logical

checks failed, SUCCESS otherwise.

Return type

Status

class py_trees.behaviours.Dummy(name: str = 'Dummy')

Bases: Behaviour

Crash test dummy used for anything dangerous.

Parameters

name (str) –

terminate(new_status: Status) None

Execute user specified instructions when the behaviour is stopped.

Users should override this method to clean up. It will be triggered when a behaviour either finishes execution (switching from RUNNING to FAILURE || SUCCESS) or it got interrupted by a higher priority branch (switching to INVALID). Remember that the initialise() method will handle resetting of variables before re-entry, so this method is about disabling resources until this behaviour’s next tick. This could be a indeterminably long time. e.g.

  • cancel an external action that got started

  • shut down any temporary communication handles

Parameters
  • new_status (Status) – the behaviour is transitioning to this new status

  • self (Behaviour) –

Return type

None

Warning

Do not set self.status = new_status here, that is automatically handled by the stop() method. Use the argument purely for introspection purposes (e.g. comparing the current state in self.status with the state it will transition to in new_status.

update() Status

Define a functor for a crash test dummy behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

class py_trees.behaviours.Failure(name: str = 'Failure')

Bases: Behaviour

Do nothing but tick over with FAILURE.

Parameters

name (str) –

terminate(new_status: Status) None

Execute user specified instructions when the behaviour is stopped.

Users should override this method to clean up. It will be triggered when a behaviour either finishes execution (switching from RUNNING to FAILURE || SUCCESS) or it got interrupted by a higher priority branch (switching to INVALID). Remember that the initialise() method will handle resetting of variables before re-entry, so this method is about disabling resources until this behaviour’s next tick. This could be a indeterminably long time. e.g.

  • cancel an external action that got started

  • shut down any temporary communication handles

Parameters
  • new_status (Status) – the behaviour is transitioning to this new status

  • self (Behaviour) –

Return type

None

Warning

Do not set self.status = new_status here, that is automatically handled by the stop() method. Use the argument purely for introspection purposes (e.g. comparing the current state in self.status with the state it will transition to in new_status.

update() Status

Define a functor for an always failing behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

class py_trees.behaviours.Periodic(name: str, n: int)[source]

Bases: Behaviour

Simply periodically rotates it’s status over all each status.

That is, RUNNING for N ticks, SUCCESS for N ticks, FAILURE for N ticks…

Parameters
  • name (str) – name of the behaviour

  • n (int) – period value (in ticks)

Note

It does not reset the count when initialising.

update() Status[source]

Increment counter and use to decide the current status.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.behaviours.ProbabilisticBehaviour(name: str, weights: Optional[List[float]] = None)[source]

Bases: Behaviour

Return a status based on a probability distribution. If unspecified - a uniform distribution will be used.

Parameters

Note

Probability distribution does not need to be normalised, it will be normalised internally.

Raises

ValueError if only some probabilities are specified

Parameters
update() Status[source]

Return a status based on a probability distribution.

Returns

SUCCESS with probability weights[0], FAILURE with probability weights[1] and RUNNING with probability weights[2].

Return type

Status

class py_trees.behaviours.Running(name: str = 'Running')

Bases: Behaviour

Do nothing but tick over with RUNNING.

Parameters

name (str) –

terminate(new_status: Status) None

Execute user specified instructions when the behaviour is stopped.

Users should override this method to clean up. It will be triggered when a behaviour either finishes execution (switching from RUNNING to FAILURE || SUCCESS) or it got interrupted by a higher priority branch (switching to INVALID). Remember that the initialise() method will handle resetting of variables before re-entry, so this method is about disabling resources until this behaviour’s next tick. This could be a indeterminably long time. e.g.

  • cancel an external action that got started

  • shut down any temporary communication handles

Parameters
  • new_status (Status) – the behaviour is transitioning to this new status

  • self (Behaviour) –

Return type

None

Warning

Do not set self.status = new_status here, that is automatically handled by the stop() method. Use the argument purely for introspection purposes (e.g. comparing the current state in self.status with the state it will transition to in new_status.

update() Status

Define a functor for an always running behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

class py_trees.behaviours.SetBlackboardVariable(name: str, variable_name: str, variable_value: Union[Any, Callable[[], Any]], overwrite: bool)[source]

Bases: Behaviour

Set the specified variable on the blackboard.

Parameters
  • variable_name (str) – name of the variable to set, may be nested, e.g. battery.percentage

  • variable_value (Union[Any, Callable[[], Any]]) – value of the variable to set

  • overwrite (bool) – when False, do not set the variable if it already exists

  • name (str) – name of the behaviour

update() Status[source]

Attempt to set the stored value in the requested blackboard variable.

Returns

FAILURE if no overwrite requested

and the variable exists, SUCCESS otherwise

Return type

Status

class py_trees.behaviours.StatusQueue(name: str, queue: List[Status], eventually: Optional[Status])[source]

Bases: Behaviour

Cycle through a specified queue of states.

Note

This does not reset when the behaviour initialises.

Parameters
  • name (str) – name of the behaviour

  • sequence – list of status values to cycle through

  • eventually (Optional[Status]) – status to use eventually, None to re-cycle the sequence

  • queue (List[Status]) –

terminate(new_status: Status) None[source]

Log debug information.

Parameters

new_status (Status) – the behaviour is transitioning to this new status

Return type

None

update() Status[source]

Pop from the queue or rotate / switch to eventual if the end has been reached.

Returns

the Status from the popped queue / eventual element

Return type

Status

class py_trees.behaviours.Success(name: str = 'Success')

Bases: Behaviour

Do nothing but tick over with SUCCESS.

Parameters

name (str) –

terminate(new_status: Status) None

Execute user specified instructions when the behaviour is stopped.

Users should override this method to clean up. It will be triggered when a behaviour either finishes execution (switching from RUNNING to FAILURE || SUCCESS) or it got interrupted by a higher priority branch (switching to INVALID). Remember that the initialise() method will handle resetting of variables before re-entry, so this method is about disabling resources until this behaviour’s next tick. This could be a indeterminably long time. e.g.

  • cancel an external action that got started

  • shut down any temporary communication handles

Parameters
  • new_status (Status) – the behaviour is transitioning to this new status

  • self (Behaviour) –

Return type

None

Warning

Do not set self.status = new_status here, that is automatically handled by the stop() method. Use the argument purely for introspection purposes (e.g. comparing the current state in self.status with the state it will transition to in new_status.

update() Status

Define a functor for an always succeeding behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

class py_trees.behaviours.SuccessEveryN(name: str, n: int)[source]

Bases: Behaviour

Non-blocking, periodic success.

This behaviour updates it’s status with SUCCESS once every N ticks, FAILURE otherwise.

Parameters
  • name (str) – name of the behaviour

  • n (int) – trigger success on every n’th tick

Tip

Use with decorators to change the status value as desired, e.g. py_trees.decorators.FailureIsRunning()

update() Status[source]

Increment the counter and decide on success/failure from that.

Returns

SUCCESS if the nth tick, FAILURE otherwise.

Return type

Status

class py_trees.behaviours.TickCounter(name: str, duration: int, completion_status: Status)[source]

Bases: Behaviour

Block for a specified tick count.

A useful utility behaviour for demos and tests. Simply ticks with RUNNING for the specified number of ticks before returning the requested completion status (SUCCESS or FAILURE).

This behaviour will reset the tick counter when initialising.

Parameters
  • name (str) – name of the behaviour

  • duration (int) – number of ticks to run

  • completion_status (Status) – status to switch to once the counter has expired

initialise() None[source]

Reset the tick counter.

Return type

None

update() Status[source]

Increment the tick counter and check to see if it should complete.

Returns

RUNNING while not expired, the given completion status otherwise

Return type

Status

class py_trees.behaviours.UnsetBlackboardVariable(name: str, key: str)[source]

Bases: Behaviour

Unset the specified variable (key-value pair) from the blackboard.

This always returns SUCCESS regardless of whether the variable was already present or not.

Parameters
  • key (str) – unset this key-value pair

  • name (str) – name of the behaviour

update() Status[source]

Unset and always return success.

Returns

SUCCESS

Return type

Status

class py_trees.behaviours.WaitForBlackboardVariable(name: str, variable_name: str)[source]

Bases: CheckBlackboardVariableExists

Block until a blackboard variable comes into existence.

This is blocking, so it will tick with status SUCCESS if the variable is found, and RUNNING otherwise.

See also

CheckBlackboardVariableExists for the non-blocking counterpart to this behaviour.

Parameters
  • variable_name (str) – name of the variable to wait for, may be nested, e.g. battery.percentage

  • name (str) – name of the behaviour

update() Status[source]

Check for existence, wait otherwise.

Returns

SUCCESS if key found, RUNNING otherwise.

Return type

Status

class py_trees.behaviours.WaitForBlackboardVariableValue(name: str, check: ComparisonExpression)[source]

Bases: CheckBlackboardVariableValue

Block until a blackboard variable matches a given value/expression.

Inspect a blackboard variable and if it exists, check that it meets the specified criteria (given by operation type and expected value). This is blocking, so it will always tick with SUCCESS or RUNNING.

See also

CheckBlackboardVariableValue for the non-blocking counterpart to this behaviour.

Note

If the variable does not yet exist on the blackboard, the behaviour will return with status RUNNING.

Parameters
  • check (ComparisonExpression) – a comparison expression to check against

  • name (str) – name of the behaviour

update() Status[source]

Check for existence, or the appropriate match on the expected value.

Returns

FAILURE

if not matched, SUCCESS otherwise.

Return type

Status

py_trees.behaviours.dummy(self: Behaviour) Status[source]

Define a functor for a crash test dummy behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

py_trees.behaviours.failure(self: Behaviour) Status[source]

Define a functor for an always failing behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

py_trees.behaviours.running(self: Behaviour) Status[source]

Define a functor for an always running behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

py_trees.behaviours.success(self: Behaviour) Status[source]

Define a functor for an always succeeding behaviour.

Parameters

self (Behaviour) – behaviour for this function to substitute update() in.

Returns

behaviour status

Return type

Status

py_trees.blackboard

Blackboards, for behaviours to write and read from.

Blackboards are not a necessary component of behaviour tree implementations, but are nonetheless, a fairly common mechanism for sharing data between behaviours in the tree. See, for example, the design notes for blackboards in Unreal Engine.

_images/blackboard.jpg

Implementations vary widely depending on the needs of the framework using them. The simplest implementations take the form of a key-value store with global access, while more rigorous implementations scope access or form a secondary graph overlaying the tree connecting data ports between behaviours.

The ‘Zen of PyTrees’ is to enable rapid development, yet be rich enough so that all of the magic is exposed for debugging purposes. The first implementation of a blackboard was merely a global key-value store with an api that lent itself to ease of use, but did not expose the data sharing between behaviours which meant any tooling used to introspect or visualise the tree, only told half the story.

The current implementation adopts a strategy similar to that of a filesystem. Each client (subsequently behaviour) registers itself for read/write access to keys on the blackboard. This is less to do with permissions and more to do with tracking users of keys on the blackboard - extremely helpful with debugging.

The alternative approach of layering a secondary data graph with parameter and input-output ports on each behaviour was discarded as being too heavy for the zen requirements of py_trees. This is in part due to the wiring costs, but also due to complexity arising from a tree’s partial graph execution (a feature which makes trees different from most computational graph frameworks) and not to regress on py_trees’ capability to dynamically insert and prune subtrees on the fly.

A high-level list of existing / planned features:

  • [+] Centralised key-value store

  • [+] Client connections with namespaced read/write access to the store

  • [+] Integration with behaviours for key-behaviour associations (debugging)

  • [+] Activity stream that logs read/write operations by clients

  • [+] Exclusive locks for writing

  • [+] Framework for key remappings

class py_trees.blackboard.ActivityItem(key: str, client_name: str, client_id: UUID, activity_type: str, previous_value: Optional[Any] = None, current_value: Optional[Any] = None)[source]

Bases: object

Holds data pertaining to activity on the blackboard.

Parameters
  • key (str) – name of the variable on the blackboard

  • client_name (str) – convenient name of the client performing the operation

  • client_id (UUID) – unique id of the client performing the operation

  • activity_type (str) – type of activity

  • previous_value (Optional[Any]) – of the given key (None if this field is not relevant)

  • current_value (Optional[Any]) – current value for the given key (None if this field is not relevant)

__init__(key: str, client_name: str, client_id: UUID, activity_type: str, previous_value: Optional[Any] = None, current_value: Optional[Any] = None)[source]
Parameters
__weakref__

list of weak references to the object (if defined)

class py_trees.blackboard.ActivityStream(maximum_size: int = 500)[source]

Bases: object

Stores the stream of events recording blackboard activity.

What got registered, unregistered, written, accessed? What operations failed due to incorrect permissions? What did the written variable change from? What did it change to? The activity stream captures all of these and more. It is a very useful mechanisms for debugging your tree from tick to tick.

Variables
  • (typing.List[ActivityItem] (data) – list of activity items, earliest first

  • maximum_size (int) – pop items if this size is exceeded

Parameters

maximum_size (int) –

__init__(maximum_size: int = 500)[source]

Initialise the stream with a maximum storage limit.

Parameters

maximum_size (int) – pop items from the stream if this size is exceeded

__weakref__

list of weak references to the object (if defined)

clear() None[source]

Delete all activities from the stream.

Return type

None

push(activity_item: ActivityItem) None[source]

Push the next activity item to the stream.

Parameters

activity_item (ActivityItem) – new item to append to the stream

Return type

None

class py_trees.blackboard.ActivityType(value)[source]

Bases: Enum

An enumerator representing the operation on a blackboard variable.

ACCESSED = 'ACCESSED'

Key accessed, either for reading, or modification of the value’s internal attributes (e.g. foo.bar).

ACCESS_DENIED = 'ACCESS_DENIED'

Client did not have access to read/write a key.

INITIALISED = 'INITIALISED'

Initialised a key-value pair on the blackboard

NO_KEY = 'NO_KEY'

Tried to access a key that does not yet exist on the blackboard.

NO_OVERWRITE = 'NO_OVERWRITE'

Tried to write but variable already exists and a no-overwrite request was respected.

READ = 'READ'

Read from the blackboard

UNSET = 'UNSET'

Key was removed from the blackboard

WRITE = 'WRITE'

Wrote to the blackboard.

class py_trees.blackboard.Blackboard[source]

Bases: object

Centralised key-value store for sharing data between behaviours.

This class is a coat-hanger for the centralised data store, metadata for it’s administration and static methods for interacting with it.

This api is intended for authors of debugging and introspection tools on the blackboard. Users should make use of the Client.

Variables
  • Blackboard.clients (Dict[uuid.UUID, str]) – client uuid-name registry

  • Blackboard.storage (Dict[str, Any]) – key-value data store

  • Blackboard.metadata (Dict[str, KeyMetaData]) – key associated metadata

  • Blackboard.activity_stream (ActivityStream) – logged activity

  • Blackboard.separator (char) – namespace separator character

__weakref__

list of weak references to the object (if defined)

static absolute_name(namespace: str, key: str) str[source]

Generate the fully qualified key name from namespace and name arguments.

Examples

'/' + 'foo'  = '/foo'
'/' + '/foo' = '/foo'
'/foo' + 'bar' = '/foo/bar'
'/foo/' + 'bar' = '/foo/bar'
'/foo' + '/foo/bar' = '/foo/bar'
'/foo' + '/bar' = '/bar'
'/foo' + 'foo/bar' = '/foo/foo/bar'
Parameters
  • namespace (str) – namespace the key should be embedded in

  • key (str) – key name (relative or absolute)

Returns

the absolute name

Return type

str

Warning

To expedite the method call (it’s used with high frequency in blackboard key lookups), no checks are made to ensure the namespace argument leads with a “/”. Nor does it check that a name in absolute form is actually embedded in the specified namespace, it just returns the given (absolute) name directly.

static clear() None[source]

Completely clear all key, value and client information from the blackboard.

This also deletes the activity stream, if it exists.

Return type

None

static disable_activity_stream() None[source]

Disable logging into the activity stream.

Return type

None

static enable_activity_stream(maximum_size: int = 500) None[source]

Enable logging into the activity stream.

Parameters

maximum_size (int) – pop items from the stream if this size is exceeded

Raises

RuntimeError if the activity stream is already enabled

Return type

None

static exists(name: str) bool[source]

Check if the specified variable exists on the blackboard.

Parameters

name (str) – name of the variable, can be nested, e.g. battery.percentage

Raises

AttributeError – if the client does not have read access to the variable

Return type

bool

static get(variable_name: str) Any[source]

Get a variable from the blackboard.

Extract the value associated with the given a variable name, can be nested, e.g. battery.percentage. This differs from the client get method in that it doesn’t pass through the client access checks. Use for debugging / introspection tooling (e.g. display methods) only (prefer the clients for rigorous programmatic access).

Parameters

variable_name (str) – of the variable to get, can be nested, e.g. battery.percentage

Raises

KeyError – if the variable or it’s nested attributes do not yet exist on the blackboard

Returns

The stored value for the given variable

Return type

Any

static key(variable_name: str) str[source]

Extract the key portion of an abitrary blackboard variable name.

Given a variable name that potentially also includes a reference to internal attributes of the variable stored on the blackboard, return the part that represents the blackboard key only.

Example: ‘/foo/bar.woohoo -> /foo/bar’.

Parameters

variable_name (str) – blackboard variable name - can be nested, e.g. battery.percentage

Returns

name of the underlying key

Return type

str

static key_with_attributes(variable_name: str) Tuple[str, str][source]

Separate key and attribrutes from a variable name.

Given a variable name that potentially also includes a reference to internal attributes of the variable stored on the blackboard, separate and return in tuple form.

Example: ‘/foo/bar.woohoo -> (/foo/bar’, ‘woohoo’)

Parameters

variable_name (str) – blackboard variable name - can be nested, e.g. battery.percentage

Returns

a tuple consisting of the key and it’s attributes (in string form)

Return type

Tuple[str, str]

static keys() Set[str][source]

Get the set of blackboard keys.

Returns

the complete set of keys registered by clients

Return type

Set[str]

static keys_filtered_by_clients(client_ids: Union[Set[UUID], List[UUID]]) Set[str][source]

Get the set of blackboard keys filtered by client unique identifiers.

Parameters

client_ids (Union[Set[UUID], List[UUID]]) – set of client uuid’s.

Returns

subset of keys that have been registered by the specified clients

Return type

Set[str]

static keys_filtered_by_regex(regex: str) Set[str][source]

Get the set of blackboard keys filtered by regex.

Parameters

regex (str) – a python regex string

Returns

subset of keys that have been registered and match the pattern

Return type

Set[str]

static relative_name(namespace: str, key: str) str[source]

Generate the abbreviated name for a key relative to the specified namespace.

Examples

'/' + 'foo'  = 'foo'
'/' + '/foo' = 'foo'
'/foo' + 'bar' = 'bar'
'/foo/' + 'bar' = 'bar'
'/foo' + '/foo/bar' = 'bar'
'/foo/' + '/foo/bar' = 'bar'
'/foo' + 'foo/bar' = 'foo/bar'
'/foo' + '/food/bar' => KeyError('/food/bar' is prefixed with a namespace conflicting with '/foo/')
Parameters
  • namespace (str) – namespace the key should be embedded in

  • key (str) – key name (relative or absolute)

Returns

the absolute name

Raises

KeyError if the key is prefixed with a conflicting namespace

Return type

str

Warning

To expedite the method call (it’s used with high frequency in blackboard key lookups), no checks are made to ensure the namespace argument leads with a “/”. Be sure to lead with a “/”!

static set(variable_name: str, value: Any) None[source]

Set a variable on the blackboard.

Set the value associated with the given variable name. The name can be nested, e.g. battery.percentage. This differs from the client get method in that it doesn’t pass through the client access checks. Use for debugging / introspection tooling (e.g. display methods) only (prefer the clients for rigorous programmatic access).

Parameters
  • variable_name (str) – of the variable to set, can be nested, e.g. battery.percentage

  • value (Any) –

Raises

AttributeError – if it is attempting to set a nested attribute tha does not exist.

Return type

None

static unset(key: str) bool[source]

Unset a variable on the blackboard.

Parameters

key (str) – name of the variable to remove

Returns

True if the variable was removed, False if it was already absent

Return type

bool

class py_trees.blackboard.Client(*, name: Optional[str] = None, namespace: Optional[str] = None)[source]

Bases: object

Client to the key-value store for sharing data between behaviours.

Examples

Blackboard clients will accept a user-friendly name or create one for you if none is provided. Regardless of what name is chosen, clients are always uniquely identified via a uuid generated on construction.

provided = py_trees.blackboard.Client(name="Provided")
print(provided)
generated = py_trees.blackboard.Client()
print(generated)
_images/blackboard_client_instantiation.png

Client Instantiation

Register read/write access for keys on the blackboard. Note, registration is not initialisation.

blackboard = py_trees.blackboard.Client(name="Client")
blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
blackboard.register_key(key="bar", access=py_trees.common.Access.READ)
blackboard.foo = "foo"
print(blackboard)
_images/blackboard_read_write.png

Variable Read/Write Registration

Keys and clients can make use of namespaces, designed by the ‘/’ char. Most methods permit a flexible expression of either relative or absolute names.

blackboard = py_trees.blackboard.Client(name="Global")
parameters = py_trees.blackboard.Client(name="Parameters", namespace="parameters")

blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
blackboard.register_key(key="/bar", access=py_trees.common.Access.WRITE)
blackboard.register_key(key="/parameters/default_speed", access=py_trees.common.Access.WRITE)
parameters.register_key(key="aggressive_speed", access=py_trees.common.Access.WRITE)

blackboard.foo = "foo"
blackboard.bar = "bar"
blackboard.parameters.default_speed = 20.0
parameters.aggressive_speed = 60.0

miss_daisy = blackboard.parameters.default_speed
van_diesel = parameters.aggressive_speed

print(blackboard)
print(parameters)
_images/blackboard_namespaces.png

Namespaces and Namespaced Clients

Disconnected instances will discover the centralised key-value store.

def check_foo():
    blackboard = py_trees.blackboard.Client(name="Reader")
    blackboard.register_key(key="foo", access=py_trees.common.Access.READ)
    print("Foo: {}".format(blackboard.foo))


blackboard = py_trees.blackboard.Client(name="Writer")
blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
blackboard.foo = "bar"
check_foo()

To respect an already initialised key on the blackboard:

blackboard = Client(name="Writer")
blackboard.register_key(key="foo", access=py_trees.common.Access.READ)
result = blackboard.set("foo", "bar", overwrite=False)

Store complex objects on the blackboard:

class Nested(object):
    def __init__(self):
        self.foo = None
        self.bar = None

    def __str__(self):
        return str(self.__dict__)


writer = py_trees.blackboard.Client(name="Writer")
writer.register_key(key="nested", access=py_trees.common.Access.WRITE)
reader = py_trees.blackboard.Client(name="Reader")
reader.register_key(key="nested", access=py_trees.common.Access.READ)

writer.nested = Nested()
writer.nested.foo = "I am foo"
writer.nested.bar = "I am bar"

foo = reader.nested.foo
print(writer)
print(reader)
_images/blackboard_nested.png

Log and display the activity stream:

py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
reader = py_trees.blackboard.Client(name="Reader")
reader.register_key(key="foo", access=py_trees.common.Access.READ)
writer = py_trees.blackboard.Client(name="Writer")
writer.register_key(key="foo", access=py_trees.common.Access.WRITE)
writer.foo = "bar"
writer.foo = "foobar"
unused_result = reader.foo
print(py_trees.display.unicode_blackboard_activity_stream())
py_trees.blackboard.Blackboard.activity_stream.clear()
_images/blackboard_activity_stream.png

Display the blackboard on the console, or part thereof:

writer = py_trees.blackboard.Client(name="Writer")
for key in {"foo", "bar", "dude", "dudette"}:
    writer.register_key(key=key, access=py_trees.common.Access.WRITE)

reader = py_trees.blackboard.Client(name="Reader")
for key in {"foo", "bar"}:
    reader.register_key(key="key", access=py_trees.common.Access.READ)

writer.foo = "foo"
writer.bar = "bar"
writer.dude = "bob"

# all key-value pairs
print(py_trees.display.unicode_blackboard())
# various filtered views
print(py_trees.display.unicode_blackboard(key_filter={"foo"}))
print(py_trees.display.unicode_blackboard(regex_filter="dud*"))
print(py_trees.display.unicode_blackboard(client_filter={reader.unique_identifier}))
# list the clients associated with each key
print(py_trees.display.unicode_blackboard(display_only_key_metadata=True))
_images/blackboard_display.png

Behaviours are not automagically connected to the blackboard but you may manually attach one or more clients so that associations between behaviours and variables can be tracked - this is very useful for introspection and debugging.

Creating a custom behaviour with blackboard variables:

class Foo(py_trees.behaviour.Behaviour):

    def __init__(self, name):
        super().__init__(name=name)
        self.blackboard = self.attach_blackboard_client(name="Foo Global")
        self.parameters = self.attach_blackboard_client(name="Foo Params", namespace="foo_parameters_")
        self.state = self.attach_blackboard_client(name="Foo State", namespace="foo_state_")

        # create a key 'foo_parameters_init' on the blackboard
        self.parameters.register_key("init", access=py_trees.common.Access.READ)
        # create a key 'foo_state_number_of_noodles' on the blackboard
        self.state.register_key("number_of_noodles", access=py_trees.common.Access.WRITE)

    def initialise(self):
        self.state.number_of_noodles = self.parameters.init

    def update(self):
        self.state.number_of_noodles += 1
        self.feedback_message = self.state.number_of_noodles
        if self.state.number_of_noodles > 5:
            return py_trees.common.Status.SUCCESS
        else:
            return py_trees.common.Status.RUNNING


# could equivalently do directly via the Blackboard static methods if
# not interested in tracking / visualising the application configuration
configuration = py_trees.blackboard.Client(name="App Config")
configuration.register_key("foo_parameters_init", access=py_trees.common.Access.WRITE)
configuration.foo_parameters_init = 3

foo = Foo(name="The Foo")
for i in range(1, 8):
    foo.tick_once()
    print("Number of Noodles: {}".format(foo.feedback_message))

Rendering a dot graph for a behaviour tree, complete with blackboard variables:

# in code
py_trees.display.render_dot_tree(py_trees.demos.blackboard.create_root())
# command line tools
py-trees-render --with-blackboard-variables py_trees.demos.blackboard.create_root
digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Blackboard Demo" [label="Blackboard Demo", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Set Nested" [label="Set Nested", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Set Nested";
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> Writer;
"Check Nested Foo" [label="Check Nested Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Check Nested Foo";
ParamsAndState [label=ParamsAndState, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> ParamsAndState;
subgraph  {
label="children_of_Blackboard Demo";
rank=same;
"Set Nested" [label="Set Nested", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Check Nested Foo" [label="Check Nested Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
ParamsAndState [label=ParamsAndState, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

Configuration [label=Configuration, shape=ellipse, style=filled, color=blue, fillcolor=gray, fontsize=7, fontcolor=blue];
"/dude" [label="/dude: Bob", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/dude" -> Writer  [color=blue, constraint=False];
Configuration -> "/dude"  [color=blue, constraint=False];
"/parameters/default_speed" [label="/parameters/default_speed: 30.0", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/parameters/default_speed" -> ParamsAndState  [color=blue, constraint=False];
Configuration -> "/parameters/default_speed"  [color=blue, constraint=False];
"/nested" [label="/nested: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/nested" -> "Check Nested Foo"  [color=blue, constraint=False];
"Set Nested" -> "/nested"  [color=blue, constraint=True];
"/spaghetti" [label="/spaghetti: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
Writer -> "/spaghetti"  [color=blue, constraint=True];
"/state/current_speed" [label="/state/current_speed: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
ParamsAndState -> "/state/current_speed"  [color=blue, constraint=True];
}

Tree with Blackboard Variables

And to demonstrate that it doesn’t become a tangled nightmare at scale, an example of a more complex tree:

digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Tutorial Eight" [label="Tutorial Eight\n--SuccessOnAll(-)--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
Topics2BB [label=Topics2BB, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Tutorial Eight" -> Topics2BB;
Scan2BB [label=Scan2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Topics2BB -> Scan2BB;
Cancel2BB [label=Cancel2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Topics2BB -> Cancel2BB;
Battery2BB [label=Battery2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Topics2BB -> Battery2BB;
subgraph  {
label=children_of_Topics2BB;
rank=same;
Scan2BB [label=Scan2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Cancel2BB [label=Cancel2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Battery2BB [label=Battery2BB, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

Tasks [label=Tasks, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
"Tutorial Eight" -> Tasks;
"Battery Low?" [label="Battery Low?", shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
Tasks -> "Battery Low?";
"Flash Red" [label="Flash Red", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Battery Low?" -> "Flash Red";
Scan [label=Scan, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Tasks -> Scan;
"Scan or Die" [label="Scan or Die", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
Scan -> "Scan or Die";
"Ere we Go" [label="Ere we Go", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Scan or Die" -> "Ere we Go";
UnDock [label=UnDock, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Ere we Go" -> UnDock;
"Scan or Be Cancelled" [label="Scan or Be Cancelled", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
"Ere we Go" -> "Scan or Be Cancelled";
"Cancelling?" [label="Cancelling?", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Scan or Be Cancelled" -> "Cancelling?";
"Cancel?" [label="Cancel?", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Cancelling?" -> "Cancel?";
"Move Home" [label="Move Home", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Cancelling?" -> "Move Home";
"Result2BB\n'cancelled'" [label="Result2BB\n'cancelled'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Cancelling?" -> "Result2BB\n'cancelled'";
subgraph  {
label="children_of_Cancelling?";
rank=same;
"Cancel?" [label="Cancel?", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Move Home" [label="Move Home", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Result2BB\n'cancelled'" [label="Result2BB\n'cancelled'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

"Move Out and Scan" [label="Move Out and Scan", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Scan or Be Cancelled" -> "Move Out and Scan";
"Move Out" [label="Move Out", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Move Out and Scan" -> "Move Out";
Scanning [label="Scanning\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
"Move Out and Scan" -> Scanning;
"Context Switch" [label="Context Switch", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Scanning -> "Context Switch";
Rotate [label=Rotate, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Scanning -> Rotate;
"Flash Blue" [label="Flash Blue", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Scanning -> "Flash Blue";
subgraph  {
label=children_of_Scanning;
rank=same;
"Context Switch" [label="Context Switch", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Rotate [label=Rotate, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Flash Blue" [label="Flash Blue", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

"Move Home*" [label="Move Home*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Move Out and Scan" -> "Move Home*";
"Result2BB\n'succeeded'" [label="Result2BB\n'succeeded'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Move Out and Scan" -> "Result2BB\n'succeeded'";
subgraph  {
label="children_of_Move Out and Scan";
rank=same;
"Move Out" [label="Move Out", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Scanning [label="Scanning\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
"Move Home*" [label="Move Home*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Result2BB\n'succeeded'" [label="Result2BB\n'succeeded'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

subgraph  {
label="children_of_Scan or Be Cancelled";
rank=same;
"Cancelling?" [label="Cancelling?", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Move Out and Scan" [label="Move Out and Scan", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
}

Dock [label=Dock, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Ere we Go" -> Dock;
Celebrate [label="Celebrate\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
"Ere we Go" -> Celebrate;
"Flash Green" [label="Flash Green", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Celebrate -> "Flash Green";
Pause [label=Pause, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Celebrate -> Pause;
subgraph  {
label=children_of_Celebrate;
rank=same;
"Flash Green" [label="Flash Green", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Pause [label=Pause, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

subgraph  {
label="children_of_Ere we Go";
rank=same;
UnDock [label=UnDock, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Scan or Be Cancelled" [label="Scan or Be Cancelled", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
Dock [label=Dock, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Celebrate [label="Celebrate\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
}

Die [label=Die, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Scan or Die" -> Die;
Notification [label="Notification\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
Die -> Notification;
"Flash Red*" [label="Flash Red*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Notification -> "Flash Red*";
"Pause*" [label="Pause*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Notification -> "Pause*";
subgraph  {
label=children_of_Notification;
rank=same;
"Flash Red*" [label="Flash Red*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Pause*" [label="Pause*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

"Result2BB\n'failed'" [label="Result2BB\n'failed'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Die -> "Result2BB\n'failed'";
subgraph  {
label=children_of_Die;
rank=same;
Notification [label="Notification\n--SuccessOnOne--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
"Result2BB\n'failed'" [label="Result2BB\n'failed'", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

subgraph  {
label="children_of_Scan or Die";
rank=same;
"Ere we Go" [label="Ere we Go", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Die [label=Die, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
}

"Send Result" [label="Send Result", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Scan -> "Send Result";
subgraph  {
label=children_of_Scan;
rank=same;
"Scan or Die" [label="Scan or Die", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
"Send Result" [label="Send Result", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Tasks -> Idle;
subgraph  {
label=children_of_Tasks;
rank=same;
"Battery Low?" [label="Battery Low?", shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
Scan [label=Scan, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

subgraph  {
label="children_of_Tutorial Eight";
rank=same;
Topics2BB [label=Topics2BB, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Tasks [label=Tasks, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
}

event_scan_button [label="event_scan_button: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
Scan2BB -> event_scan_button  [color=blue, constraint=True];
event_cancel_button [label="event_cancel_button: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
event_cancel_button -> "Cancel?"  [color=blue, constraint=False];
Cancel2BB -> event_cancel_button  [color=blue, constraint=True];
battery [label="battery: sensor_msgs.msg.B...", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
Battery2BB -> battery  [color=blue, constraint=True];
battery_low_warning [label="battery_low_warning: False", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
battery_low_warning -> "Battery Low?"  [color=blue, constraint=False];
Battery2BB -> battery_low_warning  [color=blue, constraint=True];
scan_result [label="scan_result: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
scan_result -> "Send Result"  [color=blue, constraint=False];
"Result2BB\n'cancelled'" -> scan_result  [color=blue, constraint=True];
"Result2BB\n'succeeded'" -> scan_result  [color=blue, constraint=True];
"Result2BB\n'failed'" -> scan_result  [color=blue, constraint=True];
}

A more complex tree

Debug deeper with judicious application of the tree, blackboard and activity stream display methods around the tree tick (refer to py_trees.visitors.DisplaySnapshotVisitor for examplar code):

_images/blackboard_trees.png

Tree level debugging

Variables
  • name (str) – client’s convenient, but not necessarily unique identifier

  • namespace (str) – apply this as a prefix to any key/variable name operations

  • unique_identifier (uuid.UUID) – client’s unique identifier

  • read (Set[str]) – set of absolute key names with read access

  • write (Set[str]) – set of absolute key names with write access

  • exclusive (Set[str]) – set of absolute key names with exclusive write access

  • required (Set[str]) – set of absolute key names required to have data present

  • str] (remappings (Dict[str,) – client key names with blackboard remappings

  • (typing.Set[str] (namespaces) – a cached list of namespaces this client accesses

Parameters
__getattr__(name: str) Any[source]

Access variables via a convenient attribute accessor.

This is also responsible for checking permissions prior to returning the variable.

Raises
  • AttributeError – if the client does not have read access to the variable

  • KeyError – if the variable does not yet exist on the blackboard

Parameters

name (str) –

Return type

Any

__init__(*, name: Optional[str] = None, namespace: Optional[str] = None)[source]

Initialise with a unique name and optionally, a namespace to operate within.

Parameters
  • name (Optional[str]) – client’s convenient identifier (stringifies the uuid if None)

  • namespace (Optional[str]) – prefix to apply to key/variable name operations

  • read – list of keys for which this client has read access

  • write – list of keys for which this client has write access

  • exclusive – list of keys for which this client has exclusive write access

Raises
  • TypeError – if the provided name is not of type str

  • ValueError – if the unique identifier has already been registered

__setattr__(name: str, value: Any) None[source]

Set variables via a convenient attribute setter.

This is also responsible for checking permissions prior to writing.

Raises

AttributeError – if the client does not have write access to the variable

Parameters
  • name (str) –

  • value (Any) –

Return type

None

__str__() str[source]

Generate a string representation for the behaviour.

Returns

the string representation

Return type

str

__weakref__

list of weak references to the object (if defined)

absolute_name(key: str) str[source]

Generate the fully qualified key name for this key.

blackboard = Client(name="FooBar", namespace="foo")
blackboard.register_key(key="bar", access=py_trees.common.Access.READ)
print("{}".format(blackboard.absolute_name("bar")))  # "/foo/bar"
Parameters

key (str) – name of the key

Returns

the absolute name

Raises

KeyError – if the key is not registered with this client

Return type

str

exists(name: str) bool[source]

Check if the specified variable exists on the blackboard.

Parameters

name (str) – name of the variable to get, can be nested, e.g. battery.percentage

Raises

AttributeError – if the client does not have read access to the variable

Return type

bool

get(name: str) Any[source]

Access via method a key on the blackboard.

This is the more cumbersome method (as opposed to simply using ‘.<name>’), but useful when the name is programatically generated.

Parameters

name (str) – name of the variable to get, can be nested, e.g. battery.percentage

Raises
  • AttributeError – if the client does not have read access to the variable

  • KeyError – if the variable or it’s nested attributes do not yet exist on the blackboard

Return type

Any

id() UUID[source]

Access the unique identifier for this client.

Returns

The uuid.UUID object

Return type

UUID

is_registered(key: str, access: Union[None, Access] = None) bool[source]

Check to see if the specified key is registered.

Parameters
  • key (str) – in either relative or absolute form

  • access (Union[None, Access]) – access property, if None, just checks for registration, regardless of property

Returns

if registered, True otherwise False

Return type

bool

register_key(key: str, access: Access, required: bool = False, remap_to: Optional[str] = None) None[source]

Register a key on the blackboard to associate with this client.

Parameters
  • key (str) – key to register

  • access (Access) – access level (read, write, exclusive write)

  • required (bool) – if true, check key exists when calling verify_required_keys_exist()

  • remap_to (Optional[str]) – remap the key to this location on the blackboard

Return type

None

Note the remap simply changes the storage location. From the perspective of the client, access via the specified ‘key’ remains the same.

Raises
  • AttributeError if exclusive write access is requested, but – write access has already been given to another client

  • TypeError if the access argument is of incorrect type

Parameters
Return type

None

set(name: str, value: Any, overwrite: bool = True) bool[source]

Set, conditionally depending on whether the variable already exists or otherwise.

This is most useful when initialising variables and multiple elements seek to do so. A good policy to adopt for your applications in these situations is a first come, first served policy. Ensure global configuration has the first opportunity followed by higher priority behaviours in the tree and so forth. Lower priority behaviours would use this to respect the pre-configured setting and at most, just validate that it is acceptable to the functionality of it’s own behaviour.

Parameters
  • name (str) – name of the variable to set

  • value (Any) – value of the variable to set

  • overwrite (bool) – do not set if the variable already exists on the blackboard

Returns

success or failure (overwrite is False and variable already set)

Raises
  • AttributeError – if the client does not have write access to the variable

  • KeyError – if the variable does not yet exist on the blackboard

Return type

bool

unregister(clear: bool = True) None[source]

Unregister this blackboard client.

If requested, clear key-value pairs if this client is the last user of those variables.

Parameters

clear (bool) – remove key-values pairs from the blackboard

Return type

None

unregister_all_keys(clear: bool = True) None[source]

Unregister all keys currently registered by this blackboard client.

If requested, clear key-value pairs if this client is the last user of those variables.

Parameters

clear (bool) – remove key-values pairs from the blackboard

Return type

None

unregister_key(key: str, clear: bool = True, update_namespace_cache: bool = True) None[source]

Unegister a key associated with this client.

Parameters
  • key (str) – key to unregister

  • clear (bool) – remove key-values pairs from the blackboard

  • update_namespace_cache (bool) – disable if you are batching

Return type

None

A method that batches calls to this method is unregister_all_keys().

Raises

KeyError if the key has not been previously registered

Parameters
  • key (str) –

  • clear (bool) –

  • update_namespace_cache (bool) –

Return type

None

unset(key: str) bool[source]

Unset a blackboard variable.

Use to completely remove a blackboard variable (key-value pair).

Parameters

key (str) – name of the variable to remove

Returns

True if the variable was removed, False if it was already absent

Return type

bool

verify_required_keys_exist() None[source]

Check for existence of all keys registered as ‘required’.

Raises: KeyError if any of the required keys do not exist on the blackboard

Return type

None

class py_trees.blackboard.IntermediateVariableFetcher(blackboard: Client, namespace: str)[source]

Bases: object

Convenient attribute accessor constrained to (possibly nested) namespaces.

Parameters
  • blackboard (Client) –

  • namespace (str) –

__init__(blackboard: Client, namespace: str)[source]
Parameters
  • blackboard (Client) –

  • namespace (str) –

__setattr__(name: str, value: Any) None[source]

Implement setattr(self, name, value).

Parameters
  • name (str) –

  • value (Any) –

Return type

None

__weakref__

list of weak references to the object (if defined)

class py_trees.blackboard.KeyMetaData[source]

Bases: object

Stores the aggregated metadata for a key on the blackboard.

__init__() None[source]
Return type

None

__weakref__

list of weak references to the object (if defined)

py_trees.common

Common definitions, methods and variables used by the py_trees library.

class py_trees.common.Access(value)[source]

Bases: Enum

Use to distinguish types of access to, e.g. variables on a blackboard.

EXCLUSIVE_WRITE = 'EXCLUSIVE_WRITE'

Exclusive lock for writing, i.e. no other writer permitted.

READ = 'READ'

Read access.

WRITE = 'WRITE'

Write access, implicitly also grants read access.

class py_trees.common.BlackBoxLevel(value)[source]

Bases: IntEnum

A hint used for visualisation.

Whether a behaviour is a blackbox entity that may be considered collapsible (i.e. everything in its subtree will not be visualised) by visualisation tools. DETAIL.

BIG_PICTURE = 3

A blackbox that represents a big picture part of the entire tree view.

COMPONENT = 2

A blackbox that encapsulates a subgroup of functionalities as a single group.

DETAIL = 1

A blackbox that encapsulates detailed activity.

NOT_A_BLACKBOX = 4

Not a blackbox, do not ever collapse.

class py_trees.common.ClearingPolicy(value)[source]

Bases: IntEnum

Policy rules for behaviours to dictate when data should be cleared/reset.

NEVER = 3

Never clear the data

ON_INITIALISE = 1

Clear when entering the initialise() method.

ON_SUCCESS = 2

Clear when returning SUCCESS.

class py_trees.common.Duration(value)[source]

Bases: Enum

Naming conventions.

INFINITE = 1.7976931348623157e+308

INFINITE oft used for perpetually blocking operations.

UNTIL_THE_BATTLE_OF_ALFREDO = 1.7976931348623157e+308

UNTIL_THE_BATTLE_OF_ALFREDO is an alias for INFINITE.

class py_trees.common.Name(value)[source]

Bases: Enum

Naming conventions.

AUTO_GENERATED = 'AUTO_GENERATED'

Automagically generate (hopefully) something sensible..

class py_trees.common.ParallelPolicy[source]

Configurable policies for Parallel behaviours.

class SuccessOnAll(synchronise: bool = True)[source]

Success depends on all children succeeding.

Return SUCCESS only when each and every child returns SUCCESS. If synchronisation is requested, any children that tick with SUCCESS will be skipped on subsequent ticks until the policy criteria is met, or one of the children returns status FAILURE.

Parameters

synchronise (bool) –

class SuccessOnOne[source]

Success depends on just one child (can be any child).

Return SUCCESS so long as at least one child has SUCCESS and the remainder are RUNNING

class SuccessOnSelected(children: List[Any], synchronise: bool = True)[source]

Success depends on an explicitly selected set of children behaviours.

Return SUCCESS so long as each child in a specified list returns SUCCESS. If synchronisation is requested, any children that tick with SUCCESS will be skipped on subsequent ticks until the policy criteria is met, or one of the children returns status FAILURE.

Parameters
class py_trees.common.Status(value)[source]

Bases: Enum

An enumerator representing the status of a behaviour.

FAILURE = 'FAILURE'

Behaviour check has failed, or execution of its action finished with a failed result.

INVALID = 'INVALID'

Behaviour is uninitialised and/or in an inactive state, i.e. not currently being ticked.

RUNNING = 'RUNNING'

Behaviour is in the middle of executing some action, result still pending.

SUCCESS = 'SUCCESS'

Behaviour check has passed, or execution of its action has finished with a successful result.

class py_trees.common.VisibilityLevel(value)[source]

Bases: IntEnum

Flag used by visualisation tools to configure visibility..

Closely associated with the BlackBoxLevel for a behaviour.

This sets the visibility level to be used for visualisations. Visibility levels correspond to reducing levels of visibility in a visualisation.

ALL = 0

Do not collapse any behaviour.

BIG_PICTURE = 3

Collapse any blackbox that isn’t marked with BIG_PICTURE.

COMPONENT = 2

Collapse blackboxes marked with COMPONENT or lower.

DETAIL = 1

Collapse blackboxes marked with DETAIL or lower.

common.string_to_visibility_level() VisibilityLevel

Will convert a string to a visibility level.

Note that it will quietly return ALL if the string is not matched to any visibility level string identifier.

Parameters

level (str) – visibility level as a string

Returns

visibility level enum

Return type

VisibilityLevel

py_trees.composites

Composites (multi-child) types for behaviour trees.

Composites are responsible for directing the path traced through the tree on a given tick (execution). They are the factories (Sequences and Parallels) and decision makers (Selectors) of a behaviour tree.

digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fontcolor=black, shape=box, fontsize=11, style=filled, fillcolor=orange];
Selector [fontcolor=black, shape=octagon, fontsize=11, style=filled, fillcolor=cyan];
Parallel [fontcolor=black, shape=parallelogram, fontsize=11, style=filled, fillcolor=gold];
}

PyTree Composites

Composite behaviours typically manage children and apply some logic to the way they execute and return a result, but generally don’t do anything themselves. Perform the checks or actions you need to do in the non-composite behaviours.

Most any desired functionality can be authored with a combination of these three composites. In fact, it is precisely this feature that makes behaviour trees attractive - it breaks down complex decision making logic to just three primitive elements. It is possible and often desirable to extend this set with custom composites of your own, but think carefully before you do - in almost every case, a combination of the existing composites will serve and as a result, you will merely compound the complexity inherent in your tree logic. This this makes it confoundingly difficult to design, introspect and debug. As an example, design sessions often revolve around a sketched graph on a whiteboard. When these graphs are composed of just five elements (Selectors, Sequences, Parallels, Decorators and Behaviours), it is very easy to understand the logic at a glance. Double the number of fundamental elements and you may as well be back at the terminal parsing code.

Tip

You should never need to subclass or create new composites.

The basic operational modes of the three composites in this library are as follows:

  • Selector: execute a child based on cascading priorities

  • Sequence: execute children sequentially

  • Parallel: execute children concurrently

This library does provide some flexibility in how each composite is implemented without breaking the fundamental nature of each (as described above). Selectors and Sequences can be configured with or without memory (resumes or resets if children are RUNNING) and the results of a parallel can be configured to wait upon all children completing, succeed on one, all or a subset thereof.

Tip

Follow the links in each composite’s documentation to the relevant demo programs.

class py_trees.composites.Composite(name: str, children: Optional[List[Behaviour]] = None)[source]

Bases: Behaviour, ABC

The parent class to all composite behaviours.

Parameters
  • name (str) – the composite behaviour name

  • children ([Behaviour]) – list of children to add

__init__(name: str, children: Optional[List[Behaviour]] = None)[source]
Parameters
add_child(child: Behaviour) UUID[source]

Add a child.

Parameters

child (Behaviour) – child to add

Raises
Returns

unique id of the child

Return type

UUID

add_children(children: List[Behaviour]) Behaviour[source]

Append a list of children to the current list.

Parameters

children ([Behaviour]) – list of children to add

Return type

Behaviour

insert_child(child: Behaviour, index: int) UUID[source]

Insert child at the specified index.

This simply directly calls the python list’s insert method using the child and index arguments.

Parameters
  • child (Behaviour) – child to insert

  • index (int) – index to insert it at

Returns

unique id of the child

Return type

uuid.UUID

prepend_child(child: Behaviour) UUID[source]

Prepend the child before all other children.

Parameters

child (Behaviour) – child to insert

Returns

unique id of the child

Return type

uuid.UUID

remove_all_children() None[source]

Remove all children. Makes sure to stop each child if necessary.

Return type

None

remove_child(child: Behaviour) int[source]

Remove the child behaviour from this composite.

Parameters

child (Behaviour) – child to delete

Returns

index of the child that was removed

Return type

int

Todo

Error handling for when child is not in this list

remove_child_by_id(child_id: UUID) None[source]

Remove the child with the specified id.

Parameters

child_id (UUID) – unique id of the child

Raises

IndexError – if the child was not found

Return type

None

replace_child(child: Behaviour, replacement: Behaviour) None[source]

Replace the child behaviour with another.

Parameters
Return type

None

stop(new_status: Status = Status.INVALID) None[source]

Provide common stop-level functionality for all composites.

The latter situation can arise for some composites, but more importantly, will always occur when high higher priority behaviour interrupts this one.

Parameters

new_status (Status) – behaviour will transition to this new status

Return type

None

abstract tick() Iterator[Behaviour][source]

Tick the composite.

All composite subclasses require a re-implementation of the tick method to provide the logic for managing multiple children (tick() merely provides default logic for when there are no children).

Return type

Iterator[Behaviour]

tip() Optional[Behaviour][source]

Recursive function to extract the last running node of the tree.

Returns

the tip function of the current child of this composite or None

Return type

Optional[Behaviour]

update() Status[source]

Unused update method.

Composites should direct the flow, whilst behaviours do the real work.

Such flows are a consequence of how the composite interacts with it’s children. The success of behaviour trees depends on this logic being simple, well defined and limited to a few well established patterns - this is what ensures that visualising a tree enables a user to quickly grasp the decision making captured therein.

For the standard patterns, this logic is limited to the ordering of execution and logical inferences on the resulting status of the composite’s children.

This is a good guideline to adhere to (i.e. don’t reach inside children to inference on custom variables, nor reach out to the system your tree is attached to).

Implementation wise, this renders the update() method redundant as all customisation to create a simple, well defined composite happens in the tick() method.

Bottom line, composites do not make use of this method. Implementing it for subclasses of the core composites will not do anything.

Return type

Status

class py_trees.composites.Parallel(name: str, policy: Base, children: Optional[List[Behaviour]] = None)[source]

Bases: Composite

Parallels enable a kind of spooky at-a-distance concurrency.

digraph pastafarianism {
graph [fontname="times-roman", splines=curved];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Parallel [fillcolor=gold, fontcolor=black, fontsize=9, label="Parallel\n--SuccessOnSelected(⚡,[B1,B2])--", shape=parallelogram, style=filled];
B1 [fillcolor=gray, fontcolor=black, fontsize=9, label=B1, shape=ellipse, style=filled];
Parallel -> B1;
B2 [fillcolor=gray, fontcolor=black, fontsize=9, label=B2, shape=ellipse, style=filled];
Parallel -> B2;
B3 [fillcolor=gray, fontcolor=black, fontsize=9, label=B3, shape=ellipse, style=filled];
Parallel -> B3;
}

A parallel ticks every child every time the parallel is itself ticked. The parallelism however, is merely conceptual. The children have actually been sequentially ticked, but from both the tree and the parallel’s purview, all children have been ticked at once.

The parallelism too, is not true in the sense that it kicks off multiple threads or processes to do work. Some behaviours may kick off threads or processes in the background, or connect to existing threads/processes. The behaviour itself however, merely monitors these and is itself encosced in a py_tree which only ever ticks in a single-threaded operation.

Policies SuccessOnAll and SuccessOnSelected may be configured to be synchronised in which case children that tick with SUCCESS will be skipped on subsequent ticks until the policy criteria is met, or one of the children returns status FAILURE.

Parallels with policy SuccessOnSelected will check in both the setup() and tick() methods to to verify the selected set of children is actually a subset of the children of this parallel.

Parameters
__init__(name: str, policy: Base, children: Optional[List[Behaviour]] = None)[source]

Initialise the behaviour with name, policy and a list of children.

Parameters
  • name (str) – the composite behaviour name

  • policy (Base) – policy for deciding success or otherwise (default: SuccessOnAll)

  • children (Optional[List[Behaviour]]) – list of children to add

setup(**kwargs: int) None[source]

Detect before ticking whether the policy configuration is invalid.

Parameters

**kwargs (dict) – distribute arguments to this behaviour and in turn, all of it’s children

Raises
  • RuntimeError – if the parallel’s policy configuration is invalid

  • Exception – be ready to catch if any of the children raise an exception

Return type

None

stop(new_status: Status = Status.INVALID) None[source]

Ensure that any running children are stopped.

Parameters

new_status (Status) – the composite is transitioning to this new status

Return type

None

tick() Iterator[Behaviour][source]

Tick over the children.

Yields

Behaviour – a reference to itself or one of its children

Raises

RuntimeError – if the policy configuration was invalid

Return type

Iterator[Behaviour]

validate_policy_configuration() None[source]

Validate the currently stored policy.

Policy configuration can be invalid if: * Policy is SuccessOnSelected and no behaviours have been specified * Policy is SuccessOnSelected and behaviours that are not children exist

Raises

RuntimeError – if policy configuration was invalid

Return type

None

class py_trees.composites.Selector(name: str, memory: bool, children: Optional[List[Behaviour]] = None)[source]

Bases: Composite

Selectors are the decision makers.

digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Selector [fontcolor=black, shape=octagon, fontsize=11, style=filled, fillcolor=cyan];
"High Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "High Priority";
"Med Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "Med Priority";
"Low Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "Low Priority";
}

A selector executes each of its child behaviours in turn until one of them succeeds (at which point it itself returns RUNNING or SUCCESS, or it runs out of children at which point it itself returns FAILURE. We usually refer to selecting children as a means of choosing between priorities. Each child and its subtree represent a decreasingly lower priority path.

Note

Switching from a low -> high priority branch causes a stop(INVALID) signal to be sent to the previously executing low priority branch. This signal will percolate down that child’s own subtree. Behaviours should make sure that they catch this and destruct appropriately.

Note

If configured with memory, higher priority checks will be skipped when a child returned with running on the previous tick. i.e. once a priority is locked in, it will run to completion and can only be interrupted if the selector is interrupted by higher priorities elsewhere in the tree.

See also

The py-trees-demo-selector program demos higher priority switching under a selector.

Parameters
  • memory (bool) – if RUNNING on the previous tick, resume with the RUNNING child

  • name (str) – the composite behaviour name

  • children ([Behaviour]) – list of children to add

__init__(name: str, memory: bool, children: Optional[List[Behaviour]] = None)[source]
Parameters
stop(new_status: Status = Status.INVALID) None[source]

Ensure that children are appropriately stopped and update status.

Parameters

new_status (Status) – the composite is transitioning to this new status

Return type

None

tick() Iterator[Behaviour][source]

Customise the tick behaviour for a selector.

This implements priority-interrupt style handling amongst the selector’s children. The selector’s status is always a reflection of it’s children’s status.

Yields

Behaviour – a reference to itself or one of its children

Return type

Iterator[Behaviour]

class py_trees.composites.Sequence(name: str, memory: bool, children: Optional[List[Behaviour]] = None)[source]

Bases: Composite

Sequences are the factory lines of behaviour trees.

digraph sequence {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Guard [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> Guard;
"Action 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 1";
"Action 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 2";
"Action 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 3";
}

A sequence will progressively tick over each of its children so long as each child returns SUCCESS. If any child returns FAILURE or RUNNING the sequence will halt and the parent will adopt the result of this child. If it reaches the last child, it returns with that result regardless.

Note

The sequence halts once it engages with a child is RUNNING, remaining behaviours are not ticked.

Note

If configured with memory and a child returned with running on the previous tick, it will proceed directly to the running behaviour, skipping any and all preceding behaviours. With memory is useful for moving through a long running series of tasks. Without memory is useful if you want conditional guards in place preceding the work that you always want checked off.

See also

The py-trees-demo-sequence program demos a simple sequence in action.

Parameters
__init__(name: str, memory: bool, children: Optional[List[Behaviour]] = None)[source]
Parameters
stop(new_status: Status = Status.INVALID) None[source]

Ensure that children are appropriately stopped and update status.

Parameters

new_status (Status) – the composite is transitioning to this new status

Return type

None

tick() Iterator[Behaviour][source]

Tick over the children.

Yields

Behaviour – a reference to itself or one of its children

Return type

Iterator[Behaviour]

py_trees.console

Simple colour definitions and syntax highlighting for the console.


Colour Definitions

The current list of colour definitions include:

  • Regular: black, red, green, yellow, blue, magenta, cyan, white,

  • Bold: bold, bold_black, bold_red, bold_green, bold_yellow, bold_blue, bold_magenta, bold_cyan, bold_white

These colour definitions can be used in the following way:

import py_trees.console as console
print(console.cyan + "    Name" + console.reset + ": " + console.yellow + "Dude" + console.reset)
py_trees.console.banner(msg: str) None[source]

Print a banner with centred text to stdout.

Parameters

msg (str) – text to centre in the banner

Return type

None

py_trees.console.colours = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']

List of all available colours.

py_trees.console.console_has_colours() bool[source]

Detect if the console (stdout) has colourising capability.

Return type

bool

py_trees.console.debug(msg: str) None[source]

Print a debug message.

Parameters
  • str – message to print

  • msg (str) –

Return type

None

py_trees.console.define_symbol_or_fallback(original: str, fallback: str, encoding: str = 'utf-8') str[source]

Go unicode, or fallback to ascii.

Return the correct encoding according to the specified encoding. Used to make sure we get an appropriate symbol, even if the shell is merely ascii as is often the case on, e.g. Jenkins CI.

Parameters
  • original (str) – the unicode string (usually just a character)

  • fallback (str) – the fallback ascii string

  • encoding (str) – the encoding to check against.

Returns

either original or fallback depending on whether exceptions were thrown.

Return type

str

py_trees.console.error(msg: str) None[source]

Print an error message.

Parameters
  • str – message to print

  • msg (str) –

Return type

None

py_trees.console.has_colours = False

Whether the loading program has access to colours or not.

py_trees.console.has_unicode(encoding: str = 'utf-8') bool[source]

Define whether the specified encoding has unicode symbols.

This is usually used to check if stdout is capable of unicode or otherwise (e.g. Jenkins CI is often be configured with unicode disabled).

Parameters

encoding (str) – the encoding to check against.

Returns

true if capable, false otherwise

Return type

bool

py_trees.console.info(msg: str) None[source]

Print an info message.

Parameters
  • str – message to print

  • msg (str) –

Return type

None

py_trees.console.logdebug(message: str) None[source]

Prefixes [DEBUG] and colours the message green.

Parameters

message (str) – message to log.

Return type

None

py_trees.console.logerror(message: str) None[source]

Prefixes [ERROR] and colours the message red.

Parameters

message (str) – message to log.

Return type

None

py_trees.console.logfatal(message: str) None[source]

Prefixes [FATAL] and colours the message bold red.

Parameters

message (str) – message to log.

Return type

None

py_trees.console.loginfo(message: str) None[source]

Prefixes [ INFO] to the message.

Parameters

message (str) – message to log.

Return type

None

py_trees.console.logwarn(message: str) None[source]

Prefixes [ WARN] and colours the message yellow.

Parameters

message (str) – message to log.

Return type

None

py_trees.console.pretty_print(msg: str, colour: str = '') None[source]

Pretty print a coloured message.

Parameters
  • msg (str) – text to print

  • colour (str) – ascii colour to use

Return type

None

py_trees.console.pretty_println(msg: str, colour: str = '') None[source]

Pretty print a coloured message with a newline.

Parameters
  • msg (str) – text to print

  • colour (str) – ascii colour to use

Return type

None

py_trees.console.read_single_keypress() str[source]

Wait for a single keypress on stdin.

This is a silly function to call if you need to do it a lot because it has to store stdin’s current setup, setup stdin for reading single keystrokes then read the single keystroke then revert stdin back after reading the keystroke.

Returns

the character of the key that was pressed

Raises

KeyboardInterrupt – if CTRL-C was pressed (keycode 0x03)

Return type

str

py_trees.console.warning(msg: str) None[source]

Print a warning message.

Parameters
  • str – message to print

  • msg (str) –

Return type

None

py_trees.decorators

Decorate your children. They make great furniture pieces.

Decorators are behaviours that manage a single child and provide common modifications to their underlying child behaviour (e.g. inverting the result). That is, they provide a means for behaviours to wear different ‘hats’ and this combinatorially expands the capabilities of your behaviour library.

_images/many-hats.png

An example:

digraph life {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Life [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Inverter [fillcolor=ghostwhite, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Life -> Inverter;
"Busy?" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Inverter -> "Busy?";
Timeout [fillcolor=ghostwhite, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Life -> Timeout;
"Have a Beer!" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Timeout -> "Have a Beer!";
}
 1#!/usr/bin/env python3
 2# -*- coding: utf-8 -*-
 3
 4import py_trees.decorators
 5import py_trees.display
 6
 7if __name__ == '__main__':
 8
 9    root = py_trees.composites.Sequence(name="Life")
10    timeout = py_trees.decorators.Timeout(
11        name="Timeout",
12        child=py_trees.behaviours.Success(name="Have a Beer!")
13    )
14    failure_is_success = py_trees.decorators.Inverter(
15        name="Inverter",
16        child=py_trees.behaviours.Success(name="Busy?")
17        )
18    root.add_children([failure_is_success, timeout])
19    py_trees.display.render_dot_tree(root)

Decorators (Hats)

Decorators with specific functionality:

And the X is Y family:

Decorators for Blocking Behaviours

It is worth making a note of the effect of decorators on blocking behaviours, i.e. those that return RUNNING before eventually returning SUCCESS or FAILURE.

A decorator, such as py_trees.decorators.RunningIsSuccess() on a blocking behaviour will immediately terminate the underlying child and re-intialise on it’s next tick. This is often surprising (to the user) but is necessary to ensure the underlying child isn’t left in a dangling state (i.e. RUNNING) as subsequent ticks move on to other parts of the tree.

A better approach in this case is to build a non-blocking variant or a combination of non-blocking behaviors that handle construction, monitoring and destruction of the activity represented by the original blocking behaviour.

class py_trees.decorators.Condition(name: str, child: Behaviour, status: Status)[source]

Bases: Decorator

A blocking conditional decorator.

Encapsulates a behaviour and wait for it’s status to flip to the desired state. This behaviour will tick with RUNNING while waiting and SUCCESS when the flip occurs.

Parameters
update() Status[source]

Check if the condtion has triggered, block otherwise.

SUCCESS if the decorated child has returned the specified status, otherwise RUNNING. This decorator will never return FAILURE

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.Count(name: str, child: Behaviour)[source]

Bases: Decorator

Count the number of times it’s child has been ticked.

This increments counters tracking the total number of times it’s child has been ticked as well as the number of times it has landed in each respective state.

It will always re-zero counters on setup().

Variables
  • total_tick_count – number of ticks in total

  • running_count – number of ticks resulting in this state

  • success_count – number of ticks resulting in this state

  • failure_count – number of ticks resulting in this state

  • interrupt_count – number of times a higher priority has interrupted

Parameters
setup(**kwargs: int) None[source]

Reset the counters.

Parameters

kwargs (int) –

Return type

None

terminate(new_status: Status) None[source]

Increment the completion / interruption counters.

Parameters

new_status (Status) –

Return type

None

update() Status[source]

Increment the counter.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.Decorator(name: str, child: Behaviour)[source]

Bases: Behaviour

Parent class for decorating a child/subtree with some additional logic.

Parameters
  • child (Behaviour) – the child to be decorated

  • name (str) – the decorator name

Raises

TypeError – if the child is not an instance of Behaviour

stop(new_status: Status) None[source]

Check if the child is running (dangling) and stop it if that is the case.

Parameters

new_status (Status) – the behaviour is transitioning to this new status

Return type

None

tick() Iterator[Behaviour][source]

Manage the decorated child through the tick.

Yields

a reference to itself or one of its children

Return type

Iterator[Behaviour]

tip() Optional[Behaviour][source]

Retrieve the tip of this behaviour’s subtree (if it has one).

This corresponds to the the deepest node that was running before the subtree traversal reversed direction and headed back to this node.

Returns

child behaviour, or None if its status is INVALID

Return type

Optional[Behaviour]

class py_trees.decorators.EternalGuard(name: str, child: Behaviour, condition: Any, blackboard_keys: Optional[Union[List[str], Set[str]]] = None)[source]

Bases: Decorator

Continuously guard (with a condition) the execution of a child/subtree.

The eternal guard checks a condition prior to every tick of the child/subtree. If at any time the condition fails, the child/subtree is invalidated.

Note

This is stronger than a conventional guard which is only checked once before any and all ticking of what follows the guard.

Parameters
  • child (Behaviour) – the child behaviour or subtree

  • condition (Any) – a functional check that determines execution or not of the subtree

  • blackboard_keys (Optional[Union[List[str], Set[str]]]) – provide read access for the conditional function to these keys

  • name (str) – the decorator name

Examples

Simple conditional function returning True/False:

def check():
     return True

foo = py_trees.behaviours.Foo()
eternal_guard = py_trees.decorators.EternalGuard(
    name="Eternal Guard",
    condition=check,
    child=foo
)

Simple conditional function returning SUCCESS/FAILURE:

def check():
     return py_trees.common.Status.SUCCESS

foo = py_trees.behaviours.Foo()
eternal_guard = py_trees.decorators.EternalGuard(
    name="Eternal Guard",
    condition=check,
    child=foo
)

Conditional function that makes checks against data on the blackboard (the blackboard client with pre-configured access is provided by the EternalGuard instance):

def check(blackboard):
     return blackboard.velocity > 3.0

foo = py_trees.behaviours.Foo()
eternal_guard = py_trees.decorators.EternalGuard(
    name="Eternal Guard",
    condition=check,
    blackboard_keys={"velocity"},
    child=foo
)

See also

py-trees-demo-eternal-guard for an alternative means of implementing the eternal guard concept using sequences without memory.

tick() Iterator[Behaviour][source]

Conditionally manage the child.

Yields

a reference to itself or one of its children

Return type

Iterator[Behaviour]

update() Status[source]

Reflect the decorated child’s status.

The update method is only ever triggered in the child’s post-tick, which implies that the condition has already been checked and passed (refer to the tick() method).

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.FailureIsRunning(name: str, child: Behaviour)[source]

Bases: Decorator

Dont stop running.

Parameters
update() Status[source]

Reflect FAILURE as RUNNING.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.FailureIsSuccess(name: str, child: Behaviour)[source]

Bases: Decorator

Be positive, always succeed.

Parameters
update() Status[source]

Reflect FAILURE as SUCCESS.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.Inverter(name: str, child: Behaviour)[source]

Bases: Decorator

A decorator that inverts the result of a class’s update function.

Parameters
update() Status[source]

Flip SUCCESS and FAILURE.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.OneShot(name: str, child: Behaviour, policy: OneShotPolicy)[source]

Bases: Decorator

A decorator that implements the oneshot pattern.

This decorator ensures that the underlying child is ticked through to completion just once and while doing so, will return with the same status as it’s child. Thereafter it will return with the final status of the underlying child.

Completion status is determined by the policy given on construction.

  • With policy ON_SUCCESSFUL_COMPLETION, the oneshot will activate only when the underlying child returns SUCCESS (i.e. it permits retries).

  • With policy ON_COMPLETION, the oneshot will activate when the child returns SUCCESS || FAILURE.

Parameters
  • name (str) –

  • child (Behaviour) –

  • policy (OneShotPolicy) –

terminate(new_status: Status) None[source]

Prevent further entry if finishing with SUCCESS.

This uses a flag to register that the behaviour has gone through to completion. In future ticks, it will block entry to the child and just return the original status result.

Parameters

new_status (Status) –

Return type

None

tick() Iterator[Behaviour][source]

Tick the child or bounce back with the original status if already completed.

Yields

a reference to itself or a behaviour in it’s child subtree

Return type

Iterator[Behaviour]

update() Status[source]

Bounce if the child has already successfully completed.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.PassThrough(name: str, child: Behaviour)[source]

Bases: Decorator

This decorator simply reflects the child’s current status.

This behaviour is useful for debugging or visualisation purposes.

Parameters
update() Status[source]

Just reflect the child status.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.Repeat(name: str, child: Behaviour, num_success: int)[source]

Bases: Decorator

Repeat.

SUCCESS is RUNNING up to a specified number at which point this decorator returns SUCCESS.

FAILURE is always FAILURE.

Parameters
  • child (Behaviour) – the child behaviour or subtree

  • num_success (int) – repeat this many times (-1 to repeat indefinitely)

  • name (str) – the decorator name

initialise() None[source]

Reset the currently registered number of successes.

Return type

None

update() Status[source]

Repeat until the nth consecutive success.

Returns

SUCCESS on nth success, RUNNING on running, or pre-nth success FAILURE failure.

Return type

Status

class py_trees.decorators.Retry(name: str, child: Behaviour, num_failures: int)[source]

Bases: Decorator

Keep trying, pastafarianism is within reach.

FAILURE is RUNNING up to a specified number of attempts.

Parameters
  • child (Behaviour) – the child behaviour or subtree

  • num_failures (int) – maximum number of permitted failures

  • name (str) – the decorator name

initialise() None[source]

Reset the currently registered number of attempts.

Return type

None

update() Status[source]

Retry until failure count is reached.

Returns

SUCCESS on success, RUNNING on running, or pre-nth failure FAILURE only on the nth failure.

Return type

Status

class py_trees.decorators.RunningIsFailure(name: str, child: Behaviour)[source]

Bases: Decorator

Got to be snappy! We want results…yesterday.

Parameters
update() Status[source]

Reflect RUNNING as FAILURE.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.RunningIsSuccess(name: str, child: Behaviour)[source]

Bases: Decorator

Don’t hang around…

Parameters
update() Status[source]

Reflect RUNNING as SUCCESS.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.StatusToBlackboard(name: str, child: Behaviour, variable_name: str)[source]

Bases: Decorator

Reflect the status of the decorator’s child to the blackboard.

Parameters
  • child (Behaviour) – the child behaviour or subtree

  • variable_name (str) – name of the blackboard variable, may be nested, e.g. foo.status

  • name (str) – the decorator name

update() Status[source]

Reflect the decorated child’s status to the blackboard.

Returns: the decorated child’s status

Return type

Status

class py_trees.decorators.SuccessIsFailure(name: str, child: Behaviour)[source]

Bases: Decorator

Be depressed, always fail.

Parameters
update() Status[source]

Reflect SUCCESS as FAILURE.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.SuccessIsRunning(name: str, child: Behaviour)[source]

Bases: Decorator

The tickling never ends…

Parameters
update() Status[source]

Reflect SUCCESS as RUNNING.

Returns

the behaviour’s new status Status

Return type

Status

class py_trees.decorators.Timeout(name: str, child: Behaviour, duration: float = 5.0)[source]

Bases: Decorator

Executes a child/subtree with a timeout.

A decorator that applies a timeout pattern to an existing behaviour. If the timeout is reached, the encapsulated behaviour’s stop() method is called with status FAILURE otherwise it will simply directly tick and return with the same status as that of it’s encapsulated behaviour.

Parameters
initialise() None[source]

Reset the feedback message and finish time on behaviour entry.

Return type

None

update() Status[source]

Fail on timeout, or block / reflect the child’s result accordingly.

Terminate the child and return FAILURE if the timeout is exceeded.

Returns

the behaviour’s new status Status

Return type

Status

py_trees.display

Display utilities for the command line.

Behaviour trees are significantly easier to design, monitor and debug with visualisations. Py Trees does provide minimal assistance to render trees to various simple output formats. Currently this includes dot graphs, strings or stdout.

py_trees.display.ascii_blackboard(key_filter: Optional[Union[List[str], Set[str]]] = None, regex_filter: Optional[str] = None, client_filter: Optional[Union[Set[UUID], List[UUID]]] = None, keys_to_highlight: Optional[List[str]] = None, display_only_key_metadata: bool = False, indent: int = 0) str[source]

Graffiti your console with ascii art for your blackboard.

Parameters
  • key_filter (Optional[Union[List[str], Set[str]]]) – filter on a set/list of blackboard keys

  • regex_filter (Optional[str]) – filter on a python regex str

  • client_filter (Optional[Union[Set[UUID], List[UUID]]]) – filter on a set/list of client uuids

  • keys_to_highlight (Optional[List[str]]) – list of keys to highlight

  • display_only_key_metadata (bool) – read/write access, … instead of values

  • indent (int) – the number of characters to indent the blackboard

Returns

a unicoded blackboard (i.e. in string form)

Return type

str

Note

registered variables that have not yet been set are marked with a ‘-’

py_trees.display.ascii_symbols = {'space': ' ', 'left_arrow': '<-', 'right_arrow': '->', 'left_right_arrow': '<->', 'bold': '', 'bold_reset': '', 'memory': 'M', 'synchronised': 's', 'sequence_with_memory': '{-}', 'selector_with_memory': '{o}', 'sequence_without_memory': '[-]', 'selector_without_memory': '[o]', 'parallel': '/_/', 'decorator': '-^-', 'behaviour': '-->', <Status.SUCCESS: 'SUCCESS'>: 'o', <Status.FAILURE: 'FAILURE'>: 'x', <Status.INVALID: 'INVALID'>: '-', <Status.RUNNING: 'RUNNING'>: '*'}

Symbols for a non-unicode, non-escape sequence capable console.

py_trees.display.ascii_tree(root: Behaviour, show_only_visited: bool = False, show_status: bool = False, visited: Optional[Dict[UUID, Status]] = None, previously_visited: Optional[Dict[UUID, Status]] = None, indent: int = 0) str[source]

Graffiti your console with ascii art for your trees.

Parameters
  • root (Behaviour) – the root of the tree, or subtree you want to show

  • show_only_visited (bool) – show only visited behaviours

  • show_status (bool) – always show status and feedback message (i.e. for every element, not just those visited)

  • visited (dict) – dictionary of (uuid.UUID) and status (Status) pairs for behaviours visited on the current tick

  • previously_visited (dict) – dictionary of behaviour id/status pairs from the previous tree tick

  • indent (int) – the number of characters to indent the tree

Returns

an ascii tree (i.e. in string form)

Return type

str

Examples

Use the SnapshotVisitor and BehaviourTree to generate snapshot information at each tick and feed that to a post tick handler that will print the traversed ascii tree complete with status and feedback messages.

_images/ascii_tree.png
def post_tick_handler(snapshot_visitor, behaviour_tree):
    print(
        py_trees.display.unicode_tree(
            behaviour_tree.root,
            visited=snapshot_visitor.visited,
            previously_visited=snapshot_visitor.visited
        )
    )

root = py_trees.composites.Sequence(name="Sequence", memory=True)
for action in ["Action 1", "Action 2", "Action 3"]:
    b = py_trees.behaviours.StatusQueue(
        name=action,
        queue=[py_trees.common.Status.RUNNING],
        eventually = py_trees.common.Status.SUCCESS
    )
    root.add_child(b)
behaviour_tree = py_trees.trees.BehaviourTree(root)
snapshot_visitor = py_trees.visitors.SnapshotVisitor()
behaviour_tree.add_post_tick_handler(
    functools.partial(post_tick_handler,
                      snapshot_visitor))
behaviour_tree.visitors.append(snapshot_visitor)
py_trees.display.dot_tree(root: Behaviour, visibility_level: VisibilityLevel = VisibilityLevel.DETAIL, collapse_decorators: bool = False, with_blackboard_variables: bool = False, with_qualified_names: bool = False) Dot[source]

Paint your tree on a pydot graph.

See also

render_dot_tree().

Parameters
  • root (Behaviour) – the root of a tree, or subtree

  • visibility_level (optional) – collapse subtrees at or under this level

  • collapse_decorators (optional) – only show the decorator (not the child), defaults to False

  • with_blackboard_variables (optional) – add nodes for the blackboard variables

  • with_qualified_names (optional) – print the class information for each behaviour in each node, defaults to False

Returns

graph

Return type

pydot.Dot

Examples

# convert the pydot graph to a string object
print("{}".format(py_trees.display.dot_graph(root).to_string()))
py_trees.display.render_dot_tree(root: Behaviour, visibility_level: VisibilityLevel = VisibilityLevel.DETAIL, collapse_decorators: bool = False, name: Optional[str] = None, target_directory: Optional[str] = None, with_blackboard_variables: bool = False, with_qualified_names: bool = False) Dict[str, str][source]

Render the dot tree to dot, svg, png. files.

By default, these are saved in the current working directory and will be named with the root behaviour name.

Parameters
  • root (Behaviour) – the root of a tree, or subtree

  • visibility_level (VisibilityLevel) – collapse subtrees at or under this level

  • collapse_decorators (bool) – only show the decorator (not the child)

  • name (Optional[str]) – name to use for the created files (defaults to the root behaviour name)

  • target_directory (Optional[str]) – default is to use the current working directory, set this to redirect elsewhere

  • with_blackboard_variables (bool) – add nodes for the blackboard variables

  • with_qualified_names (bool) – print the class names of each behaviour in the dot node

Return type

Dict[str, str]

Example

Render a simple tree to dot/svg/png file:

digraph sequence {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Guard [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> Guard;
"Action 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 1";
"Action 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 2";
"Action 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 3";
}
root = py_trees.composites.Sequence(name="Sequence", memory=True)
for job in ["Action 1", "Action 2", "Action 3"]:
    success_after_two = py_trees.behaviours.StatusQueue(
        name=job,
        queue=[py_trees.common.Status.RUNNING],
        eventually = py_trees.common.Status.SUCCESS
    )
    root.add_child(success_after_two)
py_trees.display.render_dot_tree(root)

Tip

A good practice is to provide a command line argument for optional rendering of a program so users can quickly visualise what tree the program will execute.

py_trees.display.unicode_blackboard(key_filter: Optional[Union[List[str], Set[str]]] = None, regex_filter: Optional[str] = None, client_filter: Optional[Union[Set[UUID], List[UUID]]] = None, keys_to_highlight: Optional[List[str]] = None, display_only_key_metadata: bool = False, indent: int = 0) str[source]

Graffiti your console with unicode art for your blackboard.

Parameters
  • key_filter (Optional[Union[List[str], Set[str]]]) – filter on a set/list of blackboard keys

  • regex_filter (Optional[str]) – filter on a python regex str

  • client_filter (Optional[Union[Set[UUID], List[UUID]]]) – filter on a set/list of client uuids

  • keys_to_highlight (Optional[List[str]]) – list of keys to highlight

  • display_only_key_metadata (bool) – read/write access, … instead of values

  • indent (int) – the number of characters to indent the blackboard

Returns

a unicoded blackboard (i.e. in string form)

Return type

str

Note

registered variables that have not yet been set are marked with a ‘-’

py_trees.display.unicode_blackboard_activity_stream(activity_stream: Optional[List[ActivityItem]] = None, indent: int = 0, show_title: bool = True) str[source]

Pretty print the blackboard stream to console.

Parameters
  • activity_stream (Optional[List[ActivityItem]]) – the log of activity, if None, get the entire activity stream

  • indent (int) – the number of characters to indent the blackboard

  • show_title (bool) – include the title in the output

Return type

str

py_trees.display.unicode_symbols = {'space': ' ', 'left_arrow': '←', 'right_arrow': '→', 'left_right_arrow': '↔', 'bold': '', 'bold_reset': '', 'memory': 'Ⓜ', 'synchronised': '⚡', 'sequence_with_memory': '{-}', 'selector_with_memory': '{o}', 'sequence_without_memory': '[-]', 'selector_without_memory': '[o]', 'parallel': '/_/', 'decorator': '-^-', 'behaviour': '-->', <Status.SUCCESS: 'SUCCESS'>: '✓', <Status.FAILURE: 'FAILURE'>: '✕', <Status.INVALID: 'INVALID'>: '-', <Status.RUNNING: 'RUNNING'>: '*'}

Symbols for a unicode, escape sequence capable console.

py_trees.display.unicode_tree(root: Behaviour, show_only_visited: bool = False, show_status: bool = False, visited: Optional[Dict[UUID, Status]] = None, previously_visited: Optional[Dict[UUID, Status]] = None, indent: int = 0) str[source]

Graffiti your console with unicode art for your trees.

Parameters
  • root (Behaviour) – the root of the tree, or subtree you want to show

  • show_only_visited (bool) – show only visited behaviours

  • show_status (bool) – always show status and feedback message (i.e. for every element, not just those visited)

  • visited (dict) – dictionary of (uuid.UUID) and status (Status) pairs for behaviours visited on the current tick

  • previously_visited (dict) – dictionary of behaviour id/status pairs from the previous tree tick

  • indent (int) – the number of characters to indent the tree

Returns

a unicode tree (i.e. in string form)

Return type

str

py_trees.display.xhtml_symbols = {'space': '<text>&#xa0;</text>', 'left_arrow': '<text>&#x2190;</text>', 'right_arrow': '<text>&#x2192;</text>', 'left_right_arrow': '<text>&#x2194;</text>', 'bold': '<b>', 'bold_reset': '</b>', 'memory': '<text>&#x24C2;</text>', 'synchronised': '<text>&#9889;</text>', 'sequence_with_memory': '<text>{-}</text>', 'selector_with_memory': '<text>{o}</text>', 'sequence_without_memory': '<text>[-]</text>', 'selector_without_memory': '<text>[o]</text>', 'parallel': '<text style="color:green;">/_/</text>', 'decorator': '<text>-^-</text>', 'behaviour': '<text>--></text>', <Status.SUCCESS: 'SUCCESS'>: '<text style="color:green;">&#x2713;</text>', <Status.FAILURE: 'FAILURE'>: '<text style="color:red;">&#x2715;</text>', <Status.INVALID: 'INVALID'>: '<text style="color:darkgoldenrod;">-</text>', <Status.RUNNING: 'RUNNING'>: '<text style="color:blue;">*</text>'}

Symbols for embedding in html.

py_trees.display.xhtml_tree(root: Behaviour, show_only_visited: bool = False, show_status: bool = False, visited: Optional[Dict[UUID, Status]] = None, previously_visited: Optional[Dict[UUID, Status]] = None, indent: int = 0) str[source]

Paint your tree on an xhtml snippet.

Parameters
  • root (Behaviour) – the root of the tree, or subtree you want to show

  • show_only_visited (bool) – show only visited behaviours

  • show_status (bool) – always show status and feedback message (i.e. for every element, not just those visited)

  • visited (Optional[Dict[UUID, Status]]) – dictionary of (uuid.UUID) and status (Status) pairs for behaviours visited on the current tick

  • previously_visited (Optional[Dict[UUID, Status]]) – dictionary of behaviour id/status pairs from the previous tree tick

  • indent (int) – the number of characters to indent the tree

Returns

an ascii tree (i.e. as a xhtml snippet)

Return type

str

Examples

import py_trees
a = py_trees.behaviours.Success()
b = py_trees.behaviours.Success()
c = c = py_trees.composites.Sequence(name="Sequence", memory=True, children=[a, b])
c.tick_once()

f = open('testies.html', 'w')
f.write('<html><head><title>Foo</title><body>')
f.write(py_trees.display.xhtml_tree(c, show_status=True))
f.write("</body></html>")

py_trees.idioms

Creators of common subtree patterns.

py_trees.idioms.either_or(conditions: List[ComparisonExpression], subtrees: List[Behaviour], name: str = 'Either Or', namespace: Optional[str] = None) Behaviour[source]

Create an idiom with selector-like qualities, but no priority concerns.

Often you need a kind of selector that doesn’t implement prioritisations, i.e. you would like different paths to be selected on a first-come, first-served basis.

task_one = py_trees.behaviours.TickCounter(name="Subtree 1", duration=2)
task_two = py_trees.behaviours.TickCounter(name="Subtree 2", duration=2)
either_or = py_trees.idioms.either_or(
    name="EitherOr",
    conditions=[
        py_trees.common.ComparisonExpression("joystick_one", "enabled", operator.eq),
        py_trees.common.ComparisonExpression("joystick_two", "enabled", operator.eq),
    ],
    subtrees=[task_one, task_two],
    namespace="either_or",
)
digraph pastafarianism {
ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
EitherOr [label=EitherOr, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
XOR [label=XOR, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
EitherOr -> XOR;
Chooser [label=Chooser, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EitherOr -> Chooser;
"Option 1" [label="Option 1", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Chooser -> "Option 1";
"Enabled?" [label="Enabled?", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 1" -> "Enabled?";
"Subtree 1" [label="Subtree 1", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 1" -> "Subtree 1";
"Option 2" [label="Option 2", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Chooser -> "Option 2";
"Enabled?*" [label="Enabled?*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 2" -> "Enabled?*";
"Subtree 2" [label="Subtree 2", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 2" -> "Subtree 2";
}

Idiom - Either Or

Up front is an XOR conditional check which locks in the result on the blackboard under the specified namespace. Locking the result in permits the conditional variables to vary in future ticks without interrupting the execution of the chosen subtree (an example of a conditional variable may be one that has registered joystick button presses).

Once the result is locked in, the relevant subtree is activated beneath the selector. The children of the selector are, from left to right, not in any order of priority since the previous xor choice has been locked in and isn’t revisited until the subtree executes to completion. Only one may be active and it cannot be interrupted by the others.

The only means of interrupting the execution is via a higher priority in the tree that this idiom is embedded in.

Parameters
  • conditions (List[ComparisonExpression]) – list of triggers that ultimately select the subtree to enable

  • subtrees (List[Behaviour]) – list of subtrees to tick from in the either_or operation

  • name (str) – the name to use for this idiom’s root behaviour

  • preemptible – whether the subtrees may preempt (interrupt) each other

  • namespace (Optional[str]) – this idiom’s private variables will be put behind this namespace

Raises

ValueError if the number of conditions does not match the number of subtrees

Return type

Behaviour

If no namespace is provided, a unique one is derived from the idiom’s name.

Todo

a version for which other subtrees can preempt (in an unprioritised manner) the active branch

py_trees.idioms.oneshot(behaviour: Behaviour, name: str = 'Oneshot', variable_name: str = 'oneshot', policy: OneShotPolicy = OneShotPolicy.ON_SUCCESSFUL_COMPLETION) Behaviour[source]

Ensure that a particular pattern is executed through to completion just once.

Thereafter it will just rebound with the completion status.

digraph oneshot {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
OneShot [fillcolor=cyan, fontcolor=black, fontsize=11, shape=octagon, style=filled];
"Oneshot w/ Guard" [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
OneShot -> "Oneshot w/ Guard";
"Not Completed?" [fillcolor=ghostwhite, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Oneshot w/ Guard" -> "Not Completed?";
"Completed?" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Not Completed?" -> "Completed?";
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
"Oneshot w/ Guard" -> Sequence;
Guard [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> Guard;
"Action 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 1";
"Action 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 2";
"Action 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 3";
"Mark Done\n[SUCCESS]" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Mark Done\n[SUCCESS]";
"Oneshot Result" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
OneShot -> "Oneshot Result";
}

Note

Set the policy to configure the oneshot to keep trying if failing, or to abort further attempts regardless of whether it finished with status FAILURE.

Parameters
  • behaviour (Behaviour) – single behaviour or composited subtree to oneshot

  • name (str) – the name to use for the oneshot root (selector)

  • variable_name (str) – name for the variable used on the blackboard, may be nested

  • policy (OneShotPolicy) – execute just once regardless of success or failure, or keep trying if failing

Returns

the root of the oneshot subtree

Return type

Behaviour

py_trees.idioms.pick_up_where_you_left_off(name: str, tasks: List[BehaviourSubClass]) Behaviour[source]

Create an idiom that enables a sequence of tasks to pick up where it left off.

Rudely interrupted while enjoying a sandwich, a caveman (just because they wore loincloths does not mean they were not civilised), picks up his club and fends off the sabre-tooth tiger invading his sanctum as if he were swatting away a gnat. Task accomplished, he returns to the joys of munching through the layers of his sandwich.

digraph pick_up_where_you_left_off {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Pick Up Where You Left Off" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
"High Priority" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Pick Up Where You Left Off" -> "High Priority";
Tasks [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Pick Up Where You Left Off" -> Tasks;
"Do or Don't" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
Tasks -> "Do or Don't";
"Done?" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Do or Don't" -> "Done?";
Worker [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Do or Don't" -> Worker;
"Task 1" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Worker -> "Task 1";
"Mark\ntask_1_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Worker -> "Mark\ntask_1_done";
"Do or Don't*" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
Tasks -> "Do or Don't*";
"Done?*" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Do or Don't*" -> "Done?*";
"Worker*" [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Do or Don't*" -> "Worker*";
"Task 2" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Worker*" -> "Task 2";
"Mark\ntask_2_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Worker*" -> "Mark\ntask_2_done";
"Clear\ntask_1_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Tasks -> "Clear\ntask_1_done";
"Clear\ntask_2_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Tasks -> "Clear\ntask_2_done";
}

Note

There are alternative ways to accomplish this idiom with their pros and cons.

a) The tasks in the sequence could be replaced by a factory behaviour that dynamically checks the state of play and spins up the tasks required each time the task sequence is first entered and invalidates/deletes them when it is either finished or invalidated. That has the advantage of not requiring much of the blackboard machinery here, but disadvantage in not making visible the task sequence itself at all times (i.e. burying details under the hood).

b) A new composite which retains the index between initialisations can also achieve the same pattern with fewer blackboard shenanigans, but suffers from an increased logical complexity cost for your trees (each new composite increases decision making complexity (O(n!)).

Parameters
  • name (str) – the name to use for the task sequence behaviour

  • tasks ([Behaviour) – lists of tasks to be sequentially performed

Returns

root of the generated subtree

Return type

Behaviour

py_trees.meta

Meta methods to create behaviours without creating behaviours themselves.

py_trees.meta.create_behaviour_from_function(func: BehaviourUpdateMethod, module: Optional[str] = None) Type[Behaviour][source]

Create a behaviour from the specified function.

This takes the specified function and drops it in to serve as the the Behaviour update() method.

The user provided fucntion must include the self argument and return a Status value.

It also automatically registers a method for the terminate() method that clears the feedback message. Other methods are left untouched.

Parameters
  • func (BehaviourUpdateMethod) – a drop-in for the update() method

  • module (Optional[str]) – suppliment it with a __module__ name if required (otherwise it will default to ‘abc.’)

Return type

Type[Behaviour]

py_trees.timers

Time related behaviours.

class py_trees.timers.Timer(name: str = 'Timer', duration: float = 5.0)[source]

Bases: Behaviour

A simple, blocking timer behaviour running off python time.time().

This behaviour is RUNNING until the timer runs out, at which point it is SUCCESS. This can be used in a wide variety of situations - pause, duration, timeout depending on how it is wired into the tree (e.g. pause in a sequence, duration/timeout in a parallel).

The timer gets reset either upon entry (initialise()) if it hasn’t already been set and gets cleared when it either runs out, or the behaviour is interrupted by a higher priority or parent cancelling it.

Parameters
  • name (str) – name of the behaviour

  • duration (float) – length of time to run (in seconds)

Raises

TypeError – if the provided duration is not a real number

Note

This succeeds the first time the behaviour is ticked after the expected finishing time.

Tip

Use the RunningIsFailure() decorator if you need FAILURE until the timer finishes.

__init__(name: str = 'Timer', duration: float = 5.0)[source]
Parameters
initialise() None[source]

Store the expected finishing time.

Return type

None

update() Status[source]

Check the timer and update the behaviour result accordingly.

Returns

RUNNING until timer expires, then SUCCESS.

Return type

Status

py_trees.trees

Tree stewardship.

While a graph of connected behaviours and composites form a tree in their own right (i.e. it can be initialised and ticked), it is usually convenient to wrap your tree in another class to take care of alot of the housework and provide some extra bells and whistles that make your tree flourish.

_images/yggdrasil.jpg

This package provides a default reference implementation that is directly usable, but can also be easily used as inspiration for your own tree custodians.

class py_trees.trees.BehaviourTree(root: Behaviour)[source]

Bases: object

Grow, water, prune your behaviour tree with this, the tree custodian.

It features a few enhancements that go above and beyond just ticking the root behaviour of a tree. These provide richer logging, introspection and dynamic management of the tree itself:

  • Pre and post tick handlers to execute code automatically before and after a tick

  • Visitor access to the parts of the tree that were traversed in a tick

  • Subtree pruning and insertion operations

  • Continuous tick-tock support

See also

The py-trees-demo-tree-stewardship program demonstrates the above features.

Parameters

root (Behaviour) – root node of the tree

Variables
  • count – number of times the tree has been ticked.

  • root – root node of the tree

  • visitors – entities that visit traversed parts of the tree when it ticks

  • pre_tick_handlers – functions that run before the entire tree is ticked

  • post_tick_handlers – functions that run after the entire tree is ticked

Raises

TypeError – if root variable is not an instance of Behaviour

add_post_tick_handler(handler: Callable[[BehaviourTree], None]) None[source]

Add a function to execute after the tree has ticked.

The function must have a single argument of type BehaviourTree.

Some ideas that are often used:

  • logging

  • modifications on the tree itself (e.g. closing down a plan)

  • sending data to visualisation tools

  • introspect the state of the tree to make and send reports

Parameters

handler (Callable[[BehaviourTree], None]) – function

Return type

None

add_pre_tick_handler(handler: Callable[[BehaviourTree], None]) None[source]

Add a function to execute before the tree is ticked.

The function must have a single argument of type BehaviourTree.

Some ideas that are often used:

  • logging (to file or stdout)

  • modifications on the tree itself (e.g. starting a new plan)

Parameters

handler (Callable[[BehaviourTree], None]) – function

Return type

None

add_visitor(visitor: VisitorBase) None[source]

Welcome a visitor.

Trees can run multiple visitors on each behaviour as they tick through a tree.

Parameters

visitor (VisitorBase) – sub-classed instance of a visitor

Return type

None

insert_subtree(child: Behaviour, unique_id: UUID, index: int) bool[source]

Insert a subtree as a child of the specified parent.

If the parent is found, this directly calls the parent’s insert_child() method using the child and index arguments.

Parameters
  • child (Behaviour) – subtree to insert

  • unique_id (UUID) – unique id of the parent

  • index (int) – insert the child at this index, pushing all children after it back one.

Returns

suceess or failure (parent not found) of the operation

Raises

TypeError – if the parent is not a Composite

Return type

bool

Todo

Could use better, more informative error handling here. Especially if the insertion has its own error handling (e.g. index out of range). Could also use a different api that relies on the id of the sibling node it should be inserted before/after.

interrupt() None[source]

Interrupt tick-tock if it is tick-tocking.

Note that this will permit a currently executing tick to finish before interrupting the tick-tock.

Return type

None

prune_subtree(unique_id: UUID) bool[source]

Prune a subtree given the unique id of the root of the subtree.

Parameters
  • root (unique id of the subtree) –

  • unique_id (UUID) –

Returns

success or failure of the operation

Raises

RuntimeError – if unique id is the root or parent does not have remove_node

Return type

bool

replace_subtree(unique_id: UUID, subtree: Behaviour) bool[source]

Replace the subtree with the specified id for the new subtree.

This is a common pattern where we’d like to swap out a whole sub-behaviour for another one.

Parameters
  • unique_id (UUID) – unique id of the parent

  • subtree (Behaviour) – root behaviour of the subtree

Return type

bool

Raises

AssertionError: if unique id is the behaviour tree’s root node id

Returns

suceess or failure of the operation

Parameters
Return type

bool

setup(timeout: Union[float, Duration] = Duration.INFINITE, visitor: Optional[VisitorBase] = None, **kwargs: int) None[source]

Crawl across the tree calling setup() on each behaviour.

Visitors can optionally be provided to provide a node-by-node analysis on the result of each node’s setup() before the next node’s setup() is called. This is useful on trees with relatively long setup times to progressively report out on the current status of the operation.

Parameters
  • timeout (Union[float, Duration]) – time (s) to wait (use common.Duration.INFINITE to block indefinitely)

  • visitor (Optional[VisitorBase]) – runnable entities on each node after it’s setup

  • **kwargs (int) – distribute args to this behaviour and in turn, to it’s children

Raises
  • Exception – be ready to catch if any of the behaviours raise an exception

  • RuntimeError – in case setup() times out

Return type

None

shutdown() None[source]

Crawl across the tree, calling shutdown() on each behaviour.

Raises

Exception – be ready to catch if any of the behaviours raise an exception

Return type

None

tick(pre_tick_handler: Optional[Callable[[BehaviourTree], None]] = None, post_tick_handler: Optional[Callable[[BehaviourTree], None]] = None) None[source]

Tick the tree just once and run any handlers before and after the tick.

This optionally accepts some one-shot handlers (c.f. those added by add_pre_tick_handler() and add_post_tick_handler() which will be automatically run every time).

The handler functions must have a single argument of type BehaviourTree.

Parameters
  • pre_tick_handler (func) – function to execute before ticking

  • post_tick_handler (func) – function to execute after ticking

  • self (BehaviourTree) –

Return type

None

tick_tock(period_ms: int, number_of_iterations: int = -1, pre_tick_handler: Optional[Callable[[BehaviourTree], None]] = None, post_tick_handler: Optional[Callable[[BehaviourTree], None]] = None) None[source]

Tick continuously with period as specified.

Depending on the implementation, the period may be more or less accurate and may drift in some cases (the default implementation here merely assumes zero time in tick and sleeps for this duration of time and consequently, will drift).

This optionally accepts some handlers that will be used for the duration of this tick tock (c.f. those added by add_pre_tick_handler() and add_post_tick_handler() which will be automatically run every time).

The handler functions must have a single argument of type BehaviourTree.

Parameters
  • period_ms (float) – sleep this much between ticks (milliseconds)

  • number_of_iterations (int) – number of iterations to tick-tock

  • pre_tick_handler (func) – function to execute before ticking

  • post_tick_handler (func) – function to execute after ticking

  • self (BehaviourTree) –

Return type

None

tip() Optional[Behaviour][source]

Get the tip of the tree.

Returns

The deepest node (behaviour) that was running before subtree traversal reversed direction, or None if this behaviour’s status is INVALID.

Return type

Optional[Behaviour]

See also

tip()

py_trees.trees.setup(root: Behaviour, timeout: Union[float, Duration] = Duration.INFINITE, visitor: Optional[VisitorBase] = None, **kwargs: int) None[source]

Crawl across a (sub)tree of behaviours calling setup() on each behaviour.

Visitors can optionally be provided to provide a node-by-node analysis on the result of each node’s setup() before the next node’s setup() is called. This is useful on trees with relatively long setup times to progressively report out on the current status of the operation.

Parameters
  • root (Behaviour) – unmanaged (sub)tree root behaviour

  • timeout (Union[float, Duration]) – time (s) to wait (use common.Duration.INFINITE to block indefinitely)

  • visitor (Optional[VisitorBase]) – runnable entities on each node after it’s setup

  • **kwargs (int) – dictionary of args to distribute to all behaviours in the (sub)tree

Raises
  • Exception – be ready to catch if any of the behaviours raise an exception

  • RuntimeError – in case setup() times out

Return type

None

py_trees.utilities

Assorted utility functions.

class py_trees.utilities.Process(*args: Any, **kwargs: Any)[source]

Bases: Process

Convenience wrapper around multiprocessing.Process.

Parameters
  • args (Any) –

  • kwargs (Any) –

property exception: Any

Check the connection, if there is an error, reflect it as an exception.

Returns

The exception.

run() None[source]

Start the process, handle exceptions if needed.

Return type

None

py_trees.utilities.get_fully_qualified_name(instance: object) str[source]

Retrieve the fully qualified name of an object.

For example, an instance of Sequence becomes ‘py_trees.composites.Sequence’.

Parameters

instance (object) – an instance of any class

Returns

the fully qualified name

Return type

str

py_trees.utilities.get_valid_filename(s: str) str[source]

Clean up and style a string so that it can be used as a filename.

This is valid only from the perspective of the py_trees package. It does place a few extra constraints on strings to keep string handling and manipulation complexities to a minimum so that sanity prevails.

  • Removes leading and trailing spaces

  • Convert other spaces and newlines to underscores

  • Remove anything that is not an alphanumeric, dash, underscore, or dot

>>> utilities.get_valid_filename("john's portrait in 2004.jpg")
'johns_portrait_in_2004.jpg'
Parameters
  • program (str) – string to convert to a valid filename

  • s (str) –

Returns

a representation of the specified string as a valid filename

Return type

str

py_trees.utilities.is_primitive(incoming: Any) bool[source]

Check if an incoming argument is a primitive type with no esoteric accessors.

That is, it has no class attributes or container style [] accessors.

Parameters

incoming (Any) – the instance to check

Returns

True or false, depending on the check against the reserved primitives

Return type

bool

py_trees.utilities.static_variables(**kwargs: Any) Callable[[C], C][source]

Attach initialised static variables to a python method.

@static_variables(counter=0)
def foo():
    foo.counter += 1
    print("Counter: {}".format(foo.counter))
Parameters

kwargs (Any) –

Return type

Callable[[C], C]

py_trees.utilities.truncate(original: str, length: int) str[source]

Provide an elided (…) version of a string if it is longer than desired.

Parameters
  • original (str) – string to elide

  • length (int) – constrain the elided string to this

Return type

str

py_trees.utilities.which(program: str) Optional[str][source]

Call the command line ‘which’ tool (convenience wrapper).

Parameters

program (str) – name of the program to find.

Returns

path to the program or None if it doesnt exist.

Return type

Optional[str]

py_trees.visitors

Visiting rights to behaviours.

Visitors are entities that can be passed to a tree implementation (e.g. BehaviourTree) and used to either visit each and every behaviour in the tree, or visit behaviours as the tree is traversed in an executing tick. At each behaviour, the visitor runs its own method on the behaviour to do as it wishes - logging, introspecting, etc.

Warning

Visitors should not modify the behaviours they visit.

class py_trees.visitors.DebugVisitor[source]

Bases: VisitorBase

Picks up and logs feedback messages and the behaviour’s status.

Logging is done with the behaviour’s logger.

run(behaviour: Behaviour) None[source]

Log behaviour information on the debug channel.

Parameters

behaviour (Behaviour) – behaviour being visited.

Return type

None

class py_trees.visitors.DisplaySnapshotVisitor(display_only_visited_behaviours: bool = False, display_blackboard: bool = False, display_activity_stream: bool = False)[source]

Bases: SnapshotVisitor

Visit the tree, capturing the visited path, it’s changes since the last tick.

Additionally print the snapshot to console.

Parameters
  • display_only_visited_behaviours (bool) – useful for cropping the unvisited part of a large tree

  • display_blackboard (bool) – print to the console the relevant part of the blackboard associated with behaviours on the visited path

  • display_activity_stream (bool) – print to the console a log of the activity on the blackboard over the last tick

finalise() None[source]

Print a summary on stdout after all behaviours have been visited.

Return type

None

initialise() None[source]

Reset and initialise all variables.

Return type

None

run(behaviour: Behaviour) None[source]

Track the root of the tree and run SnapshotVisitor.

Parameters

behaviour (Behaviour) – behaviour being visited.

Return type

None

class py_trees.visitors.SnapshotVisitor[source]

Bases: VisitorBase

Creates a snapshot of the tree state (behaviour status’ only).

Visits the ticked part of a tree, checking off the status against the set of status results recorded in the previous tick. If there has been a change, it flags it. This is useful for determining when to trigger, e.g. logging.

Variables
  • changed (Bool) – flagged if there is a difference in the visited path or Status of any behaviour on the path

  • visited (dict) – dictionary of behaviour id (uuid.UUID) and status (Status) pairs from the current tick

  • previously_visited (dict) – dictionary of behaviour id (uuid.UUID) and status (Status) pairs from the previous tick

  • running_nodes ([uuid.UUID]) – list of id’s for behaviours which were traversed in the current tick

  • previously_running_nodes ([uuid.UUID]) – list of id’s for behaviours which were traversed in the last tick

  • visited_blackboard_ids (Set[uuid.UUID]) – blackboard client id’s on the visited path

  • visited_blackboard_keys (Set[str]) – blackboard variable keys on the visited path

See also

The py-trees-demo-logging program demonstrates use of this visitor to trigger logging of a tree serialisation.

initialise() None[source]

Store the last snapshot for comparison with the next incoming snapshot.

This should get called before a tree ticks.

Return type

None

run(behaviour: Behaviour) None[source]

Catch the id, status and store it.

Additionally add it to the running list if it is RUNNING.

Parameters

behaviour (Behaviour) – behaviour that is ticking

Return type

None

class py_trees.visitors.VisitorBase(full: bool = False)[source]

Bases: object

Parent template for visitor types.

Visitors are primarily designed to work with BehaviourTree but they can be used in the same way for other tree custodian implementations.

Parameters

full (bool) – flag to indicate whether it should be used to visit only traversed nodes or the entire tree

Variables

full – flag to indicate whether it should be used to visit only traversed nodes or the entire tree

finalise() None[source]

Override if any work needs to be performed after ticks (i.e. showing data).

Return type

None

initialise() None[source]

Override if any resetting of variables needs to be performed between ticks (i.e. visitations).

Return type

None

run(behaviour: Behaviour) None[source]

Converse with the behaviour.

This method gets run as each behaviour is ticked. Override it to perform some activity - e.g. introspect the behaviour to store/process logging data for visualisations.

Parameters

behaviour (Behaviour) – behaviour that is ticking

Return type

None