def control(self, currentCV: np.ndarray, idq0SP: np.ndarray = np.zeros(3), **kwargs): """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current :param idq0SP: 1d-array with 3 entries, one for each phase in dq0. Current set points for current sourcing ctrl :return: Modulation index for the inverter in abc """ # Get the next phase rotation angle to implement phase = self._phaseDDS.step(self.f_nom) # Transform the feedback to the dq0 frame CVIdq0 = abc_to_dq0(currentCV, phase) # current setpoint SPIdq0 = np.array(idq0SP[:]) SPIabc = dq0_to_abc(SPIdq0, phase) # Current controller calculations MVdq0 = self._currentPI.step(SPIdq0, CVIdq0) MVabc = dq0_to_abc(MVdq0, phase) # Add intern measurment self._last_meas = [phase, *CVIdq0, *SPIdq0, *SPIabc, *MVdq0, *MVabc] # Transform the MVs back to the abc frame return MVabc
def rew_fun(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx iabc_master = data[idx[0]] # 3 phase currents at LC inductors phase = data[idx[1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors # set points (sp) isp_dq0_master = data[idx[2]] # setting dq current reference isp_abc_master = dq0_to_abc(isp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc(vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # control error = mean-root-error (MRE) of reference minus measurement # (due to normalization the control error is often around zero -> compared to MSE metric, the MRE provides # better, i.e. more significant, gradients) # plus barrier penalty for violating the current constraint error = np.sum((np.abs((isp_abc_master - iabc_master)) / i_lim) ** 0.5, axis=0) \ + -np.sum(mu * np.log(1 - np.maximum(np.abs(iabc_master) - i_nom, 0) / (i_lim - i_nom)), axis=0) \ + np.sum((np.abs((vsp_abc_master - vabc_master)) / net.v_nom) ** 0.5, axis=0) return -error.squeeze()
def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs): """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current :param voltageCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for voltage :return: Modulation index for the inverter in abc """ instPow = -inst_power(voltageCV, currentCV) freq = self._PdroopController.step(instPow) # Get the next phase rotation angle to implement self.phase = phase = self._phaseDDS.step(freq) instQ = -inst_reactive(voltageCV, currentCV) voltage = self._QdroopController.step(instQ) # Transform the feedback to the dq0 frame CVIdq0 = abc_to_dq0(currentCV, phase) CVVdq0 = abc_to_dq0(voltageCV, phase) # If available, calulate load current using observer iabc_out_etimate = np.empty(N_PHASE) if self._observer is None: iabc_out_etimate = np.zeros(N_PHASE) else: for j in range(N_PHASE): iabc_out_etimate[j] = self._observer[j].cal_estimate( y=[currentCV[j], voltageCV[j]], u=self._lastMabc[j]) i_dq0_out_estimat = abc_to_dq0(iabc_out_etimate, phase) # Voltage controller calculations VSP = voltage # Voltage SP in dq0 (static for the moment) SPVdq0 = np.array([VSP, 0, 0]) SPVabc = dq0_to_abc(SPVdq0, phase) SPIdq0 = self._voltagePI.step(SPVdq0, CVVdq0, i_dq0_out_estimat) SPIabc = dq0_to_abc(SPIdq0, phase) # Current controller calculations MVdq0 = self._currentPI.step(SPIdq0, CVIdq0) MVabc = dq0_to_abc(MVdq0, phase) # Transform the MVs back to the abc frame self._lastMabc = MVabc # Add intern measurment self._last_meas = [ phase, instPow, instQ, freq, *SPVdq0, *SPVabc, *SPIdq0, *SPIabc, *CVVdq0, *CVIdq0, *i_dq0_out_estimat, *iabc_out_etimate, *MVdq0, *MVabc ] return MVabc
def calculate(self): super().calculate() instPow = -inst_power(self.v, self.i) freq = self.pdroop_ctl.step(instPow) # Get the next phase rotation angle to implement phase = self.dds.step(freq) instQ = -inst_reactive(self.v, self.i) v_refd = self.qdroop_ctl.step(instQ) v_refdq0 = np.array([v_refd, 0, 0]) * self.v_ref return dict(i_ref=dq0_to_abc(self.i_ref, phase), v_ref=dq0_to_abc(v_refdq0, phase))
def rew_fun_funnel(self, cols: List[str], data: np.ndarray, risk) -> float: """ Defines the reward function for the environment. Uses the observations and set-points to evaluate the quality of the used parameters. Takes current and voltage measurements and set-points to calculate the mean-root control error and uses a logarithmic barrier function in case of violating the current limit. Barrier function is adjustable using parameter mu. :param cols: list of variable names of the data :param data: observation data from the environment (ControlVariables, e.g. currents and voltages) :return: Error as negative reward """ self.set_idx(cols) idx = self._idx phase = data[idx[ 1]] # phase from the master controller needed for transformation vabc_master = data[idx[3]] # 3 phase currents at LC inductors vdq0_master = data[idx[5]] # set points (sp) vsp_dq0_master = data[idx[4]] # setting dq voltage reference vsp_abc_master = dq0_to_abc( vsp_dq0_master, phase) # convert dq set-points into three-phase abc coordinates # calculate err = vdq0_master - vsp_dq0_master if (abs(err) > self.funnel).any(): # E2 + Offset error = (np.sum((np.abs( (vsp_abc_master - vabc_master)) / self.v_limit)**0.5, axis=0) + 20) / self.max_episode_steps else: # E1 <! E2 and E1 << Offset error = np.sum((np.abs( (vsp_abc_master - vabc_master)) / self.v_limit)**2, axis=0) / self.max_episode_steps return -error.squeeze()
def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs): instPow = -inst_power(voltageCV, currentCV) freq = self._PdroopController.step(instPow) # Get the next phase rotation angle to implement phase = self._phaseDDS.step(freq) instQ = -inst_reactive(voltageCV, currentCV) voltage = self._QdroopController.step(instQ) VSP = voltage * 1.732050807568877 # Voltage SP in dq0 (static for the moment) SPVdq0 = np.array([VSP, 0, 0]) # Get the voltage SPs in abc vector # print("SPVdq0: {}, phase: {}".format(SPVdq0,phase)) SPV = dq0_to_abc(SPVdq0, phase) # print("QInst: {}, Volt {}".format(instQ,VSP)) SPI = self._voltagePI.step(SPV, voltageCV) # Average voltages from modulation indices created by current controller return self._currentPI.step(SPI, currentCV)
def control(self, currentCV: np.ndarray, voltageCV: np.ndarray, **kwargs): """ Performs the calculations for a discrete step of the controller :param currentCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for current :param voltageCV: 1d-array with 3 entries, one for each phase in abc. The feedback values for voltage :return: Modulation index for the inverter in abc """ instPow = -inst_power(voltageCV, currentCV) freq = self._PdroopController.step(instPow) # Get the next phase rotation angle to implement phase = self._phaseDDS.step(freq) instQ = -inst_reactive(voltageCV, currentCV) voltage = self._QdroopController.step(instQ) # Transform the feedback to the dq0 frame CVIdq0 = abc_to_dq0(currentCV, phase) CVVdq0 = abc_to_dq0(voltageCV, phase) # Voltage controller calculations VSP = voltage # Voltage SP in dq0 (static for the moment) SPVdq0 = np.array([VSP, 0, 0]) SPIdq0 = self._voltagePI.step(SPVdq0, CVVdq0) # Current controller calculations MVdq0 = self._currentPI.step(SPIdq0, CVIdq0) # Add intern measurment self.history.append([ phase, *SPVdq0, *SPIdq0, *CVVdq0, *CVIdq0, *MVdq0, instPow, instQ, freq ]) # Transform the MVs back to the abc frame return dq0_to_abc(MVdq0, phase)
def calculate(self): super().calculate() # Get the next phase rotation angle to implement phase = self.dds.step(self.f_nom) return dict(i_ref=dq0_to_abc(self.i_ref, phase))
def calculate(self): super().calculate() _, _, phase = self.pll.step(self.v) return dict(i_ref=dq0_to_abc(self.i_ref, phase))