The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
The autoreload extension is already loaded. To reload it, use:
%reload_ext autoreload
A node comprises four functions, reference, perceptual, comparator and output. Executing the node will run each of the functions in the order indicated above and return the output value.
The functions can actually be a collection of functions, each executed in the order they are added. This allows a chain of functions in case pre-processing is required, or post-processing in the case of the output.
ControlUnitIndices (value, names=None, module=None, qualname=None, type=None, start=1)
An enumeration.
PCTNode (reference=None, perception=None, comparator=None, output=None, default=True, name='pctnode', history=False, build_links=False, mode=0, namespace=None, **pargs)
A single PCT controller.
PCTNodeData (name='pctnodedata')
Data collected for a PCTNode
A node can be created simply.
pctnode PCTNode fc50e711-62e3-11ef-a9a5-5c879c15de65
----------------------------
REF: constant Constant | 0
PER: variable Variable | 0
COM: subtract Subtract | 0 | links constant variable
OUT: proportional Proportional | gain 1 | 0 | links subtract
----------------------------
That creates a node with default functions. Those are, a constant of 1 for the reference, a variable, with initial value 0, for the perception and a proportional function for the output, with a gain of 10.
A node can also be created by providing a name, and setting the history to True. The latter means that the values of all the functions are recorded during execution, which is useful for plotting the data later, as can be seen below.
node = PCTNode(name="mypctnode", history=True, reference = reference, output=Proportional(10, namespace=namespace), namespace=namespace)
node.summary()
mypctnode PCTNode fc6106b5-62e3-11ef-8e63-5c879c15de65
----------------------------
REF: constant Constant | 1
PER: variable Variable | 0
COM: subtract Subtract | 0 | links constant variable
OUT: proportional Proportional | gain 10 | 0 | links subtract
----------------------------
Another way of creating a node is by first declaring the functions you want and passing them into the constructor.
Yet another way to create a node is from a text configuration.
config_node = PCTNode.from_config({ 'name': 'mypctnode',
'refcoll': {'0': {'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 10}},
'percoll': {'0': {'type': 'Variable', 'name': 'velocity', 'value': 0.2, 'links': {}}},
'comcoll': {'0': {'type': 'Subtract', 'name': 'subtract', 'value': 1, 'links': {0: 'constant', 1: 'velocity'}}},
'outcoll': {'0': {'type': 'Proportional', 'name': 'proportional', 'value': 10, 'links': {0: 'subtract'}, 'gain': 10}}})
# config_node = PCTNode.from_config({ 'name': 'mypctnode1',
# 'refcoll': {'0': {'type': 'Proportional', 'name': 'proportional', 'value': 0, 'links': {}, 'gain': 10}},
# 'percoll': {'0': {'type': 'Variable', 'name': 'velocity', 'value': 0.2, 'links': {}}},
# 'comcoll': {'0': {'type': 'Subtract', 'name': 'subtract', 'value': 1, 'links': {0: 'constant', 1: 'velocity'}}},
# 'outcoll': {'0': {'type': 'Proportional', 'name': 'proportional', 'value': 10, 'links': {0: 'subtract'}, 'gain': 10}}}, namespace=namespace)
The details of a node can be viewed in a number of ways, which is useful for checking the configuration. The summary method prints to the screen. The get_config method returns a string in a JSON format.
integratingnode PCTNode fc7424bb-62e3-11ef-9101-5c879c15de65
----------------------------
REF: velocity_reference Variable | 0
PER: constant_perception Constant | 10
COM: subtract Subtract | 0 | links velocity_reference constant_perception
OUT: integrator Integration | gain 10 slow 100 | 0 | links subtract
----------------------------
#print(integratingnode.get_config())
assert integratingnode.get_config() == {'type': 'PCTNode', 'name': 'integratingnode', 'refcoll': {'0': {'type': 'Variable', 'name': 'velocity_reference', 'value': 0, 'links': {}}}, 'percoll': {'0': {'type': 'Constant', 'name': 'constant_perception', 'value': 10, 'links': {}}}, 'comcoll': {'0': {'type': 'Subtract', 'name': 'subtract', 'value': 0, 'links': {0: 'velocity_reference', 1: 'constant_perception'}}}, 'outcoll': {'0': {'type': 'Integration', 'name': 'integrator', 'value': 0, 'links': {0: 'subtract'}, 'gain': 10, 'slow': 100}}}
integratingnode.get_config()
{'type': 'PCTNode',
'name': 'integratingnode',
'refcoll': {'0': {'type': 'Variable',
'name': 'velocity_reference',
'value': 0,
'links': {}}},
'percoll': {'0': {'type': 'Constant',
'name': 'constant_perception',
'value': 10,
'links': {}}},
'comcoll': {'0': {'type': 'Subtract',
'name': 'subtract',
'value': 0,
'links': {0: 'velocity_reference', 1: 'constant_perception'}}},
'outcoll': {'0': {'type': 'Integration',
'name': 'integrator',
'value': 0,
'links': {0: 'subtract'},
'gain': 10,
'slow': 100}}}
A node can also be viewed graphically as a network of connected nodes.
For the purposes of this example we first create a function which is a very basic model of the physical environment. It defines how the world behaves when we pass it the output of the control system.
In the following cell we start with a velocity of zero. The node is run once (second line), the output of which is the force to apply in the world velocity_model. That returns the updated velocity which we pass back into the node to be used in the next iteration of the loop.
velocity=0
force = node()
velocity = velocity_model(velocity, force, mass)
node.set_perception_value(velocity)
print(force)
assert force == 10
10
The node can be run in a loop as shown below. With verbose set to True the output of each loop will be printed to the screen.
pctnode = PCTNode(history=True)
pctnode.set_function_name("perception", "velocity")
pctnode.set_function_name("reference", "reference")
for i in range(40):
print(i, end=" ")
force = pctnode(verbose=True)
vel = velocity_model(pctnode.get_perception_value(), force, mass)
pctnode.set_perception_value(vel)
0 0.000 0.000 0.000 0.000
1 0.000 0.000 0.000 0.000
2 0.000 0.000 0.000 0.000
3 0.000 0.000 0.000 0.000
4 0.000 0.000 0.000 0.000
5 0.000 0.000 0.000 0.000
6 0.000 0.000 0.000 0.000
7 0.000 0.000 0.000 0.000
8 0.000 0.000 0.000 0.000
9 0.000 0.000 0.000 0.000
10 0.000 0.000 0.000 0.000
11 0.000 0.000 0.000 0.000
12 0.000 0.000 0.000 0.000
13 0.000 0.000 0.000 0.000
14 0.000 0.000 0.000 0.000
15 0.000 0.000 0.000 0.000
16 0.000 0.000 0.000 0.000
17 0.000 0.000 0.000 0.000
18 0.000 0.000 0.000 0.000
19 0.000 0.000 0.000 0.000
20 0.000 0.000 0.000 0.000
21 0.000 0.000 0.000 0.000
22 0.000 0.000 0.000 0.000
23 0.000 0.000 0.000 0.000
24 0.000 0.000 0.000 0.000
25 0.000 0.000 0.000 0.000
26 0.000 0.000 0.000 0.000
27 0.000 0.000 0.000 0.000
28 0.000 0.000 0.000 0.000
29 0.000 0.000 0.000 0.000
30 0.000 0.000 0.000 0.000
31 0.000 0.000 0.000 0.000
32 0.000 0.000 0.000 0.000
33 0.000 0.000 0.000 0.000
34 0.000 0.000 0.000 0.000
35 0.000 0.000 0.000 0.000
36 0.000 0.000 0.000 0.000
37 0.000 0.000 0.000 0.000
38 0.000 0.000 0.000 0.000
39 0.000 0.000 0.000 0.000
Save a node to file.
Create a node from file.
integratingnode PCTNode fcd83c93-62e3-11ef-9c4b-5c879c15de65
----------------------------
REF: velocity_reference Variable | 0
PER: constant_perception Constant | 10
COM: subtract Subtract | 0 | links velocity_reference constant_perception
OUT: integrator Integration | gain 10 slow 100 | 0 | links subtract
----------------------------
{'type': 'PCTNode', 'name': 'integratingnode', 'refcoll': {'0': {'type': 'Variable', 'name': 'velocity_reference', 'value': 0, 'links': {}}}, 'percoll': {'0': {'type': 'Constant', 'name': 'constant_perception', 'value': 10, 'links': {}}}, 'comcoll': {'0': {'type': 'Subtract', 'name': 'subtract', 'value': 0, 'links': {0: 'velocity_reference', 1: 'constant_perception'}}}, 'outcoll': {'0': {'type': 'Integration', 'name': 'integrator', 'value': 0, 'links': {0: 'subtract'}, 'gain': 10, 'slow': 100}}}
As the history of the variable pctnode was set to True the data is available for analysis. It can be plotted with python libraries such as matplotlib or plotly. Here is an example with the latter.
The graph shows the changing perception values as it is controlled to match the reference value.
import plotly.graph_objects as go
fig = go.Figure(layout_title_text="Velocity Goal")
fig.add_trace(go.Scatter(y=pctnode.history.data['refcoll']['reference'], name="ref"))
fig.add_trace(go.Scatter(y=pctnode.history.data['percoll']['velocity'], name="perc"))
This following code is only for the purposes of displaying image of the graph generated by the above code.