Source code for direct_data_driven_mpc.utilities.controller.initial_data_generation
"""
Functions for generating initial input-output data from a system.
This module provides functions for generating input-output data from both LTI
and nonlinear systems, and randomizing the initial state of LTI systems.
"""
import numpy as np
from numpy.random import Generator
from direct_data_driven_mpc.utilities.controller.controller_params import (
DataDrivenMPCParams,
LTIDataDrivenMPCParams,
)
from direct_data_driven_mpc.utilities.models.lti_model import LTIModel
from direct_data_driven_mpc.utilities.models.nonlinear_model import (
NonlinearSystem,
)
[docs]
def randomize_initial_system_state(
system_model: LTIModel,
controller_config: LTIDataDrivenMPCParams,
np_random: Generator,
) -> np.ndarray:
"""
Randomly generate a plausible initial state for a Linear Time-Invariant
(LTI) system model.
This function initializes the system state with random values within the
[-1, 1] range. Afterward, it simulates the system using random input and
noise sequences to generate an input-output trajectory, which is then used
to estimate the initial system state.
Note:
The random input sequence is generated based on the `u_range`
parameter from the controller configuration (`controller_config`). The
noise sequence is generated considering the defined noise bounds from
the system.
Args:
system_model (LTIModel): An `LTIModel` instance representing a Linear
Time-Invariant (LTI) system.
controller_config (LTIDataDrivenMPCParams): A dictionary containing
parameters for a Data-Driven MPC controller designed for Linear
Time-Invariant (LTI) systems, including the range of the
persistently exciting input (`u_range`).
np_random (Generator): A Numpy random number generator for generating
the random initial system state, persistently exciting input, and
system output noise.
Returns:
np.ndarray: A vector of shape `(n, )` representing the estimated
initial state of the system, where `n` is the system's order.
"""
# Retrieve model parameters
ns = system_model.n # System order (simulation)
m = system_model.m # Number of inputs
p = system_model.p # Number of outputs
eps_max_sim = system_model.eps_max # Upper bound of the system
# measurement noise (simulation)
# Retrieve Data-Driven MPC controller parameters
u_range = controller_config["u_range"] # Range of the persistently
# exciting input u_d
# Randomize initial system state before excitation
x_i0 = np_random.uniform(-1.0, 1.0, size=ns)
assert isinstance(x_i0, np.ndarray) # Prevent mypy [arg-type] error
system_model.set_state(state=x_i0)
# Generate a random input array
u_i = np.hstack(
[
np_random.uniform(u_range[i, 0], u_range[i, 1], (ns, 1))
for i in range(m)
]
)
# Generate bounded uniformly distributed additive measurement noise
w_i = eps_max_sim * np_random.uniform(-1.0, 1.0, (ns, p))
# Simulate the system with the generated random input and noise
# sequences to obtain output data
y_i = system_model.simulate(U=u_i, W=w_i, steps=ns)
# Calculate the initial state of the system
# from the input-output trajectory
x_0 = system_model.get_initial_state_from_trajectory(
U=u_i.flatten(), Y=y_i.flatten()
)
return x_0
[docs]
def generate_initial_input_output_data(
system_model: LTIModel | NonlinearSystem,
controller_config: DataDrivenMPCParams,
np_random: Generator,
) -> tuple[np.ndarray, np.ndarray]:
"""
Generate input-output trajectory data from a system using Data-Driven MPC
controller parameters.
This function generates a persistently exciting input `u_d` and random
noise based on the specified controller and system parameters. Then, it
simulates the system using these input and noise sequences to generate the
output response `y_d`. The resulting `u_d` and `y_d` arrays represent the
input-output trajectory data measured from the system, which is necessary
for system characterization in a Data-Driven MPC formulation.
Args:
system_model (LTIModel | NonlinearSystem): An instance of `LTIModel`,
representing a Linear Time-Invariant (LTI) system, or
`NonlinearSystem`, representing a nonlinear system.
controller_config (DataDrivenMPCParams): A dictionary containing
parameters for a Data-Driven MPC controller designed for Linear
Time-Invariant (LTI) or nonlinear systems. Includes the initial
input-output trajectory length (`N`) and the range of the
persistently exciting input (`u_range`).
np_random (Generator): A Numpy random number generator for generating
the persistently exciting input and random noise for the system's
output.
Returns:
tuple[np.ndarray, np.ndarray]: A tuple containing two arrays: a
persistently exciting input (`u_d`) and the system's output response
(`y_d`). The input array has shape `(N, m)` and the output array has
shape `(N, p)`, where `N` is the trajectory length, `m` is the number
of control inputs, and `p` is the number of system outputs.
"""
# Retrieve model parameters
m = system_model.m # Number of inputs
p = system_model.p # Number of outputs
eps_max_sim = system_model.eps_max # Upper bound of the system
# measurement noise (simulation)
# Retrieve Data-Driven MPC controller parameters
N = controller_config["N"] # Initial input-output trajectory length
u_range = controller_config["u_range"] # Range of the persistently
# exciting input u_d
# Generate a persistently exciting input `u_d` from 0 to (N - 1)
u_d = np.hstack(
[
np_random.uniform(u_range[i, 0], u_range[i, 1], (N, 1))
for i in range(m)
]
)
# Generate bounded uniformly distributed additive measurement noise
w_d = eps_max_sim * np_random.uniform(-1.0, 1.0, (N, p))
# Simulate the system with the persistently exciting input `u_d` and
# the generated noise sequence to obtain output data
y_d = system_model.simulate(U=u_d, W=w_d, steps=N)
return u_d, y_d
[docs]
def simulate_n_input_output_measurements(
system_model: LTIModel,
controller_config: LTIDataDrivenMPCParams,
np_random: Generator,
) -> tuple[np.ndarray, np.ndarray]:
"""
Simulate a control input setpoint applied to a system over `n` (the
estimated system order) time steps and return the resulting input-output
data sequences.
This function retrieves the control input setpoint (`u_s`) and the
estimated system order (`n`) from a Data-Driven MPC controller
configuration. Then, it simulates the system using a constant input `u_s`
and random output noise over `n` time steps. The resulting input-output
trajectory can be used to update the past `n` input-output measurements
of a previously initialized Data-Driven MPC controller, allowing it to
operate on a system with a different state.
Note:
This function is used for scenarios where a Data-Driven MPC controller
has been initialized but needs to be adjusted to match different
system states.
Args:
system_model (LTIModel): An `LTIModel` instance representing a Linear
Time-Invariant (LTI) system.
controller_config (LTIDataDrivenMPCParams): A dictionary containing
parameters for a Data-Driven MPC controller designed for Linear
Time-Invariant (LTI) systems, including the estimated system order
(`n`) and the control input setpoint (`u_s`).
np_random (Generator): A Numpy random number generator for generating
random noise for the system's output.
Returns:
tuple[np.ndarray, np.ndarray]: A tuple containing two arrays:
- An array of shape `(n, m)` representing the constant input setpoint
applied to the system over `n` time steps, where `n` is the system
order and `m` is the number of control inputs.
- An array of shape `(n, p)` representing the output response of the
system, where `n` is the system order and `p` is the number of system
outputs.
"""
# Retrieve model parameters
m = system_model.m # Number of inputs
p = system_model.p # Number of outputs
eps_max_sim = system_model.eps_max # Upper bound of the system
# measurement noise
# Retrieve Data-Driven MPC controller parameters
n = controller_config["n"] # Estimated system order
u_s = controller_config["u_s"] # Control input setpoint
# Construct input array from controller's input setpoint
U_n = np.tile(u_s, (n, 1)).reshape(n, m)
# Generate bounded uniformly distributed additive measurement noise
W_n = eps_max_sim * np_random.uniform(-1.0, 1.0, (n, p))
# Simulate the system with the constant input and generated
# noise sequences
Y_n = system_model.simulate(U=U_n, W=W_n, steps=n)
return U_n, Y_n