Skip to content

Commit 8780bdc

Browse files
committed
add feedfwd keyword arguments (no functionality yet)
1 parent 12dda4e commit 8780bdc

1 file changed

Lines changed: 62 additions & 27 deletions

File tree

control/statefbk.py

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,25 @@
3939
#
4040
# $Id$
4141

42-
# External packages and modules
42+
import warnings
43+
4344
import numpy as np
4445
import scipy as sp
45-
import warnings
4646

4747
from . import statesp
48-
from .mateqn import care, dare, _check_shape
49-
from .statesp import StateSpace, _ssmatrix, _convert_to_statespace, ss
48+
from .config import _process_legacy_keyword
49+
from .exception import ControlArgument, ControlDimension, \
50+
ControlNotImplemented, ControlSlycot
51+
from .iosys import _process_indices, _process_labels, isctime, isdtime
5052
from .lti import LTI
51-
from .iosys import isdtime, isctime, _process_indices, _process_labels
53+
from .mateqn import _check_shape, care, dare
5254
from .nlsys import NonlinearIOSystem, interconnect
53-
from .exception import ControlSlycot, ControlArgument, ControlDimension, \
54-
ControlNotImplemented
55-
from .config import _process_legacy_keyword
55+
from .statesp import StateSpace, _convert_to_statespace, _ssmatrix, ss
5656

5757
# Make sure we have access to the right slycot routines
5858
try:
5959
from slycot import sb03md57
60+
6061
# wrap without the deprecation warning
6162
def sb03md(n, C, A, U, dico, job='X',fact='N',trana='N',ldwork=None):
6263
ret = sb03md57(A, U, C, dico, job, fact, trana, ldwork)
@@ -581,8 +582,9 @@ def dlqr(*args, **kwargs):
581582

582583
# Function to create an I/O sytems representing a state feedback controller
583584
def create_statefbk_iosystem(
584-
sys, gain, integral_action=None, estimator=None, controller_type=None,
585-
xd_labels=None, ud_labels=None, gainsched_indices=None,
585+
sys, gain, feedfwd_gain=None, integral_action=None, estimator=None,
586+
controller_type=None, xd_labels=None, ud_labels=None,
587+
feedfwd_pattern='trajgen', gainsched_indices=None,
586588
gainsched_method='linear', control_indices=None, state_indices=None,
587589
name=None, inputs=None, outputs=None, states=None, **kwargs):
588590
r"""Create an I/O system using a (full) state feedback controller.
@@ -592,7 +594,7 @@ def create_statefbk_iosystem(
592594
593595
.. math:: u = u_d - K_p (x - x_d) - K_i \int(C x - C x_d)
594596
595-
It can be called in the form::
597+
by calling
596598
597599
ctrl, clsys = ct.create_statefbk_iosystem(sys, K)
598600
@@ -608,6 +610,18 @@ def create_statefbk_iosystem(
608610
609611
where :math:`\mu` represents the scheduling variable.
610612
613+
Alternatively, a control of the form
614+
615+
.. math:: u = k_f r - K_p x - K_i \int(C x - r)
616+
617+
can be created by calling
618+
619+
ctrl, clsys = ct.create_statefbk_iosystem(
620+
sys, K, kf, feedfwd_pattern='refgain')
621+
622+
In either form, an estimator can also be used to compute the estimated
623+
state from the input and output measurements.
624+
611625
Parameters
612626
----------
613627
sys : NonlinearIOSystem
@@ -640,6 +654,15 @@ def create_statefbk_iosystem(
640654
ud_labels. These settings can also be overridden using the
641655
`inputs` keyword.
642656
657+
feedfwd_pattern : str, optional
658+
If set to 'refgain', the reference gain design pattern is used to
659+
create the controller instead of the trajectory generation pattern.
660+
661+
feedfwd_gain : array_like, optional
662+
Specify the feedforward gain, `k_f`. Used only for the reference
663+
gain design pattern. If not given and if `sys` is a `StateSpace`
664+
(linear) system, will be computed as -1/(C (A-BK)^{-1}) B.
665+
643666
integral_action : ndarray, optional
644667
If this keyword is specified, the controller can include integral
645668
action in addition to state feedback. The value of the
@@ -841,20 +864,26 @@ def create_statefbk_iosystem(
841864
raise ControlArgument(f"unknown controller_type '{controller_type}'")
842865

843866
# Figure out the labels to use
844-
xd_labels = _process_labels(
845-
xd_labels, 'xd', ['xd[{i}]'.format(i=i) for i in range(sys_nstates)])
846-
ud_labels = _process_labels(
847-
ud_labels, 'ud', ['ud[{i}]'.format(i=i) for i in range(sys_ninputs)])
848-
849-
# Create the signal and system names
850-
if inputs is None:
851-
inputs = xd_labels + ud_labels + estimator.output_labels
867+
if feedfwd_pattern == 'trajgen':
868+
xd_labels = _process_labels(xd_labels, 'xd', [
869+
'xd[{i}]'.format(i=i) for i in range(sys_nstates)])
870+
ud_labels = _process_labels(ud_labels, 'ud', [
871+
'ud[{i}]'.format(i=i) for i in range(sys_ninputs)])
872+
873+
# Create the signal and system names
874+
if inputs is None:
875+
inputs = xd_labels + ud_labels + estimator.output_labels
876+
elif feedfwd_pattern == 'refgain':
877+
raise NotImplementedError("reference gain pattern not yet implemented")
878+
else:
879+
raise NotImplementedError(f"unknown pattern '{feedfwd_pattern}'")
880+
852881
if outputs is None:
853882
outputs = [sys.input_labels[i] for i in control_indices]
854883
if states is None:
855884
states = nintegrators
856885

857-
# Process gainscheduling variables, if present
886+
# Process gain scheduling variables, if present
858887
if gainsched:
859888
# Create a copy of the scheduling variable indices (default = xd)
860889
gainsched_indices = _process_indices(
@@ -897,7 +926,7 @@ def _compute_gain(mu):
897926
return K
898927

899928
# Define the controller system
900-
if controller_type == 'nonlinear':
929+
if controller_type == 'nonlinear' and feedfwd_pattern == 'trajgen':
901930
# Create an I/O system for the state feedback gains
902931
def _control_update(t, states, inputs, params):
903932
# Split input into desired state, nominal input, and current state
@@ -931,7 +960,7 @@ def _control_output(t, states, inputs, params):
931960
_control_update, _control_output, name=name, inputs=inputs,
932961
outputs=outputs, states=states, params=params)
933962

934-
elif controller_type == 'iosystem':
963+
elif controller_type == 'iosystem' and feedfwd_pattern == 'trajgen':
935964
# Use the passed system to compute feedback compensation
936965
def _control_update(t, states, inputs, params):
937966
# Split input into desired state, nominal input, and current state
@@ -955,7 +984,7 @@ def _control_output(t, states, inputs, params):
955984
_control_update, _control_output, name=name, inputs=inputs,
956985
outputs=outputs, states=fbkctrl.state_labels, dt=fbkctrl.dt)
957986

958-
elif controller_type == 'linear' or controller_type is None:
987+
elif controller_type in 'linear' and feedfwd_pattern == 'trajgen':
959988
# Create the matrices implementing the controller
960989
if isctime(sys):
961990
# Continuous time: integrator
@@ -973,6 +1002,12 @@ def _control_output(t, states, inputs, params):
9731002
A_lqr, B_lqr, C_lqr, D_lqr, dt=sys.dt, name=name,
9741003
inputs=inputs, outputs=outputs, states=states)
9751004

1005+
elif feedfwd_pattern == 'refgain':
1006+
if controller_type not in ['linear', 'iosystem']:
1007+
raise ControlArgument(
1008+
"refgain design pattern only supports linear controllers")
1009+
raise NotImplementedError("reference gain pattern not yet implemented")
1010+
9761011
else:
9771012
raise ControlArgument(f"unknown controller_type '{controller_type}'")
9781013

@@ -1020,7 +1055,7 @@ def ctrb(A, B, t=None):
10201055
bmat = _ssmatrix(B)
10211056
n = np.shape(amat)[0]
10221057
m = np.shape(bmat)[1]
1023-
1058+
10241059
if t is None or t > n:
10251060
t = n
10261061

@@ -1042,7 +1077,7 @@ def obsv(A, C, t=None):
10421077
Dynamics and output matrix of the system
10431078
t : None or integer
10441079
maximum time horizon of the controllability matrix, max = A.shape[0]
1045-
1080+
10461081
Returns
10471082
-------
10481083
O : 2D array (or matrix)
@@ -1062,14 +1097,14 @@ def obsv(A, C, t=None):
10621097
cmat = _ssmatrix(C)
10631098
n = np.shape(amat)[0]
10641099
p = np.shape(cmat)[0]
1065-
1100+
10661101
if t is None or t > n:
10671102
t = n
10681103

10691104
# Construct the observability matrix
10701105
obsv = np.zeros((t * p, n))
10711106
obsv[:p, :] = cmat
1072-
1107+
10731108
for k in range(1, t):
10741109
obsv[k * p:(k + 1) * p, :] = np.dot(obsv[(k - 1) * p:k * p, :], amat)
10751110

0 commit comments

Comments
 (0)