Idioms

A library of subtree creators that build common, but complex patterns of behaviours.

Common decision making patterns can often be realised using a specific combination of fundamental behaviours and the blackboard. Even if this somewhat verbosely populates the tree, this is preferable to creating new composites types or overriding existing composites since this will increase tree logic complexity and/or bury details under the hood (both of which add an exponential cost to introspection/visualisation).

In this package these patterns will be referred to as PyTree Idioms and in this module you will find convenience functions that assist in creating them.

The subsections below introduce each composite briefly. For a full listing of each composite’s methods, visit the py_trees.idioms module api documentation.

Either Or

idioms.either_or(subtrees, name='Either Or', namespace=None)

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

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

Return type:Behaviour

Eternal Guard

idioms.eternal_guard(name='Eternal Guard', conditions=None, blackboard_namespace=None)

Create an idiom that continuously guards a long running sequence of tasks.

The eternal guard idiom implements a stronger guard than the typical check at the beginning of a sequence of tasks. Here they guard continuously while the task sequence is being executed. While executing, if any of the guards should update with status FAILURE, then the task sequence is terminated.

digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Eternal Guard" [fillcolor=gold, fontcolor=black, fontsize=9, label="Eternal Guard\n--SuccessOnAll(-)--", shape=parallelogram, style=filled];
StatusToBB [fillcolor=ghostwhite, fontcolor=black, fontsize=9, label=StatusToBB, shape=ellipse, style=filled];
"Eternal Guard" -> StatusToBB;
"Condition 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 1", shape=ellipse, style=filled];
StatusToBB -> "Condition 1";
"StatusToBB*" [fillcolor=ghostwhite, fontcolor=black, fontsize=9, label="StatusToBB*", shape=ellipse, style=filled];
"Eternal Guard" -> "StatusToBB*";
"Condition 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 2", shape=ellipse, style=filled];
"StatusToBB*" -> "Condition 2";
"Guarded Tasks" [fillcolor=cyan, fontcolor=black, fontsize=9, label="Guarded Tasks", shape=octagon, style=filled];
"Eternal Guard" -> "Guarded Tasks";
"Abort on\nCondition 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Abort on\nCondition 1", shape=ellipse, style=filled];
"Guarded Tasks" -> "Abort on\nCondition 1";
"Abort on\nCondition 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Abort on\nCondition 2", shape=ellipse, style=filled];
"Guarded Tasks" -> "Abort on\nCondition 2";
Tasks [fillcolor=orange, fontcolor=black, fontsize=9, label=Tasks, shape=box, style=filled];
"Guarded Tasks" -> Tasks;
Task1 [fillcolor=gray, fontcolor=black, fontsize=9, label=Task1, shape=ellipse, style=filled];
Tasks -> Task1;
Task2 [fillcolor=gray, fontcolor=black, fontsize=9, label=Task2, shape=ellipse, style=filled];
Tasks -> Task2;
}
Parameters:
  • subtree (Behaviour) – behaviour(s) that actually do the work
  • name (str) – the name to use on the root behaviour of the idiom subtree
  • conditions (Optional[List[Behaviour]]) – behaviours on which tasks are conditional
  • blackboard_namespace (Optional[str]) – applied to condition variable results stored on the blackboard (default: derived from the idiom name)
Return type:

Behaviour

Returns:

the root of the idiom subtree

Oneshot

idioms.oneshot(name='Oneshot', variable_name='oneshot', policy=<OneShotPolicy.ON_SUCCESSFUL_COMPLETION: [<Status.SUCCESS: 'SUCCESS'>]>)

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

Pickup Where You left Off

idioms.pick_up_where_you_left_off(tasks=None)

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