Skip to content

Commit

Permalink
Refactor tge DC-bus voltage controller
Browse files Browse the repository at this point in the history
  • Loading branch information
mhinkkan committed Sep 4, 2024
1 parent ef0ef2f commit 177b9a5
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 45 deletions.
7 changes: 5 additions & 2 deletions examples/grid/grid_following/plot_gfl_dc_bus_10kva.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"""

# %%
import numpy as np

from motulator.grid import model, control
from motulator.grid.utils import (
BaseValues, ACFilterPars, NominalValues, plot)
Expand Down Expand Up @@ -38,11 +40,12 @@

# Create the control system
cfg = control.GFLControlCfg(
L=.2*base.L, nom_u=base.u, nom_w=base.w, max_i=1.5*base.i, C_dc=1e-3)
L=.2*base.L, nom_u=base.u, nom_w=base.w, max_i=1.5*base.i)
ctrl = control.GFLControl(cfg)

# Add the DC-bus voltage controller to the control system
ctrl.dc_bus_volt_ctrl = control.DCBusVoltageController(p_max=base.p)
ctrl.dc_bus_voltage_ctrl = control.DCBusVoltageController(
C_dc=1e-3, alpha_dc=2*np.pi*30, max_p=base.p)

# %%
# Set the time-dependent reference and disturbance signals.
Expand Down
5 changes: 2 additions & 3 deletions motulator/drive/control/sm/_current_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,13 @@ def __init__(self, par, alpha_c):
self.L_d = par.L_d
self.L_q = par.L_q

def output(self, ref_i, i):
def output(self, ref_i, i, u_ff=0):
# Extends the base class method by transforming the currents to the
# flux linkages, which is a simple way to take the saliency into
# account
ref_psi = self.L_d*ref_i.real + 1j*self.L_q*ref_i.imag
psi = self.L_d*i.real + 1j*self.L_q*i.imag

return super().output(ref_psi, psi)
return super().output(ref_psi, psi, u_ff)


# %%
Expand Down
56 changes: 30 additions & 26 deletions motulator/grid/control/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,23 @@ def update(self, T_s, fbk):
# %%
class DCBusVoltageController(PIController):
"""
DC-bus voltage controller.
PI controller for the DC-bus voltage.
This provides an interface for a DC-bus voltage controller. The gains are
initialized based on the desired closed-loop bandwidth and the DC-bus
capacitance estimate. The controller regulates the square of the DC-bus
voltage in order to have a linear closed-loop system [#Hur2001]_.
This is a PI controller for the DC-bus voltage. The controller regulates
the energy stored in the DC-bus capacitor (scaled square of the DC-bus
voltage) in order to have a linear closed-loop system [#Hur2001]_. The
gains are initialized based on the desired closed-loop bandwidth.
Parameters
----------
C_dc : float
DC-bus capacitance (F).
alpha_dc : float
Closed-loop bandwidth (rad/s).
zeta : float, optional
Damping ratio of the closed-loop system. The default is 1.
alpha_dc : float, optional
Closed-loop bandwidth (rad/s). The default is 2*np.pi*30.
p_max : float, optional
Maximum converter power (W). The default is `inf`.
max_p : float, optional
Limit for the maximum converter power (W). The default is `inf`.
References
----------
Expand All @@ -84,11 +86,18 @@ class DCBusVoltageController(PIController):
"""

def __init__(self, zeta=1, alpha_dc=2*np.pi*30, p_max=np.inf):
k_p = -2*zeta*alpha_dc
k_i = -alpha_dc**2
def __init__(self, C_dc, alpha_dc, zeta=1, max_p=np.inf):
k_p, k_i = -2*zeta*alpha_dc, -alpha_dc**2
k_t = k_p
super().__init__(k_p, k_i, k_t, p_max)
super().__init__(k_p, k_i, k_t, max_p)
self.C_dc = C_dc

def output(self, ref_u_dc, u_dc, u_ff=0):
# pylint: disable=arguments-renamed
# Extends the base class method by transforming the
ref_W_dc = .5*self.C_dc*ref_u_dc**2
W_dc = .5*self.C_dc*u_dc**2
return super().output(ref_W_dc, W_dc, u_ff)


# %%
Expand All @@ -102,8 +111,6 @@ class GridConverterControlSystem(ControlSystem, ABC):
Parameters
----------
C_dc : float, optional
DC-bus capacitance (F). The default is None.
T_s : float
Sampling period (s).
Expand All @@ -125,15 +132,14 @@ class GridConverterControlSystem(ControlSystem, ABC):
DC-voltage reference (V) as a function of time (s). This signal
is needed in DC-bus voltage control mode.
dc_bus_volt_ctrl : DCBusVoltageController | None
dc_bus_voltage_ctrl : DCBusVoltageController | None
DC-bus voltage controller. The default is None.
"""

def __init__(self, C_dc, T_s):
def __init__(self, T_s):
super().__init__(T_s)
self.C_dc = C_dc
self.dc_bus_volt_ctrl = None
self.dc_bus_voltage_ctrl = None
self.pwm = PWM(overmodulation="MPE")
self.ref = SimpleNamespace()

Expand Down Expand Up @@ -203,15 +209,13 @@ def get_power_reference(self, fbk, ref):
Reactive power reference (VAr).
"""
if self.dc_bus_volt_ctrl:
if self.dc_bus_voltage_ctrl:
# DC-bus voltage control mode
ref.u_dc = self.ref.u_dc(ref.t)
ref_W_dc = .5*self.C_dc*ref.u_dc**2
W_dc = .5*self.C_dc*fbk.u_dc**2
ref.p_g = self.dc_bus_volt_ctrl.output(ref_W_dc, W_dc)
ref.p_g = self.dc_bus_voltage_ctrl.output(ref.u_dc, fbk.u_dc)
else:
# Power control mode
ref.u_dc = None
#ref.u_dc = None
ref.p_g = self.ref.p_g(ref.t)

# Reactive power reference
Expand All @@ -223,8 +227,8 @@ def get_power_reference(self, fbk, ref):
def update(self, fbk, ref):
"""Extend the base class method."""
super().update(fbk, ref)
if self.dc_bus_volt_ctrl:
self.dc_bus_volt_ctrl.update(ref.T_s, ref.p_g)
if self.dc_bus_voltage_ctrl:
self.dc_bus_voltage_ctrl.update(ref.T_s, ref.p_g)


# %%
Expand Down
5 changes: 1 addition & 4 deletions motulator/grid/control/_grid_following.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ class GFLControlCfg:
Current-control bandwidth (rad/s). The default is 2*pi*400.
alpha_pll : float, optional
PLL frequency-tracking bandwidth (rad/s). The default is 2*pi*20.
C_dc : float, optional
DC-bus capacitance (F). The default is None.
"""
L: float
Expand All @@ -42,7 +40,6 @@ class GFLControlCfg:
T_s: float = 100e-6
alpha_c: float = 2*np.pi*400
alpha_pll: float = 2*np.pi*20
C_dc: float = None


# %%
Expand All @@ -66,7 +63,7 @@ class GFLControl(GridConverterControlSystem):
"""

def __init__(self, cfg):
super().__init__(cfg.C_dc, cfg.T_s)
super().__init__(cfg.T_s)
self.cfg = cfg
self.current_ctrl = CurrentController(cfg)
self.pll = PLL(cfg.alpha_pll, cfg.nom_u, cfg.nom_w)
Expand Down
7 changes: 2 additions & 5 deletions motulator/grid/control/_observer_gfm.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ class ObserverBasedGFMControlCfg:
R : float, optional
Total series resistance (Ω). The default is 0.
R_a : float, optional
Active resistance (Ω). The default is 0.25*num_u/max_i.
Active resistance (Ω). The default is 0.25*nom_u/max_i.
T_s : float, optional
Sampling period of the controller (s). The default is 100e-6.
alpha_c : float, optional
Current control bandwidth (rad/s). The default is 2*pi*400.
alpha_o : float, optional
Observer gain (rad/s). The default is 2*pi*50.
C_dc : float, optional
DC-bus capacitance (F). The default is None.
"""
L: float
Expand All @@ -47,7 +45,6 @@ class ObserverBasedGFMControlCfg:
T_s: float = 100e-6
alpha_c: float = 2*np.pi*400
alpha_o: float = 2*np.pi*50
C_dc: float = None

def __post_init__(self):
if self.R_a is None:
Expand Down Expand Up @@ -84,7 +81,7 @@ class ObserverBasedGFMControl(GridConverterControlSystem):
"""

def __init__(self, cfg):
super().__init__(cfg.C_dc, cfg.T_s)
super().__init__(cfg.T_s)
self.cfg = cfg
self.observer = DisturbanceObserver(
cfg.nom_w, cfg.L, cfg.alpha_o, cfg.nom_u)
Expand Down
7 changes: 2 additions & 5 deletions motulator/grid/control/_power_synchronization.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ class RFPSCControlCfg:
max_i : float
Maximum current (A), peak value.
R_a : float, optional
Active resistance (Ω). The default is 0.25*num_u/max_i.
Active resistance (Ω). The default is 0.25*nom_u/max_i.
T_s : float, optional
Sampling period of the controller (s). The default is 100e-6.
w_b : float, optional
Current low-pass filter bandwidth (rad/s). The default is 2*pi*5.
C_dc : float, optional
DC-bus capacitance (F). The default is None.
"""
nom_u: float
Expand All @@ -39,7 +37,6 @@ class RFPSCControlCfg:
R_a: float = None
T_s: float = 100e-6
w_b: float = 2*np.pi*5
C_dc: float = None

def __post_init__(self):
if self.R_a is None:
Expand Down Expand Up @@ -69,7 +66,7 @@ class RFPSCControl(GridConverterControlSystem):
"""

def __init__(self, cfg):
super().__init__(cfg.C_dc, cfg.T_s)
super().__init__(cfg.T_s)
self.cfg = cfg
self.current_limiter = CurrentLimiter(cfg.max_i)
self.ref.q_g = 0
Expand Down

0 comments on commit 177b9a5

Please sign in to comment.