Source code for gym_electric_motor.physical_system_wrappers.flux_observer

import gymnasium
import numpy as np

import gym_electric_motor as gem

from .physical_system_wrapper import PhysicalSystemWrapper


[docs]class FluxObserver(PhysicalSystemWrapper): """The FluxObserver extends the systems state vector of induction machine environments by estimated flux states ``psi_abs``, and ``psi_angle``. The flux is estimated as follows: .. math:: psi_{abs} = |\Psi| .. math:: psi_{angle} = \\angle{\Psi} .. math:: \Psi \in \mathbb{C} .. math:: I_{s, \\alpha \\beta} = \\left( I_{s,\\alpha}, I_{s, \\beta} \\right) ^T .. math:: \Delta \Psi_k = \\frac {(I_{s, \\alpha}+jI_{s, \\beta}) R_r L_m}{L_r} - \Psi_{k-1}(\\frac{R_r}{L_r}+ j\omega) .. math :: \Psi_k = \sum_{i=0}^k (\Psi_{k-1} + \Delta\Psi_k) \\tau """ def __init__(self, current_names=("i_sa", "i_sb", "i_sc"), physical_system=None): """ Args: current_names(Iterable[string]): Names of the currents to be observed to estimate the flux. (Default: ``('i_sa', 'i_sb', 'i_sc')``) physical_system(PhysicalSystem): (Optional) Physical System to initialize this observer. If not passed, the observer will be initialized during environment creation. """ self._current_indices = None self._l_m = None # Main induction self._l_r = None # Induction of the rotor self._r_r = None # Rotor resistance self._p = None # Pole pair number self._i_s_idx = None self._omega_idx = None # Integrated values of the flux for the two directions (Re: alpha, Im: beta) self._integrated = complex(0, 0) self._current_names = current_names super(FluxObserver, self).__init__(physical_system) @staticmethod def _abc_to_alphabeta_transformation(i_s): return gem.physical_systems.electric_motors.ThreePhaseMotor.t_23(i_s)
[docs] def set_physical_system(self, physical_system): # Docstring of super class assert isinstance( physical_system.electrical_motor, gem.physical_systems.electric_motors.InductionMotor, ) super().set_physical_system(physical_system) mp = physical_system.electrical_motor.motor_parameter self._l_m = mp["l_m"] # Main induction self._l_r = mp["l_m"] + mp["l_sigr"] # Induction of the rotor self._r_r = mp["r_r"] # Rotor resistance self._p = mp["p"] # Pole pair number psi_limit = self._l_m * physical_system.limits[physical_system.state_names.index("i_sd")] low = np.concatenate((physical_system.state_space.low, [-psi_limit, -np.pi])) high = np.concatenate((physical_system.state_space.high, [psi_limit, np.pi])) self.state_space = gymnasium.spaces.Box(low, high, dtype=np.float64) self._current_indices = [physical_system.state_positions[name] for name in self._current_names] self._limits = np.concatenate((physical_system.limits, [psi_limit, np.pi])) self._nominal_state = np.concatenate((physical_system.nominal_state, [psi_limit, np.pi])) self._state_names = physical_system.state_names + ["psi_abs", "psi_angle"] self._state_positions = {key: index for index, key in enumerate(self._state_names)} self._i_s_idx = [physical_system.state_positions[name] for name in self._current_names] self._omega_idx = physical_system.state_positions["omega"] return self
[docs] def reset(self): # Docstring of super class self._integrated = complex(0, 0) return np.concatenate((super().reset(), [0.0, 0.0]))
[docs] def simulate(self, action): # Docstring of super class state_norm = self._physical_system.simulate(action) state = state_norm * self._physical_system.limits i_s = state[self._i_s_idx] omega = state[self._omega_idx] * self._p # Transform current into alpha, beta coordinates [i_s_alpha, i_s_beta] = self._abc_to_alphabeta_transformation(i_s) # Calculate delta flux delta_psi = complex(i_s_alpha, i_s_beta) * self._r_r * self._l_m / self._l_r - self._integrated * complex( self._r_r / self._l_r, -omega ) # Integrate the flux self._integrated += delta_psi * self._physical_system.tau return np.concatenate((state, [np.abs(self._integrated), np.angle(self._integrated)])) / self._limits