Demos
py-trees-demo-action-behaviour
A py_trees demo.
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: str)[source]
Bases:
BehaviourDemonstrates the at-a-distance style action behaviour.
This behaviour connects to a separately running process (initiated in setup()) and proceeeds to work with that subprocess to initiate a task and monitor the progress of that task at each tick until completed. While the task is running the behaviour returns
RUNNING.On completion, the the behaviour returns with success or failure (depending on success or failure of the task itself).
Key point - this behaviour itself should not be doing any work!
- Parameters:
name (str) –
- setup(**kwargs: Any) None[source]
Kickstart the separate process this behaviour will work with.
Ordinarily this process will be already running. In this case, setup is usually just responsible for verifying it exists.
- Parameters:
kwargs (Any) –
- Return type:
None
- py_trees.demos.action.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.action.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.action.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.action.planning(pipe_connection: Connection) None[source]
Emulate a (potentially) long running external process.
- Parameters:
pipe_connection (Connection) – connection to the planning process
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.action
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-action-behaviour
17
18.. image:: images/action.gif
19"""
20
21##############################################################################
22# Imports
23##############################################################################
24
25import argparse
26import atexit
27import multiprocessing
28import multiprocessing.connection
29import time
30import typing
31
32import py_trees.common
33import py_trees.console as console
34
35##############################################################################
36# Classes
37##############################################################################
38
39
40def description() -> str:
41 """
42 Print description and usage information about the program.
43
44 Returns:
45 the program description string
46 """
47 content = "Demonstrates the characteristics of a typical 'action' behaviour.\n"
48 content += "\n"
49 content += "* Mocks an external process and connects to it in the setup() method\n"
50 content += "* Kickstarts new goals with the external process in the initialise() method\n"
51 content += "* Monitors the ongoing goal status in the update() method\n"
52 content += "* Determines RUNNING/SUCCESS pending feedback from the external process\n"
53
54 if py_trees.console.has_colours:
55 banner_line = console.green + "*" * 79 + "\n" + console.reset
56 s = banner_line
57 s += console.bold_white + "Action Behaviour".center(79) + "\n" + console.reset
58 s += banner_line
59 s += "\n"
60 s += content
61 s += "\n"
62 s += banner_line
63 else:
64 s = content
65 return s
66
67
68def epilog() -> str | None:
69 """
70 Print a noodly epilog for --help.
71
72 Returns:
73 the noodly message
74 """
75 if py_trees.console.has_colours:
76 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
77 else:
78 return None
79
80
81def command_line_argument_parser() -> argparse.ArgumentParser:
82 """
83 Process command line arguments.
84
85 Returns:
86 the argument parser
87 """
88 return argparse.ArgumentParser(
89 description=description(),
90 epilog=epilog(),
91 formatter_class=argparse.RawDescriptionHelpFormatter,
92 )
93
94
95def planning(pipe_connection: multiprocessing.connection.Connection) -> None:
96 """Emulate a (potentially) long running external process.
97
98 Args:
99 pipe_connection: connection to the planning process
100 """
101 idle = True
102 percentage_complete = 0
103 try:
104 while True:
105 if pipe_connection.poll():
106 pipe_connection.recv()
107 percentage_complete = 0
108 idle = False
109 if not idle:
110 percentage_complete += 10
111 pipe_connection.send([percentage_complete])
112 if percentage_complete == 100:
113 idle = True
114 time.sleep(0.5)
115 except KeyboardInterrupt:
116 pass
117
118
119class Action(py_trees.behaviour.Behaviour):
120 """Demonstrates the at-a-distance style action behaviour.
121
122 This behaviour connects to a separately running process
123 (initiated in setup()) and proceeeds to work with that subprocess to
124 initiate a task and monitor the progress of that task at each tick
125 until completed. While the task is running the behaviour returns
126 :data:`~py_trees.common.Status.RUNNING`.
127
128 On completion, the the behaviour returns with success or failure
129 (depending on success or failure of the task itself).
130
131 Key point - this behaviour itself should not be doing any work!
132 """
133
134 def __init__(self, name: str):
135 """Configure the name of the behaviour."""
136 super().__init__(name)
137 self.logger.debug(f"{self.__class__.__name__}.__init__()")
138
139 def setup(self, **kwargs: typing.Any) -> None:
140 """Kickstart the separate process this behaviour will work with.
141
142 Ordinarily this process will be already running. In this case,
143 setup is usually just responsible for verifying it exists.
144 """
145 self.logger.debug(f"{self.__class__.__name__}.setup()->connections to an external process")
146 self.parent_connection, self.child_connection = multiprocessing.Pipe()
147 self.planning = multiprocessing.Process(target=planning, args=(self.child_connection,))
148 atexit.register(self.planning.terminate)
149 self.planning.start()
150
151 def initialise(self) -> None:
152 """Reset a counter variable."""
153 self.logger.debug(f"{self.__class__.__name__}.initialise()->sending new goal")
154 self.parent_connection.send(["new goal"])
155 self.percentage_completion = 0
156
157 def update(self) -> py_trees.common.Status:
158 """Increment the counter, monitor and decide on a new status."""
159 new_status = py_trees.common.Status.RUNNING
160 if self.parent_connection.poll():
161 self.percentage_completion = self.parent_connection.recv().pop()
162 if self.percentage_completion == 100:
163 new_status = py_trees.common.Status.SUCCESS
164 if new_status == py_trees.common.Status.SUCCESS:
165 self.feedback_message = "Processing finished"
166 self.logger.debug(
167 f"{self.__class__.__name__}.update()[{self.status}->{new_status}][{self.feedback_message}]"
168 )
169 else:
170 self.feedback_message = f"{self.percentage_completion}%"
171 self.logger.debug(f"{self.__class__.__name__}.update()[{self.status}][{self.feedback_message}]")
172 return new_status
173
174 def terminate(self, new_status: py_trees.common.Status) -> None:
175 """Nothing to clean up in this example."""
176 self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}]")
177
178
179##############################################################################
180# Main
181##############################################################################
182
183
184def main() -> None:
185 """Entry point for the demo script."""
186 command_line_argument_parser().parse_args()
187
188 print(description())
189
190 py_trees.logging.level = py_trees.logging.Level.DEBUG
191
192 action = Action(name="Action")
193 action.setup()
194 try:
195 for _unused_i in range(0, 12):
196 action.tick_once()
197 time.sleep(0.5)
198 print("\n")
199 except KeyboardInterrupt:
200 pass
py-trees-demo-behaviour-lifecycle
A py_trees demo.
- . argparse::
- module:
py_trees.demos.lifecycle
- func:
command_line_argument_parser
- prog:
py-trees-demo-behaviour-lifecycle
- class py_trees.demos.lifecycle.Counter(name: str = 'Counter')[source]
Bases:
BehaviourSimple counting behaviour.
Increments a counter from zero at each tick
Finishes with success if the counter reaches three
Resets the counter in the initialise() method.
- Parameters:
name (str) –
- __init__(name: str = 'Counter')[source]
Configure the name of the behaviour.
- Parameters:
name (str) –
- setup(**kwargs: Any) None[source]
No delayed initialisation required for this example.
- Parameters:
kwargs (Any) –
- Return type:
None
- py_trees.demos.lifecycle.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.lifecycle.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.lifecycle.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13. argparse::
14 :module: py_trees.demos.lifecycle
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-behaviour-lifecycle
17
18.. image:: images/lifecycle.gif
19"""
20
21##############################################################################
22# Imports
23##############################################################################
24
25import argparse
26import time
27import typing
28
29import py_trees
30import py_trees.console as console
31
32##############################################################################
33# Classes
34##############################################################################
35
36
37def description() -> str:
38 """
39 Print description and usage information about the program.
40
41 Returns:
42 the program description string
43 """
44 content = "Demonstrates a typical day in the life of a behaviour.\n\n"
45 content += "This behaviour will count from 1 to 3 and then reset and repeat. As it does\n"
46 content += "so, it logs and displays the methods as they are called - construction, setup,\n"
47 content += "initialisation, ticking and termination.\n"
48 if py_trees.console.has_colours:
49 banner_line = console.green + "*" * 79 + "\n" + console.reset
50 s = banner_line
51 s += console.bold_white + "Behaviour Lifecycle".center(79) + "\n" + console.reset
52 s += banner_line
53 s += "\n"
54 s += content
55 s += "\n"
56 s += banner_line
57 else:
58 s = content
59 return s
60
61
62def epilog() -> str | None:
63 """
64 Print a noodly epilog for --help.
65
66 Returns:
67 the noodly message
68 """
69 if py_trees.console.has_colours:
70 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
71 else:
72 return None
73
74
75def command_line_argument_parser() -> argparse.ArgumentParser:
76 """
77 Process command line arguments.
78
79 Returns:
80 the argument parser
81 """
82 return argparse.ArgumentParser(
83 description=description(),
84 epilog=epilog(),
85 formatter_class=argparse.RawDescriptionHelpFormatter,
86 )
87
88
89class Counter(py_trees.behaviour.Behaviour):
90 """Simple counting behaviour.
91
92 * Increments a counter from zero at each tick
93 * Finishes with success if the counter reaches three
94 * Resets the counter in the initialise() method.
95 """
96
97 def __init__(self, name: str = "Counter"):
98 """Configure the name of the behaviour."""
99 super().__init__(name)
100 self.logger.debug(f"{self.__class__.__name__}.__init__()")
101
102 def setup(self, **kwargs: typing.Any) -> None:
103 """No delayed initialisation required for this example."""
104 self.logger.debug(f"{self.__class__.__name__}.setup()")
105
106 def initialise(self) -> None:
107 """Reset a counter variable."""
108 self.logger.debug(f"{self.__class__.__name__}.initialise()")
109 self.counter = 0
110
111 def update(self) -> py_trees.common.Status:
112 """Increment the counter and decide on a new status."""
113 self.counter += 1
114 new_status = py_trees.common.Status.SUCCESS if self.counter == 3 else py_trees.common.Status.RUNNING
115 if new_status == py_trees.common.Status.SUCCESS:
116 self.feedback_message = f"counting...{self.counter} - phew, thats enough for today"
117 else:
118 self.feedback_message = "still counting"
119 self.logger.debug(f"{self.__class__.__name__}.update()[{self.status}->{new_status}][{self.feedback_message}]")
120 return new_status
121
122 def terminate(self, new_status: py_trees.common.Status) -> None:
123 """Nothing to clean up in this example."""
124 self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}]")
125
126
127##############################################################################
128# Main
129##############################################################################
130
131
132def main() -> None:
133 """Entry point for the demo script."""
134 command_line_argument_parser().parse_args()
135
136 print(description())
137
138 py_trees.logging.level = py_trees.logging.Level.DEBUG
139
140 counter = Counter()
141 counter.setup()
142 try:
143 for _unused_i in range(0, 7):
144 counter.tick_once()
145 time.sleep(0.5)
146 print("\n")
147 except KeyboardInterrupt:
148 print("")
149 pass
py-trees-demo-blackboard
A py_trees demo.
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 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];
}](_images/graphviz-2ce3a0fc455ca273ad541bdc2c8cd077d696e2ee.png)
Dot Graph
Console Screenshot
- class py_trees.demos.blackboard.BlackboardWriter(name: str)[source]
Bases:
BehaviourWrite some more interesting / complex types to the blacbkoard.
- Parameters:
name (str) –
- class py_trees.demos.blackboard.Nested[source]
Bases:
objectA more complex object to interact with on the blackboard.
- __weakref__
list of weak references to the object
- class py_trees.demos.blackboard.ParamsAndState(name: str)[source]
Bases:
BehaviourParameter and state storage on the blackboard.
This behaviour demonstrates the usage of namespaces and multiple clients to perform getting and setting of parameters and state in a concise and convenient manner.
- Parameters:
name (str) –
- __init__(name: str)[source]
Set up separate blackboard clients for parameters and state.
- Parameters:
name (str) – behaviour name
- py_trees.demos.blackboard.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.blackboard.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.blackboard.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.blackboard.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.blackboard
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-blackboard
17
18.. graphviz:: dot/demo-blackboard.dot
19 :align: center
20 :caption: Dot Graph
21
22.. figure:: images/blackboard_demo.png
23 :align: center
24
25 Console Screenshot
26"""
27
28##############################################################################
29# Imports
30##############################################################################
31
32import argparse
33import operator
34import sys
35
36import py_trees
37import py_trees.console as console
38
39##############################################################################
40# Classes
41##############################################################################
42
43
44def description() -> str:
45 """
46 Print description and usage information about the program.
47
48 Returns:
49 the program description string
50 """
51 content = "Demonstrates usage of the blackboard and related behaviours.\n"
52 content += "\n"
53 content += "A sequence is populated with a few behaviours that exercise\n"
54 content += "reading and writing on the Blackboard in interesting ways.\n"
55
56 if py_trees.console.has_colours:
57 banner_line = console.green + "*" * 79 + "\n" + console.reset
58 s = banner_line
59 s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
60 s += banner_line
61 s += "\n"
62 s += content
63 s += "\n"
64 s += banner_line
65 else:
66 s = content
67 return s
68
69
70def epilog() -> str | None:
71 """
72 Print a noodly epilog for --help.
73
74 Returns:
75 the noodly message
76 """
77 if py_trees.console.has_colours:
78 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
79 else:
80 return None
81
82
83def command_line_argument_parser() -> argparse.ArgumentParser:
84 """
85 Process command line arguments.
86
87 Returns:
88 the argument parser
89 """
90 parser = argparse.ArgumentParser(
91 description=description(),
92 epilog=epilog(),
93 formatter_class=argparse.RawDescriptionHelpFormatter,
94 )
95 render_group = parser.add_mutually_exclusive_group()
96 render_group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
97 render_group.add_argument(
98 "--render-with-blackboard-variables",
99 action="store_true",
100 help="render dot tree to file with blackboard variables",
101 )
102 return parser
103
104
105class Nested:
106 """A more complex object to interact with on the blackboard."""
107
108 def __init__(self) -> None:
109 """Initialise variables to some arbitrary defaults."""
110 self.foo = "bar"
111
112 def __str__(self) -> str:
113 return str({"foo": self.foo})
114
115
116class BlackboardWriter(py_trees.behaviour.Behaviour):
117 """Write some more interesting / complex types to the blacbkoard."""
118
119 def __init__(self, name: str):
120 """Set up the blackboard.
121
122 Args:
123 name: behaviour name
124 """
125 super().__init__(name=name)
126 self.blackboard = self.attach_blackboard_client()
127 self.blackboard.register_key(key="dude", access=py_trees.common.Access.READ)
128 self.blackboard.register_key(key="spaghetti", access=py_trees.common.Access.WRITE)
129
130 self.logger.debug(f"{self.__class__.__name__}.__init__()")
131
132 def update(self) -> py_trees.common.Status:
133 """Write a dictionary to the blackboard.
134
135 This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
136 """
137 self.logger.debug(f"{self.__class__.__name__}.update()")
138 try:
139 _ = self.blackboard.dude
140 except KeyError:
141 pass
142 try:
143 _ = self.blackboard.dudette
144 except AttributeError:
145 pass
146 try:
147 self.blackboard.dudette = "Jane"
148 except AttributeError:
149 pass
150 self.blackboard.spaghetti = {"type": "Carbonara", "quantity": 1}
151 self.blackboard.spaghetti = {"type": "Gnocchi", "quantity": 2}
152 try:
153 self.blackboard.set("spaghetti", {"type": "Bolognese", "quantity": 3}, overwrite=False)
154 except AttributeError:
155 pass
156 return py_trees.common.Status.SUCCESS
157
158
159class ParamsAndState(py_trees.behaviour.Behaviour):
160 """Parameter and state storage on the blackboard.
161
162 This behaviour demonstrates the usage of namespaces and
163 multiple clients to perform getting and setting of
164 parameters and state in a concise and convenient manner.
165 """
166
167 def __init__(self, name: str):
168 """Set up separate blackboard clients for parameters and state.
169
170 Args:
171 name: behaviour name
172 """
173 super().__init__(name=name)
174 # namespaces can include the separator or may leave it out
175 # they can also be nested, e.g. /agent/state, /agent/parameters
176 self.parameters = self.attach_blackboard_client("Params", "parameters")
177 self.state = self.attach_blackboard_client("State", "state")
178 self.parameters.register_key(key="default_speed", access=py_trees.common.Access.READ)
179 self.state.register_key(key="current_speed", access=py_trees.common.Access.WRITE)
180
181 def initialise(self) -> None:
182 """Initialise speed from the stored parameter variable on the blackboard."""
183 try:
184 self.state.current_speed = self.parameters.default_speed
185 except KeyError as e:
186 raise RuntimeError(f"parameter 'default_speed' not found [{str(e)}]") from e
187
188 def update(self) -> py_trees.common.Status:
189 """
190 Check speed and either increment, or complete if it has reached a threshold.
191
192 Returns:
193 :data:`~py_trees.common.Status.RUNNING` if incrementing, :data:`~py_trees.common.Status.SUCCESS` otherwise.
194 """
195 if self.state.current_speed > 40.0:
196 return py_trees.common.Status.SUCCESS
197 else:
198 self.state.current_speed += 1.0
199 return py_trees.common.Status.RUNNING
200
201
202def create_root() -> py_trees.behaviour.Behaviour:
203 """
204 Create the root behaviour and its subtree.
205
206 Returns:
207 the root behaviour
208 """
209 root = py_trees.composites.Sequence(name="Blackboard Demo", memory=True)
210 set_blackboard_variable = py_trees.behaviours.SetBlackboardVariable(
211 name="Set Nested",
212 variable_name="nested",
213 variable_value=Nested(),
214 overwrite=True,
215 )
216 write_blackboard_variable = BlackboardWriter(name="Writer")
217 check_blackboard_variable = py_trees.behaviours.CheckBlackboardVariableValue(
218 name="Check Nested Foo",
219 check=py_trees.common.ComparisonExpression(variable="nested.foo", value="bar", operator=operator.eq),
220 )
221 params_and_state = ParamsAndState(name="ParamsAndState")
222 root.add_children(
223 [
224 set_blackboard_variable,
225 write_blackboard_variable,
226 check_blackboard_variable,
227 params_and_state,
228 ]
229 )
230 return root
231
232
233##############################################################################
234# Main
235##############################################################################
236
237
238def main() -> None:
239 """Entry point for the demo script."""
240 args = command_line_argument_parser().parse_args()
241 print(description())
242 py_trees.logging.level = py_trees.logging.Level.DEBUG
243 py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
244 blackboard = py_trees.blackboard.Client(name="Configuration")
245 blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
246 blackboard.register_key(key="/parameters/default_speed", access=py_trees.common.Access.EXCLUSIVE_WRITE)
247 blackboard.dude = "Bob"
248 blackboard.parameters.default_speed = 30.0
249
250 root = create_root()
251
252 ####################
253 # Rendering
254 ####################
255 if args.render:
256 py_trees.display.render_dot_tree(root, with_blackboard_variables=False)
257 sys.exit()
258 if args.render_with_blackboard_variables:
259 py_trees.display.render_dot_tree(root, with_blackboard_variables=True)
260 sys.exit()
261
262 ####################
263 # Execute
264 ####################
265 root.setup_with_descendants()
266 unset_blackboard = py_trees.blackboard.Client(name="Unsetter")
267 unset_blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
268 print("\n--------- Tick 0 ---------\n")
269 root.tick_once()
270 print("\n")
271 print(py_trees.display.unicode_tree(root, show_status=True))
272 print("--------------------------\n")
273 print(py_trees.display.unicode_blackboard())
274 print("--------------------------\n")
275 print(py_trees.display.unicode_blackboard(display_only_key_metadata=True))
276 print("--------------------------\n")
277 unset_blackboard.unset("foo")
278 print(py_trees.display.unicode_blackboard_activity_stream())
py-trees-demo-blackboard-namespaces
A py_trees demo.
Demonstrates usage of blackboard namespaces.
usage: py-trees-demo-blackboard-namespaces [-h]
Console Screenshot
- py_trees.demos.blackboard_namespaces.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.blackboard_namespaces.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.blackboard_namespaces.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.blackboard_namespaces.main() None[source]
Entry point for the demo script.
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.blackboard_namespaces
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-blackboard-namespaces
17
18.. figure:: images/blackboard_namespaces.png
19 :align: center
20
21 Console Screenshot
22"""
23
24##############################################################################
25# Imports
26##############################################################################
27
28import argparse
29
30import py_trees
31import py_trees.console as console
32
33##############################################################################
34# Classes
35##############################################################################
36
37
38def description() -> str:
39 """
40 Print description and usage information about the program.
41
42 Returns:
43 the program description string
44 """
45 content = "Demonstrates usage of blackboard namespaces.\n"
46 content += "\n"
47
48 if py_trees.console.has_colours:
49 banner_line = console.green + "*" * 79 + "\n" + console.reset
50 s = banner_line
51 s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
52 s += banner_line
53 s += "\n"
54 s += content
55 s += "\n"
56 s += banner_line
57 else:
58 s = content
59 return s
60
61
62def epilog() -> str | None:
63 """
64 Print a noodly epilog for --help.
65
66 Returns:
67 the noodly message
68 """
69 if py_trees.console.has_colours:
70 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
71 else:
72 return None
73
74
75def command_line_argument_parser() -> argparse.ArgumentParser:
76 """
77 Process command line arguments.
78
79 Returns:
80 the argument parser
81 """
82 parser = argparse.ArgumentParser(
83 description=description(),
84 epilog=epilog(),
85 formatter_class=argparse.RawDescriptionHelpFormatter,
86 )
87 return parser
88
89
90##############################################################################
91# Main
92##############################################################################
93
94
95def main() -> None:
96 """Entry point for the demo script."""
97 _ = command_line_argument_parser().parse_args() # configuration only, no args to process
98 print(description())
99 print("-------------------------------------------------------------------------------")
100 print("$ py_trees.blackboard.Client(name='Blackboard')")
101 print("$ foo.register_key(key='dude', access=py_trees.common.Access.WRITE)")
102 print("$ foo.register_key(key='/dudette', access=py_trees.common.Access.WRITE)")
103 print("$ foo.register_key(key='/foo/bar/wow', access=py_trees.common.Access.WRITE)")
104 print("-------------------------------------------------------------------------------")
105 blackboard = py_trees.blackboard.Client(name="Blackboard")
106 blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
107 blackboard.register_key(key="/dudette", access=py_trees.common.Access.WRITE)
108 blackboard.register_key(key="/foo/bar/wow", access=py_trees.common.Access.WRITE)
109 print(blackboard)
110 print("-------------------------------------------------------------------------------")
111 print("$ blackboard.dude = 'Bob'")
112 print("$ blackboard.dudette = 'Jade'")
113 print("-------------------------------------------------------------------------------")
114 blackboard.dude = "Bob"
115 blackboard.dudette = "Jade"
116 print(py_trees.display.unicode_blackboard())
117 print("-------------------------------------------------------------------------------")
118 print("$ blackboard.foo.bar.wow = 'foobar'")
119 print("-------------------------------------------------------------------------------")
120 blackboard.foo.bar.wow = "foobar"
121 print(py_trees.display.unicode_blackboard())
122 print("-------------------------------------------------------------------------------")
123 print("$ py_trees.blackboard.Client(name='Foo', namespace='foo')")
124 print("$ foo.register_key(key='awesome', access=py_trees.common.Access.WRITE)")
125 print("$ foo.register_key(key='/brilliant', access=py_trees.common.Access.WRITE)")
126 print("$ foo.register_key(key='/foo/clever', access=py_trees.common.Access.WRITE)")
127 print("-------------------------------------------------------------------------------")
128 foo = py_trees.blackboard.Client(name="Foo", namespace="foo")
129 foo.register_key(key="awesome", access=py_trees.common.Access.WRITE)
130 # TODO: should /brilliant be namespaced or go directly to root?
131 foo.register_key(key="/brilliant", access=py_trees.common.Access.WRITE)
132 # absolute names are ok, so long as they include the namespace
133 foo.register_key(key="/foo/clever", access=py_trees.common.Access.WRITE)
134 print(foo)
135 print("-------------------------------------------------------------------------------")
136 print("$ foo.awesome = True")
137 print("$ foo.set('/brilliant', False)")
138 print("$ foo.clever = True")
139 print("-------------------------------------------------------------------------------")
140 foo.awesome = True
141 # Only accessible via set since it's not in the namespace
142 foo.set("/brilliant", False)
143 # This will fail since it looks for the namespaced /foo/brilliant key
144 # foo.brilliant = False
145 foo.clever = True
146 print(py_trees.display.unicode_blackboard())
py-trees-demo-blackboard-remappings
A py_trees demo.
Demonstrates usage of blackbord remappings.
Demonstration is via an exemplar behaviour making use of remappings..
usage: py-trees-demo-blackboard-remappings [-h]
Console Screenshot
- class py_trees.demos.blackboard_remappings.Remap(name: str, remap_to: dict[str, str])[source]
Bases:
BehaviourCustom writer that submits a more complicated variable to the blackboard.
- py_trees.demos.blackboard_remappings.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.blackboard_remappings.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.blackboard_remappings.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.blackboard_remappings.main() None[source]
Entry point for the demo script.
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.blackboard_remappings
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-blackboard-remappings
17
18.. figure:: images/blackboard_remappings.png
19 :align: center
20
21 Console Screenshot
22"""
23
24##############################################################################
25# Imports
26##############################################################################
27
28import argparse
29
30import py_trees
31import py_trees.console as console
32
33##############################################################################
34# Classes
35##############################################################################
36
37
38def description() -> str:
39 """
40 Print description and usage information about the program.
41
42 Returns:
43 the program description string
44 """
45 content = "Demonstrates usage of blackbord remappings.\n"
46 content += "\n"
47 content += "Demonstration is via an exemplar behaviour making use of remappings..\n"
48
49 if py_trees.console.has_colours:
50 banner_line = console.green + "*" * 79 + "\n" + console.reset
51 s = banner_line
52 s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
53 s += banner_line
54 s += "\n"
55 s += content
56 s += "\n"
57 s += banner_line
58 else:
59 s = content
60 return s
61
62
63def epilog() -> str | None:
64 """
65 Print a noodly epilog for --help.
66
67 Returns:
68 the noodly message
69 """
70 if py_trees.console.has_colours:
71 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
72 else:
73 return None
74
75
76def command_line_argument_parser() -> argparse.ArgumentParser:
77 """
78 Process command line arguments.
79
80 Returns:
81 the argument parser
82 """
83 parser = argparse.ArgumentParser(
84 description=description(),
85 epilog=epilog(),
86 formatter_class=argparse.RawDescriptionHelpFormatter,
87 )
88 return parser
89
90
91class Remap(py_trees.behaviour.Behaviour):
92 """Custom writer that submits a more complicated variable to the blackboard."""
93
94 def __init__(self, name: str, remap_to: dict[str, str]):
95 """
96 Set up the blackboard and remap variables.
97
98 Args:
99 name: behaviour name
100 remap_to: remappings (from variable name to variable name)
101 """
102 super().__init__(name=name)
103 self.logger.debug(f"{self.__class__.__name__}.__init__()")
104 self.blackboard = self.attach_blackboard_client()
105 self.blackboard.register_key(
106 key="/foo/bar/wow",
107 access=py_trees.common.Access.WRITE,
108 remap_to=remap_to["/foo/bar/wow"],
109 )
110
111 def update(self) -> py_trees.common.Status:
112 """Write a dictionary to the blackboard.
113
114 This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
115 """
116 self.logger.debug(f"{self.__class__.__name__}.update()")
117 self.blackboard.foo.bar.wow = "colander"
118
119 return py_trees.common.Status.SUCCESS
120
121
122##############################################################################
123# Main
124##############################################################################
125
126
127def main() -> None:
128 """Entry point for the demo script."""
129 _ = command_line_argument_parser().parse_args() # configuration only, no arg processing
130 print(description())
131 py_trees.logging.level = py_trees.logging.Level.DEBUG
132 py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
133 root = Remap(name="Remap", remap_to={"/foo/bar/wow": "/parameters/wow"})
134
135 ####################
136 # Execute
137 ####################
138 root.tick_once()
139 print(root.blackboard)
140 print(py_trees.display.unicode_blackboard())
141 print(py_trees.display.unicode_blackboard_activity_stream())
py-trees-demo-context-switching
A py_trees demo.
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: str = 'ContextSwitch')[source]
Bases:
BehaviourAn example of a context switching class.
This class 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.
- Parameters:
name (str) –
- __init__(name: str = 'ContextSwitch')[source]
Initialise with a behaviour name.
- Parameters:
name (str) –
- py_trees.demos.context_switching.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.context_switching.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.context_switching.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.context_switching.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.context_switching.main() None[source]
Entry point for the demo script.
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.context_switching
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-context-switching
17
18.. graphviz:: dot/demo-context_switching.dot
19
20.. image:: images/context_switching.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import sys
29import time
30
31import py_trees
32import py_trees.console as console
33
34##############################################################################
35# Classes
36##############################################################################
37
38
39def description() -> str:
40 """
41 Print description and usage information about the program.
42
43 Returns:
44 the program description string
45 """
46 content = "Demonstrates context switching with parallels and sequences.\n"
47 content += "\n"
48 content += "A context switching behaviour is run in parallel with a work sequence.\n"
49 content += "Switching the context occurs in the initialise() and terminate() methods\n"
50 content += "of the context switching behaviour. Note that whether the sequence results\n"
51 content += "in failure or success, the context switch behaviour will always call the\n"
52 content += "terminate() method to restore the context. It will also call terminate()\n"
53 content += "to restore the context in the event of a higher priority parent cancelling\n"
54 content += "this parallel subtree.\n"
55 if py_trees.console.has_colours:
56 banner_line = console.green + "*" * 79 + "\n" + console.reset
57 s = banner_line
58 s += console.bold_white + "Context Switching".center(79) + "\n" + console.reset
59 s += banner_line
60 s += "\n"
61 s += content
62 s += "\n"
63 s += banner_line
64 else:
65 s = content
66 return s
67
68
69def epilog() -> str | None:
70 """
71 Print a noodly epilog for --help.
72
73 Returns:
74 the noodly message
75 """
76 if py_trees.console.has_colours:
77 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
78 else:
79 return None
80
81
82def command_line_argument_parser() -> argparse.ArgumentParser:
83 """
84 Process command line arguments.
85
86 Returns:
87 the argument parser
88 """
89 parser = argparse.ArgumentParser(
90 description=description(),
91 epilog=epilog(),
92 formatter_class=argparse.RawDescriptionHelpFormatter,
93 )
94 parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
95 return parser
96
97
98class ContextSwitch(py_trees.behaviour.Behaviour):
99 """
100 An example of a context switching class.
101
102 This class sets (in ``initialise()``)
103 and restores a context (in ``terminate()``). Use in parallel with a
104 sequence/subtree that does the work while in this context.
105
106 .. attention:: Simply setting a pair of behaviours (set and reset context) on
107 either end of a sequence will not suffice for context switching. In the case
108 that one of the work behaviours in the sequence fails, the final reset context
109 switch will never trigger.
110 """
111
112 def __init__(self, name: str = "ContextSwitch"):
113 """Initialise with a behaviour name."""
114 super().__init__(name)
115 self.feedback_message = "no context"
116
117 def initialise(self) -> None:
118 """Backup and set a new context."""
119 self.logger.debug(f"{self.__class__.__name__}.initialise()[switch context]")
120 # Some actions that:
121 # 1. retrieve the current context from somewhere
122 # 2. cache the context internally
123 # 3. apply a new context
124 self.feedback_message = "new context"
125
126 def update(self) -> py_trees.common.Status:
127 """Just returns RUNNING while it waits for other activities to finish."""
128 self.logger.debug(f"{self.__class__.__name__}.update()[RUNNING][{self.feedback_message}]")
129 return py_trees.common.Status.RUNNING
130
131 def terminate(self, new_status: py_trees.common.Status) -> None:
132 """Restore the context with the previously backed up context."""
133 self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}][restore context]")
134 # Some actions that:
135 # 1. restore the cached context
136 self.feedback_message = "restored context"
137
138
139def create_root() -> py_trees.behaviour.Behaviour:
140 """
141 Create the root behaviour and its subtree.
142
143 Returns:
144 the root behaviour
145 """
146 root = py_trees.composites.Parallel(name="Parallel", policy=py_trees.common.ParallelPolicy.SuccessOnOne())
147 context_switch = ContextSwitch(name="Context")
148 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
149 for job in ["Action 1", "Action 2"]:
150 success_after_two = py_trees.behaviours.StatusQueue(
151 name=job,
152 queue=[py_trees.common.Status.RUNNING, py_trees.common.Status.RUNNING],
153 eventually=py_trees.common.Status.SUCCESS,
154 )
155 sequence.add_child(success_after_two)
156 root.add_child(context_switch)
157 root.add_child(sequence)
158 return root
159
160
161##############################################################################
162# Main
163##############################################################################
164
165
166def main() -> None:
167 """Entry point for the demo script."""
168 args = command_line_argument_parser().parse_args()
169 print(description())
170 py_trees.logging.level = py_trees.logging.Level.DEBUG
171
172 root = create_root()
173
174 ####################
175 # Rendering
176 ####################
177 if args.render:
178 py_trees.display.render_dot_tree(root)
179 sys.exit()
180
181 ####################
182 # Execute
183 ####################
184 root.setup_with_descendants()
185 for i in range(1, 6):
186 try:
187 print(f"\n--------- Tick {i} ---------\n")
188 root.tick_once()
189 print("\n")
190 print(f"{py_trees.display.unicode_tree(root, show_status=True)}")
191 time.sleep(1.0)
192 except KeyboardInterrupt:
193 break
194 print("\n")
py-trees-demo-dot-graphs
A py_trees demo.
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)
- py_trees.demos.dot_graphs.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.dot_graphs.create_tree(level: str) Behaviour[source]
Create the root behaviour and its subtree.
- py_trees.demos.dot_graphs.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.dot_graphs.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.dot_graphs
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-dot-graphs
17
18.. graphviz:: dot/demo-dot-graphs.dot
19
20"""
21
22##############################################################################
23# Imports
24##############################################################################
25
26import argparse
27import subprocess
28
29import py_trees
30import py_trees.console as console
31
32##############################################################################
33# Classes
34##############################################################################
35
36
37def description() -> str:
38 """
39 Print description and usage information about the program.
40
41 Returns:
42 the program description string
43 """
44 name = "py-trees-demo-dot-graphs"
45 content = "Renders a dot graph for a simple tree, with blackboxes.\n"
46 if py_trees.console.has_colours:
47 banner_line = console.green + "*" * 79 + "\n" + console.reset
48 s = banner_line
49 s += console.bold_white + "Dot Graphs".center(79) + "\n" + console.reset
50 s += banner_line
51 s += "\n"
52 s += content
53 s += "\n"
54 s += console.white
55 s += console.bold + " Generate Full Dot Graph" + console.reset + "\n"
56 s += "\n"
57 s += console.cyan + f" {name}" + console.reset + "\n"
58 s += "\n"
59 s += console.bold + " With Varying Visibility Levels" + console.reset + "\n"
60 s += "\n"
61 s += console.cyan + f" {name}" + console.yellow + " --level=all" + console.reset + "\n"
62 s += console.cyan + f" {name}" + console.yellow + " --level=detail" + console.reset + "\n"
63 s += console.cyan + f" {name}" + console.yellow + " --level=component" + console.reset + "\n"
64 s += console.cyan + f" {name}" + console.yellow + " --level=big_picture" + console.reset + "\n"
65 s += "\n"
66 s += banner_line
67 else:
68 s = content
69 return s
70
71
72def epilog() -> str | None:
73 """
74 Print a noodly epilog for --help.
75
76 Returns:
77 the noodly message
78 """
79 if py_trees.console.has_colours:
80 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
81 else:
82 return None
83
84
85def command_line_argument_parser() -> argparse.ArgumentParser:
86 """
87 Process command line arguments.
88
89 Returns:
90 the argument parser
91 """
92 parser = argparse.ArgumentParser(
93 description=description(),
94 epilog=epilog(),
95 formatter_class=argparse.RawDescriptionHelpFormatter,
96 )
97 parser.add_argument(
98 "-l",
99 "--level",
100 action="store",
101 default="fine_detail",
102 choices=["all", "fine_detail", "detail", "component", "big_picture"],
103 help="visibility level",
104 )
105 return parser
106
107
108def create_tree(level: str) -> py_trees.behaviour.Behaviour:
109 """
110 Create the root behaviour and its subtree.
111
112 Returns:
113 the root behaviour
114 """
115 root = py_trees.composites.Selector(name=f"Demo Dot Graphs {level}", memory=False)
116 first_blackbox = py_trees.composites.Sequence(name="BlackBox 1", memory=True)
117 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
118 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
119 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
120 first_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.BIG_PICTURE
121 second_blackbox = py_trees.composites.Sequence(name="Blackbox 2", memory=True)
122 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
123 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
124 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
125 second_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
126 third_blackbox = py_trees.composites.Sequence(name="Blackbox 3", memory=True)
127 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
128 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
129 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
130 third_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.DETAIL
131 root.add_child(first_blackbox)
132 root.add_child(second_blackbox)
133 first_blackbox.add_child(third_blackbox)
134 return root
135
136
137##############################################################################
138# Main
139##############################################################################
140
141
142def main() -> None:
143 """Entry point for the demo script."""
144 args = command_line_argument_parser().parse_args()
145 args.enum_level = py_trees.common.string_to_visibility_level(args.level)
146 print(description())
147 py_trees.logging.level = py_trees.logging.Level.DEBUG
148
149 root = create_tree(args.level)
150 py_trees.display.render_dot_tree(root, args.enum_level)
151
152 if py_trees.utilities.which("xdot"):
153 try:
154 subprocess.call(["xdot", f"demo_dot_graphs_{args.level}.dot"])
155 except KeyboardInterrupt:
156 pass
157 else:
158 print("")
159 console.logerror("No xdot viewer found, skipping display [hint: sudo apt install xdot]")
160 print("")
py-trees-demo-either-or
A py_trees demo.
A demonstration of the ‘either_or’ idiom.
This behaviour tree pattern enables triggering of subtrees with equal priority (first in, first served).
EVENTS
3 : joystick one enabled, task one starts
5 : task one finishes
6 : joystick two enabled, task two starts
7 : joystick one enabled, task one ignored, task two continues
8 : task two finishes
usage: py-trees-demo-either-or [-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 {
ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Root [label="Root\n--SuccessOnAll(-)--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
Reset [label=Reset, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> Reset;
"Joy1 - Disabled" [label="Joy1 - Disabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Reset -> "Joy1 - Disabled";
"Joy2 - Disabled" [label="Joy2 - Disabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Reset -> "Joy2 - Disabled";
"Joy1 Events" [label="Joy1 Events", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> "Joy1 Events";
FisR [label=FisR, shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
"Joy1 Events" -> FisR;
"Joystick 1" [label="Joystick 1", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
FisR -> "Joystick 1";
"Joy1 - Enabled" [label="Joy1 - Enabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Joy1 Events" -> "Joy1 - Enabled";
"Joy2 Events" [label="Joy2 Events", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> "Joy2 Events";
"FisR*" [label="FisR*", shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
"Joy2 Events" -> "FisR*";
"Joystick 2" [label="Joystick 2", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"FisR*" -> "Joystick 2";
"Joy2 - Enabled" [label="Joy2 - Enabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Joy2 Events" -> "Joy2 - Enabled";
Tasks [label=Tasks, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
Root -> Tasks;
EitherOr [label=EitherOr, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Tasks -> EitherOr;
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?";
"Task 1" [label="Task 1", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 1" -> "Task 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?*";
"Task 2" [label="Task 2", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 2" -> "Task 2";
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Tasks -> Idle;
}](_images/graphviz-3dca64ea408dbf9f2c553714096fd4d5c2eed1bf.png)
- py_trees.demos.either_or.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.either_or.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.either_or.description(root: Behaviour) str[source]
Print description and usage information about the program.
- py_trees.demos.either_or.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.either_or.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]
Print data about the part of the tree visited.
- Parameters:
snapshot_handler – gather data about the part of the tree visited
behaviour_tree (BehaviourTree) – tree to gather data from
snapshot_visitor (SnapshotVisitor) –
- Return type:
None
- py_trees.demos.either_or.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]
Print a banner with current tick count prior to ticking the tree.
- Parameters:
behaviour_tree (BehaviourTree) – the tree to tick (used to fetch the count number)
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.either_or
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-either-or
17
18.. graphviz:: dot/demo-either-or.dot
19
20.. image:: images/either_or.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import functools
29import operator
30import sys
31import time
32
33import py_trees
34import py_trees.console as console
35
36##############################################################################
37# Classes
38##############################################################################
39
40
41def description(root: py_trees.behaviour.Behaviour) -> str:
42 """
43 Print description and usage information about the program.
44
45 Returns:
46 the program description string
47 """
48 content = "A demonstration of the 'either_or' idiom.\n\n"
49 content += "This behaviour tree pattern enables triggering of subtrees\n"
50 content += "with equal priority (first in, first served).\n"
51 content += "\n"
52 content += "EVENTS\n"
53 content += "\n"
54 content += " - 3 : joystick one enabled, task one starts\n"
55 content += " - 5 : task one finishes\n"
56 content += " - 6 : joystick two enabled, task two starts\n"
57 content += " - 7 : joystick one enabled, task one ignored, task two continues\n"
58 content += " - 8 : task two finishes\n"
59 content += "\n"
60 if py_trees.console.has_colours:
61 banner_line = console.green + "*" * 79 + "\n" + console.reset
62 s = banner_line
63 s += console.bold_white + "Either Or".center(79) + "\n" + console.reset
64 s += banner_line
65 s += "\n"
66 s += content
67 s += "\n"
68 s += banner_line
69 else:
70 s = content
71 return s
72
73
74def epilog() -> str | None:
75 """
76 Print a noodly epilog for --help.
77
78 Returns:
79 the noodly message
80 """
81 if py_trees.console.has_colours:
82 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
83 else:
84 return None
85
86
87def command_line_argument_parser() -> argparse.ArgumentParser:
88 """
89 Process command line arguments.
90
91 Returns:
92 the argument parser
93 """
94 parser = argparse.ArgumentParser(
95 description=description(create_root()),
96 epilog=epilog(),
97 formatter_class=argparse.RawDescriptionHelpFormatter,
98 )
99 group = parser.add_mutually_exclusive_group()
100 group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
101 group.add_argument(
102 "-i",
103 "--interactive",
104 action="store_true",
105 help="pause and wait for keypress at each tick",
106 )
107 return parser
108
109
110def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
111 """Print a banner with current tick count prior to ticking the tree.
112
113 Args:
114 behaviour_tree: the tree to tick (used to fetch the count number)
115 """
116 print(f"\n--------- Run {behaviour_tree.count} ---------\n")
117
118
119def post_tick_handler(
120 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
121 behaviour_tree: py_trees.trees.BehaviourTree,
122) -> None:
123 """
124 Print data about the part of the tree visited.
125
126 Args:
127 snapshot_handler: gather data about the part of the tree visited
128 behaviour_tree: tree to gather data from
129 """
130 print(
131 "\n"
132 + py_trees.display.unicode_tree(
133 root=behaviour_tree.root,
134 visited=snapshot_visitor.visited,
135 previously_visited=snapshot_visitor.previously_visited,
136 )
137 )
138 print(py_trees.display.unicode_blackboard())
139
140
141def create_root() -> py_trees.behaviour.Behaviour:
142 """
143 Create the root behaviour and its subtree.
144
145 Returns:
146 the root behaviour
147 """
148 trigger_one = py_trees.decorators.FailureIsRunning(
149 name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 1", n=4)
150 )
151 trigger_two = py_trees.decorators.FailureIsRunning(
152 name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 2", n=7)
153 )
154 enable_joystick_one = py_trees.behaviours.SetBlackboardVariable(
155 name="Joy1 - Enabled",
156 variable_name="joystick_one",
157 variable_value="enabled",
158 overwrite=True,
159 )
160 enable_joystick_two = py_trees.behaviours.SetBlackboardVariable(
161 name="Joy2 - Enabled",
162 variable_name="joystick_two",
163 variable_value="enabled",
164 overwrite=True,
165 )
166 reset_joystick_one = py_trees.behaviours.SetBlackboardVariable(
167 name="Joy1 - Disabled",
168 variable_name="joystick_one",
169 variable_value="disabled",
170 overwrite=True,
171 )
172 reset_joystick_two = py_trees.behaviours.SetBlackboardVariable(
173 name="Joy2 - Disabled",
174 variable_name="joystick_two",
175 variable_value="disabled",
176 overwrite=True,
177 )
178 task_one = py_trees.behaviours.TickCounter(
179 name="Task 1", duration=2, completion_status=py_trees.common.Status.SUCCESS
180 )
181 task_two = py_trees.behaviours.TickCounter(
182 name="Task 2", duration=2, completion_status=py_trees.common.Status.SUCCESS
183 )
184 idle = py_trees.behaviours.Running(name="Idle")
185 either_or = py_trees.idioms.either_or(
186 name="Either Or",
187 conditions=[
188 py_trees.common.ComparisonExpression("joystick_one", "enabled", operator.eq),
189 py_trees.common.ComparisonExpression("joystick_two", "enabled", operator.eq),
190 ],
191 subtrees=[task_one, task_two],
192 namespace="either_or",
193 )
194 root = py_trees.composites.Parallel(
195 name="Root",
196 policy=py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False),
197 )
198 reset = py_trees.composites.Sequence(name="Reset", memory=True)
199 reset.add_children([reset_joystick_one, reset_joystick_two])
200 joystick_one_events = py_trees.composites.Sequence(name="Joy1 Events", memory=True)
201 joystick_one_events.add_children([trigger_one, enable_joystick_one])
202 joystick_two_events = py_trees.composites.Sequence(name="Joy2 Events", memory=True)
203 joystick_two_events.add_children([trigger_two, enable_joystick_two])
204 tasks = py_trees.composites.Selector(name="Tasks", memory=False)
205 tasks.add_children([either_or, idle])
206 root.add_children([reset, joystick_one_events, joystick_two_events, tasks])
207 return root
208
209
210##############################################################################
211# Main
212##############################################################################
213
214
215def main() -> None:
216 """Entry point for the demo script."""
217 args = command_line_argument_parser().parse_args()
218 # py_trees.logging.level = py_trees.logging.Level.DEBUG
219 root = create_root()
220 print(description(root))
221
222 ####################
223 # Rendering
224 ####################
225 if args.render:
226 py_trees.display.render_dot_tree(root)
227 sys.exit()
228
229 ####################
230 # Tree Stewardship
231 ####################
232 behaviour_tree = py_trees.trees.BehaviourTree(root)
233 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
234 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
235 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
236 behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
237 behaviour_tree.visitors.append(snapshot_visitor)
238 behaviour_tree.setup(timeout=15)
239
240 ####################
241 # Tick Tock
242 ####################
243 if args.interactive:
244 py_trees.console.read_single_keypress()
245 for _unused_i in range(1, 11):
246 try:
247 behaviour_tree.tick()
248 if args.interactive:
249 py_trees.console.read_single_keypress()
250 else:
251 time.sleep(0.5)
252 except KeyboardInterrupt:
253 break
254 print("\n")
py-trees-demo-eternal-guard
A py_trees demo.
A demonstration of the ‘eternal guard’ concept.
Two binary (F|S) conditional checks will fire every tick, thus providing a fail-fast mechanism for the long running sequence of tasks behind them.
usage: py-trees-demo-eternal-guard [-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 {
ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Eternal Guard" [fillcolor=orange, fontcolor=black, fontsize=9, label="Eternal Guard", shape=box, style=filled];
"Condition 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 1", shape=ellipse, style=filled];
"Eternal Guard" -> "Condition 1";
"Condition 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 2", shape=ellipse, style=filled];
"Eternal Guard" -> "Condition 2";
"Task Sequence" [fillcolor=orange, fontcolor=black, fontsize=9, label="Ⓜ Task Sequence", shape=box, style=filled];
"Eternal Guard" -> "Task Sequence";
"Worker 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Worker 1", shape=ellipse, style=filled];
"Task Sequence" -> "Worker 1";
"Worker 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Worker 2", shape=ellipse, style=filled];
"Task Sequence" -> "Worker 2";
}](_images/graphviz-5184d308d84ad7348d9821a9114508c02bd51181.png)
- py_trees.demos.eternal_guard.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.eternal_guard.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.eternal_guard.description(root: Behaviour) str[source]
Print description and usage information about the program.
- py_trees.demos.eternal_guard.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.eternal_guard.main() None[source]
Entry point for the demo script.
- Return type:
None
- py_trees.demos.eternal_guard.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]
Print data about the part of the tree visited.
- Parameters:
snapshot_handler – gather data about the part of the tree visited
behaviour_tree (BehaviourTree) – tree to gather data from
snapshot_visitor (SnapshotVisitor) –
- Return type:
None
- py_trees.demos.eternal_guard.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]
Print a banner with current tick count prior to ticking the tree.
- Parameters:
behaviour_tree (BehaviourTree) – the tree to tick (used to fetch the count number)
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.eternal_guard
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-eternal-guard
17
18.. graphviz:: dot/demo-eternal-guard.dot
19
20.. image:: images/eternal_guard.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import functools
29import sys
30import time
31
32import py_trees
33import py_trees.console as console
34
35##############################################################################
36# Classes
37##############################################################################
38
39
40def description(root: py_trees.behaviour.Behaviour) -> str:
41 """
42 Print description and usage information about the program.
43
44 Returns:
45 the program description string
46 """
47 content = "A demonstration of the 'eternal guard' concept.\n\n"
48 content += "Two binary (F|S) conditional checks will fire every\n"
49 content += "tick, thus providing a fail-fast mechanism for the\n"
50 content += "long running sequence of tasks behind them.\n"
51 content += "\n"
52 if py_trees.console.has_colours:
53 banner_line = console.green + "*" * 79 + "\n" + console.reset
54 s = banner_line
55 s += console.bold_white + "Eternal Guard".center(79) + "\n" + console.reset
56 s += banner_line
57 s += "\n"
58 s += content
59 s += "\n"
60 s += py_trees.display.unicode_tree(root)
61 s += "\n"
62 s += banner_line
63 else:
64 s = content
65 return s
66
67
68def epilog() -> str | None:
69 """
70 Print a noodly epilog for --help.
71
72 Returns:
73 the noodly message
74 """
75 if py_trees.console.has_colours:
76 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
77 else:
78 return None
79
80
81def command_line_argument_parser() -> argparse.ArgumentParser:
82 """
83 Process command line arguments.
84
85 Returns:
86 the argument parser
87 """
88 parser = argparse.ArgumentParser(
89 description=description(create_root()),
90 epilog=epilog(),
91 formatter_class=argparse.RawDescriptionHelpFormatter,
92 )
93 group = parser.add_mutually_exclusive_group()
94 group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
95 group.add_argument(
96 "-i",
97 "--interactive",
98 action="store_true",
99 help="pause and wait for keypress at each tick",
100 )
101 return parser
102
103
104def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
105 """Print a banner with current tick count prior to ticking the tree.
106
107 Args:
108 behaviour_tree: the tree to tick (used to fetch the count number)
109 """
110 print(f"\n--------- Run {behaviour_tree.count} ---------\n")
111
112
113def post_tick_handler(
114 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
115 behaviour_tree: py_trees.trees.BehaviourTree,
116) -> None:
117 """
118 Print data about the part of the tree visited.
119
120 Args:
121 snapshot_handler: gather data about the part of the tree visited
122 behaviour_tree: tree to gather data from
123 """
124 print(
125 "\n"
126 + py_trees.display.unicode_tree(
127 root=behaviour_tree.root,
128 visited=snapshot_visitor.visited,
129 previously_visited=snapshot_visitor.previously_visited,
130 )
131 )
132 print(py_trees.display.unicode_blackboard())
133
134
135def create_root() -> py_trees.behaviour.Behaviour:
136 """
137 Create the root behaviour and its subtree.
138
139 Returns:
140 the root behaviour
141 """
142 eternal_guard = py_trees.composites.Sequence(name="Eternal Guard", memory=False)
143 condition_one = py_trees.behaviours.StatusQueue(
144 name="Condition 1",
145 queue=[
146 py_trees.common.Status.SUCCESS,
147 py_trees.common.Status.FAILURE,
148 py_trees.common.Status.SUCCESS,
149 ],
150 eventually=py_trees.common.Status.SUCCESS,
151 )
152 condition_two = py_trees.behaviours.StatusQueue(
153 name="Condition 2",
154 queue=[
155 py_trees.common.Status.SUCCESS,
156 py_trees.common.Status.SUCCESS,
157 py_trees.common.Status.FAILURE,
158 ],
159 eventually=py_trees.common.Status.SUCCESS,
160 )
161 task_sequence = py_trees.composites.Sequence(name="Task Sequence", memory=True)
162 task_one = py_trees.behaviours.Success(name="Worker 1")
163 task_two = py_trees.behaviours.Running(name="Worker 2")
164
165 eternal_guard.add_children([condition_one, condition_two, task_sequence])
166 task_sequence.add_children([task_one, task_two])
167 return eternal_guard
168
169
170##############################################################################
171# Main
172##############################################################################
173
174
175def main() -> None:
176 """Entry point for the demo script."""
177 args = command_line_argument_parser().parse_args()
178 # py_trees.logging.level = py_trees.logging.Level.DEBUG
179 root = create_root()
180 print(description(root))
181
182 ####################
183 # Rendering
184 ####################
185 if args.render:
186 py_trees.display.render_dot_tree(root)
187 sys.exit()
188
189 ####################
190 # Tree Stewardship
191 ####################
192 behaviour_tree = py_trees.trees.BehaviourTree(root)
193 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
194 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
195 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
196 behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
197 behaviour_tree.visitors.append(snapshot_visitor)
198 behaviour_tree.setup(timeout=15)
199
200 ####################
201 # Tick Tock
202 ####################
203 if args.interactive:
204 py_trees.console.read_single_keypress()
205 for _unused_i in range(1, 11):
206 try:
207 behaviour_tree.tick()
208 if args.interactive:
209 py_trees.console.read_single_keypress()
210 else:
211 time.sleep(0.5)
212 except KeyboardInterrupt:
213 break
214 print("\n")
py-trees-demo-logging
A py_trees demo.
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.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.logging.create_tree() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.logging.description(root: Behaviour) str[source]
Print description and usage information about the program.
- py_trees.demos.logging.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.logging.logger(snapshot_visitor: DisplaySnapshotVisitor, behaviour_tree: BehaviourTree) None[source]
Log the tree (relevant parts thereof) to a yaml file.
Use as a post-tick handler for a tree.
- Parameters:
snapshot_visitor (DisplaySnapshotVisitor) –
behaviour_tree (BehaviourTree) –
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.logging
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-logging
17
18.. graphviz:: dot/demo-logging.dot
19
20.. image:: images/logging.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import functools
29import json
30import sys
31import time
32import typing
33
34import py_trees
35import py_trees.console as console
36
37##############################################################################
38# Classes
39##############################################################################
40
41
42def description(root: py_trees.behaviour.Behaviour) -> str:
43 """
44 Print description and usage information about the program.
45
46 Returns:
47 the program description string
48 """
49 content = "A demonstration of logging with trees.\n\n"
50 content += "This demo utilises a SnapshotVisitor to trigger\n"
51 content += "a post-tick handler to dump a serialisation of the\n"
52 content += "tree to a json log file.\n"
53 content += "\n"
54 content += "This coupling of visitor and post-tick handler can be\n"
55 content += "used for any kind of event handling - the visitor is the\n"
56 content += "trigger and the post-tick handler the action. Aside from\n"
57 content += "logging, the most common use case is to serialise the tree\n"
58 content += "for messaging to a graphical, runtime monitor.\n"
59 content += "\n"
60 if py_trees.console.has_colours:
61 banner_line = console.green + "*" * 79 + "\n" + console.reset
62 s = banner_line
63 s += console.bold_white + "Logging".center(79) + "\n" + console.reset
64 s += banner_line
65 s += "\n"
66 s += content
67 s += "\n"
68 s += py_trees.display.unicode_tree(root)
69 s += "\n"
70 s += banner_line
71 else:
72 s = content
73 return s
74
75
76def epilog() -> str | None:
77 """
78 Print a noodly epilog for --help.
79
80 Returns:
81 the noodly message
82 """
83 if py_trees.console.has_colours:
84 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
85 else:
86 return None
87
88
89def command_line_argument_parser() -> argparse.ArgumentParser:
90 """
91 Process command line arguments.
92
93 Returns:
94 the argument parser
95 """
96 parser = argparse.ArgumentParser(
97 description=description(create_tree()),
98 epilog=epilog(),
99 formatter_class=argparse.RawDescriptionHelpFormatter,
100 )
101 group = parser.add_mutually_exclusive_group()
102 group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
103 group.add_argument(
104 "-i",
105 "--interactive",
106 action="store_true",
107 help="pause and wait for keypress at each tick",
108 )
109 return parser
110
111
112def logger(
113 snapshot_visitor: py_trees.visitors.DisplaySnapshotVisitor,
114 behaviour_tree: py_trees.trees.BehaviourTree,
115) -> None:
116 """Log the tree (relevant parts thereof) to a yaml file.
117
118 Use as a post-tick handler for a tree.
119 """
120 if snapshot_visitor.changed:
121 print(console.cyan + "Logging.......................yes\n" + console.reset)
122 tree_serialisation = {"tick": behaviour_tree.count, "nodes": []}
123 for node in behaviour_tree.root.iterate():
124 node_type_str = "Behaviour"
125 for behaviour_type in [
126 py_trees.composites.Sequence,
127 py_trees.composites.Selector,
128 py_trees.composites.Parallel,
129 py_trees.decorators.Decorator,
130 ]:
131 if isinstance(node, behaviour_type):
132 node_type_str = behaviour_type.__name__
133 if node.tip() is not None:
134 node_tip = node.tip()
135 assert node_tip is not None # help mypy
136 node_tip_id = str(node_tip.id)
137 else:
138 node_tip_id = "none"
139 node_snapshot = {
140 "name": node.name,
141 "id": str(node.id),
142 "parent_id": str(node.parent.id) if node.parent else "none",
143 "child_ids": [str(child.id) for child in node.children],
144 "tip_id": node_tip_id,
145 "class_name": str(node.__module__) + "." + str(type(node).__name__),
146 "type": node_type_str,
147 "status": node.status.value,
148 "message": node.feedback_message,
149 "is_active": True if node.id in snapshot_visitor.visited else False,
150 }
151 typing.cast(list, tree_serialisation["nodes"]).append(node_snapshot)
152 if behaviour_tree.count == 0:
153 with open("dump.json", "w+") as outfile:
154 json.dump(tree_serialisation, outfile, indent=4)
155 else:
156 with open("dump.json", "a") as outfile:
157 json.dump(tree_serialisation, outfile, indent=4)
158 else:
159 print(console.yellow + "Logging.......................no\n" + console.reset)
160
161
162def create_tree() -> py_trees.behaviour.Behaviour:
163 """
164 Create the root behaviour and its subtree.
165
166 Returns:
167 the root behaviour
168 """
169 every_n_success = py_trees.behaviours.SuccessEveryN("EveryN", 5)
170 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
171 guard = py_trees.behaviours.Success("Guard")
172 periodic_success = py_trees.behaviours.Periodic("Periodic", 3)
173 finisher = py_trees.behaviours.Success("Finisher")
174 sequence.add_child(guard)
175 sequence.add_child(periodic_success)
176 sequence.add_child(finisher)
177 sequence.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
178 idle = py_trees.behaviours.Success("Idle")
179 root = py_trees.composites.Selector(name="Logging", memory=False)
180 root.add_child(every_n_success)
181 root.add_child(sequence)
182 root.add_child(idle)
183 return root
184
185
186##############################################################################
187# Main
188##############################################################################
189
190
191def main() -> None:
192 """Entry point for the demo script."""
193 args = command_line_argument_parser().parse_args()
194 py_trees.logging.level = py_trees.logging.Level.DEBUG
195 tree = create_tree()
196 print(description(tree))
197
198 ####################
199 # Rendering
200 ####################
201 if args.render:
202 py_trees.display.render_dot_tree(tree)
203 sys.exit()
204
205 ####################
206 # Tree Stewardship
207 ####################
208 behaviour_tree = py_trees.trees.BehaviourTree(tree)
209
210 debug_visitor = py_trees.visitors.DebugVisitor()
211 snapshot_visitor = py_trees.visitors.DisplaySnapshotVisitor()
212
213 behaviour_tree.visitors.append(debug_visitor)
214 behaviour_tree.visitors.append(snapshot_visitor)
215
216 behaviour_tree.add_post_tick_handler(functools.partial(logger, snapshot_visitor))
217
218 behaviour_tree.setup(timeout=15)
219
220 ####################
221 # Tick Tock
222 ####################
223 if args.interactive:
224 py_trees.console.read_single_keypress()
225 while True:
226 try:
227 behaviour_tree.tick()
228 if args.interactive:
229 py_trees.console.read_single_keypress()
230 else:
231 time.sleep(0.5)
232 except KeyboardInterrupt:
233 break
234 print("\n")
py-trees-demo-selector
A py_trees demo.
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)
- py_trees.demos.selector.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.selector.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.selector.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.selector.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.selector
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-selector
17
18.. graphviz:: dot/demo-selector.dot
19
20.. image:: images/selector.gif
21
22"""
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import sys
29import time
30
31import py_trees
32import py_trees.console as console
33
34##############################################################################
35# Classes
36##############################################################################
37
38
39def description() -> str:
40 """
41 Print description and usage information about the program.
42
43 Returns:
44 the program description string
45 """
46 content = "Higher priority switching and interruption in the children of a selector.\n"
47 content += "\n"
48 content += "In this example the higher priority child is setup to fail initially,\n"
49 content += "falling back to the continually running second child. On the third\n"
50 content += "tick, the first child succeeds and cancels the hitherto running child.\n"
51 if py_trees.console.has_colours:
52 banner_line = console.green + "*" * 79 + "\n" + console.reset
53 s = banner_line
54 s += console.bold_white + "Selectors".center(79) + "\n" + console.reset
55 s += banner_line
56 s += "\n"
57 s += content
58 s += "\n"
59 s += banner_line
60 else:
61 s = content
62 return s
63
64
65def epilog() -> str | None:
66 """
67 Print a noodly epilog for --help.
68
69 Returns:
70 the noodly message
71 """
72 if py_trees.console.has_colours:
73 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
74 else:
75 return None
76
77
78def command_line_argument_parser() -> argparse.ArgumentParser:
79 """
80 Process command line arguments.
81
82 Returns:
83 the argument parser
84 """
85 parser = argparse.ArgumentParser(
86 description=description(),
87 epilog=epilog(),
88 formatter_class=argparse.RawDescriptionHelpFormatter,
89 )
90 parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
91 return parser
92
93
94def create_root() -> py_trees.behaviour.Behaviour:
95 """
96 Create the root behaviour and its subtree.
97
98 Returns:
99 the root behaviour
100 """
101 root = py_trees.composites.Selector(name="Selector", memory=False)
102 ffs = py_trees.behaviours.StatusQueue(
103 name="FFS",
104 queue=[
105 py_trees.common.Status.FAILURE,
106 py_trees.common.Status.FAILURE,
107 py_trees.common.Status.SUCCESS,
108 ],
109 eventually=py_trees.common.Status.SUCCESS,
110 )
111 always_running = py_trees.behaviours.Running(name="Running")
112 root.add_children([ffs, always_running])
113 return root
114
115
116##############################################################################
117# Main
118##############################################################################
119
120
121def main() -> None:
122 """Entry point for the demo script."""
123 args = command_line_argument_parser().parse_args()
124 print(description())
125 py_trees.logging.level = py_trees.logging.Level.DEBUG
126
127 root = create_root()
128
129 ####################
130 # Rendering
131 ####################
132 if args.render:
133 py_trees.display.render_dot_tree(root)
134 sys.exit()
135
136 ####################
137 # Execute
138 ####################
139 root.setup_with_descendants()
140 for i in range(1, 4):
141 try:
142 print(f"\n--------- Tick {i} ---------\n")
143 root.tick_once()
144 print("\n")
145 print(py_trees.display.unicode_tree(root=root, show_status=True))
146 time.sleep(1.0)
147 except KeyboardInterrupt:
148 break
149 print("\n")
py-trees-demo-sequence
A py_trees demo.
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)
- py_trees.demos.sequence.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.sequence.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.sequence.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.sequence.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.sequence
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-sequence
17
18.. graphviz:: dot/demo-sequence.dot
19
20.. image:: images/sequence.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import sys
29import time
30
31import py_trees
32import py_trees.console as console
33
34##############################################################################
35# Classes
36##############################################################################
37
38
39def description() -> str:
40 """
41 Print description and usage information about the program.
42
43 Returns:
44 the program description string
45 """
46 content = "Demonstrates sequences in action.\n\n"
47 content += "A sequence is populated with 2-tick jobs that are allowed to run through to\n"
48 content += "completion.\n"
49
50 if py_trees.console.has_colours:
51 banner_line = console.green + "*" * 79 + "\n" + console.reset
52 s = banner_line
53 s += console.bold_white + "Sequences".center(79) + "\n" + console.reset
54 s += banner_line
55 s += "\n"
56 s += content
57 s += "\n"
58 s += banner_line
59 else:
60 s = content
61 return s
62
63
64def epilog() -> str | None:
65 """
66 Print a noodly epilog for --help.
67
68 Returns:
69 the noodly message
70 """
71 if py_trees.console.has_colours:
72 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
73 else:
74 return None
75
76
77def command_line_argument_parser() -> argparse.ArgumentParser:
78 """
79 Process command line arguments.
80
81 Returns:
82 the argument parser
83 """
84 parser = argparse.ArgumentParser(
85 description=description(),
86 epilog=epilog(),
87 formatter_class=argparse.RawDescriptionHelpFormatter,
88 )
89 parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
90 return parser
91
92
93def create_root() -> py_trees.behaviour.Behaviour:
94 """
95 Create the root behaviour and its subtree.
96
97 Returns:
98 the root behaviour
99 """
100 root = py_trees.composites.Sequence(name="Sequence", memory=True)
101 for action in ["Action 1", "Action 2", "Action 3"]:
102 rssss = py_trees.behaviours.StatusQueue(
103 name=action,
104 queue=[
105 py_trees.common.Status.RUNNING,
106 py_trees.common.Status.SUCCESS,
107 ],
108 eventually=py_trees.common.Status.SUCCESS,
109 )
110 root.add_child(rssss)
111 return root
112
113
114##############################################################################
115# Main
116##############################################################################
117
118
119def main() -> None:
120 """Entry point for the demo script."""
121 args = command_line_argument_parser().parse_args()
122 print(description())
123 py_trees.logging.level = py_trees.logging.Level.DEBUG
124
125 root = create_root()
126
127 ####################
128 # Rendering
129 ####################
130 if args.render:
131 py_trees.display.render_dot_tree(root)
132 sys.exit()
133
134 ####################
135 # Execute
136 ####################
137 root.setup_with_descendants()
138 for i in range(1, 6):
139 try:
140 print(f"\n--------- Tick {i} ---------\n")
141 root.tick_once()
142 print("\n")
143 print(py_trees.display.unicode_tree(root=root, show_status=True))
144 time.sleep(1.0)
145 except KeyboardInterrupt:
146 break
147 print("\n")
py-trees-demo-tree-stewardship
A py_trees demo.
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:
BehaviourGathers blackboard data from other behaviours and prints a summary.
To be used at the end of the tree run.
- class py_trees.demos.stewardship.PeriodicSuccess[source]
Bases:
PeriodicWrite the period from
Periodicas a variable on the blackboard.
- class py_trees.demos.stewardship.SuccessEveryN[source]
Bases:
SuccessEveryNAdd a blackboard counter to
SuccessEveryN.- update() Status[source]
Run the
SuccessEveryNupdate and write to blackboard.- Returns:
Returns the
StatusofSuccessEveryN- Return type:
- py_trees.demos.stewardship.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.stewardship.create_tree() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.stewardship.description() str[source]
Print description and usage information about the program.
- Returns:
the program description string
- Return type:
- py_trees.demos.stewardship.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.stewardship.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]
Generate a simple pre-tick banner printing to stdout.
- Parameters:
behaviour_tree (BehaviourTree) –
- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.stewardship
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-tree-stewardship
17
18.. graphviz:: dot/demo-tree-stewardship.dot
19
20.. image:: images/tree_stewardship.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import sys
29import time
30
31import py_trees
32import py_trees.console as console
33
34##############################################################################
35# Classes
36##############################################################################
37
38
39def description() -> str:
40 """
41 Print description and usage information about the program.
42
43 Returns:
44 the program description string
45 """
46 content = "A demonstration of tree stewardship.\n\n"
47 content += "A slightly less trivial tree that uses a simple stdout pre-tick handler\n"
48 content += "and both the debug and snapshot visitors for logging and displaying\n"
49 content += "the state of the tree.\n"
50 content += "\n"
51 content += "EVENTS\n"
52 content += "\n"
53 content += " - 3 : sequence switches from running to success\n"
54 content += " - 4 : selector's first child flicks to success once only\n"
55 content += " - 8 : the fallback idler kicks in as everything else fails\n"
56 content += " - 14 : the first child kicks in again, aborting a running sequence behind it\n"
57 content += "\n"
58 if py_trees.console.has_colours:
59 banner_line = console.green + "*" * 79 + "\n" + console.reset
60 s = banner_line
61 s += console.bold_white + "Trees".center(79) + "\n" + console.reset
62 s += banner_line
63 s += "\n"
64 s += content
65 s += "\n"
66 s += banner_line
67 else:
68 s = content
69 return s
70
71
72def epilog() -> str | None:
73 """
74 Print a noodly epilog for --help.
75
76 Returns:
77 the noodly message
78 """
79 if py_trees.console.has_colours:
80 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
81 else:
82 return None
83
84
85def command_line_argument_parser() -> argparse.ArgumentParser:
86 """
87 Process command line arguments.
88
89 Returns:
90 the argument parser
91 """
92 parser = argparse.ArgumentParser(
93 description=description(),
94 epilog=epilog(),
95 formatter_class=argparse.RawDescriptionHelpFormatter,
96 )
97 group = parser.add_mutually_exclusive_group()
98 group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
99 group.add_argument(
100 "--render-with-blackboard-variables",
101 action="store_true",
102 help="render dot tree to file with blackboard variables",
103 )
104 group.add_argument(
105 "-i",
106 "--interactive",
107 action="store_true",
108 help="pause and wait for keypress at each tick",
109 )
110 return parser
111
112
113def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
114 """Generate a simple pre-tick banner printing to stdout."""
115 print(f"\n--------- Run {behaviour_tree.count} ---------\n")
116
117
118class SuccessEveryN(py_trees.behaviours.SuccessEveryN):
119 """Add a blackboard counter to :class:`~py_trees.behaviours.SuccessEveryN`."""
120
121 def __init__(self) -> None:
122 """Set up the blackboard."""
123 super().__init__(name="EveryN", n=5)
124 self.blackboard = self.attach_blackboard_client(name=self.name)
125 self.blackboard.register_key("count", access=py_trees.common.Access.WRITE)
126
127 def update(self) -> py_trees.common.Status:
128 """
129 Run the :class:`~py_trees.behaviours.SuccessEveryN` update and write to blackboard.
130
131 Returns:
132 Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.SuccessEveryN`
133 """
134 status = super().update()
135 self.blackboard.count = self.count
136 return status
137
138
139class PeriodicSuccess(py_trees.behaviours.Periodic):
140 """Write the period from :class:`~py_trees.behaviours.Periodic` as a variable on the blackboard."""
141
142 def __init__(self) -> None:
143 """Initialise :class:`~py_trees.behaviours.Periodic` and set up the blackboard."""
144 super().__init__(name="Periodic", n=3)
145 self.blackboard = self.attach_blackboard_client(name=self.name)
146 self.blackboard.register_key("period", access=py_trees.common.Access.WRITE)
147
148 def update(self) -> py_trees.common.Status:
149 """
150 Run the :class:`~py_trees.behaviours.Periodic` update and write to blackboard.
151
152 Returns:
153 Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.Periodic`
154 """
155 status = super().update()
156 self.blackboard.period = self.period
157 return status
158
159
160class Finisher(py_trees.behaviour.Behaviour):
161 """
162 Gathers blackboard data from other behaviours and prints a summary.
163
164 To be used at the end of the tree run.
165 """
166
167 def __init__(self) -> None:
168 """Set up the blackboard."""
169 super().__init__(name="Finisher")
170 self.blackboard = self.attach_blackboard_client(name=self.name)
171 self.blackboard.register_key("count", access=py_trees.common.Access.READ)
172 self.blackboard.register_key("period", access=py_trees.common.Access.READ)
173
174 def update(self) -> py_trees.common.Status:
175 """
176 Fetch blackboard variables and print a summary.
177
178 Returns:
179 Always returns :data:`~py_trees.common.Status.SUCCESS`.
180 """
181 print(console.green + "---------------------------" + console.reset)
182 print(console.bold + " Finisher" + console.reset)
183 print(console.green + f" Count : {self.blackboard.count}" + console.reset)
184 print(console.green + f" Period: {self.blackboard.period}" + console.reset)
185 print(console.green + "---------------------------" + console.reset)
186 return py_trees.common.Status.SUCCESS
187
188
189def create_tree() -> py_trees.behaviour.Behaviour:
190 """
191 Create the root behaviour and its subtree.
192
193 Returns:
194 the root behaviour
195 """
196 every_n_success = SuccessEveryN()
197 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
198 guard = py_trees.behaviours.Success("Guard")
199 periodic_success = PeriodicSuccess()
200 finisher = Finisher()
201 sequence.add_child(guard)
202 sequence.add_child(periodic_success)
203 sequence.add_child(finisher)
204 idle = py_trees.behaviours.Success("Idle")
205 root = py_trees.composites.Selector(name="Demo Tree", memory=False)
206 root.add_child(every_n_success)
207 root.add_child(sequence)
208 root.add_child(idle)
209 return root
210
211
212##############################################################################
213# Main
214##############################################################################
215
216
217def main() -> None:
218 """Entry point for the demo script."""
219 args = command_line_argument_parser().parse_args()
220 py_trees.logging.level = py_trees.logging.Level.DEBUG
221 tree = create_tree()
222 print(description())
223
224 ####################
225 # Rendering
226 ####################
227 if args.render:
228 py_trees.display.render_dot_tree(tree)
229 sys.exit()
230
231 if args.render_with_blackboard_variables:
232 py_trees.display.render_dot_tree(tree, with_blackboard_variables=True)
233 sys.exit()
234
235 ####################
236 # Tree Stewardship
237 ####################
238 py_trees.blackboard.Blackboard.enable_activity_stream(100)
239 behaviour_tree = py_trees.trees.BehaviourTree(tree)
240 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
241 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
242 behaviour_tree.visitors.append(
243 py_trees.visitors.DisplaySnapshotVisitor(display_blackboard=True, display_activity_stream=True)
244 )
245 behaviour_tree.setup(timeout=15)
246
247 ####################
248 # Tick Tock
249 ####################
250 if args.interactive:
251 py_trees.console.read_single_keypress()
252 while True:
253 try:
254 behaviour_tree.tick()
255 if args.interactive:
256 py_trees.console.read_single_keypress()
257 else:
258 time.sleep(0.5)
259 except KeyboardInterrupt:
260 break
261 print("\n")
py-trees-demo-pick-up-where-you-left-off
A py_trees demo.
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.command_line_argument_parser() ArgumentParser[source]
Process command line arguments.
- Returns:
the argument parser
- Return type:
- py_trees.demos.pick_up_where_you_left_off.create_root() Behaviour[source]
Create the root behaviour and its subtree.
- Returns:
the root behaviour
- Return type:
- py_trees.demos.pick_up_where_you_left_off.description(root: Behaviour) str[source]
Print description and usage information about the program.
- py_trees.demos.pick_up_where_you_left_off.epilog() str | None[source]
Print a noodly epilog for –help.
- Returns:
the noodly message
- Return type:
str | None
- py_trees.demos.pick_up_where_you_left_off.main() None[source]
Entry point for the demo script.
- Return type:
None
- py_trees.demos.pick_up_where_you_left_off.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]
Print an ascii tree with the current snapshot status.
- Parameters:
snapshot_visitor (SnapshotVisitor) –
behaviour_tree (BehaviourTree) –
- Return type:
None
- py_trees.demos.pick_up_where_you_left_off.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]
Print a banner immediately before every tick of the tree.
- Parameters:
behaviour_tree (
BehaviourTree) – the tree custodian- Return type:
None
1#!/usr/bin/env python
2#
3# License: BSD
4# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
5#
6##############################################################################
7# Documentation
8##############################################################################
9
10"""
11A py_trees demo.
12
13.. argparse::
14 :module: py_trees.demos.pick_up_where_you_left_off
15 :func: command_line_argument_parser
16 :prog: py-trees-demo-pick-up-where-you-left-off
17
18.. graphviz:: dot/pick_up_where_you_left_off.dot
19
20.. image:: images/pick_up_where_you_left_off.gif
21"""
22
23##############################################################################
24# Imports
25##############################################################################
26
27import argparse
28import functools
29import sys
30import time
31
32import py_trees
33import py_trees.console as console
34
35##############################################################################
36# Classes
37##############################################################################
38
39
40def description(root: py_trees.behaviour.Behaviour) -> str:
41 """
42 Print description and usage information about the program.
43
44 Returns:
45 the program description string
46 """
47 content = "A demonstration of the 'pick up where you left off' idiom.\n\n"
48 content += "A common behaviour tree pattern that allows you to resume\n"
49 content += "work after being interrupted by a high priority interrupt.\n"
50 content += "\n"
51 content += "EVENTS\n"
52 content += "\n"
53 content += " - 2 : task one done, task two running\n"
54 content += " - 3 : high priority interrupt\n"
55 content += " - 7 : task two restarts\n"
56 content += " - 9 : task two done\n"
57 content += "\n"
58 if py_trees.console.has_colours:
59 banner_line = console.green + "*" * 79 + "\n" + console.reset
60 s = banner_line
61 s += console.bold_white + "Pick Up Where you Left Off".center(79) + "\n" + console.reset
62 s += banner_line
63 s += "\n"
64 s += content
65 s += "\n"
66 s += py_trees.display.unicode_tree(root)
67 s += "\n"
68 s += banner_line
69 else:
70 s = content
71 return s
72
73
74def epilog() -> str | None:
75 """
76 Print a noodly epilog for --help.
77
78 Returns:
79 the noodly message
80 """
81 if py_trees.console.has_colours:
82 return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
83 else:
84 return None
85
86
87def command_line_argument_parser() -> argparse.ArgumentParser:
88 """
89 Process command line arguments.
90
91 Returns:
92 the argument parser
93 """
94 parser = argparse.ArgumentParser(
95 description=description(create_root()),
96 epilog=epilog(),
97 formatter_class=argparse.RawDescriptionHelpFormatter,
98 )
99 group = parser.add_mutually_exclusive_group()
100 group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
101 group.add_argument(
102 "-i",
103 "--interactive",
104 action="store_true",
105 help="pause and wait for keypress at each tick",
106 )
107 return parser
108
109
110def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
111 """Print a banner immediately before every tick of the tree.
112
113 Args:
114 behaviour_tree (:class:`~py_trees.trees.BehaviourTree`): the tree custodian
115
116 """
117 print(f"\n--------- Run {behaviour_tree.count} ---------\n")
118
119
120def post_tick_handler(
121 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
122 behaviour_tree: py_trees.trees.BehaviourTree,
123) -> None:
124 """Print an ascii tree with the current snapshot status."""
125 print(
126 "\n"
127 + py_trees.display.unicode_tree(
128 root=behaviour_tree.root,
129 visited=snapshot_visitor.visited,
130 previously_visited=snapshot_visitor.previously_visited,
131 )
132 )
133
134
135def create_root() -> py_trees.behaviour.Behaviour:
136 """
137 Create the root behaviour and its subtree.
138
139 Returns:
140 the root behaviour
141 """
142 task_one = py_trees.behaviours.StatusQueue(
143 name="Task 1",
144 queue=[
145 py_trees.common.Status.RUNNING,
146 py_trees.common.Status.RUNNING,
147 ],
148 eventually=py_trees.common.Status.SUCCESS,
149 )
150 task_two = py_trees.behaviours.StatusQueue(
151 name="Task 2",
152 queue=[
153 py_trees.common.Status.RUNNING,
154 py_trees.common.Status.RUNNING,
155 ],
156 eventually=py_trees.common.Status.SUCCESS,
157 )
158 high_priority_interrupt = py_trees.decorators.RunningIsFailure(
159 name="Running is Failure",
160 child=py_trees.behaviours.Periodic(name="High Priority", n=3),
161 )
162 piwylo = py_trees.idioms.pick_up_where_you_left_off(name="Pick Up\nWhere You\nLeft Off", tasks=[task_one, task_two])
163 root = py_trees.composites.Selector(name="Root", memory=False)
164 root.add_children([high_priority_interrupt, piwylo])
165
166 return root
167
168
169##############################################################################
170# Main
171##############################################################################
172
173
174def main() -> None:
175 """Entry point for the demo script."""
176 args = command_line_argument_parser().parse_args()
177 py_trees.logging.level = py_trees.logging.Level.DEBUG
178 root = create_root()
179 print(description(root))
180
181 ####################
182 # Rendering
183 ####################
184 if args.render:
185 py_trees.display.render_dot_tree(root)
186 sys.exit()
187
188 ####################
189 # Tree Stewardship
190 ####################
191 behaviour_tree = py_trees.trees.BehaviourTree(root)
192 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
193 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
194 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
195 behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
196 behaviour_tree.visitors.append(snapshot_visitor)
197 behaviour_tree.setup(timeout=15)
198
199 ####################
200 # Tick Tock
201 ####################
202 if args.interactive:
203 py_trees.console.read_single_keypress()
204 for _unused_i in range(1, 11):
205 try:
206 behaviour_tree.tick()
207 if args.interactive:
208 py_trees.console.read_single_keypress()
209 else:
210 time.sleep(0.5)
211 except KeyboardInterrupt:
212 break
213 print("\n")