From 594291f30134ebeed7e1e64b6c408c291daef7bb Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Wed, 19 Feb 2025 15:41:20 +0530 Subject: [PATCH 01/14] six phase motor - inprogress --- .../electric_motors/SixPhase_PMSM.py | 81 +++++++++++++++++++ .../electric_motors/six_phase_motor.py | 77 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py create mode 100644 src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py new file mode 100644 index 00000000..e835157b --- /dev/null +++ b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py @@ -0,0 +1,81 @@ +import math + +import numpy as np + +from .six_phase_motor import SixPhaseMotor + + +class SixPhasePMSM(SixPhaseMotor): + """ + ===================== ========== ============= =========================================== + Motor Parameter Unit Default Value Description + ===================== ========== ============= =========================================== + r_s Ohm 64.3e-3 Stator resistance + l_d H 125e-6 Direct axis inductance + l_q H 126e-6 Quadrature axis inductance + l_x H 39e-6 x-axis inductance + l_y H 35e-6 y-axis inductance + p 1 5 Pole pair number + psi_PM Vs 4.7e-3 flux linkage of the permanent magnets + ===================== ========== ============= =========================================== + + =============== ====== ============================================= + Motor Currents Unit Description + =============== ====== ============================================= + i_sd A Direct axis current + i_sq A Quadrature axis current + i_sx A + i_sy A + i_salpha A Stator current in alpha direction + i_sbeta A Stator current in beta direction + i_sX A + i_sY A + i_sa1 A + i_sa2 A + i_sb1 A + i_sb2 A + i_sc1 A + i_sc2 A + + =============== ====== ============================================= + =============== ====== ============================================= + Motor Voltages Unit Description + =============== ====== ============================================= + u_sd V Direct axis voltage + =============== ====== ============================================= + + ======== =========================================================== + Limits / Nominal Value Dictionary Entries: + -------- ----------------------------------------------------------- + Entry Description + ======== =========================================================== + i General current limit / nominal value + ======== =========================================================== + + """ +#### Parameters taken from https://ieeexplore.ieee.org/document/10372153 + _default_motor_parameter = { + "p": 5, + "l_d": 125e-6, + "l_q": 126e-6, + "l_x": 39e-6, + "l_y": 35e-6, + "r_s": 64.3e-3, + "psi_PM": 4.7e-3, + } + #_default_limits = ? + #_default_nominal_values = ? + #_model_constants = None + #_default_initializer = {"states": {?},"interval": None,"random_init": None,"random_params": (None, None),} + + + + @property + def motor_parameter(self): + # Docstring of superclass + return self._motor_parameter + + @property + def initializer(self): + # Docstring of superclass + return self._initializer \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py new file mode 100644 index 00000000..e49cda50 --- /dev/null +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py @@ -0,0 +1,77 @@ +import math + +import numpy as np + +from .electric_motor import ElectricMotor + + +class SixPhaseMotor(ElectricMotor): + """ + The SixPhaseMotor and its subclasses implement the technical system of Six Phase Motors. + + """ + + # transformation matrix from abc to alpha-beta representation + # VSD is the modeling approach used for multi-phase machines, which represents a generalized form of the clarke transformation + # for the six phase machine with Winding configuration in the stator- and rotor-fixed coordinate systems with δ =(2/3)*π and γ =π/6 + _t46= 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1] + ]) + + # i_s - stator current + @staticmethod + def t_46(quantities): + """ + Transformation from abc representation to alpha-beta representation + + Args: + quantities: The properties in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]'' + + Returns: + The converted quantities in the alpha-beta representation like ''[i_salpha, i_sbeta, i_sX, i_sY]'' + """ + return np.matmul(SixPhaseMotor._t46, quantities) + + @staticmethod + def q(quantities, epsilon): + """ + Transformation of the abc representation into dq using the electrical angle + + Args: + quantities: the properties in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]'' + epsilon: electrical rotor position + + Returns: + The converted quantities in the dq representation like ''[i_sd, i_sq, i_sx, i_sy, i_s0+, i_s0-]''. + since 2N topology is considered (case where the neutral points are not connected) i_s0+, i_s0- will not be taken into account + """ + t_vsd = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1] + ]) + cos = math.cos(epsilon) + sin = math.sin(epsilon) + tp_alphaBetaXY = np.array([ + [cos, sin, 0, 0, 0, 0], + [-sin, cos, 0, 0, 0, 0], + [0, 0, cos, sin, 0, 0], + [0, 0, -sin, cos, 0, 0], + [0, 0, 0, 0, 1, 0] + [0, 0, 0, 0, 0, 1] + ]) + tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) + return np.matmul(tp_vsd, quantities) + + def _torque_limit(self): + """ + Returns: + Maximal possible torque for the given limits in self._limits + """ + raise NotImplementedError() \ No newline at end of file From 3a55e3393a0d1fe30df1cc03263919f0958f3db2 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Wed, 26 Feb 2025 20:45:13 +0530 Subject: [PATCH 02/14] ode for the sixphase PMSM --- .../electric_motors/SixPhase_PMSM.py | 122 +++++++++++++++--- .../physical_systems/physical_systems.py | 38 ++++++ 2 files changed, 143 insertions(+), 17 deletions(-) diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py index e835157b..581e2c65 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py @@ -42,6 +42,9 @@ class SixPhasePMSM(SixPhaseMotor): Motor Voltages Unit Description =============== ====== ============================================= u_sd V Direct axis voltage + u_sq V Quadrature axis voltage + u_sx V + u_sx V =============== ====== ============================================= ======== =========================================================== @@ -53,22 +56,13 @@ class SixPhasePMSM(SixPhaseMotor): ======== =========================================================== """ -#### Parameters taken from https://ieeexplore.ieee.org/document/10372153 - _default_motor_parameter = { - "p": 5, - "l_d": 125e-6, - "l_q": 126e-6, - "l_x": 39e-6, - "l_y": 35e-6, - "r_s": 64.3e-3, - "psi_PM": 4.7e-3, - } - #_default_limits = ? - #_default_nominal_values = ? - #_model_constants = None - #_default_initializer = {"states": {?},"interval": None,"random_init": None,"random_params": (None, None),} - - + I_SD_IDX = 0 + I_SQ_IDX = 1 + I_SX_IDX = 2 + I_SY_IDX = 3 + CURRENTS_IDX = [0, 1, 2, 3] + CURRENTS = ["i_sd", "i_sq", "i_sx", "i_sy"] + VOLTAGES = ["u_sd", "u_sq", "u_sx", "u_sx"] @property def motor_parameter(self): @@ -78,4 +72,98 @@ def motor_parameter(self): @property def initializer(self): # Docstring of superclass - return self._initializer \ No newline at end of file + return self._initializer + + #### Parameters taken from https://ieeexplore.ieee.org/document/10372153 + _default_motor_parameter = {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3,} + #_default_limits = ? + #_default_nominal_values = ? + _default_initializer = { + "states": {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } + + _model_constants = None + + _initializer = None + + def __init__( + self, + motor_parameter=None, + nominal_values=None, + limit_values=None, + motor_initializer=None, + ): + # Docstring of superclass + nominal_values = nominal_values or {} + limit_values = limit_values or {} + super().__init__(motor_parameter, nominal_values, limit_values, motor_initializer) + self._update_model() + self._update_limits() + + def _update_model(self): + """Updates the motor's model parameters with the motor parameters. + + Called internally when the motor parameters are changed or the motor is initialized. + """ + mp = self._motor_parameter + self._model_constants = np.array([ + [-mp['r_s'], mp['l_q'], 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, -mp['r_s'], -mp['l_d'], -mp['psi_PM'], 1, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, -mp['r_s'], -mp['l_y'], 1, 0, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -mp['r_s'], mp['l_x'], 1] + + ]) + self._model_constants[self.I_SD_IDX] = self._model_constants[self.I_SD_IDX] / mp["l_d"] + self._model_constants[self.I_SQ_IDX] = self._model_constants[self.I_SQ_IDX] / mp["l_q"] + self._model_constants[self.I_SX_IDX] = self._model_constants[self.I_SX_IDX] / mp["l_x"] + self._model_constants[self.I_SY_IDX] = self._model_constants[self.I_SY_IDX] / mp["l_y"] + + + def electrical_ode(self, state, u_dqxy, omega, *_): + """ + The differential equation of the Six phase PMSM. + + Args: + state: The current state of the motor. [i_sd, i_sq, i_sx, i_sy] + omega: electrical rotational speed + u_qdxy: The input voltages [u_sd, u_sq, u_sx, u_sy] + + Returns: + The derivatives of the state vector d/dt([i_sd, i_sq, i_sx, i_sy]) + """ + return np.matmul( + self._model_constants, + np.array( + [ + state[self.I_SD_IDX], + omega * state[self.I_SQ_IDX], + u_dqxy[0], + state[self.I_SQ_IDX], + omega * state[self.I_SD_IDX], + omega, + u_dqxy[1], + state[self.I_SX_IDX], + omega * state[self.I_SY_IDX], + u_dqxy[2], + state[self.I_SY_IDX], + omega * state[self.I_SX_IDX], + u_dqxy[3] + ] + ), + ) + + + def i_in(self, state): + # Docstring of superclass + return state[self.CURRENTS_IDX] + + def reset(self, state_space, state_positions, **__): + # Docstring of superclass + if self._initializer and self._initializer["states"]: + self.initialize(state_space, state_positions) + return np.asarray(list(self._initial_states.values())) + else: + return np.zeros(len(self.CURRENTS) + 1) diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index 5e8640e9..d5d7ca4a 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1111,3 +1111,41 @@ def reset(self, *_): ] ) return system_state / self._limits + +class SixPhaseMotorSystem(SCMLSystem): + """ + SCML-System that implements the basic transformations needed for six phase drives. + """ + + def abc_to_alphabeta_space(self, abc_quantities): + """ + Transformation from abc to alphabeta space + + Args: + abc_quantities: The properties in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]'' + + Returns: + The converted quantities in the alpha-beta representation like ''[i_salpha, i_sbeta, i_sX, i_sY]'' + """ + alphabeta_quantity = self._electrical_motor.t_46(abc_quantities) + return alphabeta_quantity + + def abc_to_dq_space(self, abc_quantities,epsilon): + """ + Transformation of the abc representation into dq using the electrical angle + + Args: + abc_quantities: the properties in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]'' + epsilon: electrical rotor position + + Returns: + The converted quantities in the dq representation like ''[i_sd, i_sq, i_sx, i_sy,]''. + """ + dqxy_quantity = self._electrical_motor.q(abc_quantities,epsilon) + return dqxy_quantity + +class SixPhasePMSM(SixPhaseMotorSystem): + def _build_state_space(self, state_names): + raise NotImplementedError + + #def _build_state_names(self): ? \ No newline at end of file From e85433735658a96c354422f5e43bf3bf3e41bb4f Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Thu, 6 Mar 2025 01:38:14 +0530 Subject: [PATCH 03/14] six phase - corrections as per the review --- .../electric_motors/SixPhase_PMSM.py | 48 +++++++++---- .../electric_motors/six_phase_motor.py | 26 ++++++- .../physical_systems/physical_systems.py | 71 +++++++++++++++++-- 3 files changed, 124 insertions(+), 21 deletions(-) diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py index 581e2c65..f424e842 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py @@ -45,6 +45,12 @@ class SixPhasePMSM(SixPhaseMotor): u_sq V Quadrature axis voltage u_sx V u_sx V + u_a1 V + u_a2 V + u_b1 V + u_b2 V + u_c1 V + u_c2 V =============== ====== ============================================= ======== =========================================================== @@ -62,7 +68,7 @@ class SixPhasePMSM(SixPhaseMotor): I_SY_IDX = 3 CURRENTS_IDX = [0, 1, 2, 3] CURRENTS = ["i_sd", "i_sq", "i_sx", "i_sy"] - VOLTAGES = ["u_sd", "u_sq", "u_sx", "u_sx"] + VOLTAGES = ["u_sd", "u_sq", "u_sx", "u_sy"] @property def motor_parameter(self): @@ -76,8 +82,8 @@ def initializer(self): #### Parameters taken from https://ieeexplore.ieee.org/document/10372153 _default_motor_parameter = {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3,} - #_default_limits = ? - #_default_nominal_values = ? + #_default_limits = ?maximum + #_default_nominal_values = ?rated _default_initializer = { "states": {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0}, "interval": None, @@ -110,10 +116,11 @@ def _update_model(self): """ mp = self._motor_parameter self._model_constants = np.array([ - [-mp['r_s'], mp['l_q'], 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, -mp['r_s'], -mp['l_d'], -mp['psi_PM'], 1, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0, -mp['r_s'], -mp['l_y'], 1, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -mp['r_s'], mp['l_x'], 1] + # omega, i_d, i_q, i_x, i_y, u_d, u_q, u_x, u_y, omega * i_d, omega * i_q, omega * i_x, omega * i_y + [ 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, 0, 0, mp['l_q'], 0, 0], + [-mp['psi_PM'], 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, -mp['l_d'], 0, 0, 0], + [ 0, 0, 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, 0, 0, -mp['l_y']], + [ 0, 0, 0, 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, mp['l_x'], 0] ]) self._model_constants[self.I_SD_IDX] = self._model_constants[self.I_SD_IDX] / mp["l_d"] @@ -138,19 +145,19 @@ def electrical_ode(self, state, u_dqxy, omega, *_): self._model_constants, np.array( [ + omega, state[self.I_SD_IDX], - omega * state[self.I_SQ_IDX], - u_dqxy[0], state[self.I_SQ_IDX], - omega * state[self.I_SD_IDX], - omega, - u_dqxy[1], state[self.I_SX_IDX], - omega * state[self.I_SY_IDX], - u_dqxy[2], state[self.I_SY_IDX], + u_dqxy[0], + u_dqxy[1], + u_dqxy[2], + u_dqxy[3], + omega * state[self.I_SD_IDX], + omega * state[self.I_SQ_IDX], omega * state[self.I_SX_IDX], - u_dqxy[3] + omega * state[self.I_SY_IDX], ] ), ) @@ -167,3 +174,14 @@ def reset(self, state_space, state_positions, **__): return np.asarray(list(self._initial_states.values())) else: return np.zeros(len(self.CURRENTS) + 1) + + #from pmsm + def torque(self, currents): + # Docstring of superclass + mp = self._motor_parameter + return ( + 1.5 * mp["p"] * (mp["psi_PM "] + (mp["l_d"] - mp["l_q"]) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] + ) + + #torque limit ? + \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py index e49cda50..bb2c99b0 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py @@ -48,6 +48,7 @@ def q(quantities, epsilon): The converted quantities in the dq representation like ''[i_sd, i_sq, i_sx, i_sy, i_s0+, i_s0-]''. since 2N topology is considered (case where the neutral points are not connected) i_s0+, i_s0- will not be taken into account """ + """ t_vsd = 1/ 3 * np.array([ [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], @@ -56,8 +57,6 @@ def q(quantities, epsilon): [1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1] ]) - cos = math.cos(epsilon) - sin = math.sin(epsilon) tp_alphaBetaXY = np.array([ [cos, sin, 0, 0, 0, 0], [-sin, cos, 0, 0, 0, 0], @@ -66,8 +65,31 @@ def q(quantities, epsilon): [0, 0, 0, 0, 1, 0] [0, 0, 0, 0, 0, 1] ]) + """ + t_vsd = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1], + ]) + + def rotation_matrix(theta): + return np.array([ + [math.cos(theta), math.sin(theta)], + [-math.sin(theta), math.cos(theta)] + ]) + + t1 = rotation_matrix(epsilon) + t2 = rotation_matrix(-epsilon) + tp_alphaBetaXY = np.block([ + [t1, np.zeros(t1.shape[0],t1.shape[1])], + [np.zeros(t2.shape[0],t2.shape[1]), t2], + ]) tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) return np.matmul(tp_vsd, quantities) + + + def _torque_limit(self): """ diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index d5d7ca4a..98df4e4f 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1145,7 +1145,70 @@ def abc_to_dq_space(self, abc_quantities,epsilon): return dqxy_quantity class SixPhasePMSM(SixPhaseMotorSystem): - def _build_state_space(self, state_names): - raise NotImplementedError - - #def _build_state_names(self): ? \ No newline at end of file + def __init__(self, control_space="dqxy", **kwargs): + """ + Args: + control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space + kwargs: Further arguments to pass tp SCMLSystem + """ + super().__init__(**kwargs) + self.control_space = control_space + if control_space == "dqxy": + assert ( + type(self._converter.action_space) == Box + ), "" + #self._action_space = ? how does it differ from a converter action space, what is the significance + + def _build_state_space(self, state_names): + # Docstring of superclass + low = -1 * np.ones_like(state_names, dtype=float) + low[self.U_SUP_IDX] = 0.0 + high = np.ones_like(state_names, dtype=float) + return Box(low, high, dtype=np.float64) + + def _build_state_names(self): + # Docstring of superclass + return self._mechanical_load.state_names + [ + "torque", + "i_a1", + "i_b1", + "i_c1", + "i_a2", + "i_b2", + "i_c2", + "i_sd", + "i_sq", + "i_sx", + "i_sy", + "u_a1", + "u_b1", + "u_c1", + "u_a2", + "u_b2", + "u_c2" + "u_sd", + "u_sq", + "u_sx", + "u_sy", + "epsilon", + "u_sup", + ] + #how to do - logic/know how ? + def _set_indices(self): + # Docstring of superclass + super()._set_indices() + self._omega_ode_idx = self._mechanical_load.OMEGA_IDX + self._load_ode_idx = list(range(len(self._mechanical_load.state_names))) + self._ode_currents_idx = list( + range( + self._load_ode_idx[-1] + 1, + self._load_ode_idx[-1] + 1 + len(self._electrical_motor.CURRENTS), + ) + ) + self._motor_ode_idx = self._ode_currents_idx + self._motor_ode_idx += [self._motor_ode_idx[-1] + 1] + self._ode_currents_idx = self._motor_ode_idx[:-1] + self.OMEGA_IDX = self.mechanical_load.OMEGA_IDX + #know why ? + #currents_lower = self.TORQUE_IDX + 1 + #currents_upper = currents_lower + 5 \ No newline at end of file From 010bb2aff61e3579546be4b81773d8badc2a105c Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Thu, 13 Mar 2025 02:26:18 +0530 Subject: [PATCH 04/14] sixphase environment - in progress --- src/gym_electric_motor/__init__.py | 5 + src/gym_electric_motor/demoSixPhasePMSM.py | 11 ++ src/gym_electric_motor/envs/__init__.py | 3 + .../envs/gym_sixphase_pmsm/__init__.py | 1 + .../gym_sixphase_pmsm/cont_cc_sixpmsm_env.py | 85 ++++++++++++++ .../electric_motors/SixPhase_PMSM.py | 5 +- .../electric_motors/__init__.py | 3 + .../electric_motors/six_phase_motor.py | 37 +++++- .../physical_systems/physical_systems.py | 111 ++++++++++++++++-- 9 files changed, 250 insertions(+), 11 deletions(-) create mode 100644 src/gym_electric_motor/demoSixPhasePMSM.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py diff --git a/src/gym_electric_motor/__init__.py b/src/gym_electric_motor/__init__.py index 828f1163..cab89988 100644 --- a/src/gym_electric_motor/__init__.py +++ b/src/gym_electric_motor/__init__.py @@ -281,3 +281,8 @@ register( id="Cont-SC-DFIM-v0", entry_point=envs_path + "ContSpeedControlDoublyFedInductionMotorEnv", **registration_kwargs ) + +#sixphase machines +register( + id="Cont-CC-SIXPMSM-v0", entry_point=envs_path + "ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) \ No newline at end of file diff --git a/src/gym_electric_motor/demoSixPhasePMSM.py b/src/gym_electric_motor/demoSixPhasePMSM.py new file mode 100644 index 00000000..88e5fdc7 --- /dev/null +++ b/src/gym_electric_motor/demoSixPhasePMSM.py @@ -0,0 +1,11 @@ +import gym_electric_motor as gem +from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + + # Select a different ode_solver with default parameters by passing a keystring +env = gem.make( + "Cont-CC-SIXPMSM-v0",) +terminated = True +for _ in range(1000): + if terminated: + state, reference = env.reset() + (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) \ No newline at end of file diff --git a/src/gym_electric_motor/envs/__init__.py b/src/gym_electric_motor/envs/__init__.py index 475000ff..43a2f6c1 100644 --- a/src/gym_electric_motor/envs/__init__.py +++ b/src/gym_electric_motor/envs/__init__.py @@ -50,6 +50,9 @@ from .gym_eesm.finite_tc_eesm_env import ( FiniteTorqueControlExternallyExcitedSynchronousMotorEnv, ) +from .gym_sixphase_pmsm.cont_cc_sixpmsm_env import ( + ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv, +) from .gym_im import ( ContCurrentControlDoublyFedInductionMotorEnv, ContCurrentControlSquirrelCageInductionMotorEnv, diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py new file mode 100644 index 00000000..8b1f8646 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py @@ -0,0 +1 @@ +from .cont_cc_sixpmsm_env import ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py new file mode 100644 index 00000000..bc7d05a7 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py @@ -0,0 +1,85 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + MultipleReferenceGenerator, + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + + default_subgenerators = ( + WienerProcessReferenceGenerator(reference_state="i_sd"), + WienerProcessReferenceGenerator(reference_state="i_sq"), + WienerProcessReferenceGenerator(reference_state="i_sx"), + WienerProcessReferenceGenerator(reference_state="i_sy") + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict(load_parameter=dict(a=0.01, b=0.01, c=0.0))), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + MultipleReferenceGenerator, + dict(sub_generators=default_subgenerators), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(i_sd=0.5, i_sq=0.5, i_sx=0.5, i_sy=0.5,)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("i_sd", "i_sq"), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py index f424e842..8ba4b8d1 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py @@ -81,8 +81,9 @@ def initializer(self): return self._initializer #### Parameters taken from https://ieeexplore.ieee.org/document/10372153 - _default_motor_parameter = {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3,} - #_default_limits = ?maximum + _default_motor_parameter = {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3, "j_rotor": 0.0110,} + # + _default_limits =dict(omega=4e3 * np.pi / 30, torque=0.0, i=400, epsilon=math.pi, u=300) #_default_nominal_values = ?rated _default_initializer = { "states": {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0}, diff --git a/src/gym_electric_motor/physical_systems/electric_motors/__init__.py b/src/gym_electric_motor/physical_systems/electric_motors/__init__.py index e6a096dd..9d11c27d 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/__init__.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/__init__.py @@ -21,3 +21,6 @@ # Three Phase Motors from .three_phase_motor import ThreePhaseMotor + +#six phase motors +from .SixPhase_PMSM import SixPhasePMSM \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py index bb2c99b0..6afb821a 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py @@ -88,8 +88,41 @@ def rotation_matrix(theta): tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) return np.matmul(tp_vsd, quantities) - - + + @staticmethod + def q_inv(quantities, epsilon): + """ + Transformation of the dq representation into abc + + Args: + quantities: the properties in the dq representation like ''[i_sd, i_sq, i_sx, i_sy, i_s0+, i_s0-]''. + epsilon: electrical rotor position + + Returns: + The converted quantities in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]''. + + """ + t_vsd = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1] + ]) + cos = math.cos(epsilon) + sin = math.sin(epsilon) + tp_alphaBetaXY = np.array([ + [cos, sin, 0, 0, 0, 0], + [-sin, cos, 0, 0, 0, 0], + [0, 0, cos, -sin, 0, 0], + [0, 0, sin, cos, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1], + ]) + tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) + inv_tpVsd = np.linalg.inv(tp_vsd) + return np.matmul(inv_tpVsd, quantities) def _torque_limit(self): """ diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index 98df4e4f..c0edb1fd 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1144,8 +1144,22 @@ def abc_to_dq_space(self, abc_quantities,epsilon): dqxy_quantity = self._electrical_motor.q(abc_quantities,epsilon) return dqxy_quantity + def dq_to_abc_space(self, dq_quantities,epsilon): + """ + Transformation of the dq representation into abc using the electrical angle + + Args: + dq_quantities: the properties in the dq representation like ''[i_sd, i_sq, i_sx, i_sy, i_s0+, i_s0-]''. + epsilon: electrical rotor position + + Returns: + The converted quantities in the abc representation like ''[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2]''. + """ + abc_quantity = self._electrical_motor.q_inv(dq_quantities,epsilon) + return abc_quantity + class SixPhasePMSM(SixPhaseMotorSystem): - def __init__(self, control_space="dqxy", **kwargs): + def __init__(self, control_space="abc", **kwargs): """ Args: control_space(str):('abc' or 'dq') Choose, if actions the actions space is in dq or abc space @@ -1153,16 +1167,16 @@ def __init__(self, control_space="dqxy", **kwargs): """ super().__init__(**kwargs) self.control_space = control_space - if control_space == "dqxy": + if control_space == "abc": assert ( type(self._converter.action_space) == Box ), "" - #self._action_space = ? how does it differ from a converter action space, what is the significance + self._action_space = Box(-1, 1, shape=(4,), dtype=np.float64) def _build_state_space(self, state_names): # Docstring of superclass low = -1 * np.ones_like(state_names, dtype=float) - low[self.U_SUP_IDX] = 0.0 + low[self.U_SUP_IDX -1] = 0.0 high = np.ones_like(state_names, dtype=float) return Box(low, high, dtype=np.float64) @@ -1209,6 +1223,89 @@ def _set_indices(self): self._motor_ode_idx += [self._motor_ode_idx[-1] + 1] self._ode_currents_idx = self._motor_ode_idx[:-1] self.OMEGA_IDX = self.mechanical_load.OMEGA_IDX - #know why ? - #currents_lower = self.TORQUE_IDX + 1 - #currents_upper = currents_lower + 5 \ No newline at end of file + self.TORQUE_IDX = len(self.mechanical_load.state_names) + currents_lower = self.TORQUE_IDX + 1 + currents_upper = currents_lower + 10 # considering six stator currents in abc and four currents in dqxy + self.CURRENTS_IDX = list(range(currents_lower, currents_upper)) + voltages_lower = currents_upper + voltages_upper = voltages_lower + 10 + self.VOLTAGES_IDX = list(range(voltages_lower, voltages_upper)) + self.EPSILON_IDX = voltages_upper + self.U_SUP_IDX = self.EPSILON_IDX + 1 + self._ode_epsilon_idx = self._motor_ode_idx[-1] + + def simulate(self, action, *_, **__): + # Docstring of superclass + ode_state = self._ode_solver.y + eps = ode_state[self._ode_epsilon_idx] + if self.control_space == "dq": + action = self.dq_to_abc_space(action, eps) + i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + switching_times = self._converter.set_action(action, self._t) + + for t in switching_times[:-1]: + i_sup = self._converter.i_sup(i_in) + u_sup = self._supply.get_voltage(self._t, i_sup) + u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = [u * u_s for u in u_in for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_in, eps) + self._ode_solver.set_f_params(u_dq) + ode_state = self._ode_solver.integrate(t) + eps = ode_state[self._ode_epsilon_idx] + i_in = self.dq_to_abc_space(self._electrical_motor.i_in(ode_state[self._ode_currents_idx]), eps) + + i_sup = self._converter.i_sup(i_in) + u_sup = self._supply.get_voltage(self._t, i_sup) + u_in = self._converter.convert(i_in, self._ode_solver.t) + u_in = [u * u_s for u in u_in for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_in, eps) + self._ode_solver.set_f_params(u_dq) + ode_state = self._ode_solver.integrate(self._t + self._tau) + self._t = self._ode_solver.t + self._k += 1 + torque = self._electrical_motor.torque(ode_state[self._motor_ode_idx]) + mechanical_state = ode_state[self._load_ode_idx] + i_dq = ode_state[self._ode_currents_idx] + i_abc = list(self.dq_to_abc_space(i_dq, eps)) + eps = ode_state[self._ode_epsilon_idx] % (2 * np.pi) + if eps > np.pi: + eps -= 2 * np.pi + + system_state = np.concatenate((mechanical_state, [torque], i_abc, i_dq, u_in, u_dq, [eps], u_sup)) + return system_state / self._limits + + def reset(self, *_): + # Docstring of superclass + motor_state = self._electrical_motor.reset(state_space=self.state_space, state_positions=self.state_positions) + mechanical_state = self._mechanical_load.reset( + state_positions=self.state_positions, + state_space=self.state_space, + nominal_state=self.nominal_state, + ) + ode_state = np.concatenate((mechanical_state, motor_state)) + u_sup = self.supply.reset() + eps = ode_state[self._ode_epsilon_idx] + if eps > np.pi: + eps -= 2 * np.pi + u_abc = self.converter.reset() + u_abc = [u * u_s for u in u_abc for u_s in u_sup] + u_dq = self.abc_to_dq_space(u_abc, eps) + i_dq = ode_state[self._ode_currents_idx] + i_abc = self.dq_to_abc_space(i_dq, eps) + torque = self.electrical_motor.torque(motor_state) + self._t = 0 + self._k = 0 + self._ode_solver.set_initial_value(ode_state, self._t) + system_state = np.concatenate( + ( + mechanical_state, + [torque], + i_abc, + i_dq, + u_abc, + u_dq, + [eps], + u_sup, + ) + ) + return system_state / self._limits \ No newline at end of file From 259da45a571de451caa483b0bf2ab9043f5d0f84 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Thu, 20 Mar 2025 01:41:52 +0530 Subject: [PATCH 05/14] Cont-CC-SIXPMSM-v0 environment ready- need critical reviews --- .../classic_controllers}/demoSixPhasePMSM.py | 11 ++- .../gym_sixphase_pmsm/cont_cc_sixpmsm_env.py | 10 +- .../electric_motors/SixPhase_PMSM.py | 98 ++++++++++++++++--- .../electric_motors/six_phase_motor.py | 21 ++-- .../physical_systems/physical_systems.py | 7 +- 5 files changed, 113 insertions(+), 34 deletions(-) rename {src/gym_electric_motor => examples/classic_controllers}/demoSixPhasePMSM.py (62%) diff --git a/src/gym_electric_motor/demoSixPhasePMSM.py b/examples/classic_controllers/demoSixPhasePMSM.py similarity index 62% rename from src/gym_electric_motor/demoSixPhasePMSM.py rename to examples/classic_controllers/demoSixPhasePMSM.py index 88e5fdc7..df482412 100644 --- a/src/gym_electric_motor/demoSixPhasePMSM.py +++ b/examples/classic_controllers/demoSixPhasePMSM.py @@ -1,11 +1,18 @@ import gym_electric_motor as gem from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator - +from gym_electric_motor.visualization import MotorDashboard +from gym_electric_motor.visualization.render_modes import RenderMode +from externally_referenced_state_plot import ExternallyReferencedStatePlot # Select a different ode_solver with default parameters by passing a keystring + + + env = gem.make( "Cont-CC-SIXPMSM-v0",) terminated = True for _ in range(1000): if terminated: state, reference = env.reset() - (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) \ No newline at end of file + (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + +env.close() \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py index bc7d05a7..b3604884 100644 --- a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py @@ -42,14 +42,18 @@ def __init__( WienerProcessReferenceGenerator(reference_state="i_sd"), WienerProcessReferenceGenerator(reference_state="i_sq"), WienerProcessReferenceGenerator(reference_state="i_sx"), - WienerProcessReferenceGenerator(reference_state="i_sy") + WienerProcessReferenceGenerator(reference_state="i_sy"), ) + default_sub_converters = ( + ps.ContB6BridgeConverter(), + ps.ContB6BridgeConverter(), + ) physical_system = SixPhasePMSM( supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), - converter=initialize(ps.PowerElectronicConverter, converter, ps.ContB6BridgeConverter, dict()), + converter=initialize(ps.PowerElectronicConverter,converter,ps.ContMultiConverter,dict(subconverters=default_sub_converters),), motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), - load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict(load_parameter=dict(a=0.01, b=0.01, c=0.0))), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), calc_jacobian=calc_jacobian, tau=tau, diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py index 8ba4b8d1..da320396 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py @@ -44,7 +44,7 @@ class SixPhasePMSM(SixPhaseMotor): u_sd V Direct axis voltage u_sq V Quadrature axis voltage u_sx V - u_sx V + u_sy V u_a1 V u_a2 V u_b1 V @@ -82,16 +82,17 @@ def initializer(self): #### Parameters taken from https://ieeexplore.ieee.org/document/10372153 _default_motor_parameter = {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3, "j_rotor": 0.0110,} - # - _default_limits =dict(omega=4e3 * np.pi / 30, torque=0.0, i=400, epsilon=math.pi, u=300) - #_default_nominal_values = ?rated + HAS_JACOBIAN = True + _default_limits =dict(omega=4e3 * np.pi / 30, torque=10.0, i=400, epsilon=math.pi, u=300) + _default_nominal_values = dict(omega=3e3 * np.pi / 30, torque=0.0, i=240, epsilon=math.pi, u=300) _default_initializer = { - "states": {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0}, + "states": {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0, "epsilon": 0.0}, "interval": None, "random_init": None, "random_params": (None, None), } - + IO_VOLTAGES = ["u_a1", "u_b1", "u_c1", "u_a2", "u_b2", "u_c2", "u_sd", "u_sq", "u_sx", "u_sy"] + IO_CURRENTS = ["i_a1", "i_b1", "i_a2", "i_b2", "i_c2", "i_c1", "i_sd", "i_sq", "i_sx", "i_sy"] _model_constants = None _initializer = None @@ -121,7 +122,8 @@ def _update_model(self): [ 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, 0, 0, mp['l_q'], 0, 0], [-mp['psi_PM'], 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, -mp['l_d'], 0, 0, 0], [ 0, 0, 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, 0, 0, -mp['l_y']], - [ 0, 0, 0, 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, mp['l_x'], 0] + [ 0, 0, 0, 0, -mp['r_s'], 0, 0, 0, 1, 0, 0, mp['l_x'], 0], + [ mp['p'], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ]) self._model_constants[self.I_SD_IDX] = self._model_constants[self.I_SD_IDX] / mp["l_d"] @@ -135,12 +137,12 @@ def electrical_ode(self, state, u_dqxy, omega, *_): The differential equation of the Six phase PMSM. Args: - state: The current state of the motor. [i_sd, i_sq, i_sx, i_sy] + state: The current state of the motor. [i_sd, i_sq, i_sx, i_sy, epsilon] omega: electrical rotational speed u_qdxy: The input voltages [u_sd, u_sq, u_sx, u_sy] Returns: - The derivatives of the state vector d/dt([i_sd, i_sq, i_sx, i_sy]) + The derivatives of the state vector d/dt([i_sd, i_sq, i_sx, i_sy, epsilon]) """ return np.matmul( self._model_constants, @@ -162,7 +164,62 @@ def electrical_ode(self, state, u_dqxy, omega, *_): ] ), ) - + def electrical_jacobian(self, state, u_in, omega, *args): + mp = self._motor_parameter + return ( + np.array( + [ # dx'/dx + # i_sd i_sq i_sx i_sy epsilon + [ + -mp["r_s"] / mp["l_d"], + mp["l_q"] / mp["l_d"] * omega, + 0, + 0, + 0 + ], + [ + -mp["l_d"] / mp["l_q"] * omega, + -mp["r_s"] / mp["l_q"], + 0, + 0, + 0 + ], + [ + 0, + 0, + -mp["r_s"] / mp["l_x"], + -mp["l_y"] / mp["l_x"] * omega, + 0 + ], + [ + 0, + 0, + mp["l_x"] / mp["l_y"] * omega, + -mp["r_s"] / mp["l_y"], + 0 + ], + [0, 0, 0, 0, 0], + ] + ), + np.array( + [ # dx'/dw + mp["l_q"] / mp["l_d"], + -mp["l_d"] / mp["l_q"], + -mp["l_y"] / mp["l_x"], + mp["l_x"] / mp["l_y"], + mp["p"], + ] + ), + np.array( + [ # dT/dx + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[self.I_SQ_IDX], + 1.5 * mp["p"] * (mp["psi_PM"] + (mp["l_d"] - mp["l_q"]) * state[self.I_SD_IDX]), + 0, + 0, + 0 + ] + ), + ) def i_in(self, state): # Docstring of superclass @@ -176,13 +233,30 @@ def reset(self, state_space, state_positions, **__): else: return np.zeros(len(self.CURRENTS) + 1) + #from pmsm def torque(self, currents): # Docstring of superclass mp = self._motor_parameter return ( - 1.5 * mp["p"] * (mp["psi_PM "] + (mp["l_d"] - mp["l_q"]) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] + 1.5 * mp["p"] * (mp["psi_PM"] + (mp["l_d"] - mp["l_q"]) * currents[self.I_SD_IDX]) * currents[self.I_SQ_IDX] ) #torque limit ? - \ No newline at end of file + + def _update_limits(self): + # Docstring of superclass + + voltage_limit = 0.5 * self._limits["u"] + voltage_nominal = 0.5 * self._nominal_values["u"] + + limits_agenda = {} + nominal_agenda = {} + for u, i in zip(self.IO_VOLTAGES, self.IO_CURRENTS): + limits_agenda[u] = voltage_limit + nominal_agenda[u] = voltage_nominal + limits_agenda[i] = self._limits.get("i", None) or self._limits[u] / self._motor_parameter["r_s"] + nominal_agenda[i] = ( + self._nominal_values.get("i", None) or self._nominal_values[u] / self._motor_parameter["r_s"] + ) + super()._update_limits(limits_agenda, nominal_agenda) \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py index 6afb821a..320a58eb 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_motor.py @@ -3,7 +3,7 @@ import numpy as np from .electric_motor import ElectricMotor - +from scipy.linalg import block_diag class SixPhaseMotor(ElectricMotor): """ @@ -60,8 +60,8 @@ def q(quantities, epsilon): tp_alphaBetaXY = np.array([ [cos, sin, 0, 0, 0, 0], [-sin, cos, 0, 0, 0, 0], - [0, 0, cos, sin, 0, 0], - [0, 0, -sin, cos, 0, 0], + [0, 0, cos, -sin, 0, 0], + [0, 0, sin, cos, 0, 0], [0, 0, 0, 0, 1, 0] [0, 0, 0, 0, 0, 1] ]) @@ -81,10 +81,7 @@ def rotation_matrix(theta): t1 = rotation_matrix(epsilon) t2 = rotation_matrix(-epsilon) - tp_alphaBetaXY = np.block([ - [t1, np.zeros(t1.shape[0],t1.shape[1])], - [np.zeros(t2.shape[0],t2.shape[1]), t2], - ]) + tp_alphaBetaXY = block_diag(t1,t2) tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) return np.matmul(tp_vsd, quantities) @@ -122,11 +119,7 @@ def q_inv(quantities, epsilon): ]) tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) inv_tpVsd = np.linalg.inv(tp_vsd) - return np.matmul(inv_tpVsd, quantities) + modified_inv_tpVsd = np.delete(inv_tpVsd, [4, 5], axis=1) + return np.matmul(modified_inv_tpVsd, quantities) - def _torque_limit(self): - """ - Returns: - Maximal possible torque for the given limits in self._limits - """ - raise NotImplementedError() \ No newline at end of file + \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index c0edb1fd..f2fd7f80 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1167,7 +1167,7 @@ def __init__(self, control_space="abc", **kwargs): """ super().__init__(**kwargs) self.control_space = control_space - if control_space == "abc": + if control_space == "dq": assert ( type(self._converter.action_space) == Box ), "" @@ -1199,7 +1199,7 @@ def _build_state_names(self): "u_c1", "u_a2", "u_b2", - "u_c2" + "u_c2", "u_sd", "u_sq", "u_sx", @@ -1272,8 +1272,9 @@ def simulate(self, action, *_, **__): eps -= 2 * np.pi system_state = np.concatenate((mechanical_state, [torque], i_abc, i_dq, u_in, u_dq, [eps], u_sup)) + print(system_state / self._limits) return system_state / self._limits - + def reset(self, *_): # Docstring of superclass motor_state = self._electrical_motor.reset(state_space=self.state_space, state_positions=self.state_positions) From c416b93c947853978d53f78a895f7fc9b6ad0116 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Wed, 26 Mar 2025 20:37:37 +0530 Subject: [PATCH 06/14] Completed SIXPMSM environments --- src/gym_electric_motor/__init__.py | 17 +- src/gym_electric_motor/envs/__init__.py | 17 ++ .../envs/gym_sixphase_pmsm/__init__.py | 7 +- .../gym_sixphase_pmsm/cont_cc_sixpmsm_env.py | 106 +++++++++- .../gym_sixphase_pmsm/cont_sc_sixpmsm_env.py | 185 +++++++++++++++++ .../gym_sixphase_pmsm/cont_tc_sixpmsm_env.py | 186 +++++++++++++++++ .../finite_cc_sixpmsm_env.py | 193 ++++++++++++++++++ .../finite_sc_sixpmsm_env.py | 186 +++++++++++++++++ .../finite_tc_sixpmsm_env.py | 185 +++++++++++++++++ .../physical_systems/physical_systems.py | 2 +- 10 files changed, 1080 insertions(+), 4 deletions(-) create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_sc_sixpmsm_env.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_tc_sixpmsm_env.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_cc_sixpmsm_env.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_sc_sixpmsm_env.py create mode 100644 src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_tc_sixpmsm_env.py diff --git a/src/gym_electric_motor/__init__.py b/src/gym_electric_motor/__init__.py index cab89988..e9f36731 100644 --- a/src/gym_electric_motor/__init__.py +++ b/src/gym_electric_motor/__init__.py @@ -285,4 +285,19 @@ #sixphase machines register( id="Cont-CC-SIXPMSM-v0", entry_point=envs_path + "ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs -) \ No newline at end of file +) +register( + id="Cont-SC-SIXPMSM-v0", entry_point=envs_path + "ContSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) +register( + id="Cont-TC-SIXPMSM-v0", entry_point=envs_path + "ContTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) +register( + id="Finite-CC-SIXPMSM-v0", entry_point=envs_path + "FiniteCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) +register( + id="Finite-SC-SIXPMSM-v0", entry_point=envs_path + "FiniteSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) +register( + id="Finite-TC-SIXPMSM-v0", entry_point=envs_path + "FiniteTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv", **registration_kwargs +) diff --git a/src/gym_electric_motor/envs/__init__.py b/src/gym_electric_motor/envs/__init__.py index 43a2f6c1..e8e38a31 100644 --- a/src/gym_electric_motor/envs/__init__.py +++ b/src/gym_electric_motor/envs/__init__.py @@ -52,7 +52,24 @@ ) from .gym_sixphase_pmsm.cont_cc_sixpmsm_env import ( ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv, + ) +from .gym_sixphase_pmsm.cont_sc_sixpmsm_env import ( + ContSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv, +) +from .gym_sixphase_pmsm.cont_tc_sixpmsm_env import ( + ContTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv, +) +from .gym_sixphase_pmsm.finite_cc_sixpmsm_env import ( + FiniteCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv, +) +from .gym_sixphase_pmsm.finite_sc_sixpmsm_env import ( + FiniteSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv, +) +from .gym_sixphase_pmsm.finite_tc_sixpmsm_env import ( + FiniteTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv, +) + from .gym_im import ( ContCurrentControlDoublyFedInductionMotorEnv, ContCurrentControlSquirrelCageInductionMotorEnv, diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py index 8b1f8646..6a1636e3 100644 --- a/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/__init__.py @@ -1 +1,6 @@ -from .cont_cc_sixpmsm_env import ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv \ No newline at end of file +from .cont_cc_sixpmsm_env import ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv +from .cont_sc_sixpmsm_env import ContSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv +from .cont_tc_sixpmsm_env import ContTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv +from .finite_cc_sixpmsm_env import FiniteCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv +from .finite_sc_sixpmsm_env import FiniteSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv +from .finite_tc_sixpmsm_env import FiniteTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py index b3604884..68852f1b 100644 --- a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_cc_sixpmsm_env.py @@ -18,6 +18,74 @@ class ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate an abc-domain continuous control set current controlled six phase permanent magnet synchr. motor. + + Key: + ``'Cont-CC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5, 'i_sx' = 0.5, 'i_sy' = 0.5`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['i_sd', 'i_sq', 'i_sx', 'i_sy']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1] * 6, high=[1] * 6) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Cont-CC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ def __init__( self, @@ -31,12 +99,48 @@ def __init__( visualization=None, state_filter=None, callbacks=(), - constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + constraints=(SquaredConstraint(("i_sd", "i_sq", "i_sx", "i_sy")),), calc_jacobian=True, tau=1e-4, physical_system_wrappers=(), **kwargs, ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ default_subgenerators = ( WienerProcessReferenceGenerator(reference_state="i_sd"), diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_sc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_sc_sixpmsm_env.py new file mode 100644 index 00000000..61b17c21 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_sc_sixpmsm_env.py @@ -0,0 +1,185 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class ContSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate an abc-domain continuous control set speed controlled six phase permanent magnet synchr. motor. + + Key: + ``'Cont-SC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.PolynomialStaticLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['omega']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1] * 6, high=[1] * 6) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Cont-SC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + + default_sub_converters = ( + ps.ContB6BridgeConverter(), + ps.ContB6BridgeConverter(), + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter,converter,ps.ContMultiConverter,dict(subconverters=default_sub_converters),), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict(load_parameter=dict(a=0.01, b=0.01, c=0.0))), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state="omega"), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(omega=1.0)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("omega",), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_tc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_tc_sixpmsm_env.py new file mode 100644 index 00000000..e6a561c6 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/cont_tc_sixpmsm_env.py @@ -0,0 +1,186 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class ContTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate an abc-domain continuous control set torque controlled six phase permanent magnet synchr. motor. + + Key: + ``'Cont-TC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.ContB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['torque']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + Box(low=[-1] * 6, high=[1] * 6) + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Cont-TC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ + + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + + default_sub_converters = ( + ps.ContB6BridgeConverter(), + ps.ContB6BridgeConverter(), + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter,converter,ps.ContMultiConverter,dict(subconverters=default_sub_converters),), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state="torque"), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(torque=1.0)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("torque",), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_cc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_cc_sixpmsm_env.py new file mode 100644 index 00000000..9efd89a3 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_cc_sixpmsm_env.py @@ -0,0 +1,193 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + MultipleReferenceGenerator, + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class FiniteCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate finite control set current controlled six phase permanent magnet synchr. motor. + + Key: + ``'Finite-CC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'i_sd' = 0.5, 'i_sq' = 0.5, 'i_sx' = 0.5, 'i_sy' = 0.5`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['i_sd', 'i_sq', 'i_sx', 'i_sy']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Finite-CC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ + + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + + default_subgenerators = ( + WienerProcessReferenceGenerator(reference_state="i_sd"), + WienerProcessReferenceGenerator(reference_state="i_sq"), + WienerProcessReferenceGenerator(reference_state="i_sx"), + WienerProcessReferenceGenerator(reference_state="i_sy"), + ) + default_sub_converters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteB6BridgeConverter(), + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter,converter,ps.FiniteMultiConverter,dict(subconverters=default_sub_converters),), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + MultipleReferenceGenerator, + dict(sub_generators=default_subgenerators), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(i_sd=0.5, i_sq=0.5, i_sx=0.5, i_sy=0.5,)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("i_sd", "i_sq"), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_sc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_sc_sixpmsm_env.py new file mode 100644 index 00000000..e7ba6904 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_sc_sixpmsm_env.py @@ -0,0 +1,186 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class FiniteSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate Finite control set speed controlled six phase permanent magnet synchr. motor. + + Key: + ``'Finite-SC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.PolynomialStaticLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'omega'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'omega' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['omega']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Finite-SC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ + + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + + default_sub_converters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteB6BridgeConverter(), + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter,converter,ps.FiniteMultiConverter,dict(subconverters=default_sub_converters),), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.PolynomialStaticLoad, dict(load_parameter=dict(a=0.01, b=0.01, c=0.0))), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state="omega"), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(omega=1.0)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("omega",), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_tc_sixpmsm_env.py b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_tc_sixpmsm_env.py new file mode 100644 index 00000000..79e83109 --- /dev/null +++ b/src/gym_electric_motor/envs/gym_sixphase_pmsm/finite_tc_sixpmsm_env.py @@ -0,0 +1,185 @@ + +from gym_electric_motor import physical_systems as ps +from gym_electric_motor.constraints import SquaredConstraint +from gym_electric_motor.core import ( + ElectricMotorEnvironment, + ElectricMotorVisualization, + ReferenceGenerator, + RewardFunction, +) +from gym_electric_motor.physical_systems.physical_systems import SixPhasePMSM +from gym_electric_motor.reference_generators import ( + WienerProcessReferenceGenerator, +) +from gym_electric_motor.reward_functions import WeightedSumOfErrors +from gym_electric_motor.utils import initialize +from gym_electric_motor.visualization import MotorDashboard + + +class FiniteTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv(ElectricMotorEnvironment): + """ + Description: + Environment to simulate Finite control set torque controlled six phase permanent magnet synchr. motor. + + Key: + ``' Finite-TC-SIXPMSM-v0'`` + + Default Components: + - Supply: :py:class:`.IdealVoltageSupply` + - Converter: :py:class:`.FiniteB6BridgeConverter` + - Motor: :py:class:`.SixPhasePMSM` + - Load: :py:class:`.ConstantSpeedLoad` + - Ode-Solver: :py:class:`.ScipyOdeSolver` + + - Reference Generator: :py:class:`.WienerProcessReferenceGenerator` *Reference Quantity:* ``'torque'`` + + - Reward Function: :py:class:`.WeightedSumOfErrors` reward_weights: ``'torque' = 1.0`` + + - Visualization: :py:class:`.MotorDashboard` current and action plots + + - Constraints: :py:class:`.SquaredConstraint` on the currents ``'i_sd', 'i_sq', 'i_sx', 'i_sy'`` + + State Variables: + ``['omega' , 'torque', 'i_a1', 'i_b1', 'i_c1', 'i_a2', 'i_b2', 'i_c2', 'i_sd', 'i_sq', 'i_sx', 'i_sy', 'u_a1', 'u_b1', 'u_c1', 'u_a2', 'u_b2', 'u_c2', 'u_sd', 'u_sq', 'u_sx', 'u_sy', 'epsilon', 'u_sup']`` + + Reference Variables: + ``['torque']`` + + Control Cycle Time: + tau = 1e-4 seconds + + Observation Space: + Type: Tuple(State_Space, Reference_Space) + + State Space: + Box(low=23 * [-1], high=23 * [1]) + + Reference Space: + Box(low=[-1, -1], high=[1, 1]) + + Action Space: + + + Initial State: + Zeros on all state variables. + + Example: + >>> import gym_electric_motor as gem + >>> from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator + >>> # Update the default arguments to the voltage supply by passing a parameter dict + >>> my_changed_voltage_supply_args = {'u_nominal': 400.0} + >>> + >>> # Replace the reference generator by passing a new instance + >>> my_new_ref_gen_instance = LaplaceProcessReferenceGenerator( + ... reference_state='i_sq', + ... sigma_range=(1e-3, 1e-2) + ... ) + >>> env = gem.make( + ... "Finite-TC-SIXPMSM-v0", + ... voltage_supply=my_changed_voltage_supply_args, + ... reference_generator=my_new_ref_gen_instance + ... ) + >>> terminated = True + >>> for _ in range(1000): + >>> if terminated: + >>> state, reference = env.reset() + >>> (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + """ + def __init__( + self, + supply=None, + converter=None, + motor=None, + load=None, + ode_solver=None, + reward_function=None, + reference_generator=None, + visualization=None, + state_filter=None, + callbacks=(), + constraints=(SquaredConstraint(("i_sq", "i_sd", "i_sx", "i_sy")),), + calc_jacobian=True, + tau=1e-4, + physical_system_wrappers=(), + **kwargs, + ): + """ + Args: + supply(env-arg): Specification of the :py:class:`.VoltageSupply` for the environment + converter(env-arg): Specification of the :py:class:`.PowerElectronicConverter` for the environment + motor(env-arg): Specification of the :py:class:`.ElectricMotor` for the environment + load(env-arg): Specification of the :py:class:`.MechanicalLoad` for the environment + ode_solver(env-arg): Specification of the :py:class:`.OdeSolver` for the environment + reward_function(env-arg): Specification of the :py:class:`.RewardFunction` for the environment + reference_generator(env-arg): Specification of the :py:class:`.ReferenceGenerator` for the environment + visualization(env-arg): Specification of the :py:class:`.ElectricMotorVisualization` for the environment + constraints(iterable(str/Constraint)): All Constraints of the environment. \n + - str: A LimitConstraints for states (episode terminates, if the quantity exceeds the limit) + can be directly specified by passing the state name here (e.g. 'i', 'omega') \n + - instance of Constraint: More complex constraints (e.g. the SquaredConstraint can be initialized and + passed to the environment. + calc_jacobian(bool): Flag, if the jacobian of the environment shall be taken into account during the + simulation. This may lead to speed improvements. Default: True + tau(float): Duration of one control step in seconds. Default: 1e-4. + state_filter(list(str)): List of states that shall be returned to the agent. Default: None (no filter) + callbacks(list(Callback)): Callbacks for user interaction. Default: () + physical_system_wrappers(list(PhysicalSystemWrapper)): List of Physical System Wrappers to modify the + actions to and states from the physical system before they are used in the environment. Default: () + + Note on the env-arg type: + All parameters of type env-arg can be selected as one of the following types: + + **instance:** Pass an already instantiated object derived from the corresponding base class + (e.g. ``reward_function=MyRewardFunction()``). This is directly used in the environment. + + **dict:** Pass a dict to update the default parameters of the default type. + (e.g. ``visualization=dict(state_plots=('omega', 'u'))``) + + **str:** Pass a string out of the registered classes to select a different class for the component. + This class is then initialized with its default parameters. + The available strings can be looked up in the documentation. (e.g. ``converter='Finite-2QC'``) + """ + + default_sub_converters = ( + ps.FiniteB6BridgeConverter(), + ps.FiniteB6BridgeConverter(), + ) + + physical_system = SixPhasePMSM( + supply=initialize(ps.VoltageSupply, supply, ps.IdealVoltageSupply, dict(u_nominal=300.0)), + converter=initialize(ps.PowerElectronicConverter,converter,ps.FiniteMultiConverter,dict(subconverters=default_sub_converters),), + motor=initialize(ps.ElectricMotor, motor, ps.electric_motors.SixPhasePMSM, dict()), + load=initialize(ps.MechanicalLoad, load, ps.ConstantSpeedLoad, dict(omega_fixed=100.0)), + ode_solver=initialize(ps.OdeSolver, ode_solver, ps.ScipyOdeSolver, dict()), + calc_jacobian=calc_jacobian, + tau=tau, + ) + reference_generator = initialize( + ReferenceGenerator, + reference_generator, + WienerProcessReferenceGenerator, + dict(reference_state="torque"), + ) + reward_function = initialize( + RewardFunction, + reward_function, + WeightedSumOfErrors, + dict(reward_weights=dict(torque=1.0)), + ) + visualization = initialize( + ElectricMotorVisualization, + visualization, + MotorDashboard, + dict(state_plots=("torque",), action_plots="all"), + ) + super().__init__( + physical_system=physical_system, + reference_generator=reference_generator, + reward_function=reward_function, + constraints=constraints, + visualization=visualization, + state_filter=state_filter, + callbacks=callbacks, + physical_system_wrappers=physical_system_wrappers, + **kwargs, + ) \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index f2fd7f80..43cf554c 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1176,7 +1176,7 @@ def __init__(self, control_space="abc", **kwargs): def _build_state_space(self, state_names): # Docstring of superclass low = -1 * np.ones_like(state_names, dtype=float) - low[self.U_SUP_IDX -1] = 0.0 + low[self.U_SUP_IDX] = 0.0 high = np.ones_like(state_names, dtype=float) return Box(low, high, dtype=np.float64) From f4c7a8719be6da5d1256f35b33f10283a7deb989 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Mon, 14 Apr 2025 15:27:41 +0530 Subject: [PATCH 07/14] test and documentation for six pmsm --- docs/parts/environments/environment.rst | 12 +- .../environments/sixpmsm/cont_cc_sixpmsm.rst | 4 + .../environments/sixpmsm/cont_sc_sixpmsm.rst | 4 + .../environments/sixpmsm/cont_tc_sixpmsm.rst | 4 + .../sixpmsm/finite_cc_sixpmsm.rst | 4 + .../sixpmsm/finite_sc_sixpmsm.rst | 4 + .../sixpmsm/finite_tc_sixpmsm.rst | 4 + .../environments/sixpmsm/sixpmsm_envs.rst | 12 + .../electric_motors/electric_motor.rst | 2 + .../electric_motors/sixphase_base.rst | 11 + .../electric_motors/sixphase_pmsm.rst | 26 + .../classic_controllers/demoSixPhasePMSM.py | 27 +- .../electric_motors/__init__.py | 2 +- .../{SixPhase_PMSM.py => six_phase_PMSM.py} | 36 +- .../physical_systems/physical_systems.py | 1 - .../test_electric_motors.py | 1317 +++++++++++++++++ 16 files changed, 1444 insertions(+), 26 deletions(-) create mode 100644 docs/parts/environments/sixpmsm/cont_cc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/cont_sc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/cont_tc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/finite_cc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/finite_sc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/finite_tc_sixpmsm.rst create mode 100644 docs/parts/environments/sixpmsm/sixpmsm_envs.rst create mode 100644 docs/parts/physical_systems/electric_motors/sixphase_base.rst create mode 100644 docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst rename src/gym_electric_motor/physical_systems/electric_motors/{SixPhase_PMSM.py => six_phase_PMSM.py} (90%) diff --git a/docs/parts/environments/environment.rst b/docs/parts/environments/environment.rst index 9ca95f28..6b8f923b 100644 --- a/docs/parts/environments/environment.rst +++ b/docs/parts/environments/environment.rst @@ -8,7 +8,7 @@ In general, all environment-ids are structured as follows: - The ``ControlType`` is in ``{Finite / Cont}`` for finite control set and continuous control set action spaces - The ``ControlTask`` is in ``{TC / SC / CC}`` (Torque / Speed / Current Control) -- The ``MotorType`` is in ``{PermExDc / ExtExDc / SeriesDc / ShuntDc / PMSM / SynRM / / EESM / DFIM / SCIM }`` +- The ``MotorType`` is in ``{PermExDc / ExtExDc / SeriesDc / ShuntDc / PMSM / SynRM / / EESM / DFIM / SCIM / SIXPMSM }`` =================================================================== ============================== @@ -96,6 +96,15 @@ Speed Control DFIM Environment ``'Cont-SC- Finite Current Control DFIM Environment ``'Finite-CC-DFIM-v0'`` Current Control DFIM Environment ``'Cont-CC-DFIM-v0'`` +**Six Phase Permanent Magnet Synchronous Motor (SIXPMSM) Environments** + +Finite Torque Control SIXPMSM Environment ``'Finite-TC-SIXPMSM-v0'`` +Torque Control SIXPMSM Environment ``'Cont-TC-SIXPMSM-v0'`` +Finite Speed Control SIXPMSM Environment ``'Finite-SC-SIXPMSM-v0'`` +Speed Control SIXPMSM Environment ``'Cont-SC-SIXPMSM-v0'`` +Finite Current Control SIXPMSM Environment ``'Finite-CC-SIXPMSM-v0'`` +Current Control SIXPMSM Environment ``'Cont-CC-SIXPMSM-v0'`` + =================================================================== ============================== .. toctree:: @@ -112,6 +121,7 @@ Current Control DFIM Environment ``'Cont-CC- synrm/synrm_envs scim/scim_envs dfim/dfim_envs + sixpmsm/sixpmsm_envs Electric Motor Base Environment diff --git a/docs/parts/environments/sixpmsm/cont_cc_sixpmsm.rst b/docs/parts/environments/sixpmsm/cont_cc_sixpmsm.rst new file mode 100644 index 00000000..4f9ff30f --- /dev/null +++ b/docs/parts/environments/sixpmsm/cont_cc_sixpmsm.rst @@ -0,0 +1,4 @@ +Current Control Six Phase Permanent Magnet Synchronous Motor Environment +***************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/cont_sc_sixpmsm.rst b/docs/parts/environments/sixpmsm/cont_sc_sixpmsm.rst new file mode 100644 index 00000000..64813ec6 --- /dev/null +++ b/docs/parts/environments/sixpmsm/cont_sc_sixpmsm.rst @@ -0,0 +1,4 @@ +Speed Control Six Phase Permanent Magnet Synchronous Motor Environment +**************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/cont_tc_sixpmsm.rst b/docs/parts/environments/sixpmsm/cont_tc_sixpmsm.rst new file mode 100644 index 00000000..1710ce87 --- /dev/null +++ b/docs/parts/environments/sixpmsm/cont_tc_sixpmsm.rst @@ -0,0 +1,4 @@ +Torque Control Six Phase Permanent Magnet Synchronous Motor Environment +****************************************************************************** +.. autoclass:: gym_electric_motor.envs.ContTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/finite_cc_sixpmsm.rst b/docs/parts/environments/sixpmsm/finite_cc_sixpmsm.rst new file mode 100644 index 00000000..bf1d9f8b --- /dev/null +++ b/docs/parts/environments/sixpmsm/finite_cc_sixpmsm.rst @@ -0,0 +1,4 @@ +Finite Control Set Current Control Six Phase Permanent Magnet Synchronous Motor Environment +********************************************************************************** +.. autoclass:: gym_electric_motor.envs.FiniteCurrentControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/finite_sc_sixpmsm.rst b/docs/parts/environments/sixpmsm/finite_sc_sixpmsm.rst new file mode 100644 index 00000000..0a55b5a4 --- /dev/null +++ b/docs/parts/environments/sixpmsm/finite_sc_sixpmsm.rst @@ -0,0 +1,4 @@ +Finite Control Set Speed Control Six Phase Permanent Magnet Synchronous Motor Environment +******************************************************************************** +.. autoclass:: gym_electric_motor.envs.FiniteSpeedControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/finite_tc_sixpmsm.rst b/docs/parts/environments/sixpmsm/finite_tc_sixpmsm.rst new file mode 100644 index 00000000..ed47bfb7 --- /dev/null +++ b/docs/parts/environments/sixpmsm/finite_tc_sixpmsm.rst @@ -0,0 +1,4 @@ +Finite Control Set Torque Control Six Phase Permanent Magnet Synchronous Motor Environment +********************************************************************************* +.. autoclass:: gym_electric_motor.envs.FiniteTorqueControlSixPhasePermanentMagnetSynchronousMotorEnv + :members: diff --git a/docs/parts/environments/sixpmsm/sixpmsm_envs.rst b/docs/parts/environments/sixpmsm/sixpmsm_envs.rst new file mode 100644 index 00000000..262d76ac --- /dev/null +++ b/docs/parts/environments/sixpmsm/sixpmsm_envs.rst @@ -0,0 +1,12 @@ +Six Phase Permanent Magnet Synchronous Motor Environments +************************************************ + + +.. toctree:: + :maxdepth: 2 + :caption: Environments: + :glob: + + * + + diff --git a/docs/parts/physical_systems/electric_motors/electric_motor.rst b/docs/parts/physical_systems/electric_motors/electric_motor.rst index 18b0902f..ec0c0e76 100644 --- a/docs/parts/physical_systems/electric_motors/electric_motor.rst +++ b/docs/parts/physical_systems/electric_motors/electric_motor.rst @@ -18,6 +18,8 @@ Electric Motors three_phase_base synchronous_base induction_base + sixphase_base + sixphase_pmsm Electric Motor Base Class ************************************* diff --git a/docs/parts/physical_systems/electric_motors/sixphase_base.rst b/docs/parts/physical_systems/electric_motors/sixphase_base.rst new file mode 100644 index 00000000..5cb7d37d --- /dev/null +++ b/docs/parts/physical_systems/electric_motors/sixphase_base.rst @@ -0,0 +1,11 @@ +Base Six Phase Motor +############################ + + + +Code Documentation +****************** + +.. autoclass:: gym_electric_motor.physical_systems.electric_motors.SixPhaseMotor + :members: + :inherited-members: diff --git a/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst new file mode 100644 index 00000000..81f744fd --- /dev/null +++ b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst @@ -0,0 +1,26 @@ +Six Phase Permanent Magnet Synchronous Motor +################################## + + +Electrical ODE +************** + +.. math:: + \frac{\mathrm{d} i_s^d}{\mathrm{d} t} = \frac{u_s^d + \omega_\mathrm{el} L_s^q i_s^q - R_s i_s^d}{L_s^d} \\ + \frac{\mathrm{d} i_s^q}{\mathrm{d} t} = \frac{u_s^q - \omega_\mathrm{el} L_s^d i_s^d - R_s i_s^q - \omega_\mathrm{el}}{L_s^q} \\ + \frac{\mathrm{d} i_s^x}{\mathrm{d} t} = \frac{u_s^x - \omega_\mathrm{el} L_s^y i_s^y - R_s i_s^x}{L_s^x} \\ + \frac{\mathrm{d} i_s^y}{\mathrm{d} t} = \frac{u_s^y + \omega_\mathrm{el} L_s^x i_s^x - R_s i_s^y}{L_s^y} \\ + + + +Torque Equation +*************** + +.. math:: T=\frac{3}{2} p (\mathit{\Psi}_\mathrm{p} +(L_\mathrm{d}-L_\mathrm{q})i_\mathrm{sd}) i_\mathrm{sq} + +Code Documentation +****************** + +.. autoclass:: gym_electric_motor.physical_systems.electric_motors.SixPhasePMSM + :members: + :inherited-members: diff --git a/examples/classic_controllers/demoSixPhasePMSM.py b/examples/classic_controllers/demoSixPhasePMSM.py index df482412..09fa81cc 100644 --- a/examples/classic_controllers/demoSixPhasePMSM.py +++ b/examples/classic_controllers/demoSixPhasePMSM.py @@ -2,17 +2,34 @@ from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.visualization.render_modes import RenderMode -from externally_referenced_state_plot import ExternallyReferencedStatePlot - # Select a different ode_solver with default parameters by passing a keystring - +from gym_electric_motor.visualization.console_printer import ConsolePrinter +from gym_electric_motor.physical_systems import ConstantSpeedLoad +my_changed_voltage_supply_args = {'u_nominal': 200.0} +motor_dashboard = MotorDashboard(state_plots=("i_a1","i_a2"),render_mode=RenderMode.Figure) +load = ConstantSpeedLoad(omega_fixed=200) env = gem.make( - "Cont-CC-SIXPMSM-v0",) + "Cont-CC-SIXPMSM-v0", + supply = my_changed_voltage_supply_args, + load = load, + visualization = motor_dashboard) terminated = True for _ in range(1000): if terminated: state, reference = env.reset() - (state, reference), reward, terminated, truncated, _ = env.step(env.action_space.sample()) + (state, reference), reward, terminated, truncated, _ = env.step(0 * env.action_space.sample()) + +motor_dashboard.initialize() +fig = motor_dashboard.figure() +fig.gca().set_ylim([-20,+20]) +fig.gca().set_xlim([0,0.02]) +fig.get_axes()[0].set_ylim([-20,+20]) +fig.gca().legend(loc='upper right') +fig.get_axes()[0].legend(loc='upper right') +fig.savefig("test") +motor_dashboard.save_to_file() + +#console.render() env.close() \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/__init__.py b/src/gym_electric_motor/physical_systems/electric_motors/__init__.py index 9d11c27d..e4ede7de 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/__init__.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/__init__.py @@ -23,4 +23,4 @@ from .three_phase_motor import ThreePhaseMotor #six phase motors -from .SixPhase_PMSM import SixPhasePMSM \ No newline at end of file +from .six_phase_PMSM import SixPhasePMSM \ No newline at end of file diff --git a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py similarity index 90% rename from src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py rename to src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py index da320396..c1c6368e 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/SixPhase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py @@ -24,18 +24,18 @@ class SixPhasePMSM(SixPhaseMotor): =============== ====== ============================================= i_sd A Direct axis current i_sq A Quadrature axis current - i_sx A - i_sy A + i_sx A Stator current in the counter-rotating system + i_sy A Stator current in the counter-rotating system i_salpha A Stator current in alpha direction i_sbeta A Stator current in beta direction - i_sX A - i_sY A - i_sa1 A - i_sa2 A - i_sb1 A - i_sb2 A - i_sc1 A - i_sc2 A + i_sX A Stator current in X direction + i_sY A Stator current in Y direction + i_sa1 A Stator current in phase a1 + i_sa2 A Stator current in phase a2 + i_sb1 A Stator current in phase b1 + i_sb2 A Stator current in phase b2 + i_sc1 A Stator current in phase c1 + i_sc2 A Stator current in phase c2 =============== ====== ============================================= =============== ====== ============================================= @@ -43,14 +43,14 @@ class SixPhasePMSM(SixPhaseMotor): =============== ====== ============================================= u_sd V Direct axis voltage u_sq V Quadrature axis voltage - u_sx V - u_sy V - u_a1 V - u_a2 V - u_b1 V - u_b2 V - u_c1 V - u_c2 V + u_sx V voltage in the counter-rotating system + u_sy V voltage in the counter-rotating system + u_a1 V Phase voltage in phase a1 + u_a2 V Phase voltage in phase a2 + u_b1 V Phase voltage in phase b1 + u_b2 V Phase voltage in phase b2 + u_c1 V Phase voltage in phase c1 + u_c2 V Phase voltage in phase c2 =============== ====== ============================================= ======== =========================================================== diff --git a/src/gym_electric_motor/physical_systems/physical_systems.py b/src/gym_electric_motor/physical_systems/physical_systems.py index 43cf554c..3845127e 100644 --- a/src/gym_electric_motor/physical_systems/physical_systems.py +++ b/src/gym_electric_motor/physical_systems/physical_systems.py @@ -1272,7 +1272,6 @@ def simulate(self, action, *_, **__): eps -= 2 * np.pi system_state = np.concatenate((mechanical_state, [torque], i_abc, i_dq, u_in, u_dq, [eps], u_sup)) - print(system_state / self._limits) return system_state / self._limits def reset(self, *_): diff --git a/tests/test_physical_systems/test_electric_motors.py b/tests/test_physical_systems/test_electric_motors.py index e69de29b..9a1ce8bf 100644 --- a/tests/test_physical_systems/test_electric_motors.py +++ b/tests/test_physical_systems/test_electric_motors.py @@ -0,0 +1,1317 @@ +import math +import pytest +import gym_electric_motor as gem +from gym_electric_motor.physical_systems.electric_motors import ( + ElectricMotor, + DcMotor, + DcExternallyExcitedMotor, + DcPermanentlyExcitedMotor, + DcSeriesMotor, + DcShuntMotor, + ThreePhaseMotor, + InductionMotor, + DoublyFedInductionMotor, + SquirrelCageInductionMotor, + SynchronousMotor, + SynchronousReluctanceMotor, + ExternallyExcitedSynchronousMotor, + PermanentMagnetSynchronousMotor, + SixPhasePMSM +) +from gym_electric_motor.physical_systems.electric_motors.six_phase_motor import SixPhaseMotor +from gymnasium.spaces import Box +from scipy.linalg import block_diag +import numpy as np + +#parameters for dc motor +test_dcmotor_parameter = {"r_a": 16e-2, "r_e": 16e-1, "l_a": 19e-5, "l_e_prime": 1.7e-2, "l_e": 5.4e-1, "j_rotor": 0.025} +test_dcmotor_nominal_values = dict(omega=350, torque=16.5, i=100, i_a=96, i_e=87, u=65, u_a=65, u_e=61) +test_dcmotor_limits = dict(omega=400, torque=38.0, i=210, i_a=210, i_e=215, u=100, u_a=100, u_e=100) +test_dcmotor_default_initializer = {"states": {"i_a": 10.0, "i_e": 15.0}, "interval": None, "random_init": None, "random_params": (None, None),} +test_electricmotor_default_initializer = {"states": {"omega": 16.0}, "interval": None, "random_init": None, "random_params": (None, None),} +test_DcPermanentlyExcitedMotor_parameter = {"r_a": 16e-5, "l_a": 19e-2, "psi_e": 0.00165, "j_rotor": 0.05,} +test_DcPermanentlyExcitedMotor_initializer = {"states": {"i": 10.0}, "interval": None, "random_init": None, "random_params": (None, None),} +test_DcSeriesMotor_parameter = {"r_a": 16e-2,"r_e": 48e-2,"l_a": 19e-3,"l_e_prime": 1.7e-2,"l_e": 5.4e-2,"j_rotor": 0.25,} +test_DcSeriesMotor_initializer = {"states": {"i": 5.0}, "interval": None, "random_init": None, "random_params": (None, None),} +test_DcShuntMotor_parameter = {"r_a": 16e-3, "r_e": 5e-1, "l_a": 20e-6, "l_e_prime": 1.7e-3, "l_e": 5.4e-3, "j_rotor": 0.025,} +test_DcShuntMotor_initializer = {"states": {"i_a": 10.0, "i_e": 0.0},"interval": None,"random_init": None,"random_params": (None, None),} +test_Induction_motor_parameter = {"p": 3,"l_m": 143.75e-3,"l_sigs": 5.87e-3,"l_sigr": 5.87e-2,"j_rotor": 1.1e-3,"r_s": 3,"r_r": 1.355,} +test_DcInductionMotor_initializer = {"states": {"i_salpha": 1.0,"i_sbeta": 0.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 0.0,},"interval": None,"random_init": None,"random_params": (None, None),} +test_ReluctanceMotor_parameter = {"p": 4,"l_d": 1e-3,"l_q": 1e-3,"j_rotor": 1e-3,"r_s": 0.5,} +test_ReluctanceMotor_initializer = { + "states": {"i_sq": 1.0, "i_sd": 2.0, "epsilon": 10.0}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } +test_ExtExcSyncMotor_parameter = {'p': 3,'l_d': 1.66e-1,'l_q': 0.35e-1,'l_m': 1.589e-5,'l_e': 1.74e-3,'j_rotor': 0.3883,'r_s': 15.55e-3,'r_e': 7.2e-3,'k': 65,} +test_ExtExcSyncMotor_initializer = { + "states": {"i_sq": 2.0, "i_sd": 1.0, "i_e": 2.0, "epsilon": 2.0}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } +test_PermMagSyncMotor_parameter = {"p": 3,"l_d": 0.37e-6,"l_q": 1.2e-6,"j_rotor": 0.03883,"r_s": 18,"psi_p": 66e-6,} +test_PermMagSyncMotor_initializer = {"states": {"i_sq": 10.0, "i_sd": 5.0, "epsilon": 10.0}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } +test_SixPhasePMSM_parameter = {"p": 6, "l_d": 125e-5, "l_q": 126e-5, "l_x": 39e-3, "l_y": 35e-3, "r_s": 64.3e-6, "psi_PM": 4.7e-6, "j_rotor": 0.0110,} +test_SixPhasePMSM_initializer = { + "states": {"i_sd": 10.0, "i_sq": 20.0, "i_sx": 10.0, "i_sy": 20.0, "epsilon": 0.0}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } +@pytest.fixture +def concreteDCMotor(): + """ + pytest fixture that returns a DC motor object + :return: Electric motor object initialized with concrete values + """ + return DcMotor(test_dcmotor_parameter,test_dcmotor_nominal_values,test_dcmotor_limits,test_dcmotor_default_initializer) + +@pytest.fixture +def concreteDcPermanentlyExcitedMotor(): + """ + pytest fixture that returns a Dc Permanently Excited motor object + :return: Permanently Excited DC motor object with concrete values + """ + return DcPermanentlyExcitedMotor(test_DcPermanentlyExcitedMotor_parameter,None,None,test_DcPermanentlyExcitedMotor_initializer) + +@pytest.fixture +def concreteDCSeriesMotor(): + """ + pytest fixture that returns a Dc Series motor object + :return: Dc Series motor object with concrete values + """ + return DcSeriesMotor(test_DcSeriesMotor_parameter,None,None,test_DcSeriesMotor_initializer) + +@pytest.fixture +def concreteDCShuntMotor(): + """ + pytest fixture that returns a Dc Shunt motor object + :return: Dc Shunt motor object with concrete values + """ + return DcShuntMotor(test_DcShuntMotor_parameter,None,None,test_DcShuntMotor_initializer) + +@pytest.fixture +def concreteInductionMotor(): + """ + pytest fixture that returns a Dc Shunt motor object + :return: Dc Shunt motor object with concrete values + """ + return InductionMotor(test_Induction_motor_parameter,None,None,test_DcInductionMotor_initializer ) + +def test_InitElectricMotor(): + """ + Test whether the Electric motor object attributes are initialized with the correct values + + """ + defaultElectricMotor = ElectricMotor() + concreteElectricMotor = ElectricMotor(None,None,None,test_electricmotor_default_initializer,None) + assert defaultElectricMotor.motor_parameter == {} + assert defaultElectricMotor.nominal_values == {} + assert defaultElectricMotor.limits == {} + assert defaultElectricMotor._initial_states == {} + assert concreteElectricMotor._initial_states == {'omega': 16.0} + with pytest.raises(NotImplementedError): + concreteElectricMotor.electrical_ode(np.random.rand(1, 1),[0,0.5,1,1.5],16.0) + +def test_InitDCMotor(concreteDCMotor): + + """ + Test whether the DC motor object are initialized with correct values" + :return: + """ + defaultDCMotor = DcMotor() + assert defaultDCMotor.motor_parameter == {"r_a": 16e-3, "r_e": 16e-2, "l_a": 19e-6, "l_e_prime": 1.7e-3, "l_e": 5.4e-3, "j_rotor": 0.0025} + assert defaultDCMotor.nominal_values == dict(omega=300, torque=16.0, i=97, i_a=97, i_e=97, u=60, u_a=60, u_e=60) + assert concreteDCMotor.motor_parameter == test_dcmotor_parameter + assert concreteDCMotor.nominal_values == test_dcmotor_nominal_values + assert concreteDCMotor.limits == test_dcmotor_limits + assert concreteDCMotor._initial_states == test_dcmotor_default_initializer["states"] + +def test_DCMotor_torque(concreteDCMotor): + + currents = [100, 10, 2, 3] + expectedtorque = concreteDCMotor.motor_parameter["l_e_prime"]* currents[0] * currents[1] + + assert concreteDCMotor.torque(currents) == expectedtorque + +def test_DCMotor_i_in(concreteDCMotor): + + currents = [100, 10, 2, 3] + expectedcurrent = currents + + assert concreteDCMotor.i_in(currents) == expectedcurrent + +def test_DCMotor_el_ode(concreteDCMotor): + + concreteDCMotor._model_constants[0] = concreteDCMotor._model_constants[0] / concreteDCMotor.motor_parameter["l_a"] + concreteDCMotor._model_constants[1] = concreteDCMotor._model_constants[1] / concreteDCMotor.motor_parameter["l_e"] + u_in = [10,20] + omega = 60 + state = [10.0,15.0] + + expectedElectricalOde = np.matmul( + concreteDCMotor._model_constants, + np.array( + [ + state[0], + state[1], + omega * state[1], + u_in[0], + u_in[1], + ] + ), + ) + + assert np.array_equal(concreteDCMotor.electrical_ode(state,u_in,omega), expectedElectricalOde) + +def test_DCMotor_get_state_space(concreteDCMotor): + u1 = Box(0, 1, shape=(2,), dtype=np.float64) + input_current = Box(-1, 1, shape=(2,), dtype=np.float64) + u2 = Box(-1, 1, shape=(2,), dtype=np.float64) + expectedLowfor_u1 = { + "omega":0, + "torque": -1, + "i_a": -1 , + "i_e": -1 , + "u_a": 0, + "u_e": 0, + } + expectedhigh = {"omega": 1, "torque": 1, "i_a": 1, "i_e": 1, "u_a": 1, "u_e": 1} + expectedLowfor_u2 = { + "omega":-1, + "torque": -1, + "i_a": -1 , + "i_e": -1 , + "u_a": -1, + "u_e": -1, + } + assert expectedLowfor_u1== concreteDCMotor.get_state_space(input_current,u1)[0] + assert expectedhigh == concreteDCMotor.get_state_space(input_current,u1)[1] + assert expectedLowfor_u2== concreteDCMotor.get_state_space(input_current,u2)[0] + assert expectedhigh == concreteDCMotor.get_state_space(input_current,u2)[1] + +def test_ConcreteDCMotor_reset(concreteDCMotor): + new_initial_state = {'i_a': 100.0, 'i_e': 20.0} + default_initial_state = {"i_a": 10.0, "i_e": 15.0} + default_Initial_state_array = [10.0, 15.0] + extex_state_positions = { + "omega": 0, + "torque": 1, + "i":2, + "i_a":3, + "i_e": 4, + "u_a": 5, + "u_e": 6, + } + + extex_state_space = Box(low=-1, high=1, shape=(7,), dtype=np.float64) + assert concreteDCMotor._initial_states == default_initial_state + concreteDCMotor._initial_states = new_initial_state + assert concreteDCMotor._initial_states == new_initial_state + assert np.array_equal(concreteDCMotor.reset(extex_state_space,extex_state_positions),default_Initial_state_array) + concreteDCMotor.initializer["states"] = new_initial_state + concreteDCMotor.reset(extex_state_space,extex_state_positions) + assert concreteDCMotor._initial_states == new_initial_state + +def test_defaultDCMotor_reset(): + defaultDCMotor = DcMotor() + new_initial_state = {'i_a': 1.0, 'i_e': 2.0} + default_initial_state = {"i_a": 0.0, "i_e": 0.0} + default_Initial_state_array = [0.0, 0.0] + extex_state_positions = { + "omega": 0, + "torque": 1, + "i":2, + "i_a":3, + "i_e": 4, + "u_a": 5, + "u_e": 6, + } + + extex_state_space = Box(low=-1, high=1, shape=(7,), dtype=np.float64) + + assert defaultDCMotor._initial_states == default_initial_state + defaultDCMotor._initial_states = new_initial_state + + assert defaultDCMotor._initial_states == new_initial_state + assert np.array_equal(defaultDCMotor.reset(extex_state_space,extex_state_positions),default_Initial_state_array) + assert defaultDCMotor._initial_states == default_initial_state + defaultDCMotor.initializer["states"] = new_initial_state + defaultDCMotor.reset(extex_state_space,extex_state_positions) + assert defaultDCMotor._initial_states == new_initial_state + +def test_defaultDCExternallyExcitedMotor(): + + defaultDcExternallyExcitedMotor = DcExternallyExcitedMotor() + defaultDCMotor = DcMotor() + assert defaultDcExternallyExcitedMotor.motor_parameter == defaultDCMotor.motor_parameter + assert defaultDcExternallyExcitedMotor._default_initializer == defaultDCMotor._default_initializer + assert defaultDcExternallyExcitedMotor._initial_limits == defaultDCMotor._initial_limits + assert defaultDcExternallyExcitedMotor._nominal_values == defaultDCMotor._nominal_values +""" +No defaultDCExternallyExcitedMotor_reset is tested as it would be same as test_defaultDCMotor_reset +""" + +def test_DcExternallyExcitedMotor_el_jacobian() : + defaultDcExternallyExcitedMotor = DcExternallyExcitedMotor() + defaultDCMotor = DcMotor() + mp = defaultDCMotor.motor_parameter + omega = 60 + state =[0.0,0.0] + I_A_IDX = 0 + I_E_IDX = 1 + expected_jacobian = ( + np.array( + [ + [-mp["r_a"] / mp["l_a"], -mp["l_e_prime"] / mp["l_a"] * omega], + [0, -mp["r_e"] / mp["l_e"]], + ] + ), + np.array([-mp["l_e_prime"] * state[I_E_IDX] / mp["l_a"], 0]), + np.array( + [ + mp["l_e_prime"] * state[I_E_IDX], + mp["l_e_prime"] * state[I_A_IDX], + ] + ), + ) + assert np.array_equal(defaultDcExternallyExcitedMotor.electrical_jacobian(state,[],omega)[0],expected_jacobian[0]) + assert np.array_equal(defaultDcExternallyExcitedMotor.electrical_jacobian(state,[],omega)[1],expected_jacobian[1]) + assert np.array_equal(defaultDcExternallyExcitedMotor.electrical_jacobian(state,[],omega)[2],expected_jacobian[2]) + +def test_InitDcPermanentlyExcitedMotor(concreteDcPermanentlyExcitedMotor): + defaultDcPermanentlyExcitedMotor = DcPermanentlyExcitedMotor() + assert defaultDcPermanentlyExcitedMotor.motor_parameter == {"r_a": 16e-3, "l_a": 19e-6, "psi_e": 0.165, "j_rotor": 0.025,} + assert defaultDcPermanentlyExcitedMotor._default_initializer == {"states": {"i": 0.0}, "interval": None, "random_init": None, "random_params": (None, None),} + assert defaultDcPermanentlyExcitedMotor.nominal_values == dict(omega=300, torque=16.0, i=97, u=60) + assert defaultDcPermanentlyExcitedMotor.limits == dict(omega=400, torque=38.0, i=210, u=60) + assert defaultDcPermanentlyExcitedMotor.HAS_JACOBIAN + assert defaultDcPermanentlyExcitedMotor.CURRENTS_IDX[0] == 0 + assert concreteDcPermanentlyExcitedMotor._default_initializer == defaultDcPermanentlyExcitedMotor._default_initializer + assert concreteDcPermanentlyExcitedMotor.nominal_values == defaultDcPermanentlyExcitedMotor.nominal_values + assert concreteDcPermanentlyExcitedMotor.motor_parameter == test_DcPermanentlyExcitedMotor_parameter + assert concreteDcPermanentlyExcitedMotor.initializer == test_DcPermanentlyExcitedMotor_initializer + assert concreteDcPermanentlyExcitedMotor._initial_states == test_DcPermanentlyExcitedMotor_initializer["states"] + +def test_DcPermanentlyExcitedMotor_torque(concreteDcPermanentlyExcitedMotor): + state = [100, 10, 2, 3] + expected_torque = concreteDcPermanentlyExcitedMotor.motor_parameter["psi_e"]*state[0] + assert concreteDcPermanentlyExcitedMotor.torque(state) == expected_torque + +def test_DcPermanentlyExcitedMotor_i_in(concreteDcPermanentlyExcitedMotor): + state = np.array([100,200,300]) + expected_return = state[concreteDcPermanentlyExcitedMotor.CURRENTS_IDX] + assert concreteDcPermanentlyExcitedMotor.i_in(state) == expected_return + + +def test__DcPermanentlyExcitedMotor_el_ode(concreteDcPermanentlyExcitedMotor): + state = [10.0,15.0] + u_in = [10,20] + omega = 60 + expectedODE = np.matmul(concreteDcPermanentlyExcitedMotor._model_constants, [omega] + np.atleast_1d(state[0]).tolist() + [u_in[0]]) + assert np.array_equal(concreteDcPermanentlyExcitedMotor.electrical_ode(state,u_in,omega),expectedODE) + +def test_DcPermanentlyExcitedMotor_el_jacobian(concreteDcPermanentlyExcitedMotor): + defaultDcPermanentlyExcitedMotor = DcPermanentlyExcitedMotor() + mp = concreteDcPermanentlyExcitedMotor.motor_parameter + u_in = [10,20] + omega = 60 + state = [10.0,15.0] + expectedJacobian = ( + np.array([[-mp["r_a"] / mp["l_a"]]]), + np.array([-mp["psi_e"] / mp["l_a"]]), + np.array([mp["psi_e"]]), + ) + default_mp = concreteDcPermanentlyExcitedMotor._default_motor_parameter + default_jacobian = ( + np.array([[-default_mp["r_a"] / default_mp["l_a"]]]), + np.array([-default_mp["psi_e"] / default_mp["l_a"]]), + np.array([default_mp["psi_e"]]), + ) + assert expectedJacobian[0] == concreteDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[0] + assert expectedJacobian[1] == concreteDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[1] + assert expectedJacobian[2] == concreteDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[2] + assert default_jacobian[0] == defaultDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[0] + assert default_jacobian[1] == defaultDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[1] + assert default_jacobian[2] == defaultDcPermanentlyExcitedMotor.electrical_jacobian(state,u_in,omega)[2] + +def test_DcPermanentlyExcitedMotor_get_state_space(concreteDcPermanentlyExcitedMotor): + u1 = Box(0, 1, shape=(2,), dtype=np.float64) + input_current = Box(-1, 1, shape=(2,), dtype=np.float64) + u2 = Box(-1, 1, shape=(2,), dtype=np.float64) + expectedLowfor_u1 ={ + "omega": 0, + "torque": -1, + "i": -1, + "u": 0, + } + expectedHighfor_u1 = { + "omega": 1, + "torque": 1, + "i": 1, + "u": 1, + } + expectedLowfor_u2 = { + "omega": -1 , + "torque": -1 , + "i": -1 , + "u": -1 , + } + expectedHighfor_u2 = { + "omega": 1 , + "torque": 1 , + "i": 1 , + "u": 1 , + } + assert expectedLowfor_u1 == concreteDcPermanentlyExcitedMotor.get_state_space(input_current,u1)[0] + assert expectedHighfor_u1 == concreteDcPermanentlyExcitedMotor.get_state_space(input_current,u1)[1] + assert expectedLowfor_u2 == concreteDcPermanentlyExcitedMotor.get_state_space(input_current,u2)[0] + assert expectedHighfor_u2 == concreteDcPermanentlyExcitedMotor.get_state_space(input_current,u2)[1] + +def test_DcPermanentlyExcitedMotor_reset(concreteDcPermanentlyExcitedMotor): + new_initial_state = {"i": 100.0} + default_initial_state = {"i": 10.0} + default_Initial_state_array = [10.0] + ex_state_positions = { + "omega": 0, + "torque": 1, + "i":2, + "u":3 + } + + ex_state_space = Box(low=-1, high=1, shape=(4,), dtype=np.float64) + assert concreteDcPermanentlyExcitedMotor._initial_states == default_initial_state + concreteDcPermanentlyExcitedMotor._initial_states = new_initial_state + assert concreteDcPermanentlyExcitedMotor._initial_states == new_initial_state + assert np.array_equal(concreteDcPermanentlyExcitedMotor.reset(ex_state_space,ex_state_positions),default_Initial_state_array) + assert concreteDcPermanentlyExcitedMotor._initial_states == default_initial_state + concreteDcPermanentlyExcitedMotor.initializer["states"] = new_initial_state + concreteDcPermanentlyExcitedMotor.reset(ex_state_space,ex_state_positions) + assert concreteDcPermanentlyExcitedMotor._initial_states == new_initial_state + """ + default motor reset + + """ + defaultDcPermanentlyExcitedMotor = DcPermanentlyExcitedMotor() + default_initialState = {"i": 0.0} + default_InitialState_array = [0.0] + + assert defaultDcPermanentlyExcitedMotor._initial_states == default_initialState + defaultDcPermanentlyExcitedMotor._initial_states = new_initial_state + assert defaultDcPermanentlyExcitedMotor._initial_states == new_initial_state + assert np.array_equal(defaultDcPermanentlyExcitedMotor.reset(ex_state_space,ex_state_positions),default_InitialState_array) + assert defaultDcPermanentlyExcitedMotor._initial_states == default_initialState + defaultDcPermanentlyExcitedMotor.initializer["states"] = new_initial_state + defaultDcPermanentlyExcitedMotor.reset(ex_state_space,ex_state_positions) + assert defaultDcPermanentlyExcitedMotor._initial_states == new_initial_state + +def test_InitDCSeriesMotor(concreteDCSeriesMotor): + defaultDCSeriesMotor = DcSeriesMotor() + assert defaultDCSeriesMotor.motor_parameter == {"r_a": 16e-3, "r_e": 48e-3, "l_a": 19e-6, "l_e_prime": 1.7e-3, "l_e": 5.4e-3, "j_rotor": 0.0025,} + assert defaultDCSeriesMotor._default_initializer == {"states": {"i": 0.0}, "interval": None, "random_init": None, "random_params": (None, None),} + assert defaultDCSeriesMotor.nominal_values == dict(omega=300, torque=16.0, i=97, i_a=97, i_e=97, u=60, u_a=60, u_e=60) + assert defaultDCSeriesMotor.limits == dict(omega=400, torque=38.0, i=210, i_a=210, i_e=210, u=60, u_a=60, u_e=60) + assert defaultDCSeriesMotor.HAS_JACOBIAN + assert defaultDCSeriesMotor.I_IDX == 0 + assert defaultDCSeriesMotor.CURRENTS_IDX[0] == 0 + assert concreteDCSeriesMotor.motor_parameter == test_DcSeriesMotor_parameter + assert concreteDCSeriesMotor._default_initializer == defaultDCSeriesMotor._default_initializer + assert concreteDCSeriesMotor.initializer == test_DcSeriesMotor_initializer + assert concreteDCSeriesMotor.nominal_values == defaultDCSeriesMotor.nominal_values + assert concreteDCSeriesMotor.limits == defaultDCSeriesMotor.limits + assert concreteDCSeriesMotor._initial_states == test_DcSeriesMotor_initializer["states"] + +def test_DCSeriesMotor_Torque(concreteDCSeriesMotor): + + currents = [100, 10, 2, 3] + expectedtorque = concreteDCSeriesMotor.motor_parameter["l_e_prime"]* currents[0] * currents[0] + assert concreteDCSeriesMotor.torque(currents) == expectedtorque + +def test_DCSeriesMotor_el_ode(concreteDCSeriesMotor): + state = [10.0,15.0] + u_in = [10,20] + omega = 60 + expectedODE = np.matmul(concreteDCSeriesMotor._model_constants,np.array([state[0], omega * state[0], u_in[0]]),) + assert np.array_equal(concreteDCSeriesMotor.electrical_ode(state,u_in,omega),expectedODE) + +def test_DCSeriesMotor_i_in(concreteDCSeriesMotor): + currents = np.array([100, 10, 2, 3]) + expectedcurrent = currents[concreteDCSeriesMotor.CURRENTS_IDX] + assert concreteDCSeriesMotor.i_in(currents) == expectedcurrent + + +def test_DCSeriesMotor_get_state_space(concreteDCSeriesMotor): + expectedHigh = {"omega": 1, "torque": 1, "i": 1, "u": 1,} + u1 = Box(0, 1, shape=(2,), dtype=np.float64) + i1 = Box(0, 1, shape=(2,), dtype=np.float64) + u2 = Box(-1, 1, shape=(2,), dtype=np.float64) + i2 = Box(-1, 1, shape=(2,), dtype=np.float64) + expectedLowfor_u1_i1 = {"omega": 0, "torque": 0, "i": 0, "u": 0,} + expectedLowfor_u1_i2 = {"omega": 0, "torque": 0, "i": -1, "u": 0,} + expectedLowfor_u2_i1 = {"omega": 0, "torque": 0, "i": 0, "u": -1,} + expectedLowfor_u2_i2 = {"omega": 0, "torque": 0, "i": -1, "u": -1,} + + assert concreteDCSeriesMotor.get_state_space(i1,u1)[0] == expectedLowfor_u1_i1 + assert concreteDCSeriesMotor.get_state_space(i1,u1)[1] == expectedHigh + assert concreteDCSeriesMotor.get_state_space(i2,u1)[0] == expectedLowfor_u1_i2 + assert concreteDCSeriesMotor.get_state_space(i2,u1)[1] == expectedHigh + assert concreteDCSeriesMotor.get_state_space(i1,u2)[0] == expectedLowfor_u2_i1 + assert concreteDCSeriesMotor.get_state_space(i1,u2)[1] == expectedHigh + assert concreteDCSeriesMotor.get_state_space(i2,u2)[0] == expectedLowfor_u2_i2 + assert concreteDCSeriesMotor.get_state_space(i2,u2)[1] == expectedHigh + +def test_DCSeriesMotor_el_jacobian(concreteDCSeriesMotor): + defaultDCSeriesMotor = DcSeriesMotor() + mp = concreteDCSeriesMotor._motor_parameter + default_mp = concreteDCSeriesMotor._default_motor_parameter + state = [10.0,15.0] + u_in = [10,20] + omega = 60 + + expectedJacobian = ( + np.array([[-(mp["r_a"] + mp["r_e"] + mp["l_e_prime"] * omega) / (mp["l_a"] + mp["l_e"])]]), + np.array([-mp["l_e_prime"] * state[concreteDCSeriesMotor.I_IDX] / (mp["l_a"] + mp["l_e"])]), + np.array([2 * mp["l_e_prime"] * state[concreteDCSeriesMotor.I_IDX]]), + ) + default_jacobian = ( + np.array([[-(default_mp["r_a"] + default_mp["r_e"] + default_mp["l_e_prime"] * omega) / (default_mp["l_a"] + default_mp["l_e"])]]), + np.array([-default_mp["l_e_prime"] * state[concreteDCSeriesMotor.I_IDX] / (default_mp["l_a"] + default_mp["l_e"])]), + np.array([2 * default_mp["l_e_prime"] * state[concreteDCSeriesMotor.I_IDX]]), + ) + assert expectedJacobian[0] == concreteDCSeriesMotor.electrical_jacobian(state,u_in,omega)[0] + assert expectedJacobian[1] == concreteDCSeriesMotor.electrical_jacobian(state,u_in,omega)[1] + assert expectedJacobian[2] == concreteDCSeriesMotor.electrical_jacobian(state,u_in,omega)[2] + assert default_jacobian[0] == defaultDCSeriesMotor.electrical_jacobian(state,u_in,omega)[0] + assert default_jacobian[1] == defaultDCSeriesMotor.electrical_jacobian(state,u_in,omega)[1] + assert default_jacobian[2] == defaultDCSeriesMotor.electrical_jacobian(state,u_in,omega)[2] + +def test_DCSeriesMotor_reset(concreteDCSeriesMotor): + default_initial_state = {"i": 5.0} + default_Initial_state_array = [5.0] + new_initial_state = {"i": 100.0} + series_state_positions = {"omega": 0,"torque": 1,"i":2, "i_a":3, "i_e":4, "u":5, "u_a":6, "u_e":7} + series_state_space = Box(low=-1, high=1, shape=(8,), dtype=np.float64) + assert concreteDCSeriesMotor._initial_states == default_initial_state + concreteDCSeriesMotor._initial_states = new_initial_state + assert concreteDCSeriesMotor._initial_states == new_initial_state + assert np.array_equal(concreteDCSeriesMotor.reset(series_state_space,series_state_positions),default_Initial_state_array) + concreteDCSeriesMotor.initializer["states"] = new_initial_state + concreteDCSeriesMotor.reset(series_state_space,series_state_positions) + assert concreteDCSeriesMotor._initial_states == new_initial_state + +def test_InitDCShuntMotor(concreteDCShuntMotor): + defaultDCShuntMotor = DcShuntMotor() + + assert defaultDCShuntMotor._default_motor_parameter =={"r_a": 16e-3, "r_e": 4e-1, "l_a": 19e-6, "l_e_prime": 1.7e-3, "l_e": 5.4e-3, "j_rotor": 0.0025,} + assert defaultDCShuntMotor._default_initializer == {"states": {"i_a": 0.0, "i_e": 0.0},"interval": None,"random_init": None,"random_params": (None, None),} + assert defaultDCShuntMotor.HAS_JACOBIAN + assert defaultDCShuntMotor._default_motor_parameter == defaultDCShuntMotor.motor_parameter + assert defaultDCShuntMotor._default_initializer == defaultDCShuntMotor.initializer + assert defaultDCShuntMotor._default_nominal_values == dict(omega=300, torque=16.0, i=97, i_a=97, i_e=97, u=60, u_a=60, u_e=60) + assert defaultDCShuntMotor._default_limits == dict(omega=400, torque=38.0, i=210, i_a=210, i_e=210, u=60, u_a=60, u_e=60) + assert concreteDCShuntMotor.motor_parameter == test_DcShuntMotor_parameter + assert concreteDCShuntMotor._default_motor_parameter == defaultDCShuntMotor._default_motor_parameter + assert concreteDCShuntMotor.initializer == test_DcShuntMotor_initializer + assert concreteDCShuntMotor._default_initializer == defaultDCShuntMotor._default_initializer + assert concreteDCShuntMotor._initial_states == test_DcShuntMotor_initializer["states"] + assert concreteDCShuntMotor._default_nominal_values == defaultDCShuntMotor._default_nominal_values + assert concreteDCShuntMotor._default_limits == defaultDCShuntMotor._default_limits + assert concreteDCShuntMotor.I_A_IDX == 0 + assert concreteDCShuntMotor.I_E_IDX == 1 + +def test_DCShuntMotor_i_in(concreteDCShuntMotor): + defaultDCShuntMotor = DcShuntMotor() + currents = [5, 10, 20, 25] + expectedcurrent = [5 + 10] + assert concreteDCShuntMotor.i_in(currents) == expectedcurrent + assert defaultDCShuntMotor.i_in(currents) == concreteDCShuntMotor.i_in(currents) + +def test_DCShuntMotor_el_ode(concreteDCShuntMotor): + state = [5, 10, 20, 25] + u_in = [10,20] + omega = 60 + expectedOde = np.matmul(concreteDCShuntMotor._model_constants,np.array([state[0],state[1],omega * state[1],u_in[0],u_in[0],]),) + assert np.array_equal(concreteDCShuntMotor.electrical_ode(state,u_in,omega),expectedOde) + +def test_DCShuntMotor_el_jacobian(concreteDCShuntMotor): + defaultDCShuntMotor = DcShuntMotor() + state = [5, 10, 20, 25] + u_in = [10,20] + omega = 60 + mp = concreteDCShuntMotor._motor_parameter + default_mp = concreteDCShuntMotor._default_motor_parameter + expectedJacobian = ( + np.array([[-mp["r_a"] / mp["l_a"], -mp["l_e_prime"] / mp["l_a"] * omega],[0, -mp["r_e"] / mp["l_e"]],]), + np.array([-mp["l_e_prime"] * state[1] / mp["l_a"], 0]), + np.array([mp["l_e_prime"] * state[1],mp["l_e_prime"] * state[0],]), + ) + defaultJacobian = ( + np.array([[-default_mp["r_a"] / default_mp["l_a"], -default_mp["l_e_prime"] / default_mp["l_a"] * omega],[0, -default_mp["r_e"] / default_mp["l_e"]],]), + np.array([-default_mp["l_e_prime"] * state[1] / default_mp["l_a"], 0]), + np.array([default_mp["l_e_prime"] * state[1],default_mp["l_e_prime"] * state[0],]), + ) + assert np.array_equal(expectedJacobian[0], concreteDCShuntMotor.electrical_jacobian(state,u_in,omega)[0]) + assert np.array_equal(expectedJacobian[1], concreteDCShuntMotor.electrical_jacobian(state,u_in,omega)[1]) + assert np.array_equal(expectedJacobian[2], concreteDCShuntMotor.electrical_jacobian(state,u_in,omega)[2]) + assert np.array_equal(defaultJacobian[0], defaultDCShuntMotor.electrical_jacobian(state,u_in,omega)[0]) + assert np.array_equal(defaultJacobian[1], defaultDCShuntMotor.electrical_jacobian(state,u_in,omega)[1]) + assert np.array_equal(defaultJacobian[2], defaultDCShuntMotor.electrical_jacobian(state,u_in,omega)[2]) + +def test_DCShuntMotor_get_state_space(concreteDCShuntMotor): + expectedHigh = {"omega": 1, "torque": 1, "i_a": 1,"i_e": 1,"u": 1,} + u1 = Box(0, 1, shape=(2,), dtype=np.float64) + i1 = Box(0, 1, shape=(2,), dtype=np.float64) + u2 = Box(-1, 1, shape=(2,), dtype=np.float64) + i2 = Box(-1, 1, shape=(2,), dtype=np.float64) + expectedLowfor_u1_i1 = {"omega": 0, "torque": 0, "i_a": 0,"i_e": 0,"u": 0,} + expectedLowfor_u1_i2 = {"omega": 0, "torque": -1, "i_a": -1,"i_e": -1,"u": 0,} + expectedLowfor_u2_i1 = {"omega": 0, "torque": 0, "i_a": 0,"i_e": 0,"u": -1,} + expectedLowfor_u2_i2 = {"omega": 0, "torque": -1, "i_a": -1,"i_e": -1,"u": -1,} + + assert concreteDCShuntMotor.get_state_space(i1,u1)[0] == expectedLowfor_u1_i1 + assert concreteDCShuntMotor.get_state_space(i1,u1)[1] == expectedHigh + assert concreteDCShuntMotor.get_state_space(i2,u1)[0] == expectedLowfor_u1_i2 + assert concreteDCShuntMotor.get_state_space(i2,u1)[1] == expectedHigh + assert concreteDCShuntMotor.get_state_space(i1,u2)[0] == expectedLowfor_u2_i1 + assert concreteDCShuntMotor.get_state_space(i1,u2)[1] == expectedHigh + assert concreteDCShuntMotor.get_state_space(i2,u2)[0] == expectedLowfor_u2_i2 + assert concreteDCShuntMotor.get_state_space(i2,u2)[1] == expectedHigh + +def test_ThreePhaseMotor_t_23(): + defaultThreePhaseMotor = ThreePhaseMotor() + t23 = 2 / 3 * np.array([ + [1, -0.5, -0.5], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3)] + ]) + u_abc = [1, 2, 3] #[u_a, u_b, u_c] + expectedU_out = np.matmul(t23, u_abc) # [u_alpha, u_beta] + assert np.array_equal(defaultThreePhaseMotor.t_23(u_abc),expectedU_out) + +def test_ThreePhaseMotor_t_32(): + defaultThreePhaseMotor = ThreePhaseMotor() + t32 = np.array([ + [1, 0], + [-0.5, 0.5 * np.sqrt(3)], + [-0.5, -0.5 * np.sqrt(3)] + ]) + u_alpha_beta = [10,20] # [u_alpha, u_beta] + expectedU_out = np.matmul(t32, u_alpha_beta) # [u_a, u_b, u_c] + assert np.array_equal(defaultThreePhaseMotor.t_32(u_alpha_beta),expectedU_out) + +def test_ThreePhaseMotor_q(): + defaultThreePhaseMotor = ThreePhaseMotor() + epsilon = 15 # Current electrical angle of the motor + input_dq = [10,20] + cos = math.cos(epsilon) + sin = math.sin(epsilon) + out_alpha_beta = cos * input_dq[0] - sin * input_dq[1], sin * input_dq[0] + cos * input_dq[1] + assert defaultThreePhaseMotor.q(input_dq,epsilon) == out_alpha_beta + +def test_ThreePhaseMotor_q_inv(): + defaultThreePhaseMotor = ThreePhaseMotor() + epsilon = 15 # Current electrical angle of the motor + e = -epsilon + input_alpha_beta = [10,20] + cos = math.cos(e) + sin = math.sin(e) + out_dq = cos * input_alpha_beta[0] - sin * input_alpha_beta[1], sin * input_alpha_beta[0] + cos * input_alpha_beta[1] + assert defaultThreePhaseMotor.q_inv(input_alpha_beta,epsilon) == out_dq + +def test_ThreePhaseMotor_q_me(): + defaultInductionMotorMotor = InductionMotor(test_Induction_motor_parameter,None,None,None,None) + epsilon = 10 #Current mechanical angle of the motor + e = epsilon* defaultInductionMotorMotor._motor_parameter["p"] + input_dq = [10,20] + cos = math.cos(e) + sin = math.sin(e) + out_alpha_beta = cos * input_dq[0] - sin * input_dq[1], sin * input_dq[0] + cos * input_dq[1] + assert defaultInductionMotorMotor.q_me(input_dq,epsilon) == out_alpha_beta + +def test_ThreePhaseMotor_q_inv_me(): + defaultInductionMotorMotor = InductionMotor(test_Induction_motor_parameter,None,None,None,None) + epsilon = 10 #Current mechanical angle of the motor + e = -epsilon* defaultInductionMotorMotor._motor_parameter["p"] + input_dq = [10,20] + cos = math.cos(e) + sin = math.sin(e) + out_alpha_beta = cos * input_dq[0] - sin * input_dq[1], sin * input_dq[0] + cos * input_dq[1] + assert defaultInductionMotorMotor.q_inv_me(input_dq,epsilon) == out_alpha_beta + +def test_InitInductionMotor(concreteInductionMotor): + defaultInductionMotor = InductionMotor() + + assert defaultInductionMotor._default_motor_parameter =={"p": 2,"l_m": 143.75e-3,"l_sigs": 5.87e-3,"l_sigr": 5.87e-3,"j_rotor": 1.1e-3,"r_s": 2.9338,"r_r": 1.355,} + assert defaultInductionMotor._default_initializer == {"states": {"i_salpha": 0.0,"i_sbeta": 0.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 0.0,}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } + assert defaultInductionMotor.HAS_JACOBIAN + assert defaultInductionMotor._default_nominal_values == dict(omega=3e3 * np.pi / 30, torque=0.0, i=3.9, epsilon=math.pi, u=560) + assert defaultInductionMotor._default_limits == dict(omega=4e3 * np.pi / 30, torque=0.0, i=5.5, epsilon=math.pi, u=560) + assert concreteInductionMotor.motor_parameter == test_Induction_motor_parameter + assert concreteInductionMotor._default_motor_parameter == defaultInductionMotor._default_motor_parameter + assert concreteInductionMotor._default_motor_parameter == defaultInductionMotor.motor_parameter + assert concreteInductionMotor._initial_states == test_DcInductionMotor_initializer["states"] + assert concreteInductionMotor._default_nominal_values == defaultInductionMotor._default_nominal_values + assert concreteInductionMotor._default_limits == defaultInductionMotor._default_limits + assert defaultInductionMotor.I_SALPHA_IDX == 0 + assert defaultInductionMotor.I_SBETA_IDX == 1 + +def test_InductionMotor_el_ode(concreteInductionMotor): + state = [5, 10, 15, 20, 10] #[i_salpha, i_sbeta, psi_ralpha, psi_rbeta, epsilon] + u_in = np.array([(10,11), (1,2)]) #[u_salpha, u_sbeta, u_ralpha, u_rbeta] + omega = 60 + expectedOde = np.matmul( + concreteInductionMotor._model_constants, + np.array( + [ + # omega, i_alpha, i_beta, psi_ralpha, psi_rbeta, omega * psi_ralpha, omega * psi_rbeta, u_salpha, u_sbeta, u_ralpha, u_rbeta, + omega, + state[concreteInductionMotor.I_SALPHA_IDX], + state[concreteInductionMotor.I_SBETA_IDX], + state[concreteInductionMotor.PSI_RALPHA_IDX], + state[concreteInductionMotor.PSI_RBETA_IDX], + omega * state[concreteInductionMotor.PSI_RALPHA_IDX], + omega * state[concreteInductionMotor.PSI_RBETA_IDX], + u_in[0, 0], + u_in[0, 1], + u_in[1, 0], + u_in[1, 1], + ] + ), + ) + assert np.array_equal(expectedOde, concreteInductionMotor.electrical_ode(state,u_in,omega)) + +def test_InductionMotor_i_in(concreteInductionMotor): + state = np.array([(1, 2), (10, 11), (20,21)]) + expectedcurrent = np.array([(1, 2), (10, 11)]) + assert np.array_equal(expectedcurrent,concreteInductionMotor.i_in(state)) + +def test_InductionMotor_torque(concreteInductionMotor): + mp = concreteInductionMotor._motor_parameter + states = [5, 10, 15, 20, 10] #[i_salpha, i_sbeta, psi_ralpha, psi_rbeta, epsilon] + expectedTorque = ( + 1.5 + * mp["p"] + * mp["l_m"] + / (mp["l_m"] + mp["l_sigr"]) + * ( + states[concreteInductionMotor.PSI_RALPHA_IDX] * states[concreteInductionMotor.I_SBETA_IDX] + - states[concreteInductionMotor.PSI_RBETA_IDX] * states[concreteInductionMotor.I_SALPHA_IDX] + ) + ) + assert np.array_equal(concreteInductionMotor.torque(states),expectedTorque) + +def test_InductionMotor_el_jacobian(concreteInductionMotor): + mp = concreteInductionMotor._motor_parameter + l_s = mp["l_m"] + mp["l_sigs"] + l_r = mp["l_m"] + mp["l_sigr"] + sigma = (l_s * l_r - mp["l_m"] ** 2) / (l_s * l_r) + tau_r = l_r / mp["r_r"] + tau_sig = sigma * l_s / (mp["r_s"] + mp["r_r"] * (mp["l_m"] ** 2) / (l_r**2)) + state = [5, 10, 15, 20, 10] #[i_salpha, i_sbeta, psi_ralpha, psi_rbeta, epsilon] + u_in = np.array([(10,11), (1,2)])#[u_salpha, u_sbeta, u_ralpha, u_rbeta] + omega = 60 + + expectedJacobian = ( + np.array( + [ # dx'/dx + # i_alpha i_beta psi_alpha psi_beta epsilon + [ + -1 / tau_sig, + 0, + mp["l_m"] * mp["r_r"] / (sigma * l_s * l_r**2), + omega * mp["l_m"] * mp["p"] / (sigma * l_r * l_s), + 0, + ], + [ + 0, + -1 / tau_sig, + -omega * mp["l_m"] * mp["p"] / (sigma * l_r * l_s), + mp["l_m"] * mp["r_r"] / (sigma * l_s * l_r**2), + 0, + ], + [mp["l_m"] / tau_r, 0, -1 / tau_r, -omega * mp["p"], 0], + [0, mp["l_m"] / tau_r, omega * mp["p"], -1 / tau_r, 0], + [0, 0, 0, 0, 0], + ] + ), + np.array( + [ # dx'/dw + mp["l_m"] * mp["p"] / (sigma * l_r * l_s) * state[concreteInductionMotor.PSI_RBETA_IDX], + -mp["l_m"] * mp["p"] / (sigma * l_r * l_s) * state[concreteInductionMotor.PSI_RALPHA_IDX], + -mp["p"] * state[concreteInductionMotor.PSI_RBETA_IDX], + mp["p"] * state[concreteInductionMotor.PSI_RALPHA_IDX], + mp["p"], + ] + ), + np.array( + [ # dT/dx + -state[concreteInductionMotor.PSI_RBETA_IDX] * 3 / 2 * mp["p"] * mp["l_m"] / l_r, + state[concreteInductionMotor.PSI_RALPHA_IDX] * 3 / 2 * mp["p"] * mp["l_m"] / l_r, + state[concreteInductionMotor.I_SBETA_IDX] * 3 / 2 * mp["p"] * mp["l_m"] / l_r, + -state[concreteInductionMotor.I_SALPHA_IDX] * 3 / 2 * mp["p"] * mp["l_m"] / l_r, + 0, + ] + ), + ) + + assert np.array_equal(expectedJacobian[0],concreteInductionMotor.electrical_jacobian(state,u_in,omega)[0]) + assert np.array_equal(expectedJacobian[1],concreteInductionMotor.electrical_jacobian(state,u_in,omega)[1]) + assert np.array_equal(expectedJacobian[2],concreteInductionMotor.electrical_jacobian(state,u_in,omega)[2]) + + +def test_InductionMotor_reset(concreteInductionMotor): + #_nominal_values ---> _initial_limits + new_initial_state = {"i_salpha": 5.0,"i_sbeta": 6.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 10.0,} + default_initial_state = {"i_salpha": 1.0,"i_sbeta": 0.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 0.0,} + default_initial_state_array = [1,0,0,0,0] + InductionMotor_state_positions = { + "i_salpha": 0, + "i_sbeta": 1, + "psi_ralpha": 2, + "psi_rbeta": 3, + "omega": 4, + "torque": 5, + "epsilon":6, + "u": 7 + } + assert concreteInductionMotor._initial_states == default_initial_state + concreteInductionMotor._initial_states = new_initial_state + assert concreteInductionMotor._initial_states == new_initial_state + InductionMotor_state_space = Box(low=-1, high=1, shape=(8,), dtype=np.float64) + #only to run the test case, these keyvalues are missing the Init called by the reset # special case for Induction motor reset + concreteInductionMotor.initial_limits['psi_ralpha']= 0.0 + concreteInductionMotor.initial_limits['psi_rbeta']= 0.0 + assert np.array_equal(concreteInductionMotor.reset(InductionMotor_state_space,InductionMotor_state_positions),default_initial_state_array) + +def test_InitDoublyFedIM(): + defaultDoublyFedIM = DoublyFedInductionMotor() + assert defaultDoublyFedIM.motor_parameter == {"p": 2,"l_m": 297.5e-3,"l_sigs": 25.71e-3,"l_sigr": 25.71e-3,"j_rotor": 13.695e-3,"r_s": 4.42,"r_r": 3.51,} + assert defaultDoublyFedIM._default_initializer == {"states": {"i_salpha": 0.0,"i_sbeta": 0.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 0.0,}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } + assert defaultDoublyFedIM._default_nominal_values == dict(omega=1650 * np.pi / 30, torque=0.0, i=7.5, epsilon=math.pi, u=720) + assert defaultDoublyFedIM._default_limits == dict(omega=1800 * np.pi / 30, torque=0.0, i=9, epsilon=math.pi, u=720) + #rotor currents are repeated some times, then the test fails. But the reason for this unexpected behaviour is not clear yet. + #assert defaultDoublyFedIM.IO_CURRENTS == (["i_sa", "i_sb", "i_sc", "i_salpha", "i_sbeta", "i_sd", "i_sq"] + defaultDoublyFedIM.IO_ROTOR_CURRENTS) + assert defaultDoublyFedIM.HAS_JACOBIAN + +def test_InitSquirrelCageIM(): + defaultSquirrelCageIM = SquirrelCageInductionMotor() + defaultDoublyFedIM = DoublyFedInductionMotor() + assert defaultSquirrelCageIM.motor_parameter == {"p": 2,"l_m": 143.75e-3,"l_sigs": 5.87e-3,"l_sigr": 5.87e-3,"j_rotor": 1.1e-3,"r_s": 2.9338,"r_r": 1.355,} + assert defaultSquirrelCageIM._default_initializer == defaultDoublyFedIM._default_initializer + assert defaultSquirrelCageIM._default_limits == dict(omega=4e3 * np.pi / 30, torque=0.0, i=5.5, epsilon=math.pi, u=560) + new_motor_parameter = {"p": 3,"l_m": 140,"l_sigs": 5.87e-3,"l_sigr": 5.87e-2,"j_rotor": 1.1e-3,"r_s": 2.9338,"r_r": 1.1,} + initializer = {"states": {"i_salpha": 0.0,"i_sbeta": 0.0,"psi_ralpha": 0.0,"psi_rbeta": 0.0,"epsilon": 0.0,}, + "interval": None, + "random_init": None, + "random_params": (None, None), + } + concreteSquirrelCageIM = SquirrelCageInductionMotor(new_motor_parameter,None,None,initializer,None) + assert concreteSquirrelCageIM.initializer == initializer + assert concreteSquirrelCageIM.motor_parameter == new_motor_parameter + +def test_SquirrelCageIM_el_ode(): + defaultSquirrelCageIM = SquirrelCageInductionMotor() + state = [5, 10, 15, 20, 10] #[i_salpha, i_sbeta, psi_ralpha, psi_rbeta, epsilon] + u_salphabeta = [10,11] + u_in = np.array([(10,11), (0,0)]) + omega = 60 + expectedOde = np.matmul( + defaultSquirrelCageIM._model_constants, + np.array( + [ + omega, + state[defaultSquirrelCageIM.I_SALPHA_IDX], + state[defaultSquirrelCageIM.I_SBETA_IDX], + state[defaultSquirrelCageIM.PSI_RALPHA_IDX], + state[defaultSquirrelCageIM.PSI_RBETA_IDX], + omega * state[defaultSquirrelCageIM.PSI_RALPHA_IDX], + omega * state[defaultSquirrelCageIM.PSI_RBETA_IDX], + u_in[0, 0], + u_in[0, 1], + u_in[1, 0], + u_in[1, 1], + ] + ), + ) + assert np.array_equal(expectedOde, defaultSquirrelCageIM.electrical_ode(state,u_salphabeta,omega)) + +# ._update_model() is not implemented and hence cant be initialized - discuss +def test_InitSynchronousMotor(): + with pytest.raises(NotImplementedError): + SynchronousMotor() + +def test_InitSynchronousReluctanceMotor(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + assert defaultReluctanceMotor._default_motor_parameter == {"p": 4,"l_d": 10.1e-3,"l_q": 4.1e-3,"j_rotor": 0.8e-3,"r_s": 0.57,} + assert defaultReluctanceMotor.HAS_JACOBIAN + assert defaultReluctanceMotor.motor_parameter == defaultReluctanceMotor._default_motor_parameter + assert defaultReluctanceMotor._initial_states == defaultReluctanceMotor._default_initializer["states"] + concreteReluctanceMotor = SynchronousReluctanceMotor(test_ReluctanceMotor_parameter,None,None,test_ReluctanceMotor_initializer) + assert concreteReluctanceMotor.motor_parameter == test_ReluctanceMotor_parameter + assert concreteReluctanceMotor._initializer == test_ReluctanceMotor_initializer + assert concreteReluctanceMotor._default_motor_parameter == defaultReluctanceMotor._default_motor_parameter + assert concreteReluctanceMotor._initial_states == test_ReluctanceMotor_initializer["states"] + assert defaultReluctanceMotor.I_SD_IDX == 0 + assert defaultReluctanceMotor.I_SQ_IDX == 1 + assert defaultReluctanceMotor.EPSILON_IDX == 2 + assert defaultReluctanceMotor.CURRENTS == ["i_sd", "i_sq"] + assert defaultReluctanceMotor.VOLTAGES == ["u_sd", "u_sq"] + +def test_SyncReluctanceMotor_torque(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + mp = defaultReluctanceMotor._motor_parameter + currents = [10,15] + expectedTorque = 1.5 * mp["p"] * ((mp["l_d"] - mp["l_q"]) * currents[defaultReluctanceMotor.I_SD_IDX]) * currents[defaultReluctanceMotor.I_SQ_IDX] + assert np.array_equal(defaultReluctanceMotor.torque(currents),expectedTorque) + +def test_SyncReluctanceMotor_el_jacobian(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + state = [2,4,10]#[i_sd, i_sq, epsilon] + omega = 60 + u_in = [5,10]#[u_sd, u_sq] + mp = defaultReluctanceMotor._motor_parameter + expectedJacobian = ( + np.array( + [ + [ + -mp["r_s"] / mp["l_d"], + mp["l_q"] / mp["l_d"] * mp["p"] * omega, + 0, + ], + [ + -mp["l_d"] / mp["l_q"] * mp["p"] * omega, + -mp["r_s"] / mp["l_q"], + 0, + ], + [0, 0, 0], + ] + ), + np.array( + [ + mp["p"] * mp["l_q"] / mp["l_d"] * state[defaultReluctanceMotor.I_SQ_IDX], + -mp["p"] * mp["l_d"] / mp["l_q"] * state[defaultReluctanceMotor.I_SD_IDX], + mp["p"], + ] + ), + np.array( + [ + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[defaultReluctanceMotor.I_SQ_IDX], + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[defaultReluctanceMotor.I_SD_IDX], + 0, + ] + ), + ) + assert np.array_equal(expectedJacobian[0],defaultReluctanceMotor.electrical_jacobian(state,u_in,omega)[0]) + assert np.array_equal(expectedJacobian[1],defaultReluctanceMotor.electrical_jacobian(state,u_in,omega)[1]) + assert np.array_equal(expectedJacobian[2],defaultReluctanceMotor.electrical_jacobian(state,u_in,omega)[2]) + +def test_SyncReluctanceMotor_el_ode(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + state = [2,4,10]#[i_sd, i_sq, epsilon] + omega = 60 + u_in = [5,10]#[u_sd, u_sq] + expectedOde = np.matmul( + defaultReluctanceMotor._model_constants, + np.array( + [ + omega, + state[defaultReluctanceMotor.I_SD_IDX], + state[defaultReluctanceMotor.I_SQ_IDX], + u_in[0], + u_in[1], + omega * state[defaultReluctanceMotor.I_SD_IDX], + omega * state[defaultReluctanceMotor.I_SQ_IDX], + ] + ), + ) + assert np.array_equal(defaultReluctanceMotor.electrical_ode(state,u_in,omega),expectedOde) + +def test_SyncReluctanceMotor_i_in(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + state = np.array([1,2,3,5,8,9]) + expectedCurrent = [1,2] + assert np.array_equal(defaultReluctanceMotor.i_in(state),expectedCurrent) + +def test_SyncReluctanceMotor_reset(): + defaultReluctanceMotor = SynchronousReluctanceMotor() + default_initial_state = {"i_sq": 0.0, "i_sd": 0.0, "epsilon": 0.0} + default_Initial_state_array = [0,0,0] + new_initial_state = {"i_sq": 1.0, "i_sd": 2.0, "epsilon": 10.0} + ReluctanceMotor_state_positions = {"i_sq": 0,"i_sd": 1,"torque": 2,"omega": 3,"epsilon": 4,"u": 5,} + ReluctanceMotor_state_space = Box(low=-1, high=1, shape=(6,), dtype=np.float64) + assert defaultReluctanceMotor._initial_states == default_initial_state + defaultReluctanceMotor._initial_states = new_initial_state + assert defaultReluctanceMotor._initial_states == new_initial_state + assert np.array_equal(defaultReluctanceMotor.reset(ReluctanceMotor_state_space,ReluctanceMotor_state_positions),default_Initial_state_array) + defaultReluctanceMotor.initializer["states"] = new_initial_state + defaultReluctanceMotor.reset(ReluctanceMotor_state_space,ReluctanceMotor_state_positions) + assert defaultReluctanceMotor._initial_states == new_initial_state + +def test_InitExtExcitedSynchronousMotor(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + assert ExtExcSyncMotor._default_motor_parameter == {'p': 3,'l_d': 1.66e-3,'l_q': 0.35e-3,'l_m': 1.589e-3,'l_e': 1.74e-3,'j_rotor': 0.3883,'r_s': 15.55e-3,'r_e': 7.2e-3,'k': 65.21,} + assert ExtExcSyncMotor.HAS_JACOBIAN + mp = ExtExcSyncMotor._motor_parameter + ExtExcSyncMotor._default_motor_parameter.update({'r_E':mp['k'] ** 2 * 3/2 * mp['r_e'], 'l_M':mp['k'] * 3/2 * mp['l_m'], 'l_E': mp['k'] ** 2 * 3/2 * mp['l_e'], 'i_k_rs':2 / 3 / mp['k'], 'sigma':1 - mp['l_M'] ** 2 / (mp['l_d'] * mp['l_E'])}) + assert ExtExcSyncMotor.motor_parameter == ExtExcSyncMotor._default_motor_parameter + assert ExtExcSyncMotor._initial_states == ExtExcSyncMotor._default_initializer["states"] + concreteExtExcSyncMotor = ExternallyExcitedSynchronousMotor(test_ExtExcSyncMotor_parameter,None,None,test_ExtExcSyncMotor_initializer) + newmp = concreteExtExcSyncMotor._motor_parameter + test_ExtExcSyncMotor_parameter.update({'r_E':newmp['k'] ** 2 * 3/2 * newmp['r_e'], 'l_M':newmp['k'] * 3/2 * newmp['l_m'], 'l_E': newmp['k'] ** 2 * 3/2 * newmp['l_e'], 'i_k_rs':2 / 3 / newmp['k'], 'sigma':1 - newmp['l_M'] ** 2 / (newmp['l_d'] * newmp['l_E'])}) + assert concreteExtExcSyncMotor.motor_parameter == test_ExtExcSyncMotor_parameter + assert concreteExtExcSyncMotor._initializer == test_ExtExcSyncMotor_initializer + assert concreteExtExcSyncMotor._default_motor_parameter == ExtExcSyncMotor._default_motor_parameter + assert concreteExtExcSyncMotor._initial_states == test_ExtExcSyncMotor_initializer["states"] + assert ExtExcSyncMotor.I_SD_IDX == 0 + assert ExtExcSyncMotor.I_SQ_IDX == 1 + assert ExtExcSyncMotor.EPSILON_IDX == 3 + assert ExtExcSyncMotor.CURRENTS == ["i_sd", "i_sq", "i_e"] + assert ExtExcSyncMotor.VOLTAGES == ["u_sd", "u_sq", "u_e"] + +""" +what should the u_in and how come the u_dqe[2], +""" +def test_ExtExcitedSynchronousMotor_el_ode(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + state = [5, 5, 10]#[i_sd, i_sq, epsilon] + omega = 60 + u_in = [50, 60, 50]#[u_sd, u_sq] + expectedOde = np.matmul( + ExtExcSyncMotor._model_constants, + np.array( + [ + omega, + state[ExtExcSyncMotor.I_SD_IDX], + state[ExtExcSyncMotor.I_SQ_IDX], + state[ExtExcSyncMotor.I_E_IDX], + u_in[0], + u_in[1], + u_in[2], + omega * state[ExtExcSyncMotor.I_SD_IDX], + omega * state[ExtExcSyncMotor.I_SQ_IDX], + omega * state[ExtExcSyncMotor.I_E_IDX] + ] + ) + ) + assert np.array_equal(ExtExcSyncMotor.electrical_ode(state,u_in,omega),expectedOde) + +def test_ExtExcitedSynchronousMotor_torque(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + mp = ExtExcSyncMotor._motor_parameter + currents = [2,3,4]#["i_sd", "i_sq", "i_e"] + expectedTorque = 1.5 * mp["p"] * (mp["l_M"] * currents[ExtExcSyncMotor.I_E_IDX] * mp["i_k_rs"] + (mp["l_d"] - mp["l_q"]) * currents[ExtExcSyncMotor.I_SD_IDX]) * currents[ExtExcSyncMotor.I_SQ_IDX] + assert np.array_equal(ExtExcSyncMotor.torque(currents),expectedTorque) + +def test_ExtExcitedSynchronousMotor_el_jacobian(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + mp = ExtExcSyncMotor._motor_parameter + state = [5, 5, 10]#[i_sd, i_sq, epsilon] + omega = 60 + u_in = [50, 60, 50]#[u_sd, u_sq] + expectedJacobian = ( + np.array([ # dx'/dx + [ -mp["r_s"] / (mp["l_d"] * mp["sigma"]), mp["l_q"] / (mp["sigma"] * mp["l_d"]) * omega * mp["p"], mp["l_M"] * mp["r_E"] / (mp["sigma"] * mp["l_d"] * mp["l_E"]) * mp["i_k_rs"], 0], + [ -mp["l_d"] / mp["l_q"] * omega * mp["p"], -mp["r_s"] / mp["l_q"], -omega * mp["p"] * mp["l_M"] / mp["l_q"] * mp["i_k_rs"], 0], + [mp["l_M"] * mp["r_s"] / (mp["sigma"] * mp["l_d"] * mp["l_E"] * mp["i_k_rs"]), -omega * mp["p"] * mp["l_M"] * mp["l_q"] / (mp["sigma"] * mp["l_d"] * mp["l_E"] * mp["i_k_rs"]), -mp["r_E"] / (mp["sigma"] * mp["l_E"]), 0], + [ 0, 0, 0, 0], + ]), + np.array([ # dx'/dw + mp["p"] * mp["l_q"] / (mp["l_d"] * mp["sigma"]) * state[ExtExcSyncMotor.I_SQ_IDX], + -mp["p"] * mp["l_d"] / mp["l_q"] * state[ExtExcSyncMotor.I_SD_IDX] - mp["p"] * mp["l_M"] / mp["l_q"] * state[ExtExcSyncMotor.I_E_IDX] * mp["i_k_rs"], + -mp["p"] * mp["l_M"] * mp["l_q"] / (mp["sigma"] * mp["l_d"] * mp["l_E"] * mp["i_k_rs"]) * state[ExtExcSyncMotor.I_SQ_IDX], + mp["p"], + ]), + np.array([ # dT/dx + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[ExtExcSyncMotor.I_SQ_IDX], + 1.5 * mp["p"] * (mp["l_M"] * state[ExtExcSyncMotor.I_E_IDX] * mp["i_k_rs"] + (mp["l_d"] - mp["l_q"]) * state[ExtExcSyncMotor.I_SD_IDX]), + 1.5 * mp["p"] * mp["l_M"] * mp["i_k_rs"] * state[ExtExcSyncMotor.I_SQ_IDX], + 0, + ]) + ) + assert np.array_equal(expectedJacobian[0],ExtExcSyncMotor.electrical_jacobian(state,u_in,omega)[0]) + assert np.array_equal(expectedJacobian[1],ExtExcSyncMotor.electrical_jacobian(state,u_in,omega)[1]) + assert np.array_equal(expectedJacobian[2],ExtExcSyncMotor.electrical_jacobian(state,u_in,omega)[2]) + +def test_ExtExcitedSynchronousMotor_i_in(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + state = np.array([10,20,3,5,8,9]) + expectedCurrent = [10,20,3] + assert np.array_equal(ExtExcSyncMotor.i_in(state),expectedCurrent) + +def test_ExtExcSyncMotor_reset(): + ExtExcSyncMotor = ExternallyExcitedSynchronousMotor() + default_initial_state = {"i_sq": 0.0, "i_sd": 0.0, "i_e": 0.0, "epsilon": 0.0} + default_Initial_state_array = [0,0,0,0] + new_initial_state = {"i_sq": 10.0, "i_sd": 0.0, "i_e": 10.0, "epsilon": 5.0} + ExtExcSyncMotor_state_positions = {"i_sq": 0,"i_sd": 1,"i_e": 2,"epsilon": 3,"torque": 4,"omega": 5,"u": 6} + ExtExcSyncMotor_state_space = Box(low=-1, high=1, shape=(7,), dtype=np.float64) + assert ExtExcSyncMotor._initial_states == default_initial_state + ExtExcSyncMotor._initial_states = new_initial_state + assert (ExtExcSyncMotor.reset(ExtExcSyncMotor_state_space,ExtExcSyncMotor_state_positions),default_Initial_state_array) + ExtExcSyncMotor.initializer["states"] = new_initial_state + ExtExcSyncMotor.reset(ExtExcSyncMotor_state_space,ExtExcSyncMotor_state_positions) + assert ExtExcSyncMotor._initial_states == new_initial_state + +def test_InitPermMagSyncMotor(): + defaultPermMagSyncMotor = PermanentMagnetSynchronousMotor() + assert defaultPermMagSyncMotor._default_motor_parameter == {"p": 3,"l_d": 0.37e-3,"l_q": 1.2e-3,"j_rotor": 0.03883,"r_s": 18e-3,"psi_p": 66e-3,} + assert defaultPermMagSyncMotor.HAS_JACOBIAN + assert defaultPermMagSyncMotor.motor_parameter == defaultPermMagSyncMotor._default_motor_parameter + assert defaultPermMagSyncMotor._initial_states == defaultPermMagSyncMotor._default_initializer["states"] + concretePermMagSyncMotor = PermanentMagnetSynchronousMotor(test_PermMagSyncMotor_parameter,None,None,test_PermMagSyncMotor_initializer) + assert concretePermMagSyncMotor.motor_parameter == test_PermMagSyncMotor_parameter + assert concretePermMagSyncMotor.initializer == test_PermMagSyncMotor_initializer + assert concretePermMagSyncMotor._initial_states == test_PermMagSyncMotor_initializer["states"] + assert defaultPermMagSyncMotor.CURRENTS_IDX == SynchronousMotor.CURRENTS_IDX + assert defaultPermMagSyncMotor.CURRENTS == SynchronousMotor.CURRENTS + +def test_PermMagSyncMotor_torque(): + defaultPermMagSyncMotor = PermanentMagnetSynchronousMotor() + mp = defaultPermMagSyncMotor._motor_parameter + currents = [3,1,2,3] + expectedTorque = ( + 1.5 * mp["p"] * (mp["psi_p"] + (mp["l_d"] - mp["l_q"]) * currents[defaultPermMagSyncMotor.I_SD_IDX]) * currents[defaultPermMagSyncMotor.I_SQ_IDX] + ) + assert defaultPermMagSyncMotor.torque(currents) == expectedTorque + +def test_PermMagSyncMotor_el_Jacobian(): + defaultPermMagSyncMotor = PermanentMagnetSynchronousMotor() + mp = defaultPermMagSyncMotor._motor_parameter + state = [5, 5] + omega = 60 + u_in = [50, 60] + expectedJacobian = ( + np.array( + [ # dx'/dx + [ + -mp["r_s"] / mp["l_d"], + mp["l_q"] / mp["l_d"] * omega * mp["p"], + 0, + ], + [ + -mp["l_d"] / mp["l_q"] * omega * mp["p"], + -mp["r_s"] / mp["l_q"], + 0, + ], + [0, 0, 0], + ] + ), + np.array( + [ # dx'/dw + mp["p"] * mp["l_q"] / mp["l_d"] * state[defaultPermMagSyncMotor.I_SQ_IDX], + -mp["p"] * mp["l_d"] / mp["l_q"] * state[defaultPermMagSyncMotor.I_SD_IDX] - mp["p"] * mp["psi_p"] / mp["l_q"], + mp["p"], + ] + ), + np.array( + [ # dT/dx + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[defaultPermMagSyncMotor.I_SQ_IDX], + 1.5 * mp["p"] * (mp["psi_p"] + (mp["l_d"] - mp["l_q"]) * state[defaultPermMagSyncMotor.I_SD_IDX]), + 0, + ] + ), + ) + assert np.array_equal(expectedJacobian[0],defaultPermMagSyncMotor.electrical_jacobian(state,u_in,omega)[0]) + +def test_SixPhaseMotor_t_46(): + defaultSixPhaseMotor = SixPhaseMotor() + t46 = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1] + ]) + i_abc = [1, 2, 3, 1, 2, 3] #[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2] + expectedI_out = np.matmul(t46, i_abc) # [i_salpha, i_sbeta, i_sX, i_sY] + assert np.array_equal(defaultSixPhaseMotor.t_46(i_abc),expectedI_out) + +def test_SixPhaseMotor_q(): + defaultSixPhaseMotor = SixPhaseMotor() + t_vsd = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1], + ]) + def rotation_matrix(theta): + return np.array([ + [math.cos(theta), math.sin(theta)], + [-math.sin(theta), math.cos(theta)] + ]) + epsilon = 10 + t1 = rotation_matrix(epsilon) + t2 = rotation_matrix(-epsilon) + tp_alphaBetaXY = block_diag(t1,t2) + tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) + i_abc = [1, 2, 3, 1, 2, 3] #[i_sa1, i_sb1, i_sc1, i_sa2, i_sb2, i_sc2] + expectedI_out = np.matmul(tp_vsd, i_abc) # [i_sd, i_sq, i_sx, i_sy] + assert np.array_equal(defaultSixPhaseMotor.q(i_abc,epsilon),expectedI_out) + +def test_SixPhaseMotor_q_inv(): + defaultSixPhaseMotor = SixPhaseMotor() + t_vsd = 1/ 3 * np.array([ + [1, -0.5, -0.5, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0], + [0, 0.5 * np.sqrt(3), -0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, -0.5, -0.5, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0], + [0, -0.5 * np.sqrt(3), 0.5 * np.sqrt(3), 0.5, 0.5, -1], + [1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1] + ]) + epsilon = 0.5 + cos = math.cos(epsilon) + sin = math.sin(epsilon) + tp_alphaBetaXY = np.array([ + [cos, sin, 0, 0, 0, 0], + [-sin, cos, 0, 0, 0, 0], + [0, 0, cos, -sin, 0, 0], + [0, 0, sin, cos, 0, 0], + [0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1], + ]) + tp_vsd = np.matmul(tp_alphaBetaXY, t_vsd) + inv_tpVsd = np.linalg.inv(tp_vsd) + modified_inv_tpVsd = np.delete(inv_tpVsd, [4, 5], axis=1) + quantities_dqxy = [1,2,2,1] + expected_abc = np.matmul(modified_inv_tpVsd, quantities_dqxy) + assert np.array_equal(defaultSixPhaseMotor.q_inv(quantities_dqxy,epsilon),expected_abc) + +def test_InitSixPhasePMSM(): + defaultSixPhasePMSM = SixPhasePMSM() + assert defaultSixPhasePMSM._default_motor_parameter == {"p": 5, "l_d": 125e-6, "l_q": 126e-6, "l_x": 39e-6, "l_y": 35e-6, "r_s": 64.3e-3, "psi_PM": 4.7e-3, "j_rotor": 0.0110,} + assert defaultSixPhasePMSM.HAS_JACOBIAN + assert defaultSixPhasePMSM.motor_parameter == defaultSixPhasePMSM._default_motor_parameter + assert defaultSixPhasePMSM._initial_states == defaultSixPhasePMSM._default_initializer["states"] + concreteSixPhasePMSM = SixPhasePMSM(test_SixPhasePMSM_parameter,None,None,test_SixPhasePMSM_initializer) + assert concreteSixPhasePMSM.motor_parameter == test_SixPhasePMSM_parameter + assert concreteSixPhasePMSM.initializer == test_SixPhasePMSM_initializer + assert concreteSixPhasePMSM._initial_states == test_SixPhasePMSM_initializer["states"] + assert defaultSixPhasePMSM.CURRENTS_IDX == SixPhasePMSM.CURRENTS_IDX + assert defaultSixPhasePMSM.CURRENTS == SixPhasePMSM.CURRENTS + assert defaultSixPhasePMSM.IO_VOLTAGES == SixPhasePMSM.IO_VOLTAGES + + +def test_SixPhasePMSM_el_ode(): + defaultSixPhasePMSM = SixPhasePMSM() + state = [6, 5, 10, 2, 1]#[i_sd, i_sq, i_sx, i_sy, epsilon] + omega = 60 + u_dqxy = [50, 60, 50, 50]#[u_sd, u_sq, u_sx, u_sy] + expectedOde = np.matmul( + defaultSixPhasePMSM._model_constants, + np.array( + [ + omega, + state[defaultSixPhasePMSM.I_SD_IDX], + state[defaultSixPhasePMSM.I_SQ_IDX], + state[defaultSixPhasePMSM.I_SX_IDX], + state[defaultSixPhasePMSM.I_SY_IDX], + u_dqxy[0], + u_dqxy[1], + u_dqxy[2], + u_dqxy[3], + omega * state[defaultSixPhasePMSM.I_SD_IDX], + omega * state[defaultSixPhasePMSM.I_SQ_IDX], + omega * state[defaultSixPhasePMSM.I_SX_IDX], + omega * state[defaultSixPhasePMSM.I_SY_IDX], + ] + ) + ) + assert np.array_equal(defaultSixPhasePMSM.electrical_ode(state,u_dqxy,omega),expectedOde) + +def test_SixPhasePMSM_el_jacobian(): + defaultSixPhasePMSM = SixPhasePMSM() + mp = defaultSixPhasePMSM._motor_parameter + state = [6, 5, 10, 2, 1]#[i_sd, i_sq, i_sx, i_sy, epsilon] + omega = 60 + u_dqxy = [50, 60, 50, 50]#[u_sd, u_sq, u_sx, u_sy] + expectedJacobian = ( + np.array( + [ # dx'/dx + # i_sd i_sq i_sx i_sy epsilon + [ + -mp["r_s"] / mp["l_d"], + mp["l_q"] / mp["l_d"] * omega, + 0, + 0, + 0 + ], + [ + -mp["l_d"] / mp["l_q"] * omega, + -mp["r_s"] / mp["l_q"], + 0, + 0, + 0 + ], + [ + 0, + 0, + -mp["r_s"] / mp["l_x"], + -mp["l_y"] / mp["l_x"] * omega, + 0 + ], + [ + 0, + 0, + mp["l_x"] / mp["l_y"] * omega, + -mp["r_s"] / mp["l_y"], + 0 + ], + [0, 0, 0, 0, 0], + ] + ), + np.array( + [ # dx'/dw + mp["l_q"] / mp["l_d"], + -mp["l_d"] / mp["l_q"], + -mp["l_y"] / mp["l_x"], + mp["l_x"] / mp["l_y"], + mp["p"], + ] + ), + np.array( + [ # dT/dx + 1.5 * mp["p"] * (mp["l_d"] - mp["l_q"]) * state[defaultSixPhasePMSM.I_SQ_IDX], + 1.5 * mp["p"] * (mp["psi_PM"] + (mp["l_d"] - mp["l_q"]) * state[defaultSixPhasePMSM.I_SD_IDX]), + 0, + 0, + 0 + ] + ), + ) + assert np.array_equal(expectedJacobian[0],defaultSixPhasePMSM.electrical_jacobian(state,u_dqxy,omega)[0]) + assert np.array_equal(expectedJacobian[1],defaultSixPhasePMSM.electrical_jacobian(state,u_dqxy,omega)[1]) + assert np.array_equal(expectedJacobian[2],defaultSixPhasePMSM.electrical_jacobian(state,u_dqxy,omega)[2]) + +def test_SixPhasePMSM_torque(): + defaultSixPhasePMSM = SixPhasePMSM() + mp = defaultSixPhasePMSM._motor_parameter + currents = [3,1,2,3] + expectedTorque = ( + 1.5 * mp["p"] * (mp["psi_PM"] + (mp["l_d"] - mp["l_q"]) * currents[defaultSixPhasePMSM.I_SD_IDX]) * currents[defaultSixPhasePMSM.I_SQ_IDX] + ) + assert defaultSixPhasePMSM.torque(currents) == expectedTorque + +def test_SixPMSM_reset(): + defaultSixPhasePMSM = SixPhasePMSM() + default_initial_state = {"i_sd": 0.0, "i_sq": 0.0, "i_sx": 0.0, "i_sy": 0.0, "epsilon": 0.0} + default_Initial_state_array = [0,0,0,0,0] + new_initial_state = {"i_sd": 10.0, "i_sq": 20.0, "i_sx": 10.0, "i_sy": 10.0, "epsilon": 10.0} + assert defaultSixPhasePMSM._initial_states == default_initial_state + defaultSixPhasePMSM_state_positions = {"i_sd": 0, "i_sq": 1, "i_sx": 2, "i_sy": 3, "epsilon": 4, "torque": 5,"omega": 6,"u": 7} + defaultSixPhasePMSM_state_space = Box(low=-1, high=1, shape=(8,), dtype=np.float64) + assert (defaultSixPhasePMSM.reset(defaultSixPhasePMSM_state_space,defaultSixPhasePMSM_state_positions),default_Initial_state_array) + defaultSixPhasePMSM._initial_states = new_initial_state + defaultSixPhasePMSM.reset(defaultSixPhasePMSM_state_space,defaultSixPhasePMSM_state_positions) + assert defaultSixPhasePMSM._initial_states == new_initial_state From b78660b7e69902f5fdd5dbac103360a9fbe5bd21 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Fri, 18 Apr 2025 01:25:45 +0530 Subject: [PATCH 08/14] correction to documentation --- .../physical_systems/electric_motors/sixphase_pmsm.rst | 2 +- examples/classic_controllers/demoSixPhasePMSM.py | 8 +++++++- src/gym_electric_motor/envs/motors.py | 6 +++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst index 81f744fd..73b65659 100644 --- a/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst +++ b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst @@ -7,7 +7,7 @@ Electrical ODE .. math:: \frac{\mathrm{d} i_s^d}{\mathrm{d} t} = \frac{u_s^d + \omega_\mathrm{el} L_s^q i_s^q - R_s i_s^d}{L_s^d} \\ - \frac{\mathrm{d} i_s^q}{\mathrm{d} t} = \frac{u_s^q - \omega_\mathrm{el} L_s^d i_s^d - R_s i_s^q - \omega_\mathrm{el}}{L_s^q} \\ + \frac{\mathrm{d} i_s^q}{\mathrm{d} t} = \frac{u_s^q - \omega_\mathrm{el} L_s^d i_s^d - R_s i_s^q - \omega_\mathrm{el} psi_\mathrm{PM}}{L_s^q} \\ \frac{\mathrm{d} i_s^x}{\mathrm{d} t} = \frac{u_s^x - \omega_\mathrm{el} L_s^y i_s^y - R_s i_s^x}{L_s^x} \\ \frac{\mathrm{d} i_s^y}{\mathrm{d} t} = \frac{u_s^y + \omega_\mathrm{el} L_s^x i_s^x - R_s i_s^y}{L_s^y} \\ diff --git a/examples/classic_controllers/demoSixPhasePMSM.py b/examples/classic_controllers/demoSixPhasePMSM.py index 09fa81cc..9f79447a 100644 --- a/examples/classic_controllers/demoSixPhasePMSM.py +++ b/examples/classic_controllers/demoSixPhasePMSM.py @@ -1,4 +1,5 @@ import gym_electric_motor as gem +from gym_electric_motor.envs.motors import ActionType, ControlType, Motor, MotorType from gym_electric_motor.reference_generators import LaplaceProcessReferenceGenerator from gym_electric_motor.visualization import MotorDashboard from gym_electric_motor.visualization.render_modes import RenderMode @@ -9,8 +10,13 @@ my_changed_voltage_supply_args = {'u_nominal': 200.0} motor_dashboard = MotorDashboard(state_plots=("i_a1","i_a2"),render_mode=RenderMode.Figure) load = ConstantSpeedLoad(omega_fixed=200) +motor = Motor( + MotorType.SixPhasePMSM, + ControlType.CurrentControl, + ActionType.Continuous, + ) env = gem.make( - "Cont-CC-SIXPMSM-v0", + motor.env_id(), supply = my_changed_voltage_supply_args, load = load, visualization = motor_dashboard) diff --git a/src/gym_electric_motor/envs/motors.py b/src/gym_electric_motor/envs/motors.py index 01475cc7..1242d2aa 100644 --- a/src/gym_electric_motor/envs/motors.py +++ b/src/gym_electric_motor/envs/motors.py @@ -3,7 +3,7 @@ MotorType = Enum( "MotorType", - ["PermanentlyExcitedDcMotor", "ExternallyExcitedDcMotor", "SeriesDc", "ShuntDc", "ExternallyExcitedSynchronousMotor", "DoublyFedInductionMotor", "SquirrelCageInductionMotor", "PermanentMagnetSynchronousMotor", "SynchronousReluctanceMotor"], + ["PermanentlyExcitedDcMotor", "ExternallyExcitedDcMotor", "SeriesDc", "ShuntDc", "ExternallyExcitedSynchronousMotor", "DoublyFedInductionMotor", "SquirrelCageInductionMotor", "PermanentMagnetSynchronousMotor", "SynchronousReluctanceMotor", "SixPhasePMSM"], ) MotorType.PermanentlyExcitedDcMotor.states = ["omega", "torque", "i", "u"] MotorType.ExternallyExcitedDcMotor.states = [ @@ -37,6 +37,9 @@ MotorType.SynchronousReluctanceMotor.states = [ "omega" , "torque", "i_sd", "i_sq", "i_a", "i_b", "i_c", "u_sd", "u_sq", "u_a", "u_b", "u_c" ] +MotorType.SixPhasePMSM.states = ["omega" , "torque", "i_a1", "i_b1", "i_c1", "i_a2", "i_b2", "i_c2", "i_sd", "i_sq", + "i_sx", "i_sy", "u_a1", "u_b1", "u_c1", "u_a2", "u_b2", "u_c2", "u_sd", "u_sq", "u_sx", "u_sy","epsilon", +] # add env_id_tag if you dont want to use enum name as env_id @@ -47,6 +50,7 @@ MotorType.SquirrelCageInductionMotor.env_id_tag = "SCIM" MotorType.PermanentMagnetSynchronousMotor.env_id_tag = "PMSM" MotorType.SynchronousReluctanceMotor.env_id_tag = "SynRM" +MotorType.SixPhasePMSM.env_id_tag = "SIXPMSM" ControlType = Enum("ControlType", ["SpeedControl", "TorqueControl", "CurrentControl"]) ControlType.SpeedControl.env_id_tag = "SC" From 74e04d5a3f5cfaa440da15650cd77e1277279902 Mon Sep 17 00:00:00 2001 From: RanilThomas Date: Thu, 24 Apr 2025 01:15:55 +0530 Subject: [PATCH 09/14] instances of d,q,x,y,s to non italic --- .../physical_systems/electric_motors/sixphase_pmsm.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst index 73b65659..37546b34 100644 --- a/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst +++ b/docs/parts/physical_systems/electric_motors/sixphase_pmsm.rst @@ -6,10 +6,10 @@ Electrical ODE ************** .. math:: - \frac{\mathrm{d} i_s^d}{\mathrm{d} t} = \frac{u_s^d + \omega_\mathrm{el} L_s^q i_s^q - R_s i_s^d}{L_s^d} \\ - \frac{\mathrm{d} i_s^q}{\mathrm{d} t} = \frac{u_s^q - \omega_\mathrm{el} L_s^d i_s^d - R_s i_s^q - \omega_\mathrm{el} psi_\mathrm{PM}}{L_s^q} \\ - \frac{\mathrm{d} i_s^x}{\mathrm{d} t} = \frac{u_s^x - \omega_\mathrm{el} L_s^y i_s^y - R_s i_s^x}{L_s^x} \\ - \frac{\mathrm{d} i_s^y}{\mathrm{d} t} = \frac{u_s^y + \omega_\mathrm{el} L_s^x i_s^x - R_s i_s^y}{L_s^y} \\ + \frac{\mathrm{d} i_\mathrm{d}}{\mathrm{d} t}&= \frac{u_\mathrm{d} + \omega_\mathrm{el} L_\mathrm{q} i_\mathrm{q} - R_\mathrm{s} i_\mathrm{d}}{L_\mathrm{d}} \\ + \frac{\mathrm{d} i_\mathrm{q}}{\mathrm{d} t}&= \frac{u_\mathrm{q} - \omega_\mathrm{el} L_\mathrm{d} i_\mathrm{d} - R_\mathrm{s} i_\mathrm{q} - \omega_\mathrm{el} \psi_\mathrm{PM}}{L_\mathrm{q}} \\ + \frac{\mathrm{d} i_\mathrm{x}}{\mathrm{d} t}&= \frac{u_\mathrm{x} - \omega_\mathrm{el} L_\mathrm{y} i_\mathrm{y} - R_\mathrm{s} i_\mathrm{x}}{L_\mathrm{x}} \\ + \frac{\mathrm{d} i_\mathrm{y}}{\mathrm{d} t}&= \frac{u_\mathrm{y} + \omega_\mathrm{el} L_\mathrm{x} i_\mathrm{x} - R_\mathrm{s} i_\mathrm{y}}{L_\mathrm{y}} \\ From 3946400a5a638bc5973dd1b93b0ae65f2daab528 Mon Sep 17 00:00:00 2001 From: HauckeBa Date: Wed, 30 Apr 2025 15:02:35 +0200 Subject: [PATCH 10/14] added source of the default parameters and modified the changelog --- CHANGELOG.md | 1 + .../physical_systems/electric_motors/six_phase_PMSM.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5cefb86..1b275656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Added - Automated testing of the existing examples - Automated testing of the electric motors +- 2x3 phase PMSM environment with tests and documentation ## Changed - Changed minimal required gymnasium version to 0.29.1. - updated the code of gem-control to be compatible with gymnasium v1.0.0 diff --git a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py index c1c6368e..b8084406 100644 --- a/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py +++ b/src/gym_electric_motor/physical_systems/electric_motors/six_phase_PMSM.py @@ -19,6 +19,11 @@ class SixPhasePMSM(SixPhaseMotor): psi_PM Vs 4.7e-3 flux linkage of the permanent magnets ===================== ========== ============= =========================================== + The default parameters originate from: + L. Broghammer et al., "Reinforcement Learning Control of Six-Phase Permanent Magnet Synchronous Machines," + 2023 13th International Electric Drives Production Conference (EDPC), Regensburg, Germany, 2023,pp. 1-8, + doi: 10.1109/EDPC60603.2023.10372153. + =============== ====== ============================================= Motor Currents Unit Description =============== ====== ============================================= From abcb9dae786260435857a2c734717228038d48d2 Mon Sep 17 00:00:00 2001 From: annava1 Date: Sat, 21 Jun 2025 18:16:01 +0200 Subject: [PATCH 11/14] EESM file - MTPCL code modified --- src/debug.ipynb | 182 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/debug.ipynb diff --git a/src/debug.ipynb b/src/debug.ipynb new file mode 100644 index 00000000..21004617 --- /dev/null +++ b/src/debug.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "565f13ab", + "metadata": {}, + "outputs": [], + "source": [ + "import gym_electric_motor as gem\n", + "import numpy as np\n", + "import scipy.interpolate as sp_interpolate\n", + "import gem_controllers as gc\n", + "from gym_electric_motor.physical_system_wrappers import FluxObserver\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2687846d", + "metadata": {}, + "outputs": [], + "source": [ + "def _calculate_luts(self):\n", + " \"\"\"\n", + " Calculates the lookup tables for the maximum torque and the optimal currents for a given torque reference.\n", + " \"\"\"\n", + " minimum_loss = []\n", + " best_params = []\n", + "\n", + " minimum_loss_psi = []\n", + " best_params_psi = []\n", + "\n", + " self.psi_max = self.l_m * self.i_e_lim + self.l_d * self.i_q_lim\n", + " torque = np.linspace(0, self.t_lim, self.t_count)\n", + "\n", + " self.t_max_psi = np.zeros(self.psi_count)\n", + "\n", + " for t in torque:\n", + " losses = []\n", + " parameter = []\n", + " for idx, psi in enumerate(np.linspace(0, self.psi_max, self.psi_count)):\n", + " losses_psi = []\n", + " parameter_psi = []\n", + " for i_e in np.linspace(0, self.i_e_lim, self.i_e_count):\n", + " i_d, i_q = self.solve_analytical(t, psi, i_e)\n", + " if np.sqrt(i_d**2 + i_q**2) < self.i_q_lim:\n", + " loss = self.loss(i_d, i_q, i_e)\n", + " params = np.array([t, psi, i_d, i_q, i_e])\n", + " losses.append(loss)\n", + " losses_psi.append(loss)\n", + " parameter.append(params)\n", + " parameter_psi.append(params)\n", + " self.t_max_psi[idx] = t\n", + " if len(losses_psi) > 0:\n", + " minimum_loss_psi.append(min(losses_psi))\n", + " best_params_psi.append(parameter_psi[losses_psi.index(minimum_loss_psi[-1])])\n", + " if len(losses) > 0:\n", + " minimum_loss.append(min(losses))\n", + " best_params.append(parameter[losses.index(minimum_loss[-1])])\n", + "\n", + " best_params = np.array(best_params)\n", + " best_params_psi = np.array(best_params_psi)\n", + "\n", + " self.t_max_psi = sp_interpolate.interp1d(\n", + " np.linspace(0, self.psi_max, self.psi_count), 0.99 * self.t_max_psi, kind=\"linear\"\n", + " )\n", + "\n", + " self.t_max = np.max(best_params[:, 0])\n", + " self.psi_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 1], kind=\"cubic\")\n", + " self.i_d_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 2], kind=\"cubic\")\n", + " self.i_q_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 3], kind=\"cubic\")\n", + " self.i_e_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 4], kind=\"cubic\")\n", + "\n", + " self.t_grid, self.psi_grid = np.mgrid[\n", + " 0 : self.t_max : complex(0, self.t_grid_count), 0 : self.psi_max : complex(self.psi_grid_count)\n", + " ]\n", + "\n", + " self.i_d_inter = sp_interpolate.griddata(\n", + " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", + " best_params_psi[:, 2],\n", + " (self.t_grid, self.psi_grid),\n", + " method=\"linear\",\n", + " )\n", + " self.i_q_inter = sp_interpolate.griddata(\n", + " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", + " best_params_psi[:, 3],\n", + " (self.t_grid, self.psi_grid),\n", + " method=\"linear\",\n", + " )\n", + " self.i_e_inter = sp_interpolate.griddata(\n", + " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", + " best_params_psi[:, 4],\n", + " (self.t_grid, self.psi_grid),\n", + " method=\"linear\",\n", + " )\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "29fd011d", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'numpy' has no attribute 'complex'.\n`np.complex` was a deprecated alias for the builtin `complex`. To avoid this error in existing code, use `complex` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.complex128` here.\nThe aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:\n https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[2], line 19\u001b[0m\n\u001b[0;32m 16\u001b[0m env \u001b[38;5;241m=\u001b[39m gem\u001b[38;5;241m.\u001b[39mmake(env_id, physical_system_wrappers\u001b[38;5;241m=\u001b[39mphysical_system_wrappers)\n\u001b[0;32m 18\u001b[0m \u001b[38;5;66;03m# Initialize the controller\u001b[39;00m\n\u001b[1;32m---> 19\u001b[0m c \u001b[38;5;241m=\u001b[39m \u001b[43mgc\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mGemController\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmake\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 20\u001b[0m \u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 21\u001b[0m \u001b[43m \u001b[49m\u001b[43menv_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 22\u001b[0m \u001b[43m \u001b[49m\u001b[43ma\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m8\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 23\u001b[0m \u001b[43m \u001b[49m\u001b[43mblock_diagram\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 24\u001b[0m \u001b[43m \u001b[49m\u001b[43mcurrent_safety_margin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0.25\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 25\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# save_block_diagram_as=(),\u001b[39;49;00m\n\u001b[0;32m 26\u001b[0m \u001b[43m)\u001b[49m\n\u001b[0;32m 28\u001b[0m \u001b[38;5;66;03m# Control the environment\u001b[39;00m\n\u001b[0;32m 29\u001b[0m c\u001b[38;5;241m.\u001b[39mcontrol_environment(env, n_steps\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m30000\u001b[39m, render_env\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, max_episode_length\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m10000\u001b[39m)\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\gem_controller.py:94\u001b[0m, in \u001b[0;36mGemController.make\u001b[1;34m(cls, env, env_id, decoupling, current_safety_margin, base_current_controller, base_speed_controller, a, should_plot, plot_references, block_diagram, save_block_diagram_as)\u001b[0m\n\u001b[0;32m 91\u001b[0m controller \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mGymElectricMotorAdapter(env, env_id, controller)\n\u001b[0;32m 93\u001b[0m \u001b[38;5;66;03m# Fit the controllers parameters to the environment\u001b[39;00m\n\u001b[1;32m---> 94\u001b[0m controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtuner_kwargs)\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m block_diagram:\n\u001b[0;32m 97\u001b[0m controller\u001b[38;5;241m.\u001b[39mbuild_block_diagram(env_id, save_block_diagram_as)\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\gem_adapter.py:110\u001b[0m, in \u001b[0;36mGymElectricMotorAdapter.tune\u001b[1;34m(self, env, env_id, tune_controller, **kwargs)\u001b[0m\n\u001b[0;32m 108\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_output_stage\u001b[38;5;241m.\u001b[39mtune(env, env_id)\n\u001b[0;32m 109\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m tune_controller:\n\u001b[1;32m--> 110\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 112\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_reference_plotter\u001b[38;5;241m.\u001b[39mtune(\n\u001b[0;32m 113\u001b[0m env,\n\u001b[0;32m 114\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mreferenced_states,\n\u001b[0;32m 115\u001b[0m plot_references\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m 116\u001b[0m maximum_reference\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mmaximum_reference,\n\u001b[0;32m 117\u001b[0m )\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\torque_controller.py:114\u001b[0m, in \u001b[0;36mTorqueController.tune\u001b[1;34m(self, env, env_id, current_safety_margin, tune_current_controller, **kwargs)\u001b[0m\n\u001b[0;32m 112\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_current_controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39mtune(env, env_id, margin\u001b[38;5;241m=\u001b[39mcurrent_safety_margin)\n\u001b[1;32m--> 114\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_operation_point_selection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtune\u001b[49m\u001b[43m(\u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43menv_id\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_safety_margin\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_referenced_currents \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mparameter_reader\u001b[38;5;241m.\u001b[39mcurrents[gc\u001b[38;5;241m.\u001b[39mutils\u001b[38;5;241m.\u001b[39mget_motor_type(env_id)]\n\u001b[0;32m 116\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m current, action_range_low, action_range_high \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(\n\u001b[0;32m 117\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_referenced_currents, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39maction_range[\u001b[38;5;241m0\u001b[39m], \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39maction_range[\u001b[38;5;241m1\u001b[39m]\n\u001b[0;32m 118\u001b[0m ):\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\stages\\operation_point_selection\\eesm_ops.py:108\u001b[0m, in \u001b[0;36mEESMOperationPointSelection.tune\u001b[1;34m(self, env, env_id, current_safety_margin)\u001b[0m\n\u001b[0;32m 94\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mloss \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m i_d, i_q, i_e: np\u001b[38;5;241m.\u001b[39mabs(i_d) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_s \u001b[38;5;241m+\u001b[39m np\u001b[38;5;241m.\u001b[39mabs(i_q) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_s \u001b[38;5;241m+\u001b[39m np\u001b[38;5;241m.\u001b[39mabs(i_e) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_e\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpoly \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m i_e, psi, torque: [\n\u001b[0;32m 97\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d \u001b[38;5;241m-\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m,\n\u001b[0;32m 98\u001b[0m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d \u001b[38;5;241m-\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 105\u001b[0m ((\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m-\u001b[39m psi\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m+\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q \u001b[38;5;241m*\u001b[39m torque \u001b[38;5;241m/\u001b[39m (\u001b[38;5;241m3\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mp)) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m,\n\u001b[0;32m 106\u001b[0m ]\n\u001b[1;32m--> 108\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_calculate_luts\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\stages\\operation_point_selection\\eesm_ops.py:184\u001b[0m, in \u001b[0;36mEESMOperationPointSelection._calculate_luts\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 180\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_q_opt \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39minterp1d(best_params[:, \u001b[38;5;241m0\u001b[39m], best_params[:, \u001b[38;5;241m3\u001b[39m], kind\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcubic\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 181\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_e_opt \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39minterp1d(best_params[:, \u001b[38;5;241m0\u001b[39m], best_params[:, \u001b[38;5;241m4\u001b[39m], kind\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcubic\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 183\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mmgrid[\n\u001b[1;32m--> 184\u001b[0m \u001b[38;5;241m0\u001b[39m : \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_max : \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcomplex\u001b[49m(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid_count), \u001b[38;5;241m0\u001b[39m : \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_max : np\u001b[38;5;241m.\u001b[39mcomplex(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid_count)\n\u001b[0;32m 185\u001b[0m ]\n\u001b[0;32m 187\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_d_inter \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39mgriddata(\n\u001b[0;32m 188\u001b[0m (best_params_psi[:, \u001b[38;5;241m0\u001b[39m], best_params_psi[:, \u001b[38;5;241m1\u001b[39m]),\n\u001b[0;32m 189\u001b[0m best_params_psi[:, \u001b[38;5;241m2\u001b[39m],\n\u001b[0;32m 190\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid),\n\u001b[0;32m 191\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 192\u001b[0m )\n\u001b[0;32m 193\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_q_inter \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39mgriddata(\n\u001b[0;32m 194\u001b[0m (best_params_psi[:, \u001b[38;5;241m0\u001b[39m], best_params_psi[:, \u001b[38;5;241m1\u001b[39m]),\n\u001b[0;32m 195\u001b[0m best_params_psi[:, \u001b[38;5;241m3\u001b[39m],\n\u001b[0;32m 196\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid),\n\u001b[0;32m 197\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 198\u001b[0m )\n", + "File \u001b[1;32mc:\\Users\\Valliammai\\anaconda3\\envs\\proj\\lib\\site-packages\\numpy\\__init__.py:424\u001b[0m, in \u001b[0;36m__getattr__\u001b[1;34m(attr)\u001b[0m\n\u001b[0;32m 419\u001b[0m warnings\u001b[38;5;241m.\u001b[39mwarn(\n\u001b[0;32m 420\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIn the future `np.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mattr\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m` will be defined as the \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 421\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcorresponding NumPy scalar.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;167;01mFutureWarning\u001b[39;00m, stacklevel\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m2\u001b[39m)\n\u001b[0;32m 423\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;129;01min\u001b[39;00m __former_attrs__:\n\u001b[1;32m--> 424\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(__former_attrs__[attr])\n\u001b[0;32m 426\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;129;01min\u001b[39;00m __expired_attributes__:\n\u001b[0;32m 427\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\n\u001b[0;32m 428\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m`np.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mattr\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m` was removed in the NumPy 2.0 release. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 429\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m__expired_attributes__[attr]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 430\u001b[0m )\n", + "\u001b[1;31mAttributeError\u001b[0m: module 'numpy' has no attribute 'complex'.\n`np.complex` was a deprecated alias for the builtin `complex`. To avoid this error in existing code, use `complex` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.complex128` here.\nThe aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:\n https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations" + ] + } + ], + "source": [ + "\n", + "\n", + " # choose the action space\n", + "action_space = 'Cont' # 'Cont' or 'Finite'\n", + "\n", + " # choose the control task\n", + "control_task = 'TC' # 'SC' (speed control), 'TC' (torque control) or 'CC' (current control)\n", + "\n", + " # chosse the motor type\n", + "motor_type = 'EESM' # 'PermExDc', 'ExtExDc', 'SeriesDc', 'ShuntDc', 'PMSM', 'EESM', 'SynRM' or 'SCIM'\n", + "\n", + "env_id = action_space + '-' + control_task + '-' + motor_type + '-v0'\n", + "\n", + " # using a flux observer for the SCIM\n", + "physical_system_wrappers = (FluxObserver(),) if motor_type == 'SCIM' else ()\n", + "\n", + " # Initilize the environment\n", + "env = gem.make(env_id, physical_system_wrappers=physical_system_wrappers)\n", + " \n", + " # Initialize the controller\n", + "c = gc.GemController.make(\n", + " env,\n", + " env_id,\n", + " a=8,\n", + " block_diagram=False,\n", + " current_safety_margin=0.25,\n", + " # save_block_diagram_as=(),\n", + " )\n", + "\n", + " # Control the environment\n", + "c.control_environment(env, n_steps=30000, render_env=True, max_episode_length=10000)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "proj", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.21" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From fddb23c8b90f459b4305ea43951f797df766cb1e Mon Sep 17 00:00:00 2001 From: annava1 Date: Sat, 21 Jun 2025 18:52:34 +0200 Subject: [PATCH 12/14] Syntax changed - 2 files --- .../stages/operation_point_selection/eesm_ops.py | 2 +- .../stages/operation_point_selection/pmsm_ops.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gem_controllers/stages/operation_point_selection/eesm_ops.py b/src/gem_controllers/stages/operation_point_selection/eesm_ops.py index e31ab378..9c54ecf8 100644 --- a/src/gem_controllers/stages/operation_point_selection/eesm_ops.py +++ b/src/gem_controllers/stages/operation_point_selection/eesm_ops.py @@ -181,7 +181,7 @@ def _calculate_luts(self): self.i_e_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 4], kind="cubic") self.t_grid, self.psi_grid = np.mgrid[ - 0 : self.t_max : np.complex(0, self.t_grid_count), 0 : self.psi_max : np.complex(self.psi_grid_count) + 0 : self.t_max : np.complex128(0, self.t_grid_count), 0 : self.psi_max : np.complex128(self.psi_grid_count) ] self.i_d_inter = sp_interpolate.griddata( diff --git a/src/gem_controllers/stages/operation_point_selection/pmsm_ops.py b/src/gem_controllers/stages/operation_point_selection/pmsm_ops.py index 6a828b11..0925a897 100644 --- a/src/gem_controllers/stages/operation_point_selection/pmsm_ops.py +++ b/src/gem_controllers/stages/operation_point_selection/pmsm_ops.py @@ -236,8 +236,8 @@ def tune(self, env: gem.core.ElectricMotorEnvironment, env_id: str, current_safe # Define the grid for the torque and fluxes self.t_grid, self.psi_grid = np.mgrid[ - np.amin(t) : np.amax(t) : np.complex(0, self.t_count), - self.psi_min : self.psi_max : np.complex(self.psi_count), + np.amin(t) : np.amax(t) : np.complex128(0, self.t_count), + self.psi_min : self.psi_max : np.complex128(self.psi_count), ] # Interpolate the functions From 95aa3caf20c44c6ae6eeb45b738dd98674d0cf37 Mon Sep 17 00:00:00 2001 From: annava1 Date: Sat, 5 Jul 2025 17:36:13 +0200 Subject: [PATCH 13/14] added debug file to gitignore --- .gitignore | 4 +++- src/debug.ipynb | 20 +++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index be0750cb..7baef984 100644 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,6 @@ examples/logs/ .vscode/settings.json .vscode/launch.json plots/ -saved_plots/ \ No newline at end of file +saved_plots/ + +*/debug.ipynb \ No newline at end of file diff --git a/src/debug.ipynb b/src/debug.ipynb index 21004617..7cd23604 100644 --- a/src/debug.ipynb +++ b/src/debug.ipynb @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "2687846d", "metadata": {}, "outputs": [], @@ -101,25 +101,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "29fd011d", "metadata": {}, "outputs": [ { - "ename": "AttributeError", - "evalue": "module 'numpy' has no attribute 'complex'.\n`np.complex` was a deprecated alias for the builtin `complex`. To avoid this error in existing code, use `complex` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.complex128` here.\nThe aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:\n https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations", + "ename": "NameError", + "evalue": "name 'gem' is not defined", "output_type": "error", "traceback": [ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[2], line 19\u001b[0m\n\u001b[0;32m 16\u001b[0m env \u001b[38;5;241m=\u001b[39m gem\u001b[38;5;241m.\u001b[39mmake(env_id, physical_system_wrappers\u001b[38;5;241m=\u001b[39mphysical_system_wrappers)\n\u001b[0;32m 18\u001b[0m \u001b[38;5;66;03m# Initialize the controller\u001b[39;00m\n\u001b[1;32m---> 19\u001b[0m c \u001b[38;5;241m=\u001b[39m \u001b[43mgc\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mGemController\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmake\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 20\u001b[0m \u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 21\u001b[0m \u001b[43m \u001b[49m\u001b[43menv_id\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 22\u001b[0m \u001b[43m \u001b[49m\u001b[43ma\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m8\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 23\u001b[0m \u001b[43m \u001b[49m\u001b[43mblock_diagram\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 24\u001b[0m \u001b[43m \u001b[49m\u001b[43mcurrent_safety_margin\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0.25\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 25\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# save_block_diagram_as=(),\u001b[39;49;00m\n\u001b[0;32m 26\u001b[0m \u001b[43m)\u001b[49m\n\u001b[0;32m 28\u001b[0m \u001b[38;5;66;03m# Control the environment\u001b[39;00m\n\u001b[0;32m 29\u001b[0m c\u001b[38;5;241m.\u001b[39mcontrol_environment(env, n_steps\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m30000\u001b[39m, render_env\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m, max_episode_length\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m10000\u001b[39m)\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\gem_controller.py:94\u001b[0m, in \u001b[0;36mGemController.make\u001b[1;34m(cls, env, env_id, decoupling, current_safety_margin, base_current_controller, base_speed_controller, a, should_plot, plot_references, block_diagram, save_block_diagram_as)\u001b[0m\n\u001b[0;32m 91\u001b[0m controller \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mGymElectricMotorAdapter(env, env_id, controller)\n\u001b[0;32m 93\u001b[0m \u001b[38;5;66;03m# Fit the controllers parameters to the environment\u001b[39;00m\n\u001b[1;32m---> 94\u001b[0m controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtuner_kwargs)\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m block_diagram:\n\u001b[0;32m 97\u001b[0m controller\u001b[38;5;241m.\u001b[39mbuild_block_diagram(env_id, save_block_diagram_as)\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\gem_adapter.py:110\u001b[0m, in \u001b[0;36mGymElectricMotorAdapter.tune\u001b[1;34m(self, env, env_id, tune_controller, **kwargs)\u001b[0m\n\u001b[0;32m 108\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_output_stage\u001b[38;5;241m.\u001b[39mtune(env, env_id)\n\u001b[0;32m 109\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m tune_controller:\n\u001b[1;32m--> 110\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 112\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_reference_plotter\u001b[38;5;241m.\u001b[39mtune(\n\u001b[0;32m 113\u001b[0m env,\n\u001b[0;32m 114\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mreferenced_states,\n\u001b[0;32m 115\u001b[0m plot_references\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m 116\u001b[0m maximum_reference\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_controller\u001b[38;5;241m.\u001b[39mmaximum_reference,\n\u001b[0;32m 117\u001b[0m )\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\torque_controller.py:114\u001b[0m, in \u001b[0;36mTorqueController.tune\u001b[1;34m(self, env, env_id, current_safety_margin, tune_current_controller, **kwargs)\u001b[0m\n\u001b[0;32m 112\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_current_controller\u001b[38;5;241m.\u001b[39mtune(env, env_id, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m 113\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39mtune(env, env_id, margin\u001b[38;5;241m=\u001b[39mcurrent_safety_margin)\n\u001b[1;32m--> 114\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_operation_point_selection\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtune\u001b[49m\u001b[43m(\u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43menv_id\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcurrent_safety_margin\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_referenced_currents \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mparameter_reader\u001b[38;5;241m.\u001b[39mcurrents[gc\u001b[38;5;241m.\u001b[39mutils\u001b[38;5;241m.\u001b[39mget_motor_type(env_id)]\n\u001b[0;32m 116\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m current, action_range_low, action_range_high \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(\n\u001b[0;32m 117\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_referenced_currents, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39maction_range[\u001b[38;5;241m0\u001b[39m], \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_clipping_stage\u001b[38;5;241m.\u001b[39maction_range[\u001b[38;5;241m1\u001b[39m]\n\u001b[0;32m 118\u001b[0m ):\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\stages\\operation_point_selection\\eesm_ops.py:108\u001b[0m, in \u001b[0;36mEESMOperationPointSelection.tune\u001b[1;34m(self, env, env_id, current_safety_margin)\u001b[0m\n\u001b[0;32m 94\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mloss \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m i_d, i_q, i_e: np\u001b[38;5;241m.\u001b[39mabs(i_d) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_s \u001b[38;5;241m+\u001b[39m np\u001b[38;5;241m.\u001b[39mabs(i_q) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_s \u001b[38;5;241m+\u001b[39m np\u001b[38;5;241m.\u001b[39mabs(i_e) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mr_e\n\u001b[0;32m 96\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpoly \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m i_e, psi, torque: [\n\u001b[0;32m 97\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d \u001b[38;5;241m-\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m,\n\u001b[0;32m 98\u001b[0m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_d \u001b[38;5;241m-\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q) \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 105\u001b[0m ((\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m-\u001b[39m psi\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m) \u001b[38;5;241m*\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_m \u001b[38;5;241m*\u001b[39m i_e) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m \u001b[38;5;241m+\u001b[39m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39ml_q \u001b[38;5;241m*\u001b[39m torque \u001b[38;5;241m/\u001b[39m (\u001b[38;5;241m3\u001b[39m \u001b[38;5;241m*\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mp)) \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m \u001b[38;5;241m2\u001b[39m,\n\u001b[0;32m 106\u001b[0m ]\n\u001b[1;32m--> 108\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_calculate_luts\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\PE_proj\\gym-electric-motor\\src\\gem_controllers\\stages\\operation_point_selection\\eesm_ops.py:184\u001b[0m, in \u001b[0;36mEESMOperationPointSelection._calculate_luts\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 180\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_q_opt \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39minterp1d(best_params[:, \u001b[38;5;241m0\u001b[39m], best_params[:, \u001b[38;5;241m3\u001b[39m], kind\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcubic\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 181\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_e_opt \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39minterp1d(best_params[:, \u001b[38;5;241m0\u001b[39m], best_params[:, \u001b[38;5;241m4\u001b[39m], kind\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcubic\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 183\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mmgrid[\n\u001b[1;32m--> 184\u001b[0m \u001b[38;5;241m0\u001b[39m : \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_max : \u001b[43mnp\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcomplex\u001b[49m(\u001b[38;5;241m0\u001b[39m, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid_count), \u001b[38;5;241m0\u001b[39m : \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_max : np\u001b[38;5;241m.\u001b[39mcomplex(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid_count)\n\u001b[0;32m 185\u001b[0m ]\n\u001b[0;32m 187\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_d_inter \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39mgriddata(\n\u001b[0;32m 188\u001b[0m (best_params_psi[:, \u001b[38;5;241m0\u001b[39m], best_params_psi[:, \u001b[38;5;241m1\u001b[39m]),\n\u001b[0;32m 189\u001b[0m best_params_psi[:, \u001b[38;5;241m2\u001b[39m],\n\u001b[0;32m 190\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid),\n\u001b[0;32m 191\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 192\u001b[0m )\n\u001b[0;32m 193\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mi_q_inter \u001b[38;5;241m=\u001b[39m sp_interpolate\u001b[38;5;241m.\u001b[39mgriddata(\n\u001b[0;32m 194\u001b[0m (best_params_psi[:, \u001b[38;5;241m0\u001b[39m], best_params_psi[:, \u001b[38;5;241m1\u001b[39m]),\n\u001b[0;32m 195\u001b[0m best_params_psi[:, \u001b[38;5;241m3\u001b[39m],\n\u001b[0;32m 196\u001b[0m (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mt_grid, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpsi_grid),\n\u001b[0;32m 197\u001b[0m method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlinear\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m 198\u001b[0m )\n", - "File \u001b[1;32mc:\\Users\\Valliammai\\anaconda3\\envs\\proj\\lib\\site-packages\\numpy\\__init__.py:424\u001b[0m, in \u001b[0;36m__getattr__\u001b[1;34m(attr)\u001b[0m\n\u001b[0;32m 419\u001b[0m warnings\u001b[38;5;241m.\u001b[39mwarn(\n\u001b[0;32m 420\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIn the future `np.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mattr\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m` will be defined as the \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 421\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcorresponding NumPy scalar.\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;167;01mFutureWarning\u001b[39;00m, stacklevel\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m2\u001b[39m)\n\u001b[0;32m 423\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;129;01min\u001b[39;00m __former_attrs__:\n\u001b[1;32m--> 424\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(__former_attrs__[attr])\n\u001b[0;32m 426\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m attr \u001b[38;5;129;01min\u001b[39;00m __expired_attributes__:\n\u001b[0;32m 427\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mAttributeError\u001b[39;00m(\n\u001b[0;32m 428\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m`np.\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mattr\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m` was removed in the NumPy 2.0 release. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 429\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m__expired_attributes__[attr]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 430\u001b[0m )\n", - "\u001b[1;31mAttributeError\u001b[0m: module 'numpy' has no attribute 'complex'.\n`np.complex` was a deprecated alias for the builtin `complex`. To avoid this error in existing code, use `complex` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.complex128` here.\nThe aliases was originally deprecated in NumPy 1.20; for more details and guidance see the original release note at:\n https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations" + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[4], line 18\u001b[0m\n\u001b[0;32m 15\u001b[0m physical_system_wrappers \u001b[38;5;241m=\u001b[39m (FluxObserver(),) \u001b[38;5;28;01mif\u001b[39;00m motor_type \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mSCIM\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m ()\n\u001b[0;32m 17\u001b[0m \u001b[38;5;66;03m# Initilize the environment\u001b[39;00m\n\u001b[1;32m---> 18\u001b[0m env \u001b[38;5;241m=\u001b[39m \u001b[43mgem\u001b[49m\u001b[38;5;241m.\u001b[39mmake(env_id, physical_system_wrappers\u001b[38;5;241m=\u001b[39mphysical_system_wrappers)\n\u001b[0;32m 20\u001b[0m \u001b[38;5;66;03m# Initialize the controller\u001b[39;00m\n\u001b[0;32m 21\u001b[0m c \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mGemController\u001b[38;5;241m.\u001b[39mmake(\n\u001b[0;32m 22\u001b[0m env,\n\u001b[0;32m 23\u001b[0m env_id,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 27\u001b[0m \u001b[38;5;66;03m# save_block_diagram_as=(),\u001b[39;00m\n\u001b[0;32m 28\u001b[0m )\n", + "\u001b[1;31mNameError\u001b[0m: name 'gem' is not defined" ] } ], From 62039558581256acf8739caf6ccd323a9ce98cb5 Mon Sep 17 00:00:00 2001 From: Barnabas Haucke-Korber <95769661+bhk11@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:03:08 +0200 Subject: [PATCH 14/14] Delete src/debug.ipynb --- src/debug.ipynb | 176 ------------------------------------------------ 1 file changed, 176 deletions(-) delete mode 100644 src/debug.ipynb diff --git a/src/debug.ipynb b/src/debug.ipynb deleted file mode 100644 index 7cd23604..00000000 --- a/src/debug.ipynb +++ /dev/null @@ -1,176 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "565f13ab", - "metadata": {}, - "outputs": [], - "source": [ - "import gym_electric_motor as gem\n", - "import numpy as np\n", - "import scipy.interpolate as sp_interpolate\n", - "import gem_controllers as gc\n", - "from gym_electric_motor.physical_system_wrappers import FluxObserver\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "2687846d", - "metadata": {}, - "outputs": [], - "source": [ - "def _calculate_luts(self):\n", - " \"\"\"\n", - " Calculates the lookup tables for the maximum torque and the optimal currents for a given torque reference.\n", - " \"\"\"\n", - " minimum_loss = []\n", - " best_params = []\n", - "\n", - " minimum_loss_psi = []\n", - " best_params_psi = []\n", - "\n", - " self.psi_max = self.l_m * self.i_e_lim + self.l_d * self.i_q_lim\n", - " torque = np.linspace(0, self.t_lim, self.t_count)\n", - "\n", - " self.t_max_psi = np.zeros(self.psi_count)\n", - "\n", - " for t in torque:\n", - " losses = []\n", - " parameter = []\n", - " for idx, psi in enumerate(np.linspace(0, self.psi_max, self.psi_count)):\n", - " losses_psi = []\n", - " parameter_psi = []\n", - " for i_e in np.linspace(0, self.i_e_lim, self.i_e_count):\n", - " i_d, i_q = self.solve_analytical(t, psi, i_e)\n", - " if np.sqrt(i_d**2 + i_q**2) < self.i_q_lim:\n", - " loss = self.loss(i_d, i_q, i_e)\n", - " params = np.array([t, psi, i_d, i_q, i_e])\n", - " losses.append(loss)\n", - " losses_psi.append(loss)\n", - " parameter.append(params)\n", - " parameter_psi.append(params)\n", - " self.t_max_psi[idx] = t\n", - " if len(losses_psi) > 0:\n", - " minimum_loss_psi.append(min(losses_psi))\n", - " best_params_psi.append(parameter_psi[losses_psi.index(minimum_loss_psi[-1])])\n", - " if len(losses) > 0:\n", - " minimum_loss.append(min(losses))\n", - " best_params.append(parameter[losses.index(minimum_loss[-1])])\n", - "\n", - " best_params = np.array(best_params)\n", - " best_params_psi = np.array(best_params_psi)\n", - "\n", - " self.t_max_psi = sp_interpolate.interp1d(\n", - " np.linspace(0, self.psi_max, self.psi_count), 0.99 * self.t_max_psi, kind=\"linear\"\n", - " )\n", - "\n", - " self.t_max = np.max(best_params[:, 0])\n", - " self.psi_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 1], kind=\"cubic\")\n", - " self.i_d_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 2], kind=\"cubic\")\n", - " self.i_q_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 3], kind=\"cubic\")\n", - " self.i_e_opt = sp_interpolate.interp1d(best_params[:, 0], best_params[:, 4], kind=\"cubic\")\n", - "\n", - " self.t_grid, self.psi_grid = np.mgrid[\n", - " 0 : self.t_max : complex(0, self.t_grid_count), 0 : self.psi_max : complex(self.psi_grid_count)\n", - " ]\n", - "\n", - " self.i_d_inter = sp_interpolate.griddata(\n", - " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", - " best_params_psi[:, 2],\n", - " (self.t_grid, self.psi_grid),\n", - " method=\"linear\",\n", - " )\n", - " self.i_q_inter = sp_interpolate.griddata(\n", - " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", - " best_params_psi[:, 3],\n", - " (self.t_grid, self.psi_grid),\n", - " method=\"linear\",\n", - " )\n", - " self.i_e_inter = sp_interpolate.griddata(\n", - " (best_params_psi[:, 0], best_params_psi[:, 1]),\n", - " best_params_psi[:, 4],\n", - " (self.t_grid, self.psi_grid),\n", - " method=\"linear\",\n", - " )\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29fd011d", - "metadata": {}, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'gem' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[4], line 18\u001b[0m\n\u001b[0;32m 15\u001b[0m physical_system_wrappers \u001b[38;5;241m=\u001b[39m (FluxObserver(),) \u001b[38;5;28;01mif\u001b[39;00m motor_type \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mSCIM\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m ()\n\u001b[0;32m 17\u001b[0m \u001b[38;5;66;03m# Initilize the environment\u001b[39;00m\n\u001b[1;32m---> 18\u001b[0m env \u001b[38;5;241m=\u001b[39m \u001b[43mgem\u001b[49m\u001b[38;5;241m.\u001b[39mmake(env_id, physical_system_wrappers\u001b[38;5;241m=\u001b[39mphysical_system_wrappers)\n\u001b[0;32m 20\u001b[0m \u001b[38;5;66;03m# Initialize the controller\u001b[39;00m\n\u001b[0;32m 21\u001b[0m c \u001b[38;5;241m=\u001b[39m gc\u001b[38;5;241m.\u001b[39mGemController\u001b[38;5;241m.\u001b[39mmake(\n\u001b[0;32m 22\u001b[0m env,\n\u001b[0;32m 23\u001b[0m env_id,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 27\u001b[0m \u001b[38;5;66;03m# save_block_diagram_as=(),\u001b[39;00m\n\u001b[0;32m 28\u001b[0m )\n", - "\u001b[1;31mNameError\u001b[0m: name 'gem' is not defined" - ] - } - ], - "source": [ - "\n", - "\n", - " # choose the action space\n", - "action_space = 'Cont' # 'Cont' or 'Finite'\n", - "\n", - " # choose the control task\n", - "control_task = 'TC' # 'SC' (speed control), 'TC' (torque control) or 'CC' (current control)\n", - "\n", - " # chosse the motor type\n", - "motor_type = 'EESM' # 'PermExDc', 'ExtExDc', 'SeriesDc', 'ShuntDc', 'PMSM', 'EESM', 'SynRM' or 'SCIM'\n", - "\n", - "env_id = action_space + '-' + control_task + '-' + motor_type + '-v0'\n", - "\n", - " # using a flux observer for the SCIM\n", - "physical_system_wrappers = (FluxObserver(),) if motor_type == 'SCIM' else ()\n", - "\n", - " # Initilize the environment\n", - "env = gem.make(env_id, physical_system_wrappers=physical_system_wrappers)\n", - " \n", - " # Initialize the controller\n", - "c = gc.GemController.make(\n", - " env,\n", - " env_id,\n", - " a=8,\n", - " block_diagram=False,\n", - " current_safety_margin=0.25,\n", - " # save_block_diagram_as=(),\n", - " )\n", - "\n", - " # Control the environment\n", - "c.control_environment(env, n_steps=30000, render_env=True, max_episode_length=10000)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "proj", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.21" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}