Demos¶
py-trees-demo-action-behaviour¶
Demonstrates the characteristics of a typical ‘action’ behaviour.
- Mocks an external process and connects to it in the setup() method
- Kickstarts new goals with the external process in the initialise() method
- Monitors the ongoing goal status in the update() method
- Determines RUNNING/SUCCESS pending feedback from the external process
usage: py-trees-demo-action-behaviour [-h]

-
class
py_trees.demos.action.
Action
(name='Action')[source]¶ Bases:
py_trees.behaviour.Behaviour
Connects to a subprocess to initiate a goal, and monitors the progress of that goal at each tick until the goal is completed, at which time the behaviour itself returns with success or failure (depending on success or failure of the goal itself).
This is typical of a behaviour that is connected to an external process responsible for driving hardware, conducting a plan, or a long running processing pipeline (e.g. planning/vision).
Key point - this behaviour itself should not be doing any work!
-
py_trees.demos.action.
planning
(pipe_connection)[source]¶ Emulates an external process which might accept long running planning jobs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.action
:func: command_line_argument_parser
:prog: py-trees-demo-action-behaviour
.. image:: images/action.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import atexit
import multiprocessing
import py_trees.common
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Demonstrates the characteristics of a typical 'action' behaviour.\n"
content += "\n"
content += "* Mocks an external process and connects to it in the setup() method\n"
content += "* Kickstarts new goals with the external process in the initialise() method\n"
content += "* Monitors the ongoing goal status in the update() method\n"
content += "* Determines RUNNING/SUCCESS pending feedback from the external process\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Action Behaviour".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
return argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
def planning(pipe_connection):
"""
Emulates an external process which might accept long running planning jobs.
"""
idle = True
percentage_complete = 0
try:
while(True):
if pipe_connection.poll():
pipe_connection.recv()
percentage_complete = 0
idle = False
if not idle:
percentage_complete += 10
pipe_connection.send([percentage_complete])
if percentage_complete == 100:
idle = True
time.sleep(0.5)
except KeyboardInterrupt:
pass
class Action(py_trees.behaviour.Behaviour):
"""
Connects to a subprocess to initiate a goal, and monitors the progress
of that goal at each tick until the goal is completed, at which time
the behaviour itself returns with success or failure (depending on
success or failure of the goal itself).
This is typical of a behaviour that is connected to an external process
responsible for driving hardware, conducting a plan, or a long running
processing pipeline (e.g. planning/vision).
Key point - this behaviour itself should not be doing any work!
"""
def __init__(self, name="Action"):
"""
Default construction.
"""
super(Action, self).__init__(name)
self.logger.debug("%s.__init__()" % (self.__class__.__name__))
def setup(self):
"""
No delayed initialisation required for this example.
"""
self.logger.debug("%s.setup()->connections to an external process" % (self.__class__.__name__))
self.parent_connection, self.child_connection = multiprocessing.Pipe()
self.planning = multiprocessing.Process(target=planning, args=(self.child_connection,))
atexit.register(self.planning.terminate)
self.planning.start()
def initialise(self):
"""
Reset a counter variable.
"""
self.logger.debug("%s.initialise()->sending new goal" % (self.__class__.__name__))
self.parent_connection.send(['new goal'])
self.percentage_completion = 0
def update(self):
"""
Increment the counter and decide upon a new status result for the behaviour.
"""
new_status = py_trees.common.Status.RUNNING
if self.parent_connection.poll():
self.percentage_completion = self.parent_connection.recv().pop()
if self.percentage_completion == 100:
new_status = py_trees.common.Status.SUCCESS
if new_status == py_trees.common.Status.SUCCESS:
self.feedback_message = "Processing finished"
self.logger.debug("%s.update()[%s->%s][%s]" % (self.__class__.__name__, self.status, new_status, self.feedback_message))
else:
self.feedback_message = "{0}%".format(self.percentage_completion)
self.logger.debug("%s.update()[%s][%s]" % (self.__class__.__name__, self.status, self.feedback_message))
return new_status
def terminate(self, new_status):
"""
Nothing to clean up in this example.
"""
self.logger.debug("%s.terminate()[%s->%s]" % (self.__class__.__name__, self.status, new_status))
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
action = Action()
action.setup()
try:
for unused_i in range(0, 12):
action.tick_once()
time.sleep(0.5)
print("\n")
except KeyboardInterrupt:
pass
|
py-trees-demo-behaviour-lifecycle¶
Demonstrates a typical day in the life of a behaviour.
This behaviour will count from 1 to 3 and then reset and repeat. As it does so, it logs and displays the methods as they are called - construction, setup, initialisation, ticking and termination.
usage: py-trees-demo-behaviour-lifecycle [-h]

-
class
py_trees.demos.lifecycle.
Counter
(name='Counter')[source]¶ Bases:
py_trees.behaviour.Behaviour
Simple counting behaviour that facilitates the demonstration of a behaviour in the demo behaviours lifecycle program.
- Increments a counter from zero at each tick
- Finishes with success if the counter reaches three
- Resets the counter in the initialise() method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.lifecycle
:func: command_line_argument_parser
:prog: py-trees-demo-behaviour-lifecycle
.. image:: images/lifecycle.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Demonstrates a typical day in the life of a behaviour.\n\n"
content += "This behaviour will count from 1 to 3 and then reset and repeat. As it does\n"
content += "so, it logs and displays the methods as they are called - construction, setup,\n"
content += "initialisation, ticking and termination.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Behaviour Lifecycle".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
return argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
class Counter(py_trees.behaviour.Behaviour):
"""
Simple counting behaviour that facilitates the demonstration of a behaviour in
the demo behaviours lifecycle program.
* Increments a counter from zero at each tick
* Finishes with success if the counter reaches three
* Resets the counter in the initialise() method.
"""
def __init__(self, name="Counter"):
"""
Default construction.
"""
super(Counter, self).__init__(name)
self.logger.debug("%s.__init__()" % (self.__class__.__name__))
def setup(self):
"""
No delayed initialisation required for this example.
"""
self.logger.debug("%s.setup()" % (self.__class__.__name__))
def initialise(self):
"""
Reset a counter variable.
"""
self.logger.debug("%s.initialise()" % (self.__class__.__name__))
self.counter = 0
def update(self):
"""
Increment the counter and decide upon a new status result for the behaviour.
"""
self.counter += 1
new_status = py_trees.common.Status.SUCCESS if self.counter == 3 else py_trees.common.Status.RUNNING
if new_status == py_trees.common.Status.SUCCESS:
self.feedback_message = "counting...{0} - phew, thats enough for today".format(self.counter)
else:
self.feedback_message = "still counting"
self.logger.debug("%s.update()[%s->%s][%s]" % (self.__class__.__name__, self.status, new_status, self.feedback_message))
return new_status
def terminate(self, new_status):
"""
Nothing to clean up in this example.
"""
self.logger.debug("%s.terminate()[%s->%s]" % (self.__class__.__name__, self.status, new_status))
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
counter = Counter()
counter.setup()
try:
for unused_i in range(0, 7):
counter.tick_once()
time.sleep(0.5)
print("\n")
except KeyboardInterrupt:
print("")
pass
|
py-trees-demo-blackboard¶
Demonstrates usage of the blackboard and related behaviours.
A sequence is populated with a few behaviours that exercise reading and writing on the Blackboard in interesting ways.
usage: py-trees-demo-blackboard [-h] [-r | --render-with-blackboard-variables]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
--render-with-blackboard-variables | |
render dot tree to file with blackboard variables Default: False |
![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 Foo" [label="Set Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Set Foo";
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> Writer;
"Check Foo" [label="Check Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Check Foo";
subgraph {
label="children_of_Blackboard Demo";
rank=same;
"Set Foo" [label="Set Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Check Foo" [label="Check Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}
"Standalone Blackboard Client" [label="Standalone Blackboard Client", 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];
"Standalone Blackboard Client" -> dude [color=blue, constraint=False];
foo [label="foo: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
foo -> "Check Foo" [color=blue, constraint=False];
"Set Foo" -> foo [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];
}](_images/graphviz-43ad9f836cff637d31349f206421b528ea046b08.png)

-
class
py_trees.demos.blackboard.
BlackboardWriter
(name='Writer')[source]¶ Bases:
py_trees.behaviour.Behaviour
Custom writer that submits a more complicated variable to the blackboard.
-
class
py_trees.demos.blackboard.
Nested
[source]¶ Bases:
object
A more complex object to interact with on the blackboard.
-
__weakref__
¶ list of weak references to the object (if defined)
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.blackboard
:func: command_line_argument_parser
:prog: py-trees-demo-blackboard
.. graphviz:: dot/demo-blackboard.dot
:align: center
.. image:: images/blackboard.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import sys
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Demonstrates usage of the blackboard and related behaviours.\n"
content += "\n"
content += "A sequence is populated with a few behaviours that exercise\n"
content += "reading and writing on the Blackboard in interesting ways.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
render_group = parser.add_mutually_exclusive_group()
render_group.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
render_group.add_argument(
'--render-with-blackboard-variables',
action='store_true',
help='render dot tree to file with blackboard variables'
)
return parser
class Nested(object):
"""
A more complex object to interact with on the blackboard.
"""
def __init__(self):
self.foo = "bar"
def __str__(self):
return str({"foo": self.foo})
class BlackboardWriter(py_trees.behaviour.Behaviour):
"""
Custom writer that submits a more complicated variable to the blackboard.
"""
def __init__(self, name="Writer"):
super().__init__(name=name)
self.blackboard.register_key(key="dude", read=True)
self.blackboard.register_key(key="spaghetti", write=True)
self.logger.debug("%s.__init__()" % (self.__class__.__name__))
def update(self):
"""
Write a dictionary to the blackboard and return :data:`~py_trees.common.Status.SUCCESS`.
"""
self.logger.debug("%s.update()" % (self.__class__.__name__))
try:
unused = self.blackboard.dude
except KeyError:
pass
try:
unused = self.blackboard.dudette
except AttributeError:
pass
try:
self.blackboard.dudette = "Jane"
except AttributeError:
pass
self.blackboard.spaghetti = {"type": "Carbonara", "quantity": 1}
self.blackboard.spaghetti = {"type": "Gnocchi", "quantity": 2}
try:
self.blackboard.set("spaghetti", {"type": "Bolognese", "quantity": 3}, overwrite=False)
except AttributeError:
pass
return py_trees.common.Status.SUCCESS
def create_root():
root = py_trees.composites.Sequence("Blackboard Demo")
set_blackboard_variable = py_trees.behaviours.SetBlackboardVariable(
name="Set Nested", variable_name="nested", variable_value=Nested()
)
write_blackboard_variable = BlackboardWriter(name="Writer")
check_blackboard_variable = py_trees.behaviours.CheckBlackboardVariableValue(
name="Check Nested Foo", variable_name="nested.foo", expected_value="bar"
)
root.add_children([set_blackboard_variable, write_blackboard_variable, check_blackboard_variable])
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
standalone_blackboard = py_trees.blackboard.BlackboardClient(
name="Standalone Blackboard Client",
write={"dude"}
)
standalone_blackboard.dude = "Bob"
root = create_root()
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(root, with_blackboard_variables=False)
sys.exit()
if args.render_with_blackboard_variables:
py_trees.display.render_dot_tree(root, with_blackboard_variables=True)
sys.exit()
####################
# Execute
####################
root.setup_with_descendants()
blackboard = py_trees.blackboard.BlackboardClient(name="Unsetter", write={"foo"})
print("\n--------- Tick 0 ---------\n")
root.tick_once()
print("\n")
print(py_trees.display.unicode_tree(root, show_status=True))
print("--------------------------\n")
print(py_trees.display.unicode_blackboard())
print("--------------------------\n")
print(py_trees.display.unicode_blackboard(display_only_key_metadata=True))
print("--------------------------\n")
blackboard.unset("foo")
print(py_trees.display.unicode_blackboard_activity_stream())
|
py-trees-demo-context-switching¶
Demonstrates context switching with parallels and sequences.
A context switching behaviour is run in parallel with a work sequence. Switching the context occurs in the initialise() and terminate() methods of the context switching behaviour. Note that whether the sequence results in failure or success, the context switch behaviour will always call the terminate() method to restore the context. It will also call terminate() to restore the context in the event of a higher priority parent cancelling this parallel subtree.
usage: py-trees-demo-context-switching [-h] [-r]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
![digraph parallel {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Parallel [fillcolor=gold, fontcolor=black, fontsize=11, shape=note, style=filled];
Context [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Parallel -> Context;
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Parallel -> Sequence;
"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";
}](_images/graphviz-144d6ec8c12be2bfcdb3f3fbc63468e73c91dc28.png)

-
class
py_trees.demos.context_switching.
ContextSwitch
(name='ContextSwitch')[source]¶ Bases:
py_trees.behaviour.Behaviour
An example of a context switching class that sets (in
initialise()
) and restores a context (interminate()
). Use in parallel with a sequence/subtree that does the work while in this context.Attention
Simply setting a pair of behaviours (set and reset context) on either end of a sequence will not suffice for context switching. In the case that one of the work behaviours in the sequence fails, the final reset context switch will never trigger.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.context_switching
:func: command_line_argument_parser
:prog: py-trees-demo-context-switching
.. graphviz:: dot/demo-context_switching.dot
.. image:: images/context_switching.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Demonstrates context switching with parallels and sequences.\n"
content += "\n"
content += "A context switching behaviour is run in parallel with a work sequence.\n"
content += "Switching the context occurs in the initialise() and terminate() methods\n"
content += "of the context switching behaviour. Note that whether the sequence results\n"
content += "in failure or success, the context switch behaviour will always call the\n"
content += "terminate() method to restore the context. It will also call terminate()\n"
content += "to restore the context in the event of a higher priority parent cancelling\n"
content += "this parallel subtree.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Context Switching".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
return parser
class ContextSwitch(py_trees.behaviour.Behaviour):
"""
An example of a context switching class that sets (in ``initialise()``)
and restores a context (in ``terminate()``). Use in parallel with a
sequence/subtree that does the work while in this context.
.. attention:: Simply setting a pair of behaviours (set and reset context) on
either end of a sequence will not suffice for context switching. In the case
that one of the work behaviours in the sequence fails, the final reset context
switch will never trigger.
"""
def __init__(self, name="ContextSwitch"):
super(ContextSwitch, self).__init__(name)
self.feedback_message = "no context"
def initialise(self):
"""
Backup and set a new context.
"""
self.logger.debug("%s.initialise()[switch context]" % (self.__class__.__name__))
# Some actions that:
# 1. retrieve the current context from somewhere
# 2. cache the context internally
# 3. apply a new context
self.feedback_message = "new context"
def update(self):
"""
Just returns RUNNING while it waits for other activities to finish.
"""
self.logger.debug("%s.update()[RUNNING][%s]" % (self.__class__.__name__, self.feedback_message))
return py_trees.common.Status.RUNNING
def terminate(self, new_status):
"""
Restore the context with the previously backed up context.
"""
self.logger.debug("%s.terminate()[%s->%s][restore context]" % (self.__class__.__name__, self.status, new_status))
# Some actions that:
# 1. restore the cached context
self.feedback_message = "restored context"
def create_root():
root = py_trees.composites.Parallel(name="Parallel", policy=py_trees.common.ParallelPolicy.SuccessOnOne())
context_switch = ContextSwitch(name="Context")
sequence = py_trees.composites.Sequence(name="Sequence")
for job in ["Action 1", "Action 2"]:
success_after_two = py_trees.behaviours.Count(name=job,
fail_until=0,
running_until=2,
success_until=10)
sequence.add_child(success_after_two)
root.add_child(context_switch)
root.add_child(sequence)
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
root = create_root()
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(root)
sys.exit()
####################
# Execute
####################
root.setup_with_descendants()
for i in range(1, 6):
try:
print("\n--------- Tick {0} ---------\n".format(i))
root.tick_once()
print("\n")
print("{}".format(py_trees.display.unicode_tree(root, show_status=True)))
time.sleep(1.0)
except KeyboardInterrupt:
break
print("\n")
|
py-trees-demo-dot-graphs¶
Renders a dot graph for a simple tree, with blackboxes.
usage: py-trees-demo-dot-graphs [-h]
[-l {all,fine_detail,detail,component,big_picture}]
Named Arguments¶
-l, --level | Possible choices: all, fine_detail, detail, component, big_picture visibility level Default: “fine_detail” |
![digraph demo_dot_graphs_fine_detail {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Demo Dot Graphs fine_detail" [fillcolor=cyan, fontcolor=black, fontsize=11, shape=octagon, style=filled];
"BlackBox 1" [fillcolor=gray20, fontcolor=white, fontsize=11, shape=box, style=filled];
"Demo Dot Graphs fine_detail" -> "BlackBox 1";
Worker [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> Worker;
"Worker*" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> "Worker*";
"Worker**" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> "Worker**";
"Blackbox 3" [fillcolor=gray20, fontcolor=dodgerblue, fontsize=11, shape=box, style=filled];
"BlackBox 1" -> "Blackbox 3";
"Worker***" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker***";
"Worker****" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker****";
"Worker*****" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker*****";
"Blackbox 2" [fillcolor=gray20, fontcolor=lawngreen, fontsize=11, shape=box, style=filled];
"Demo Dot Graphs fine_detail" -> "Blackbox 2";
"Worker******" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker******";
"Worker*******" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker*******";
"Worker********" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker********";
}](_images/graphviz-b41075e63ea73d726ec066cfe411d41f29b92b79.png)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.dot_graphs
:func: command_line_argument_parser
:prog: py-trees-demo-dot-graphs
.. graphviz:: dot/demo-dot-graphs.dot
"""
##############################################################################
# Imports
##############################################################################
import argparse
import subprocess
import py_trees
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
name = "py-trees-demo-dot-graphs"
content = "Renders a dot graph for a simple tree, with blackboxes.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Dot Graphs".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += console.white
s += console.bold + " Generate Full Dot Graph" + console.reset + "\n"
s += "\n"
s += console.cyan + " {0}".format(name) + console.reset + "\n"
s += "\n"
s += console.bold + " With Varying Visibility Levels" + console.reset + "\n"
s += "\n"
s += console.cyan + " {0}".format(name) + console.yellow + " --level=all" + console.reset + "\n"
s += console.cyan + " {0}".format(name) + console.yellow + " --level=detail" + console.reset + "\n"
s += console.cyan + " {0}".format(name) + console.yellow + " --level=component" + console.reset + "\n"
s += console.cyan + " {0}".format(name) + console.yellow + " --level=big_picture" + console.reset + "\n"
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('-l', '--level', action='store',
default='fine_detail',
choices=['all', 'fine_detail', 'detail', 'component', 'big_picture'],
help='visibility level')
return parser
def create_tree(level):
root = py_trees.composites.Selector("Demo Dot Graphs %s" % level)
first_blackbox = py_trees.composites.Sequence("BlackBox 1")
first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
first_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.BIG_PICTURE
second_blackbox = py_trees.composites.Sequence("Blackbox 2")
second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
second_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
third_blackbox = py_trees.composites.Sequence("Blackbox 3")
third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
third_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.DETAIL
root.add_child(first_blackbox)
root.add_child(second_blackbox)
first_blackbox.add_child(third_blackbox)
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
args.enum_level = py_trees.common.string_to_visibility_level(args.level)
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
root = create_tree(args.level)
py_trees.display.render_dot_tree(root, args.enum_level)
if py_trees.utilities.which("xdot"):
try:
subprocess.call(["xdot", "demo_dot_graphs_%s.dot" % args.level])
except KeyboardInterrupt:
pass
else:
print("")
console.logerror("No xdot viewer found, skipping display [hint: sudo apt install xdot]")
print("")
|
py-trees-demo-logging¶
A demonstration of logging with trees.
This demo utilises a SnapshotVisitor to trigger a post-tick handler to dump a serialisation of the tree to a json log file.
This coupling of visitor and post-tick handler can be used for any kind of event handling - the visitor is the trigger and the post-tick handler the action. Aside from logging, the most common use case is to serialise the tree for messaging to a graphical, runtime monitor.
usage: py-trees-demo-logging [-h] [-r | -i]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
-i, --interactive | |
pause and wait for keypress at each tick Default: False |
![digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Logging [label=Logging, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Logging -> EveryN;
Sequence [label=Sequence, shape=box, style=filled, fillcolor=gray20, fontsize=9, fontcolor=lawngreen];
Logging -> Sequence;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Guard;
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Periodic;
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Finisher;
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Logging -> Idle;
}](_images/graphviz-b8f9fe1b5881e00e8c140343f7526cc79469d8d3.png)

-
py_trees.demos.logging.
logger
(snapshot_visitor, behaviour_tree)[source]¶ A post-tick handler that logs the tree (relevant parts thereof) to a yaml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.logging
:func: command_line_argument_parser
:prog: py-trees-demo-logging
.. graphviz:: dot/demo-logging.dot
.. image:: images/logging.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import functools
import json
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description(root):
content = "A demonstration of logging with trees.\n\n"
content += "This demo utilises a SnapshotVisitor to trigger\n"
content += "a post-tick handler to dump a serialisation of the\n"
content += "tree to a json log file.\n"
content += "\n"
content += "This coupling of visitor and post-tick handler can be\n"
content += "used for any kind of event handling - the visitor is the\n"
content += "trigger and the post-tick handler the action. Aside from\n"
content += "logging, the most common use case is to serialise the tree\n"
content += "for messaging to a graphical, runtime monitor.\n"
content += "\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Logging".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(create_tree()),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
group = parser.add_mutually_exclusive_group()
group.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
group.add_argument('-i', '--interactive', action='store_true', help='pause and wait for keypress at each tick')
return parser
def logger(snapshot_visitor, behaviour_tree):
"""
A post-tick handler that logs the tree (relevant parts thereof) to a yaml file.
"""
if snapshot_visitor.changed:
print(console.cyan + "Logging.......................yes\n" + console.reset)
tree_serialisation = {
'tick': behaviour_tree.count,
'nodes': []
}
for node in behaviour_tree.root.iterate():
node_type_str = "Behaviour"
for behaviour_type in [py_trees.composites.Sequence,
py_trees.composites.Selector,
py_trees.composites.Parallel,
py_trees.decorators.Decorator]:
if isinstance(node, behaviour_type):
node_type_str = behaviour_type.__name__
node_snapshot = {
'name': node.name,
'id': str(node.id),
'parent_id': str(node.parent.id) if node.parent else "none",
'child_ids': [str(child.id) for child in node.children],
'tip_id': str(node.tip().id) if node.tip() else 'none',
'class_name': str(node.__module__) + '.' + str(type(node).__name__),
'type': node_type_str,
'status': node.status.value,
'message': node.feedback_message,
'is_active': True if node.id in snapshot_visitor.visited else False
}
tree_serialisation['nodes'].append(node_snapshot)
if behaviour_tree.count == 0:
with open('dump.json', 'w+') as outfile:
json.dump(tree_serialisation, outfile, indent=4)
else:
with open('dump.json', 'a') as outfile:
json.dump(tree_serialisation, outfile, indent=4)
else:
print(console.yellow + "Logging.......................no\n" + console.reset)
def create_tree():
every_n_success = py_trees.behaviours.SuccessEveryN("EveryN", 5)
sequence = py_trees.composites.Sequence(name="Sequence")
guard = py_trees.behaviours.Success("Guard")
periodic_success = py_trees.behaviours.Periodic("Periodic", 3)
finisher = py_trees.behaviours.Success("Finisher")
sequence.add_child(guard)
sequence.add_child(periodic_success)
sequence.add_child(finisher)
sequence.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
idle = py_trees.behaviours.Success("Idle")
root = py_trees.composites.Selector(name="Logging")
root.add_child(every_n_success)
root.add_child(sequence)
root.add_child(idle)
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
py_trees.logging.level = py_trees.logging.Level.DEBUG
tree = create_tree()
print(description(tree))
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(tree)
sys.exit()
####################
# Tree Stewardship
####################
behaviour_tree = py_trees.trees.BehaviourTree(tree)
debug_visitor = py_trees.visitors.DebugVisitor()
snapshot_visitor = py_trees.visitors.DisplaySnapshotVisitor()
behaviour_tree.visitors.append(debug_visitor)
behaviour_tree.visitors.append(snapshot_visitor)
behaviour_tree.add_post_tick_handler(functools.partial(logger, snapshot_visitor))
behaviour_tree.setup(timeout=15)
####################
# Tick Tock
####################
if args.interactive:
py_trees.console.read_single_keypress()
while True:
try:
behaviour_tree.tick()
if args.interactive:
py_trees.console.read_single_keypress()
else:
time.sleep(0.5)
except KeyboardInterrupt:
break
print("\n")
|
py-trees-demo-selector¶
Higher priority switching and interruption in the children of a selector.
In this example the higher priority child is setup to fail initially, falling back to the continually running second child. On the third tick, the first child succeeds and cancels the hitherto running child.
usage: py-trees-demo-selector [-h] [-r]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
![digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Selector [fillcolor=cyan, fontcolor=black, fontsize=11, shape=octagon, style=filled];
"After Two" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Selector -> "After Two";
Running [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Selector -> Running;
}](_images/graphviz-b4aa24a02309f2e46044b21ea42658e7e90ade83.png)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.selector
:func: command_line_argument_parser
:prog: py-trees-demo-selector
.. graphviz:: dot/demo-selector.dot
.. image:: images/selector.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Higher priority switching and interruption in the children of a selector.\n"
content += "\n"
content += "In this example the higher priority child is setup to fail initially,\n"
content += "falling back to the continually running second child. On the third\n"
content += "tick, the first child succeeds and cancels the hitherto running child.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Selectors".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
return parser
def create_root():
root = py_trees.composites.Selector("Selector")
success_after_two = py_trees.behaviours.Count(name="After Two",
fail_until=2,
running_until=2,
success_until=10)
always_running = py_trees.behaviours.Running(name="Running")
root.add_children([success_after_two, always_running])
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
root = create_root()
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(root)
sys.exit()
####################
# Execute
####################
root.setup_with_descendants()
for i in range(1, 4):
try:
print("\n--------- Tick {0} ---------\n".format(i))
root.tick_once()
print("\n")
print(py_trees.display.unicode_tree(root=root, show_status=True))
time.sleep(1.0)
except KeyboardInterrupt:
break
print("\n")
|
py-trees-demo-sequence¶
Demonstrates sequences in action.
A sequence is populated with 2-tick jobs that are allowed to run through to completion.
usage: py-trees-demo-sequence [-h] [-r]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
![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];
"Job 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 1";
"Job 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 2";
"Job 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 3";
}](_images/graphviz-a17534354d720fe257813f6d517c4ed874bf0950.png)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.sequence
:func: command_line_argument_parser
:prog: py-trees-demo-sequence
.. graphviz:: dot/demo-sequence.dot
.. image:: images/sequence.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "Demonstrates sequences in action.\n\n"
content += "A sequence is populated with 2-tick jobs that are allowed to run through to\n"
content += "completion.\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Sequences".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
return parser
def create_root():
root = py_trees.composites.Sequence("Sequence")
for action in ["Action 1", "Action 2", "Action 3"]:
success_after_two = py_trees.behaviours.Count(name=action,
fail_until=0,
running_until=1,
success_until=10)
root.add_child(success_after_two)
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
print(description())
py_trees.logging.level = py_trees.logging.Level.DEBUG
root = create_root()
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(root)
sys.exit()
####################
# Execute
####################
root.setup_with_descendants()
for i in range(1, 6):
try:
print("\n--------- Tick {0} ---------\n".format(i))
root.tick_once()
print("\n")
print(py_trees.display.unicode_tree(root=root, show_status=True))
time.sleep(1.0)
except KeyboardInterrupt:
break
print("\n")
|
py-trees-demo-tree-stewardship¶
A demonstration of tree stewardship.
A slightly less trivial tree that uses a simple stdout pre-tick handler and both the debug and snapshot visitors for logging and displaying the state of the tree.
EVENTS
- 3 : sequence switches from running to success
- 4 : selector’s first child flicks to success once only
- 8 : the fallback idler kicks in as everything else fails
- 14 : the first child kicks in again, aborting a running sequence behind it
usage: py-trees-demo-tree-stewardship [-h]
[-r | --render-with-blackboard-variables | -i]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
--render-with-blackboard-variables | |
render dot tree to file with blackboard variables Default: False | |
-i, --interactive | |
pause and wait for keypress at each tick Default: False |
![digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Demo Tree" [label="Demo Tree", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Demo Tree" -> EveryN;
Sequence [label=Sequence, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Demo Tree" -> Sequence;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Guard;
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Periodic;
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Finisher;
subgraph {
label=children_of_Sequence;
rank=same;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Demo Tree" -> Idle;
subgraph {
label="children_of_Demo Tree";
rank=same;
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence [label=Sequence, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}
count [label="count: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
count -> Finisher [color=blue, constraint=False];
EveryN -> count [color=blue, constraint=True];
period [label="period: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
period -> Finisher [color=blue, constraint=False];
Periodic -> period [color=blue, constraint=True];
}](_images/graphviz-1b26933e3c1f0171b5bb981ef605c729d7fc79d1.png)

-
class
py_trees.demos.stewardship.
Finisher
[source]¶ Bases:
py_trees.behaviour.Behaviour
-
update
()[source]¶ Note
User Customisable Callback
Returns: the behaviour’s new status Status
Return type: Status
Subclasses may 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 on by the
tick()
mechanism.Tip
This method should be almost instantaneous and non-blocking
-
-
class
py_trees.demos.stewardship.
PeriodicSuccess
[source]¶ Bases:
py_trees.behaviours.Periodic
-
update
()[source]¶ Note
User Customisable Callback
Returns: the behaviour’s new status Status
Return type: Status
Subclasses may 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 on by the
tick()
mechanism.Tip
This method should be almost instantaneous and non-blocking
-
-
class
py_trees.demos.stewardship.
SuccessEveryN
[source]¶ Bases:
py_trees.behaviours.SuccessEveryN
-
update
()[source]¶ Note
User Customisable Callback
Returns: the behaviour’s new status Status
Return type: Status
Subclasses may 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 on by the
tick()
mechanism.Tip
This method should be almost instantaneous and non-blocking
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.stewardship
:func: command_line_argument_parser
:prog: py-trees-demo-tree-stewardship
.. graphviz:: dot/demo-tree-stewardship.dot
.. image:: images/tree_stewardship.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description():
content = "A demonstration of tree stewardship.\n\n"
content += "A slightly less trivial tree that uses a simple stdout pre-tick handler\n"
content += "and both the debug and snapshot visitors for logging and displaying\n"
content += "the state of the tree.\n"
content += "\n"
content += "EVENTS\n"
content += "\n"
content += " - 3 : sequence switches from running to success\n"
content += " - 4 : selector's first child flicks to success once only\n"
content += " - 8 : the fallback idler kicks in as everything else fails\n"
content += " - 14 : the first child kicks in again, aborting a running sequence behind it\n"
content += "\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Trees".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
group = parser.add_mutually_exclusive_group()
group.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
group.add_argument(
'--render-with-blackboard-variables',
action='store_true',
help='render dot tree to file with blackboard variables'
)
group.add_argument('-i', '--interactive', action='store_true', help='pause and wait for keypress at each tick')
return parser
def pre_tick_handler(behaviour_tree):
print("\n--------- Run %s ---------\n" % behaviour_tree.count)
class SuccessEveryN(py_trees.behaviours.SuccessEveryN):
def __init__(self):
super().__init__(name="EveryN", n=5)
self.blackboard.register_key("count", write=True)
def update(self):
status = super().update()
self.blackboard.count = self.count
return status
class PeriodicSuccess(py_trees.behaviours.Periodic):
def __init__(self):
super().__init__(name="Periodic", n=3)
self.blackboard.register_key("period", write=True)
def update(self):
status = super().update()
self.blackboard.period = self.period
return status
class Finisher(py_trees.behaviour.Behaviour):
def __init__(self):
super().__init__(name="Finisher")
self.blackboard.register_key("count", read=True)
self.blackboard.register_key("period", read=True)
def update(self):
print(console.green + "---------------------------" + console.reset)
print(console.bold + " Finisher" + console.reset)
print(console.green + " Count : {}".format(self.blackboard.count) + console.reset)
print(console.green + " Period: {}".format(self.blackboard.period) + console.reset)
print(console.green + "---------------------------" + console.reset)
return py_trees.common.Status.SUCCESS
def create_tree():
every_n_success = SuccessEveryN()
sequence = py_trees.composites.Sequence(name="Sequence")
guard = py_trees.behaviours.Success("Guard")
periodic_success = PeriodicSuccess()
finisher = Finisher()
sequence.add_child(guard)
sequence.add_child(periodic_success)
sequence.add_child(finisher)
idle = py_trees.behaviours.Success("Idle")
root = py_trees.composites.Selector(name="Demo Tree")
root.add_child(every_n_success)
root.add_child(sequence)
root.add_child(idle)
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
py_trees.logging.level = py_trees.logging.Level.DEBUG
tree = create_tree()
print(description())
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(tree)
sys.exit()
if args.render_with_blackboard_variables:
py_trees.display.render_dot_tree(tree, with_blackboard_variables=True)
sys.exit()
####################
# Tree Stewardship
####################
py_trees.blackboard.Blackboard.enable_activity_stream(100)
behaviour_tree = py_trees.trees.BehaviourTree(tree)
behaviour_tree.add_pre_tick_handler(pre_tick_handler)
behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
behaviour_tree.visitors.append(
py_trees.visitors.DisplaySnapshotVisitor(
display_blackboard=True,
display_activity_stream=True)
)
behaviour_tree.setup(timeout=15)
####################
# Tick Tock
####################
if args.interactive:
py_trees.console.read_single_keypress()
while True:
try:
behaviour_tree.tick()
if args.interactive:
py_trees.console.read_single_keypress()
else:
time.sleep(0.5)
except KeyboardInterrupt:
break
print("\n")
|
py-trees-demo-pick-up-where-you-left-off¶
A demonstration of the ‘pick up where you left off’ idiom.
A common behaviour tree pattern that allows you to resume work after being interrupted by a high priority interrupt.
EVENTS
- 2 : task one done, task two running
- 3 : high priority interrupt
- 7 : task two restarts
- 9 : task two done
usage: py-trees-demo-pick-up-where-you-left-off [-h] [-r | -i]
Named Arguments¶
-r, --render | render dot tree to file Default: False |
-i, --interactive | |
pause and wait for keypress at each tick Default: False |
![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";
}](_images/graphviz-e9d5d068f09686da1f6cc4f6b6143ecd0fbf157e.png)

-
py_trees.demos.pick_up_where_you_left_off.
post_tick_handler
(snapshot_visitor, behaviour_tree)[source]¶ Prints an ascii tree with the current snapshot status.
-
py_trees.demos.pick_up_where_you_left_off.
pre_tick_handler
(behaviour_tree)[source]¶ This prints a banner and will run immediately before every tick of the tree.
Parameters: behaviour_tree ( BehaviourTree
) – the tree custodian
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | #!/usr/bin/env python
#
# License: BSD
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
#
##############################################################################
# Documentation
##############################################################################
"""
.. argparse::
:module: py_trees.demos.pick_up_where_you_left_off
:func: command_line_argument_parser
:prog: py-trees-demo-pick-up-where-you-left-off
.. graphviz:: dot/pick_up_where_you_left_off.dot
.. image:: images/pick_up_where_you_left_off.gif
"""
##############################################################################
# Imports
##############################################################################
import argparse
import functools
import py_trees
import sys
import time
import py_trees.console as console
##############################################################################
# Classes
##############################################################################
def description(root):
content = "A demonstration of the 'pick up where you left off' idiom.\n\n"
content += "A common behaviour tree pattern that allows you to resume\n"
content += "work after being interrupted by a high priority interrupt.\n"
content += "\n"
content += "EVENTS\n"
content += "\n"
content += " - 2 : task one done, task two running\n"
content += " - 3 : high priority interrupt\n"
content += " - 7 : task two restarts\n"
content += " - 9 : task two done\n"
content += "\n"
if py_trees.console.has_colours:
banner_line = console.green + "*" * 79 + "\n" + console.reset
s = "\n"
s += banner_line
s += console.bold_white + "Trees".center(79) + "\n" + console.reset
s += banner_line
s += "\n"
s += content
s += "\n"
s += banner_line
else:
s = content
return s
def epilog():
if py_trees.console.has_colours:
return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
else:
return None
def command_line_argument_parser():
parser = argparse.ArgumentParser(description=description(create_root()),
epilog=epilog(),
formatter_class=argparse.RawDescriptionHelpFormatter,
)
group = parser.add_mutually_exclusive_group()
group.add_argument('-r', '--render', action='store_true', help='render dot tree to file')
group.add_argument('-i', '--interactive', action='store_true', help='pause and wait for keypress at each tick')
return parser
def pre_tick_handler(behaviour_tree):
"""
This prints a banner and will run immediately before every tick of the tree.
Args:
behaviour_tree (:class:`~py_trees.trees.BehaviourTree`): the tree custodian
"""
print("\n--------- Run %s ---------\n" % behaviour_tree.count)
def post_tick_handler(snapshot_visitor, behaviour_tree):
"""
Prints an ascii tree with the current snapshot status.
"""
print(
"\n" + py_trees.display.unicode_tree(
root=behaviour_tree.root,
visited=snapshot_visitor.visited,
previously_visited=snapshot_visitor.previously_visited
)
)
def create_root():
task_one = py_trees.behaviours.Count(
name="Task 1",
fail_until=0,
running_until=2,
success_until=10
)
task_two = py_trees.behaviours.Count(
name="Task 2",
fail_until=0,
running_until=2,
success_until=10
)
high_priority_interrupt = py_trees.decorators.RunningIsFailure(
child=py_trees.behaviours.Periodic(
name="High Priority",
n=3
)
)
piwylo = py_trees.idioms.pick_up_where_you_left_off(
name="Pick Up\nWhere You\nLeft Off",
tasks=[task_one, task_two]
)
root = py_trees.composites.Selector(name="Root")
root.add_children([high_priority_interrupt, piwylo])
return root
##############################################################################
# Main
##############################################################################
def main():
"""
Entry point for the demo script.
"""
args = command_line_argument_parser().parse_args()
py_trees.logging.level = py_trees.logging.Level.DEBUG
root = create_root()
print(description(root))
####################
# Rendering
####################
if args.render:
py_trees.display.render_dot_tree(root)
sys.exit()
####################
# Tree Stewardship
####################
behaviour_tree = py_trees.trees.BehaviourTree(root)
behaviour_tree.add_pre_tick_handler(pre_tick_handler)
behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
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)
behaviour_tree.setup(timeout=15)
####################
# Tick Tock
####################
if args.interactive:
py_trees.console.read_single_keypress()
for unused_i in range(1, 11):
try:
behaviour_tree.tick()
if args.interactive:
py_trees.console.read_single_keypress()
else:
time.sleep(0.5)
except KeyboardInterrupt:
break
print("\n")
|