diff --git a/examples/GEM_Control_Cookbook.ipynb b/examples/GEM_Control_Cookbook.ipynb index db4ea43..0d47480 100644 --- a/examples/GEM_Control_Cookbook.ipynb +++ b/examples/GEM_Control_Cookbook.ipynb @@ -67,7 +67,11 @@ "cell_type": "code", "execution_count": null, "id": "8a3eede7", - "metadata": {}, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, "outputs": [], "source": [ "### 1.2 Supported Motors\n", diff --git a/gem_controllers/gem_adapter.py b/gem_controllers/gem_adapter.py index a6e5045..560dc1e 100644 --- a/gem_controllers/gem_adapter.py +++ b/gem_controllers/gem_adapter.py @@ -105,7 +105,9 @@ def tune(self, env, env_id, tune_controller=True, **kwargs): self._output_stage.tune(env, env_id) if tune_controller: self._controller.tune(env, env_id, **kwargs) - self._reference_plotter.tune(env, self._controller.referenced_states, **kwargs) + + self._reference_plotter.tune(env, self._controller.referenced_states, plot_references=True, + maximum_reference=self._controller.maximum_reference) def build_block_diagram(self, env_id, save_block_diagram_as): self._block_diagram = gc.build_block_diagram(self, env_id, save_block_diagram_as) diff --git a/gem_controllers/pi_current_controller.py b/gem_controllers/pi_current_controller.py index 63d1b66..849b753 100644 --- a/gem_controllers/pi_current_controller.py +++ b/gem_controllers/pi_current_controller.py @@ -76,6 +76,10 @@ def referenced_states(self): """Referenced states of the current control stage.""" return np.array([]) + @property + def maximum_reference(self): + return dict() + def __init__(self, env, env_id, base_current_controller='PI', decoupling=True): """ Initilizes a PI current control stage. diff --git a/gem_controllers/pi_speed_controller.py b/gem_controllers/pi_speed_controller.py index 14a7fec..d04392e 100644 --- a/gem_controllers/pi_speed_controller.py +++ b/gem_controllers/pi_speed_controller.py @@ -50,6 +50,10 @@ def references(self): def referenced_states(self): return np.append(self._torque_controller.referenced_states, 'torque') + @property + def maximum_reference(self): + return self._torque_controller.maximum_reference + def __init__( self, _env: (gem.core.ElectricMotorEnvironment, None) = None, diff --git a/gem_controllers/reference_plotter.py b/gem_controllers/reference_plotter.py index 4c93170..995a64f 100644 --- a/gem_controllers/reference_plotter.py +++ b/gem_controllers/reference_plotter.py @@ -6,9 +6,11 @@ class ReferencePlotter: def __init__(self): self._referenced_plots = [] self._referenced_states = [] + self._maximum_reference = None self._plot_references = None + self._maximum_reference_set = False - def tune(self, env, referenced_states, plot_references, **_): + def tune(self, env, referenced_states, plot_references, maximum_reference, **_): """ Tune the reference plotter. @@ -16,6 +18,7 @@ def tune(self, env, referenced_states, plot_references, **_): env(ElectricMotorEnvironment): The GEM-Environment that the controller shall be created for. referenced_states(np.ndarray): Array of all referenced states. plot_references(bool): Flag, if the references of the subordinate stages should be plotted. + maximum_reference(dict): Dict containing all limited reference currents. """ if plot_references: @@ -29,6 +32,11 @@ def tune(self, env, referenced_states, plot_references, **_): for plot in self._referenced_plots: plot._referenced = True + self._maximum_reference = maximum_reference + + def add_maximum_reference(self, state, value): + self._maximum_reference[state] = value + def update_plots(self, references): """ Update the state plots of the GEM environment. @@ -37,5 +45,18 @@ def update_plots(self, references): references(np.ndarray): Array of all reference values of the subordinate stages. """ + if not self._maximum_reference_set: + for plot in self._referenced_plots: + if plot.state in ['i_e', 'i_a', 'i'] and plot.state in self._maximum_reference.keys(): + label = dict(i='$i^*$$_{\mathrm{ max}}$', i_a='$i^*_{a}$$_\mathrm{ max}}$', + i_e='$i^*_{e}$$_\mathrm{ max}}$') + plot._axis.axhline(self._maximum_reference[plot.state][0], c='g', linewidth=0.75, linestyle='--') + plot._axis.axhline(self._maximum_reference[plot.state][1], c='g', linewidth=0.75, linestyle='--') + labels = [legend._text for legend in plot._axis.get_legend().texts] + [label[plot.state]] + lines = plot._axis.lines[0:len(labels)-1] + plot._axis.lines[-1:] + plot._axis.legend(lines, labels, loc='upper left', numpoints=20) + + self._maximum_reference_set = True + for plot, state in zip(self._referenced_plots, self._referenced_states): plot._ref_data[plot.data_idx] = references[state] diff --git a/gem_controllers/stages/clipping_stages/combined_clipping_stage.py b/gem_controllers/stages/clipping_stages/combined_clipping_stage.py index 738033d..4fbec75 100644 --- a/gem_controllers/stages/clipping_stages/combined_clipping_stage.py +++ b/gem_controllers/stages/clipping_stages/combined_clipping_stage.py @@ -13,6 +13,15 @@ def clipping_difference(self) -> np.ndarray: """Difference between the reference and the clipped reference""" return self._clipping_difference + @property + def action_range(self) -> Tuple[np.ndarray, np.ndarray]: + """Action range of the controller stage""" + action_range_low = np.zeros(len(self._squared_clipped_states) + len(self._absolute_clipped_states)) + action_range_high = np.zeros(len(self._squared_clipped_states) + len(self._absolute_clipped_states)) + action_range_low[-1] = self._action_range_absolute[0][0] + action_range_high[-1] = self._action_range_absolute[1][0] + return action_range_low, action_range_high + def __init__(self, control_task='CC'): self._action_range_absolute = np.array([]), np.array([]) self._limit_squred_clipping = np.array([]) diff --git a/gem_controllers/stages/clipping_stages/squared_clipping_stage.py b/gem_controllers/stages/clipping_stages/squared_clipping_stage.py index 60d8fb1..6285572 100644 --- a/gem_controllers/stages/clipping_stages/squared_clipping_stage.py +++ b/gem_controllers/stages/clipping_stages/squared_clipping_stage.py @@ -23,6 +23,11 @@ def margin(self): """Margin of the controlled states""" return self._margin + @property + def action_range(self): + """Action range of the controller stage""" + return [] + def __init__(self, control_task='CC'): """ Args: diff --git a/gem_controllers/torque_controller.py b/gem_controllers/torque_controller.py index 54d2a4b..b548f1a 100644 --- a/gem_controllers/torque_controller.py +++ b/gem_controllers/torque_controller.py @@ -54,6 +54,10 @@ def references(self): def referenced_states(self): return np.append(self._current_controller.referenced_states, self._referenced_currents) + @property + def maximum_reference(self): + return self._maximum_reference + def __init__( self, env: (gem.core.ElectricMotorEnvironment, None) = None, @@ -91,6 +95,7 @@ def __init__( self._clipping_stage = gc.stages.clipping_stages.SquaredClippingStage('TC') self._current_reference = np.array([]) self._referenced_currents = np.array([]) + self._maximum_reference = dict() def tune(self, env, env_id, current_safety_margin=0.2, tune_current_controller=True, **kwargs): """ @@ -108,6 +113,11 @@ def tune(self, env, env_id, current_safety_margin=0.2, tune_current_controller=T self._clipping_stage.tune(env, env_id, margin=current_safety_margin) self._operation_point_selection.tune(env, env_id, current_safety_margin) self._referenced_currents = gc.parameter_reader.currents[gc.utils.get_motor_type(env_id)] + for current, action_range_low, action_range_high in zip(self._referenced_currents, + self._clipping_stage.action_range[0], + self._clipping_stage.action_range[1]): + if current in ['i', 'i_a', 'i_e']: + self._maximum_reference[current] = [action_range_low, action_range_high] def torque_control(self, state, reference): """