Source code for gem_controllers.pi_current_controller

import numpy as np

import gem_controllers as gc


[docs]class PICurrentController(gc.CurrentController): """This class forms the PI Current controller, for any motor.""" @property def signal_names(self): """Signal names of the calculated values.""" return ['u_PI', 'u_ff', 'u_out'] @property def transformation_stage(self): """Coordinate transformation stage at the output""" return self._transformation_stage @property def current_base_controller(self) -> gc.stages.BaseController: """Base controller for the current control""" return self._current_base_controller @current_base_controller.setter def current_base_controller(self, value: gc.stages.BaseController): assert isinstance(value, gc.stages.BaseController) self._current_base_controller = value @property def emf_feedforward(self) -> gc.stages.EMFFeedforward: """EMF feedforward stage of the current controller""" return self._emf_feedforward @emf_feedforward.setter def emf_feedforward(self, value: gc.stages.EMFFeedforward): assert isinstance(value, gc.stages.EMFFeedforward) self._emf_feedforward = value @property def stages(self): """List of the stages up to the current controller""" stages_ = [self._current_base_controller] if self._decoupling: stages_.append(self._emf_feedforward) if self._coordinate_transformation_required: stages_.append(self._transformation_stage) stages_.append(self._clipping_stage) return stages_ @property def voltage_reference(self) -> np.ndarray: """Reference values for the input voltages""" return self._voltage_reference @property def clipping_stage(self): """Clipping stage of the current controller""" return self._clipping_stage @property def t_n(self): """Time constant of the current controller""" if hasattr(self._current_base_controller, 'p_gain') \ and hasattr(self._current_base_controller, 'i_gain'): return self._current_base_controller.p_gain / self._current_base_controller.i_gain else: return self._tau_current_loop @property def references(self): """Reference values of the current control stage.""" return dict() @property def referenced_states(self): """Referenced states of the current control stage.""" return np.array([]) def __init__(self, env, env_id, base_current_controller='PI', decoupling=True): """ Initilizes a PI current control stage. Args: env(ElectricMotorEnvironment): The GEM-Environment that the controller shall be created for. env_id(str): The corresponding environment-id to specify the concrete environment. base_current_controller(str): Selection which base controller should be used for the current control stage. decoupling(bool): Flag, if a EMF-Feedforward correction stage should be used in the pi current controller. """ super().__init__() self._current_base_controller = None self._emf_feedforward = None self._transformation_stage = None self._tau_current_loop = np.array([0.0]) self._coordinate_transformation_required = False self._decoupling = decoupling self._voltage_reference = np.array([]) self._transformation_stage = gc.stages.AbcTransformation() # Choose the emf feedforward function if gc.utils.get_motor_type(env_id) in gc.parameter_reader.induction_motors: self._emf_feedforward = gc.stages.EMFFeedforwardInd() elif gc.utils.get_motor_type(env_id) == 'EESM': self._emf_feedforward = gc.stages.EMFFeedforwardEESM() else: self._emf_feedforward = gc.stages.EMFFeedforward() # Choose the clipping function if gc.utils.get_motor_type(env_id) == 'EESM': self._clipping_stage = gc.stages.clipping_stages.CombinedClippingStage('CC') elif gc.utils.get_motor_type(env_id) in gc.parameter_reader.ac_motors: self._clipping_stage = gc.stages.clipping_stages.SquaredClippingStage('CC') else: self._clipping_stage = gc.stages.clipping_stages.AbsoluteClippingStage('CC') self._anti_windup_stage = gc.stages.AntiWindup('CC') self._current_base_controller = gc.stages.base_controllers.get(base_current_controller)('CC')
[docs] def tune(self, env, env_id, a=4, **kwargs): """ Tune the components of the current control stage. Args: env(ElectricMotorEnvironment): The GEM-Environment that the controller shall be created for. env_id(str): The corresponding environment-id to specify the concrete environment. a(float): Design parameter of the symmetric optimum for the base controllers """ action_type = gc.utils.get_action_type(env_id) motor_type = gc.utils.get_motor_type(env_id) if action_type in ['Finite', 'Cont'] and motor_type in gc.parameter_reader.ac_motors: self._coordinate_transformation_required = True if self._coordinate_transformation_required: self._transformation_stage.tune(env, env_id) self._emf_feedforward.tune(env, env_id) self._current_base_controller.tune(env, env_id, a) self._anti_windup_stage.tune(env, env_id) self._clipping_stage.tune(env, env_id) self._voltage_reference = np.zeros( len(gc.parameter_reader.voltages[gc.utils.get_motor_type(env_id)]), dtype=float ) self._tau_current_loop = gc.parameter_reader.tau_current_loop_reader[motor_type](env)
[docs] def current_control(self, state, current_reference): """ Calculate the input voltages. Args: state(np.array): state of the environment current_reference(np.array): current references Returns: np.array: voltage references """ # Calculate the voltage reference by the base controllers voltage_reference = self._current_base_controller(state, current_reference) # Decouple the voltage components if self._decoupling: voltage_reference = self._emf_feedforward(state, voltage_reference) # Clip the voltage inputs to the action space self._voltage_reference = self._clipping_stage(state, voltage_reference) # Transform the voltages in the correct coordinate system if self._coordinate_transformation_required: voltage_reference = self._transformation_stage(state, voltage_reference) # Integrate the I-Controllers if hasattr(self._current_base_controller, 'integrator'): delta = self._anti_windup_stage( state, current_reference, self._clipping_stage.clipping_difference ) self._current_base_controller.integrator += delta return voltage_reference
[docs] def control(self, state, reference): """ Claculate the reference values for the input voltages. Args: state(np.array): actual state of the environment reference(np.array): current references Returns: np.ndarray: voltage references """ self._voltage_reference = self.current_control(state, reference) return self._voltage_reference
[docs] def reset(self): """Reset all components of the stage""" for stage in self.stages: stage.reset()