Source code for gem_controllers.gem_controller

import gym_electric_motor.core

import gem_controllers as gc
import numpy as np


[docs]class GemController: """The GemController is the base for all motor controllers in the gem-control package. A gem-controller consists of multiple stages that execute different control tasks like speed-control, a reference to current set point mapping or input and output processing. Furthermore, the GemController has got a `GemController.make` factory function that automatically designs and tunes a classical cascaded motor controller based on classic control techniques like the proportional-integral (PI) controller to control a gym-electric-motor environment. """ @property def signals(self): """Input signals of the controller""" return [] @property def signal_names(self): """Signal names of the controller""" return []
[docs] @classmethod def make( cls, env: gym_electric_motor.core.ElectricMotorEnvironment, env_id: str, decoupling: bool = True, current_safety_margin: float = 0.2, base_current_controller: str = 'PI', base_speed_controller: str = 'PI', a: int = 4, plot_references: bool = True, block_diagram: bool = True, save_block_diagram_as: (str, tuple) = None, ): """A factory function that generates (and parameterizes) a matching GemController for a given gym-electric-motor environment `env`. 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. decoupling(bool): Flag, if a EMF-Feedforward correction stage should be used in the pi current controller. current_safety_margin(float in [0..1]): The ratio between the maximum current set point the reference controller generates and the absolute current limit. base_speed_controller('PI'/'PID'/'P'/'ThreePoint'): Selection of the basic control algorithm for the speed controller. base_current_controller('PI'/'PID'/'P'/'ThreePoint'): Selection of the basic control algorithm for the current controller. a(float): Tuning parameter of the symmetrical optimum. plot_references(bool): Flag, if the reference values of the underlying control circuits should be plotted block_diagram(bool): Selection whether the block diagram should be displayed save_block_diagram_as(str, tuple): Selection of whether the block diagram should be saved Returns: GemController: An initialized (and tuned) instance of a controller that fits to the specified environment. """ control_task = gc.utils.get_control_task(env_id) tuner_kwargs = dict() # Initialize the current control stage controller = gc.PICurrentController( env, env_id, base_current_controller=base_current_controller, decoupling=decoupling ) tuner_kwargs['a'] = a tuner_kwargs['plot_references'] = plot_references if control_task in ['TC', 'SC']: # Initilize the operation point selection controller = gc.TorqueController(env, env_id, current_controller=controller) tuner_kwargs['current_safety_margin'] = current_safety_margin if control_task == 'SC': # Initilize the speed control stage controller = gc.PISpeedController( env, env_id, torque_controller=controller, base_speed_controller=base_speed_controller ) # Wrap the controller with the adapter to map the inputs and outputs to the environment controller = gc.GymElectricMotorAdapter(env, env_id, controller) # Fit the controllers parameters to the environment controller.tune(env, env_id, **tuner_kwargs) if block_diagram: controller.build_block_diagram(env_id, save_block_diagram_as) return controller
@property def stages(self): """Stages of the GEM Controller""" return self._stages def __init__(self): self._stages = []
[docs] def get_signal_value(self, signal_name): """ Get the value of a signal calling by the signal name. Args: signal_name(str): Name of a signal of the state Returns: float """ return self.signals[self.signal_names.index(signal_name)]
[docs] def control(self, state, reference): """ Calculate the voltage reference. Args: state(np.array): state of the environment reference(np.array): speed references Returns: np.array: reference voltage """ raise NotImplementedError
[docs] def reset(self): """Reset all stages of the controller""" for stage in self._stages: stage.reset()
def tune(self, env, env_id, **kwargs): pass
[docs] def control_environment(self, env, n_steps, max_episode_length=np.inf, render_env=False): """ Function to control an environment with the GemController. Args: env(ElectricMotorEnvironment): The GEM-Environment that the controller should control. n_steps(int): Number of iteration steps. max_episode_length(int): Maximum length of an epsiode, after which the environment and controller should be reset. render_env(bool): Flag, if the states of the environment should be plotted. """ state, reference = env.reset() if self.block_diagram: self.block_diagram.open() self.reset() current_episode_length = 0 for _ in range(n_steps): # Simulate the environment and controller for n steps if render_env: env.render() # Plot the states action = self.control(state, reference) # Calculate the action (state, reference), _, done, _ = env.step(action) # Simulate one step of the environment if done or current_episode_length >= max_episode_length: # Reset the environment and controller state, reference = env.reset() self.reset() current_episode_length = 0 current_episode_length = current_episode_length + 1 if self.block_diagram: self.block_diagram.close()