@@ -613,7 +613,7 @@ def create_statefbk_iosystem(
613613 The I/O system that represents the process dynamics. If no estimator
614614 is given, the output of this system should represent the full state.
615615
616- gain : ndarray or tuple
616+ gain : ndarray, tuple, or I/O system
617617 If an array is given, it represents the state feedback gain (K).
618618 This matrix defines the gains to be applied to the system. If
619619 `integral_action` is None, then the dimensions of this array
@@ -627,6 +627,9 @@ def create_statefbk_iosystem(
627627 which the gains are computed. The `gainsched_indices` parameter
628628 should be used to specify the scheduling variables.
629629
630+ If an I/O system is given, the error e = x - xd is passed to the
631+ system and the output is used as the feedback compensation term.
632+
630633 xd_labels, ud_labels : str or list of str, optional
631634 Set the name of the signals to use for the desired state and
632635 inputs. If a single string is specified, it should be a
@@ -796,7 +799,15 @@ def create_statefbk_iosystem(
796799 # Stack gains and points if past as a list
797800 gains = np .stack (gains )
798801 points = np .stack (points )
799- gainsched = True
802+ gainsched = True
803+
804+ elif isinstance (gain , NonlinearIOSystem ):
805+ if controller_type not in ['iosystem' , None ]:
806+ raise ControlArgument (
807+ f"incompatible controller type '{ controller_type } '" )
808+ fbkctrl = gain
809+ controller_type = 'iosystem'
810+ gainsched = False
800811
801812 else :
802813 raise ControlArgument ("gain must be an array or a tuple" )
@@ -808,7 +819,7 @@ def create_statefbk_iosystem(
808819 " gain scheduled controller" )
809820 elif controller_type is None :
810821 controller_type = 'nonlinear' if gainsched else 'linear'
811- elif controller_type not in {'linear' , 'nonlinear' }:
822+ elif controller_type not in {'linear' , 'nonlinear' , 'iosystem' }:
812823 raise ControlArgument (f"unknown controller_type '{ controller_type } '" )
813824
814825 # Figure out the labels to use
@@ -902,6 +913,30 @@ def _control_output(t, states, inputs, params):
902913 _control_update , _control_output , name = name , inputs = inputs ,
903914 outputs = outputs , states = states , params = params )
904915
916+ elif controller_type == 'iosystem' :
917+ # Use the passed system to compute feedback compensation
918+ def _control_update (t , states , inputs , params ):
919+ # Split input into desired state, nominal input, and current state
920+ xd_vec = inputs [0 :sys_nstates ]
921+ x_vec = inputs [- sys_nstates :]
922+
923+ # Compute the integral error in the xy coordinates
924+ return fbkctrl .updfcn (t , states , (x_vec - xd_vec ), params )
925+
926+ def _control_output (t , states , inputs , params ):
927+ # Split input into desired state, nominal input, and current state
928+ xd_vec = inputs [0 :sys_nstates ]
929+ ud_vec = inputs [sys_nstates :sys_nstates + sys_ninputs ]
930+ x_vec = inputs [- sys_nstates :]
931+
932+ # Compute the control law
933+ return ud_vec + fbkctrl .outfcn (t , states , (x_vec - xd_vec ), params )
934+
935+ # TODO: add a way to pass parameters
936+ ctrl = NonlinearIOSystem (
937+ _control_update , _control_output , name = name , inputs = inputs ,
938+ outputs = outputs , states = fbkctrl .state_labels , dt = fbkctrl .dt )
939+
905940 elif controller_type == 'linear' or controller_type is None :
906941 # Create the matrices implementing the controller
907942 if isctime (sys ):
0 commit comments