Writing a node with parameters
Overview
This tutorial shows how to implement nodes in careBT which use input and/or output parameters. In this example an ActionNode
,
called AddTwoNumbersAction
, is implemented which receives two numbers (?x, ?y) as input parameters, adds these two numbers and binds the
result to an output parameter (?z). This is, of course, a simplified example, as adding two numbers is typically not done by an custom
action node. But it demonstrates how parameters are working in careBT, without caring about the scenario and how “realistic” it is.
The signature (input/output parameters) of a node is defined by a string provided to the constructor of the careBT node the custom node inherits from and works for all careBT nodes in the same way. The syntax is <list of input parameters> => <list of output parameters> with parameter names starting with ?. Each parameter is then available in the custom node class with the name _<variable-name>.
Create the AddTwoNumbersAction ActionNode
Create a file named action_with_params.py
with following content.
Or use the provided file: action_with_params.py
1from carebt import ActionNode
2from carebt import NodeStatus
3
4
5class AddTwoNumbersAction(ActionNode):
6 """The `AddTwoNumbersAction` demonstrates a careBT `ActionNode`.
7
8 The `AddTwoNumbersAction` demonstrates a careBT `ActionNode` with two
9 input parameters and one output parameter. It takes the two inputs,
10 adds them and returns the result.
11
12 Input Parameters
13 ----------------
14 ?x : int, default = 0
15 The first value
16 ?y : int, default = 0
17 The second value
18
19 Output Parameters
20 -----------------
21 ?z : int
22 The sum of ?x and ?y
23
24 """
25
26 def __init__(self, bt_runner):
27 super().__init__(bt_runner, '?x ?y => ?z')
28
29 def on_init(self) -> None:
30 if(self._x is None):
31 self._x = 0
32 if(self._y is None):
33 self._y = 0
34
35 def on_tick(self) -> None:
36 self._z = self._x + self._y
37 print(f'AddTwoNumbersAction: calculating: {self._x} + {self._y} = {self._z}')
38 self.set_status(NodeStatus.SUCCESS)
The code explained
The first two statements are the includes for the ActionNode
and the NodeStatus
.
from carebt import ActionNode
from carebt import NodeStatus
The AddTwoNumbersAction
node is implemented as a Python class which inherits from ActionNode
.
class AddTwoNumbersAction(ActionNode):
The class definition is followed by the Docstring documentation of the node, which also documents the interface (input/output parameters).
"""The `AddTwoNumbersAction` demonstrates a careBT `ActionNode`.
The `AddTwoNumbersAction` demonstrates a careBT `ActionNode` with two
input parameters and one output parameter. It takes the two inputs,
adds them and returns the result.
Input Parameters
----------------
?x : int, default = 0
The first value
?y : int, default = 0
The second value
Output Parameters
-----------------
?z : int
The sum of ?x and ?y
"""
The constructor (__init__
) of the AddTwoNumbersAction
node needs to call the constructor (super().__init__
)
of the ActionNode
and passes the bt_runner and the signature as arguments. The input parameters are ?x and ?y,
and the output parameter is ?z. These parameters are then available inside the node as member variables, called:
_x, _y and _z.
def __init__(self, bt_runner):
super().__init__(bt_runner, '?x ?y => ?z')
The on_init
function is called rigth after the node was created. It is the place to put the code which should
be executed once, after the node was created. In this example it is implemented that the two input parameters are
checked if values are bound to them during creation. If the variables are not bound (... is None
), the value
is set zero.
def on_init(self) -> None:
if(self._x is None):
self._x = 0
if(self._y is None):
self._y = 0
In the on_tick
function the two inputs are added and assiged to the output. Furthermore, this calculation is
printed on standard output and the node status is set to SUCCESS
. Thus, the AddTwoNumbersAction
node is not ticked again.
def on_tick(self) -> None:
self._z = self._x + self._y
print(f'AddTwoNumbersAction: calculating: {self._x} + {self._y} = {self._z}')
self.set_status(NodeStatus.SUCCESS)
Run the example
Start the Python interpreter and run the AddTwoNumbersAction
node:
>>> import carebt
>>> from carebt.examples.action_with_params import AddTwoNumbersAction
>>> bt_runner = carebt.BehaviorTreeRunner()
>>> bt_runner.run(AddTwoNumbersAction, '2 3 => ?sum')
AddTwoNumbersAction: calculating: 2 + 3 = 5
>>> bt_runner.run(AddTwoNumbersAction, '4 => ?sum')
2021-11-12 22:13:07 WARN AddTwoNumbersAction takes 2 argument(s), but 1 was/were provided
AddTwoNumbersAction: calculating: 4 + 0 = 4
>>> bt_runner.run(AddTwoNumbersAction, '=> ?sum')
2021-11-12 22:13:48 WARN AddTwoNumbersAction takes 2 argument(s), but 0 was/were provided
AddTwoNumbersAction: calculating: 0 + 0 = 0
Above the AddTwoNumbersAction
node is executed three times. The first execution is the standard case,
where two input parameters are provided as expected. In the second and third execution one or both
input parameters are missing. This is announced as a CareBT-warning and the default values are
used.