Source code for soft4pes.control.mpc.controllers.lcl_vc_mpc_ctr
"""
Model predictive control (MPC) for the control of the capacitor voltage.
"""
from types import SimpleNamespace
import numpy as np
from soft4pes.utils import dq_2_alpha_beta
from soft4pes.control.common.controller import Controller
[docs]class LCLVcMpcCtr(Controller):
"""
Model predictive control (MPC) for the control of the capacitor voltage.
Parameters
----------
solver : solver object
Solver for an MPC algorithm.
lambda_u : float
Weighting factor for the control effort.
Np : int
Prediction horizon steps.
disc_method : str, optional
Discretization method for the state-space model. Default is 'exact_discretization'.
I_conv_max : float
Maximum converter current [p.u.].
xi_I_conv : float
Slack variable weight for the current constraint.
Attributes
----------
solver : solver object
Solver for MPC.
lambda_u : float
Weighting factor for the control effort.
Np : int
Prediction horizon.
disc_method : str
Discretization method for the state-space model.
u_km1_abc : 1 x 3 ndarray of floats
Previous (step k-1) three-phase switch position or modulating signal.
state_space : SimpleNamespace
The state-space model of the system.
vg : 1 x 2 ndarray of floats
Grid voltage [p.u.].
R : 1 x 1 ndarray of floats
Weight matrix for the soft constraints.
c : 1 x 1 ndarray of floats
State constraints.
C_constr : 2 x 6 ndarray of ints
Output matrix for the constrained states.
C : 2 x 6 ndarray of ints
System output matrix.
Q : 2 x 2 ndarray of ints
Weighting matrix for the output variables.
"""
def __init__(self,
solver,
lambda_u,
Np,
I_conv_max=1.2,
xi_I_conv=1e6,
disc_method='exact_discretization'):
super().__init__()
[docs] self.lambda_u = lambda_u
[docs] self.disc_method = disc_method
[docs] self.u_km1_abc = np.array([0, 0, 0])
[docs] self.state_space = None
[docs] self.vg = np.array([0, 0])
# Weight matrix for the constraints (R) and vector for state constraints (c)
[docs] self.R = np.array([[xi_I_conv]])
[docs] self.c = np.array([[I_conv_max]])
# Output matrix for the constrained states
[docs] self.C_constr = np.block(
[np.eye(2), np.zeros((2, 2)),
np.zeros((2, 2))])
# Output matrix (C) and weighting matrix (Q) for the output variables
[docs] self.C = np.block([[np.zeros((2, 2)), np.zeros((2, 2)), np.eye(2)]])
[docs] def execute(self, sys, kTs):
"""
Perform MPC and save the controller data.
Parameters
----------
sys : system object
System model.
kTs : float
Current discrete time instant [s].
Returns
-------
SimpleNamespace
SimpleNameSpace containing the converter three-phase switch position or modulating
signal.
"""
# Get the discrete state-space model of the system
if self.state_space is None:
self.state_space = sys.get_discrete_state_space(
self.Ts, self.disc_method)
# Get the grid voltage and save it for future use
self.vg = sys.get_grid_voltage(kTs)
# Get the reference at step k
vc_ref_dq = self.input.vc_ref_dq
# Get the grid-voltage angle and calculate the reference in alpha-beta frame
theta = np.arctan2(self.vg[1], self.vg[0])
vc_ref = dq_2_alpha_beta(vc_ref_dq, theta)
# Make a rotation matrix
Ts_pu = self.Ts * sys.base.w
delta_theta = sys.par.wg * Ts_pu
R_rot = np.array([[np.cos(delta_theta), -np.sin(delta_theta)], \
[np.sin(delta_theta), np.cos(delta_theta)]])
# Predict the output (capacitor voltage) reference within the horizon based on the reference
# value at step k
y_ref = np.zeros((self.Np + 1, 2))
y_ref[0, :] = vc_ref
for ell in range(self.Np):
y_ref[ell + 1, :] = np.dot(R_rot, y_ref[ell, :])
# Solve the control problem
u_abc = self.solver(sys, self, y_ref)
self.u_km1_abc = u_abc
self.output = SimpleNamespace(u_abc=u_abc)
return self.output
[docs] def get_next_state(self, sys, xk, u_abc, k):
"""
Get the next state of the system.
Parameters
----------
sys : system object
The system model.
xk : 1 x 6 ndarray of floats
The current state of the system.
u_abc : 1 x 3 ndarray of floats
Converter three-phase switch position or modulating signal.
k : int
The solver prediction step.
Returns
-------
1 x 6 ndarray of floats
The next state of the system.
"""
# Get the grid voltage at step k by rotating it
Ts_pu = self.Ts * sys.base.w
delta_theta = k * sys.par.wg * Ts_pu
R = np.array([[np.cos(delta_theta), -np.sin(delta_theta)], \
[np.sin(delta_theta), np.cos(delta_theta)]])
vg_k = np.dot(R, self.vg)
return np.dot(self.state_space.A, xk) + np.dot(
self.state_space.B1, u_abc) + np.dot(self.state_space.B2, vg_k)