StateSpaceShield Class

class automationshield.StateSpaceShield(n_states: int, discretisation: str | None = None)[source]

Bases: BaseDummyShield

This class can be used to simulate a general state-space model and control it using a automationshield.ShieldController instance. The state-space system must be a SISO system.

Create a class that inherits from StateSpaceShield and override the following methods:

  • calculate_a(), calculate_b(), calculate_c(), calculate_d(): These methods should return state-space matrices A, B, C and D, respectively. They take the current state vector as input, which makes it possible to update the matrices depending on the current state of the system. This way, a non-linear system can be simulated accurately.

  • Optional: get_equilibrium_point(): Override this method when you’re including non-linear behaviour. This method should return the equilibrium state and required input to maintain this state.

Parameters:
  • n_states (int) – Number of states of the system.

  • discretisation (str) – Type of discretisation to use. Must be one of EXACT, EULER or TUSTIN; defaults to TUSTIN.

EXACT = 'exact'

Set discretisation to this constant for exact discretisation.

EULER = 'euler'

Set discretisation to this constant for Euler discretisation.

TUSTIN = 'tustin'

Set discretisation to this constant for Tustin discretisation.

x: ndarray[tuple[int, ...], dtype[float64]]

State vector of shape (n_states, 1).

u: ndarray[tuple[int, ...], dtype[float64]]

Input vector of shape (1, 1).

t0: float

Time of last step. Used to calculate real time step sizes.

property discretisation: str

Get or set discretisation method used. Must be one of EXACT, EULER or TUSTIN. In order to use EXACT, Scipy must be installed.

Raises:

RunTimeError – When trying to set EXACT without Scipy installed.

calculate_a(state: ndarray[tuple[int, ...], dtype[float64]]) ndarray[tuple[int, ...], dtype[float64]][source]

Calculate A matrix. This method must be implemented on a subclass.

Parameters:

state (ndarray[tuple[int, ...], dtype[float64]]) – Current state of the system.

Raises:

NotImplementedError – When method is not overridden on the subclass.

Returns:

Numpy array. Must be a 2D array of shape (n_states, n_states).

Return type:

ndarray[tuple[int, …], dtype[float64]]

calculate_b(state: ndarray[tuple[int, ...], dtype[float64]]) ndarray[tuple[int, ...], dtype[float64]][source]

Calculate B matrix. This method must be implemented on a subclass.

Parameters:

state (ndarray[tuple[int, ...], dtype[float64]]) – Current state of the system.

Raises:

NotImplementedError – When method is not overridden on the subclass.

Returns:

Numpy array. Must be a 2D array of shape (n_states, 1).

Return type:

ndarray[tuple[int, …], dtype[float64]]

calculate_c(state: ndarray[tuple[int, ...], dtype[float64]]) ndarray[tuple[int, ...], dtype[float64]][source]

Calculate C matrix. This method must be implemented on a subclass.

Parameters:

state (ndarray[tuple[int, ...], dtype[float64]]) – Current state of the system.

Raises:

NotImplementedError – When method is not overridden on the subclass.

Returns:

Numpy array. Must be a 2D array of shape (1, n_states).

Return type:

ndarray[tuple[int, …], dtype[float64]]

calculate_d(state: ndarray[tuple[int, ...], dtype[float64]]) ndarray[tuple[int, ...], dtype[float64]][source]

Calculate A matrix. This method must be implemented on a subclass.

Parameters:

state (ndarray[tuple[int, ...], dtype[float64]]) – Current state of the system.

Raises:

NotImplementedError – When method is not overridden on the subclass.

Returns:

Numpy array. Must be a 2D array of shape (1, 1).

Return type:

ndarray[tuple[int, …], dtype[float64]]

get_equilibrium_point(state: ndarray[tuple[int, ...], dtype[float64]]) tuple[ndarray[tuple[int, ...], dtype[float64]], float][source]

Return equilibrium input and state for the current state. Used for linearising at the current state. By default, the equilibrium state and input are returned as (0, 0), in which case this method has no effect.

Parameters:

state (ndarray[tuple[int, ...], dtype[float64]]) – Current state.

Returns:

Equilibrium state and input, respectively.

Return type:

tuple[ndarray[tuple[int, …], dtype[float64]], float]

discretise(a: ndarray[tuple[int, ...], dtype[float64]], b: ndarray[tuple[int, ...], dtype[float64]], dt: float) tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]][source]

Calculate discrete-time matrices \(A_D\) and \(B_D\) from their continuous-time counterpart obtained from calculate_a() and calculate_b(), respectively, using the discretisation method set.

Parameters:
Returns:

Discrete-time matrices \(A_D\) and \(B_D\).

Return type:

tuple[ndarray[tuple[int, …], dtype[float64]], ndarray[tuple[int, …], dtype[float64]]]

discrete_exact(a: ndarray[tuple[int, ...], dtype[float64]], b: ndarray[tuple[int, ...], dtype[float64]], dt: float) tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]][source]

Calculate discrete-time matrices \(A_D\) and \(B_D\) from their continuous-time counterpart obtained from calculate_a() and calculate_b(), respectively, using the exact discretisation method.

\[\begin{split}A_D &= e^{A \cdot dt} \\ B_D &= A^{-1} \cdot \left(A_D - I \right) \cdot B\end{split}\]
Parameters:
Returns:

Discrete-time matrices \(A_D\) and \(B_D\).

Return type:

tuple[ndarray[tuple[int, …], dtype[float64]], ndarray[tuple[int, …], dtype[float64]]]

discrete_tustin(a: ndarray[tuple[int, ...], dtype[float64]], b: ndarray[tuple[int, ...], dtype[float64]], dt: float) tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]][source]

Calculate discrete-time matrices \(A_D\) and \(B_D\) from their continuous-time counterpart obtained from calculate_a() and calculate_b(), respectively, using the Tustin discretisation method.

\[\begin{split}A_D &= \left(I + \frac{A \cdot dt}{2} \right) \cdot \left( I - \frac{A \cdot dt}{2} \right)^{-1} \\ B_D &= A^{-1} \cdot \left(A_D - I \right) \cdot B\end{split}\]
Parameters:
Returns:

Discrete-time matrices \(A_D\) and \(B_D\).

Return type:

tuple[ndarray[tuple[int, …], dtype[float64]], ndarray[tuple[int, …], dtype[float64]]]

discrete_euler(a: ndarray[tuple[int, ...], dtype[float64]], b: ndarray[tuple[int, ...], dtype[float64]], dt: float) tuple[ndarray[tuple[int, ...], dtype[float64]], ndarray[tuple[int, ...], dtype[float64]]][source]

Calculate discrete-time matrices \(A_D\) and \(B_D\) from their continuous-time counterpart obtained from calculate_a() and calculate_b(), respectively, using the forward Euler discretisation method.

\[\begin{split}A_D &= I + A \cdot dt \\ B_D &= B \cdot dt\end{split}\]
Parameters:
Returns:

Discrete-time matrices \(A_D\) and \(B_D\).

Return type:

tuple[ndarray[tuple[int, …], dtype[float64]], ndarray[tuple[int, …], dtype[float64]]]

condition_input(input: float) float[source]

Condition the input before applying it to the system. Override this method in your subclass to customise how the input is conditioned. By default, the input is returned unchanged.

Parameters:

input (float) – Input value.

Returns:

Conditioned input value.

Return type:

float

read() tuple[int, float][source]

The automationshield.shields.BaseShield.read() method is overridden to replicate the behaviour from a physical shield. This method calculates the output of the system and returns it. The output corresponding to the potentiometer value of a physical shield is set to 0.

\[y = C \cdot x + D \cdot u\]
Returns:

Output of the system.

Return type:

tuple[int, float]

write(flag: int, input: float) float[source]

The automationshield.shields.BaseShield.read() method is overridden to replicate the behaviour from a physical shield. This method does the following:

  • Update the time step;

  • Calculate the equilibrium point input \(u_e\) and state \(x_e\);

  • Calculate the discrete-time system and input matrices \(A_D\) and \(B_D\);

  • Update the state vector using the equation below.

\[x_{k+1} = x_e + A_D \cdot \left( x_k - x_{e,k} \right) + B_D \cdot \left( u_k - u_{e,k} \right)\]

Finally, the input is update with the conditioned input value. Conditioning is performed through StateSpaceShield.condition_input().

Parameters:
  • flag (int) – Run flag for a physical shield. Ignored.

  • input (float) – Input value.

Returns:

Conditioned input value.

Return type:

float