Código fuente para rutinas_simulacion

""" 
Archivo que contiene la clase SimpleThread la cual ejecuta la simulacion de sistemas de control en hilo diferente al principal, esto se realiza de esta forma debido a que la simulacion puede tardar en algunos casos varios segundos, de ejecutarse en el hilo principal presentaria un comportamiento de bloqueo en la ventana principal 
"""


from rutinas.rutinas_fuzzy import FuzzyController
from rutinas.rutinas_fuzzy import FISParser
from rutinas.discreto_sim import ss_discreta, PID_discreto, derivadas_discretas
from collections import deque
from PySide2 import QtCore

import controlmdf as ctrl
import numpy as np

import copy
import json


[documentos]class SimpleThread(QtCore.QThread): """ Clase para realizar la simulacion de sistemas de control en un hilo diferente al principal :param QThread: Clase para crear un hilo paralelo al principal :type QThread: ObjectType """ finished = QtCore.Signal(object, list) update_progresBar = QtCore.Signal(object, float) error_gui = QtCore.Signal(object, int)
[documentos] def __init__(self, window, regresar, update_bar, error_gui, list_info, parent=None): """ Constructor para recibir las variables y funciones del hilo principal :param window: Objeto que contiene a la ventana principal :type window: object :param regresar: Funcion a la que regresa una vez terminada la simulacion, plot_final_results de simulacionHandler.py :type regresar: function :param update_bar: Funcion para actualizar la barra de progreso, update_progresBar_function de simulacionHandler.py :type update_bar: function :param error_gui: Funcion para mostrar los errores ocurridos durante la simulacion, error_gui de simulacionHandler.py :type error_gui: function :param list_info: Lista con toda la informacion necesaria :type list_info: list :param parent: Sin efecto, defaults to None :type parent: NoneType, optional """ QtCore.QThread.__init__(self, parent) self.window = window self.window.main.principalTab.setDisabled(True) self.window.main.progressBar.show() self.finished.connect(regresar) self.update_progresBar.connect(update_bar) self.error_gui.connect(error_gui) self.list_info = copy.deepcopy(list_info) self.esquema = self.list_info[0] self.system = self.list_info[1] self.Tiempo = self.list_info[2] self.dt = self.list_info[3] self.escalon = self.list_info[4] self.sensor_flag = self.list_info[5] self.accionador_flag = self.list_info[6] self.saturador_flag = self.list_info[7] self.kp, self.ki, self.kd, self.N = map(float, self.list_info[8]) self.fuzzy_path1, self.fuzzy_path2 = self.list_info[9] self.rk_base = self.list_info[10] self.metodo_adaptativo = self.list_info[11] self.solver_configuration = self.list_info[12] self.flag_filtro = self.list_info[13]
[documentos] def stop(self): """ Funcion para detener el hilo """ self._isRunning = False
[documentos] def run(self): """ Funcion a ejecutar cuando se hace el llamado a self.start() """ # PID Clasico if self.esquema in [0]: try: Tiempo, y, sc, u = self.run_pid() self.finished.emit( self.window, [Tiempo, y, sc, u, ctrl.isdtime(self.system, strict=True)]) self.stop() except: self.error_gui.emit(self.window, 0) self.stop() # Esquemas difusos if self.esquema in [1,2,3,4,5,6,7,8]: try: Tiempo, y, sc, u = self.run_fuzzy() self.finished.emit( self.window, [Tiempo, y, sc, u, ctrl.isdtime(self.system, strict=True)]) self.stop() except IndexError: self.error_gui.emit(self.window, 1) self.stop() except AssertionError: self.error_gui.emit(self.window, 2) self.stop()
[documentos] def run_pid(self): """ Funcion para realizar la simulacion de sistemas de control con controlador PID clasico """ # Captura de las ganancias if self.window.main.kpCheck.isChecked(): kp = float(self.kp) else: kp = 0.0 if self.window.main.kiCheck.isChecked(): ki = float(self.ki) else: ki = 0.0 if self.window.main.kdCheck.isChecked(): kd = float(self.kd) else: kd = 0.0 # Transformando a ecuaciones de espacio de estados en caso de que sea funcion de transferencia if isinstance(self.system, ctrl.TransferFunction): self.system = ctrl.tf2ss(self.system) x = np.zeros_like(self.system.B).astype('float64') system = self.system.A.astype('float64'), self.system.B.astype('float64'), self.system.C.astype('float64'), self.system.D.astype('float64') tiempo_total = self.Tiempo tiempo = 0 if isinstance(self.escalon, float): # Escalon simple u = self.escalon max_tiempo = [self.Tiempo] else: # Escalon avanzado it = iter(self.escalon) u_value = deque([0]) max_tiempo = [] for i, valor in enumerate(it): max_tiempo.append(next(it)) u_value.append(valor) index_tbound = len(max_tiempo) max_tiempo.append(tiempo_total) # Necesario para evitar tamaños de paso excesivos dado el algoritmo adaptativo if ctrl.isdtime(self.system, strict=True): tiempo += max_tiempo[0] - self.dt - self.dt/10 else: tiempo += max_tiempo[0] - 0.0000011 # Representacion del 20% de la simulacion porcentajeBar = int(tiempo_total * 33 / 100) if porcentajeBar == 0: porcentajeBar = 1 h = 0.000001 # Tamaño de paso inicial salida = deque([0]) # Lista de salida, se utiliza deque para mejorar la velocidad sc_f = deque([0]) # Lista de la señal de control, se utiliza deque para mejorar la velocidad sc_t = 0.0 # Señal de control cambiante si_t = 0.0 # Acumulador de la señal integral # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): error_a = np.asarray([0.0, 0.0]) solve = ss_discreta PIDf = PID_discreto h_new = self.dt h = self.dt else: error_a = np.asarray([0.0, 0.0]) solve = self.rk_base PIDf = self.metodo_adaptativo # En caso de que se habilite el accionador if self.accionador_flag: acc_num = json.loads(self.window.main.numAccionador.text()) acc_dem = json.loads(self.window.main.demAccionador.text()) acc_system = ctrl.TransferFunction(acc_num, acc_dem, delay=0) if ctrl.isdtime( self.system, strict=True ) and self.window.main.SimulacionstackedWidget.currentIndex() == 0: acc_system = ctrl.sample_system( acc_system, self.dt, self.window.main.tfcomboBox4.currentText()) elif ctrl.isdtime(self.system, strict=True): acc_system = ctrl.sample_system( acc_system, self.dt, self.window.main.sscomboBox4.currentText()) acc_system = ctrl.tf2ss(acc_system) acc_x = np.zeros_like(acc_system.B).astype('float64') acc_system = acc_system.A.astype('float64'), acc_system.B.astype('float64'), acc_system.C.astype('float64'), acc_system.D.astype('float64') # En caso de que se habilite el saturador if self.saturador_flag: lim_inferior = float(self.window.main.inferiorSaturador.text()) lim_superior = float(self.window.main.superiorSaturador.text()) # En caso de que se habilite el sensor if self.sensor_flag: sensor_num = json.loads(self.window.main.numSensor.text()) sensor_dem = json.loads(self.window.main.demSensor.text()) sensor_system = ctrl.TransferFunction(sensor_num, sensor_dem, delay=0) if ctrl.isdtime( self.system, strict=True ) and self.window.main.SimulacionstackedWidget.currentIndex() == 0: sensor_system = ctrl.sample_system( sensor_system, self.dt, self.window.main.tfcomboBox4.currentText()) elif ctrl.isdtime(self.system, strict=True): sensor_system = ctrl.sample_system( sensor_system, self.dt, self.window.main.sscomboBox4.currentText()) sensor_system = ctrl.tf2ss(sensor_system) sensor_x = np.zeros_like(sensor_system.B).astype('float64') sensor_system = sensor_system.A.astype('float64'), sensor_system.B.astype('float64'), sensor_system.C.astype('float64'), sensor_system.D.astype('float64') salida2 = deque([0]) if self.N*kd == 0: # N debe mantenerce debido al algoritmo utilizado por la libreria de control para llevar de funcion # de transferencia a ecuaciones de espacio de estados # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N)) self.N = 50 kd = 0.0 pid = ctrl.tf2ss(ctrl.TransferFunction( [self.N * kd + kp, self.N * kp + ki, self.N * ki], [1, self.N, 0])) elif not self.flag_filtro: # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N)) pid = ctrl.tf2ss(ctrl.TransferFunction( [self.N * kd + kp, self.N * kp + ki, self.N * ki], [1, self.N, 0])) else: # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N))*(1/(10/(N*kd) + 1)) pid = ctrl.tf2ss( ctrl.TransferFunction([ 10 * kp, self.N**2 * kd**2 + self.N * kd * kp + 10 * self.N * kp + 10*ki, self.N**2 * kd * kp + kd * self.N * ki + 10 * self.N * ki, self.N**2 * kd * ki ], [10, 10 * self.N + self.N * kd, self.N**2 * kd, 0])) x_pid = np.zeros_like(pid.B).astype('float64') pid = pid.A.astype('float64'), pid.B.astype('float64'), pid.C.astype('float64'), pid.D.astype('float64') i = 0 setpoint_window = 0 Tiempo_list = [0] setpoint = [0] # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion de PID entre continuo y discreto if ctrl.isdtime(self.system, strict=True): sc_t, si_t, error_a = PIDf(error, h, si_t, error_a, kp, ki, kd) else: h, h_new, sc_t, x_pid = PIDf(pid, h, tiempo, max_tiempo[setpoint_window], x_pid, error, *self.solver_configuration) # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo +=h Tiempo_list.append(tiempo) h = h_new i +=1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint)
[documentos] def run_fuzzy(self): """ Funcion para realizar la simulacion de sistemas de control de esquemas difusos """ # Captura de las ganancias if self.window.main.kpCheck.isChecked(): kp = float(self.kp) else: kp = 0.0 if self.window.main.kiCheck.isChecked(): ki = float(self.ki) else: ki = 0.0 if self.window.main.kdCheck.isChecked(): kd = float(self.kd) else: kd = 0.0 # Creacion del controlador difuso if len(self.fuzzy_path1) > 1 and self.esquema in [1, 2, 3, 4, 5, 6, 7, 8]: if '.json' in self.fuzzy_path1: with open(self.fuzzy_path1, "r") as f: InputList1, OutputList1, RuleEtiquetas1 = json.load(f) else: temp_parser = FISParser(self.fuzzy_path1) try: InputList1, OutputList1, RuleEtiquetas1 = temp_parser.fis_to_json() except TypeError: raise IndexError try: controlador_validator(self, self.esquema, InputList1, OutputList1, RuleEtiquetas1) except AssertionError: raise AssertionError fuzzy_c1 = FuzzyController(InputList1, OutputList1, RuleEtiquetas1) # Creacion del controlador difuso 2 (PD) if len(self.fuzzy_path2) > 1 and self.esquema in [4]: if '.json' in self.fuzzy_path2: with open(self.fuzzy_path2, "r") as f: InputList2, OutputList2, RuleEtiquetas2 = json.load(f) else: temp_parser = FISParser(self.fuzzy_path2) try: InputList2, OutputList2, RuleEtiquetas2 = temp_parser.fis_to_json() except TypeError: raise IndexError try: controlador_validator(self, self.esquema, InputList2, OutputList2, RuleEtiquetas2) except AssertionError: raise AssertionError fuzzy_c2 = FuzzyController(InputList2, OutputList2, RuleEtiquetas2) # Transformando a ecuaciones de espacio de estados en caso de que sea funcion de transferencia if isinstance(self.system, ctrl.TransferFunction): self.system = ctrl.tf2ss(self.system) x = np.zeros_like(self.system.B).astype('float64') system = self.system.A.astype('float64'), self.system.B.astype('float64'), self.system.C.astype('float64'), self.system.D.astype('float64') tiempo_total = self.Tiempo tiempo = 0 if isinstance(self.escalon, float): # Escalon simple u = self.escalon max_tiempo = [self.Tiempo] else: # Escalon avanzado it = iter(self.escalon) u_value = deque([0]) max_tiempo = [] for i, valor in enumerate(it): max_tiempo.append(next(it)) u_value.append(valor) index_tbound = len(max_tiempo) max_tiempo.append(tiempo_total) # Necesario para evitar tamaños de paso excesivos dado el algoritmo adaptativo if ctrl.isdtime(self.system, strict=True): tiempo += max_tiempo[0] - self.dt - self.dt/10 else: tiempo += max_tiempo[0] - 0.0000011 # Representacion del 20% de la simulacion porcentajeBar = int(tiempo_total * 20 / 100) if porcentajeBar == 0: porcentajeBar = 1 h = 0.000001 # Tamaño de paso inicial salida = deque([0]) # Lista de salida, se utiliza deque para mejorar la velocidad sc_f = deque([0]) # Lista de la señal de control, se utiliza deque para mejorar la velocidad sc_t = 0.0 # Señal de control cambiante si_t = 0.0 # Acumulador de la señal integral # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): error_a = np.asarray([0.0, 0.0]) solve = ss_discreta PIDf = PID_discreto h_new = self.dt h = self.dt else: error_a = np.asarray([0.0, 0.0]) solve = self.rk_base PIDf = self.metodo_adaptativo # En caso de que se habilite el accionador if self.accionador_flag: acc_num = json.loads(self.window.main.numAccionador.text()) acc_dem = json.loads(self.window.main.demAccionador.text()) acc_system = ctrl.tf2ss(ctrl.TransferFunction(acc_num, acc_dem, delay=0)) if ctrl.isdtime( self.system, strict=True ) and self.window.main.SimulacionstackedWidget.currentIndex() == 0: acc_system = ctrl.sample_system( acc_system, self.dt, self.window.main.tfcomboBox4.currentText()) elif ctrl.isdtime(self.system, strict=True): acc_system = ctrl.sample_system( acc_system, self.dt, self.window.main.sscomboBox4.currentText()) acc_x = np.zeros_like(acc_system.B).astype('float64') acc_system = acc_system.A.astype('float64'), acc_system.B.astype('float64'), acc_system.C.astype('float64'), acc_system.D.astype('float64') # En caso de que se habilite el saturador if self.saturador_flag: lim_inferior = float(self.window.main.inferiorSaturador.text()) lim_superior = float(self.window.main.superiorSaturador.text()) # En caso de que se habilite el sensor if self.sensor_flag: sensor_num = json.loads(self.window.main.numSensor.text()) sensor_dem = json.loads(self.window.main.demSensor.text()) sensor_system = ctrl.tf2ss( ctrl.TransferFunction(sensor_num, sensor_dem, delay=0)) if ctrl.isdtime( self.system, strict=True ) and self.window.main.SimulacionstackedWidget.currentIndex() == 0: sensor_system = ctrl.sample_system( sensor_system, self.dt, self.window.main.tfcomboBox4.currentText()) elif ctrl.isdtime(self.system, strict=True): sensor_system = ctrl.sample_system( sensor_system, self.dt, self.window.main.sscomboBox4.currentText()) sensor_x = np.zeros_like(sensor_system.B).astype('float64') sensor_system = sensor_system.A.astype('float64'), sensor_system.B.astype('float64'), sensor_system.C.astype('float64'), sensor_system.D.astype('float64') salida2 = deque([0]) i = 0 setpoint_window = 0 Tiempo_list = [0] setpoint = [0] # Particularizacion por cada esquema if self.esquema == 1: # PID difuso f_signal_anterior = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') # Para la segunda derivada del error derivada2 = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada2 = np.zeros_like(derivada2.B).astype('float64') derivada2 = derivada2.A.astype('float64'), derivada2.B.astype('float64'), derivada2.C.astype('float64'), derivada2.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') # Para la segunda derivada del error derivada2 = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada2 = np.zeros_like(derivada2.B).astype('float64') derivada2 = derivada2.A.astype('float64'), derivada2.B.astype('float64'), derivada2.C.astype('float64'), derivada2.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) derivada2 = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada2 = np.zeros_like(derivada2.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[-1] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new2, d2_error, x_derivada2 = PIDf(derivada2, h, tiempo, max_tiempo[setpoint_window], x_derivada2, error, *self.solver_configuration) htemp, h_new1, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) h = min(h, htemp) h_new = min(h_new1, h_new2) else: d_error = 0 d2_error = 0 # Calculo del controaldor difuso f_signal = fuzzy_c1.calcular_valor([error, d_error, d2_error], [0] * 1)[0] sc_t = sc_t + (f_signal + f_signal_anterior)*h/2 f_signal_anterior = f_signal # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo +=h Tiempo_list.append(tiempo) h = h_new i +=1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 2: # PI difuso f_signal_anterior = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo del controlador difuso f_signal = fuzzy_c1.calcular_valor([error, d_error], [0] * 1)[0] sc_t = sc_t + (f_signal + f_signal_anterior)*h/2 f_signal_anterior = f_signal # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo +=h Tiempo_list.append(tiempo) h = h_new i +=1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 3: # PD difuso if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo del controlador difuso sc_t = fuzzy_c1.calcular_valor([error, d_error], [0] * 1)[0] # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo +=h Tiempo_list.append(tiempo) h = h_new i +=1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 4: # PI difuso + PD difuso spi = 0 f_signal_anterior = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo de los controaldores difusos f_signal = fuzzy_c1.calcular_valor([error, d_error], [0] * 1)[0] spi = spi + (f_signal + f_signal_anterior) * h/2 f_signal_anterior = f_signal spd = fuzzy_c2.calcular_valor([error, d_error], [0] * 1)[0] sc_t = spi + spd # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo +=h Tiempo_list.append(tiempo) h = h_new i +=1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 5: # PI difuso + D clasico spi = 0 f_signal_anterior = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo del controlador difuso f_signal = fuzzy_c1.calcular_valor([error, d_error], [0] * 1)[0] spi = spi + (f_signal + f_signal_anterior) * h/2 f_signal_anterior = f_signal sc_t = spi + d_error*kd # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo += h Tiempo_list.append(tiempo) h = h_new i += 1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 6: # PD difuso + I Clasico spi = 0 error_integral = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo del controaldor difuso spi = spi + (error + error_integral)*h/2 error_integral = error spd = fuzzy_c1.calcular_valor([error, d_error], [0] * 1)[0] sc_t = spi*ki + spd # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo += h Tiempo_list.append(tiempo) h = h_new i += 1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 7: # Programador de ganancias spi = 0 error_integral = 0 if not self.N == 0 and self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([1], [10 / self.N, 1]) * ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') elif not self.N == 0 and not self.flag_filtro: # Para la derivada del error derivada = ctrl.tf2ss( ctrl.TransferFunction([self.N, 0], [1, self.N])) x_derivada = np.zeros_like(derivada.B).astype('float64') derivada = derivada.A.astype('float64'), derivada.B.astype('float64'), derivada.C.astype('float64'), derivada.D.astype('float64') else: # En caso de que N sea igual a cero derivada = ctrl.tf2ss(ctrl.TransferFunction([0], [1])) x_derivada = np.zeros_like(derivada.B) h = 0.05 h_new = h # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): d_error, d2_error, error_a = derivadas_discretas(error, h, error_a) else: if self.N != 0: h, h_new, d_error, x_derivada = PIDf(derivada, h, tiempo, max_tiempo[setpoint_window], x_derivada, error, *self.solver_configuration) else: d_error = 0 # Calculo del controaldor difuso kp, ki, kd = fuzzy_c1.calcular_valor([error, d_error], [0] * 3) spi = spi + (error + error_integral)*h/2 error_integral = error sc_t = spi*ki + d_error*kd + error*kp # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo += h Tiempo_list.append(tiempo) h = h_new i += 1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint) if self.esquema == 8: # PID clasico + difuso simple if self.N*kd == 0: # N debe mantenerce debido al algoritmo utilizado por la libreria de control para llevar de funcion # de transferencia a ecuaciones de espacio de estados # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N)) self.N = 50 kd = 0.0 pid = ctrl.tf2ss(ctrl.TransferFunction( [self.N * kd + kp, self.N * kp + ki, self.N * ki], [1, self.N, 0])) elif not self.flag_filtro: # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N)) pid = ctrl.tf2ss(ctrl.TransferFunction( [self.N * kd + kp, self.N * kp + ki, self.N * ki], [1, self.N, 0])) else: # Controlador PID con la forma: # PID = kp + ki/s + (kd*N*s/(s + N))*(1/(10/(N*kd) + 1)) pid = ctrl.tf2ss( ctrl.TransferFunction([ 10 * kp, self.N**2 * kd**2 + self.N * kd * kp + 10 * self.N * kp + 10*ki, self.N**2 * kd * kp + kd * self.N * ki + 10 * self.N * ki, self.N**2 * kd * ki ], [10, 10 * self.N + self.N * kd, self.N**2 * kd, 0])) x_pid = np.zeros_like(pid.B).astype('float64') pid = pid.A.astype('float64'), pid.B.astype('float64'), pid.C.astype('float64'), pid.D.astype('float64') # Inicio de la simulacion while tiempo < tiempo_total: # Para alternar los valores del setpoint avanzado if not isinstance(self.escalon, float): if tiempo + h >= max_tiempo[ setpoint_window] and setpoint_window < index_tbound: setpoint_window += 1 u = u_value[setpoint_window] # Calculo del error error = u - salida[i] # Distincion entre continuo y discreto if ctrl.isdtime(self.system, strict=True): s_pid, si_t, error_a = PIDf(error, h, si_t, error_a, kp, ki, kd) else: h, h_new, s_pid, x_pid = PIDf(pid, h, tiempo, max_tiempo[setpoint_window], x_pid, error, *self.solver_configuration) # calculo del controlador difuso s_fuzzy = fuzzy_c1.calcular_valor([error], [0] * 1)[0] # Suma del controlador clasico y difuso sc_t = s_pid + s_fuzzy # En caso de que se habilite el accionador if self.accionador_flag: sc_t, acc_x = solve(*acc_system, acc_x, h, sc_t) # En caso de que se habilite el saturador if self.saturador_flag: sc_t = min(max(sc_t, lim_inferior), lim_superior) # Salida del sistema y, x = solve(*system, x, h, sc_t) # Acumulacion de la señal de control sc_f.append(sc_t) # En caso de que se habilite el sensor if self.sensor_flag: salida2.append(y) y, sensor_x = solve(*sensor_system, sensor_x, h, salida2[-1]) # Acumulacion de la salida salida.append(y) # Actualizacion de la barra de progreso cada 20% de avance if int(tiempo) % porcentajeBar == 0: self.update_progresBar.emit(self.window, int(tiempo) * 100 / tiempo_total) # Acumulacion del setpoint setpoint.append(u) # Acumulacion del tiempo tiempo += h Tiempo_list.append(tiempo) h = h_new i += 1 # En caso de que se habilite el sensor if self.sensor_flag: salida = salida2 return copy.deepcopy(Tiempo_list), copy.deepcopy(salida), copy.deepcopy(sc_f), copy.deepcopy(setpoint)
[documentos]def system_creator_tf(self, numerador, denominador): """ Funcion para la creacion del sistema a partir de los coeficientes del numerador y del denominador de la funcion de transferencia :param numerador: Coeficientes del numerador :type numerador: list :param denominador: Coeficientes del denominador :type denominador: list """ if self.main.tfdelaycheckBox4.isChecked(): delay = json.loads(self.main.tfdelayEdit4.text()) else: delay = 0 system = ctrl.TransferFunction(numerador, denominador, delay=delay) # Agregando delay con aproximacion por pade para sistemas continuos if delay and not self.main.tfdiscretocheckBox4.isChecked(): pade = ctrl.TransferFunction(*ctrl.pade(delay, int(self.main.padeOrder.text()))) system = system*pade # En caso de que el sistema sea discreto if self.main.tfdiscretocheckBox4.isChecked(): system = ctrl.sample_system(system, self.dt, self.main.tfcomboBox4.currentText(), delay=delay) if delay: delayV = [0] * (int(delay / self.dt) + 1) delayV[0] = 1 system = system * ctrl.TransferFunction([1], delayV, self.dt) return system
[documentos]def system_creator_ss(self, A, B, C, D): """ Funcion para la creacion del sistema a partir de la matriz de estado, matriz de entrada, matriz de salida y la matriz de transmision directa la ecuacion de espacio de estados :param A: Matriz de estados :type A: list :param B: Matriz de entrada :type B: list :param C: Matriz de salida :type C: list :param D: Matriz de transmision directa :type D: list """ if self.main.ssdelaycheckBox4.isChecked(): delay = json.loads(self.main.ssdelayEdit4.text()) else: delay = 0 system = ctrl.StateSpace(A, B, C, D, delay=delay) # Agregando delay con aproximacion por pade para sistemas continuos if delay and not self.main.ssdiscretocheckBox4.isChecked(): pade = ctrl.TransferFunction(*ctrl.pade(delay, int(self.main.padeOrder.text()))) system = system*pade # En caso de que el sistema sea discreto if self.main.ssdiscretocheckBox4.isChecked(): system = ctrl.sample_system(system, self.dt, self.main.sscomboBox4.currentText(), delay=delay) if delay: delayV = [0] * (int(delay / self.dt) + 1) delayV[0] = 1 system = system * ctrl.TransferFunction([1], delayV, self.dt) return system
[documentos]def controlador_validator(self, esquema, InputList, OutputList, RuleEtiquetas): """ Funcion para validar los controladores difusos con respecto al esquema de control seleccionado :param esquema: Esquema de control seleccionado representado por un valor :type esquema: int :param InputList: Lista de entradas :type InputList: list :param OutputList: Lista de salidas :type OutputList: list :param RuleEtiquetas: Lista con set de reglas :type RuleEtiquetas: list """ if esquema == 1: # PID difuso if len(InputList) == 3 and len(OutputList) == 1 and len(RuleEtiquetas) != 0: return else: raise AssertionError if esquema in [2, 3, 4, 5, 6]: # PI difuso, PD difuso, PI difuso + PD difuso, PI difuso + D Clasico, PD difuso + I Clasico if len(InputList) == 2 and len(OutputList) == 1 and len(RuleEtiquetas) != 0: return else: raise AssertionError if esquema == 7: # Programador de ganancias if len(InputList) == 2 and len(OutputList) == 3 and len(RuleEtiquetas) != 0: return else: raise AssertionError if esquema == 8: # PID Clasico + Difuso simple if len(InputList) == 1 and len(OutputList) == 1 and len(RuleEtiquetas) != 0: return else: raise AssertionError