Simulating a System Using a Shield Interface

The StateSpaceShield class makes it possible to simulate SISO systems in the framework of this package. You can create a subclass, define $A$, $B$, $C$ and $D$ matrices and run an experiment the same way as you would for one of the physical device classes. Optionally, you can locally linearise the system during runtime to integrate non-linear behaviour. This example will go through the steps required to set up a custom state-space system as a Shield interface.

1%%capture
2
3import numpy as np
4import scipy as sp
5
6from automationshield import StateSpaceShield, ShieldController
7from automationshield.plotting import Plotter

Simple Example

First, we create a new class that inherits from StateSpaceShield. We override the methods to calculate the matrices $A$, $B$, $C$ and $D$. In this case, the matrices don’t depend on the current state, i.e. the system is assumed to be perfectly linear.

 1class MySimShield(StateSpaceShield):
 2    def calculate_a(self, state):
 3        return np.array(
 4            [[-6, -25],
 5             [ 1,  0 ]]
 6        )
 7
 8    def calculate_b(self, state):
 9        return np.array(
10            [[1],
11             [0]]
12        )
13
14    def calculate_c(self, state):
15        return np.array([[1, 0]])
16
17    def calculate_d(self, state):
18        return np.array([[0]])

We also create a controller giving a unit step input

1class MyController(ShieldController):
2    def controller(self, t: float, dt: float, ref: float, pot: float, sensor: float) -> float:
3        return 1

Running a simulation is done the exact same way as for a physical shield device. You can optionally set the discretisation method to discretise the continuous-time matrices. In this case, the Tustin method is used, which is also the default. For other options, refer to the documentation.

1my_shield = MySimShield(n_states=2, discretisation=MySimShield.TUSTIN)
2controller = MyController(shield=my_shield)
3
4freq = 50
5cycles = 4*freq
6
7results = controller.run(freq=freq, cycles=cycles)
8
9fig, ax = Plotter(show_ref=False, show_dt=False).plot(results)

Advanced Example

For a more advanced example, check out the source code of the AeroShieldMimic class. The code shows how to use the get_equilibrium_point method to calculate an equilibrium each loop and linearise around it and the condition_input method to condition the input before passing it to the system.

get_equilibrium_point Method

In the example of the AeroShieldMimic, this method calculates the input required to hold the pendulum at the current angle, using an experimentally established polynomial. It then constructs the equilibrium state, which is the state for the system at rest at the current angle.

condition_input Method

By default, whatever input is passed from the controller is applied as-is. However, the AeroShield devices cannot provide negative inputs, and for compatibility, the input should be in percent. This is handled by calling the saturate_percent method, which is also used for the physical AeroShield.