class Dosificadora:
    def __init__(self):
        #Crear el objeto de la clase dosificadora

        ##Convenciones: axxxx: a significa atributo
        #Con-> Concentrado
        #Min-> Mineral
        #Lev-> Levadura

        #Puertos de control para las valvulas
        self.avTolva = 2
        self.avMineral = 16
        self.avLevadura = 27

        #Puertos de asignacion de las celdas de carga
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        #Puertos control de motores
        self.amCon = (7, 8)  #Formato tupla: (Encendido, velocidad)
        self.amMin = (20, 21)  #Formato tupla: (velocidad, sentido)
        self.amLev = (25, 26)

        #Sensibilidades celdas de carga
        self.asMin = 1030.3320
        self.asLev = 2563.3821
        self.asC1 = 1
        self.asC2 = 1
        self.asC3 = 1
        self.asC4 = 1
        self.asConc = 53.2201

        #Valores de Tara para cada celda de carga
        self.asZeroMin = 0
        self.asZeroLev = 0
        self.asZeroC1 = 0
        self.asZeroC2 = 0
        self.asZeroC3 = 0
        self.asZeroC4 = 0

        #Masas objetivo
        self.aConObj = 1
        self.aMinObj = 1
        self.aLevObj = 1
        self.aMasaObj = [self.aConObj, self.aMinObj, self.aLevObj]

        #Parametros del filtro tamizador y media movil
        self.aPeso_kbuffer = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0,
                               0.0]]  #Formato lista [Con,Min,Lev]
        self.aSk = [0.0, 0.0, 0.0]  #Formato listas [Con,Min,Lev]
        self.aContador = [0, 0, 0]
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]

        #Valores para algoritmo de control
        self.aMultiplo = [0.8, 0.8, 0.8]  #Formato listas [Con,Min,Lev]
        self.aDeltaRef = [0.0, 0.0, 0.0]

        self.aInt = [0.0, 0.0, 0.0]
        self.aKp = [0.00051743, 0.0, 0.0]
        self.aKi = [0.0, 0.0, 0.0]
        self.aKd = [0.00080848, 0.0, 0.0]
        self.aN = [0.3704]

        self.aVk_1 = 0.0
        self.aEk_1 = 0.0
        self.aYk_1 = 0.0
        self.aDk_1 = 0.0
        self.aUk_1 = 0
        self.aRk_1 = 0

        self.aInt_Retardo = 0
        self.aTcontador = 0
        self.aDoCalcularKp = [True, True, True]
        self.aPWM = [0.0, 0.0, 0.0]

        self.aAceleracion = [300.0, 0.0, 0.0]
        #Otros atributos
        self.asText = "________________"  #Separador de Textos
        self.minCon = 39.0  #Menor ciclo de PWM permitido para el concentrado
        self.maxCon = 99.0  #Mayor ciclo de PWM permitido para el concentrado
        self.razon = [
            60.0, 50.0, 10.0
        ]  #Mayor tasa de cambio permitida por el filtro tamizador
        #Formato lista [Con,Min,Lev]
        self.aConCrucero = 70.0  #Velocidad crucero motor Con
        self.aConMin = 60.0  #Minima velocidad para mover el motor

    def __del__(self):
        #Metodo destructor de objeto
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def inicializarPuertos(self):
        #Encargado de iniciar el estado de los puertos de RPi.

        print("\n________________\nIniciando puertos\n________________\n")
        #Configurar puertos
        #Valvulas
        GPIO.setup(self.avTolva, GPIO.OUT)
        GPIO.setup(self.avMineral, GPIO.OUT)
        GPIO.setup(self.avLevadura, GPIO.OUT)

        #Motores
        #Concentrado
        GPIO.setup(self.amCon[0], GPIO.OUT)
        GPIO.setup(self.amCon[1], GPIO.OUT)

        #Mineral
        GPIO.setup(self.amMin[0], GPIO.OUT)
        GPIO.setup(self.amLev[0], GPIO.OUT)

        #Levadura
        GPIO.setup(self.amMin[1], GPIO.OUT)
        GPIO.setup(self.amLev[1], GPIO.OUT)

        #Colocar todos los puertos en BAJO "LOW".
        GPIO.output(self.avTolva, 0)
        GPIO.output(self.avMineral, 0)
        GPIO.output(self.avLevadura, 0)

        GPIO.output(self.amCon[0], 0)
        GPIO.output(self.amCon[1], 0)

        GPIO.output(self.amMin[0], 0)
        GPIO.output(self.amMin[1], 0)

        GPIO.output(self.amLev[0], 0)
        GPIO.output(self.amLev[1], 0)

    def inicializarMotores(self):
        #Iniciar el estado de los motores
        #Frecuencia de PWM
        self.amMinPWM = GPIO.PWM(self.amMin[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amLevPWM = GPIO.PWM(self.amLev[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amConPWM = GPIO.PWM(self.amCon[1], 250)

        ##Iniciar PWM en valor 0
        self.amMinPWM.start(0)
        self.amLevPWM.start(0)
        self.amConPWM.start(0)

    def inicializarCeldas(self):
        #Inciar celdas de carga
        print(
            "\n________________\nIniciando celdas de carga\n________________\n"
        )
        #Formato tupla: self.alsensorA	=	(dt,sck)

        #Celda de carga Concentrado C1
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)
        #Celda de carga Levadura Mineral
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

        self.resetearCeldas()

    def encenderMotores(self, motor):
        #Metodo que activa los motores
        #Entrada: 	self-> 	Objeto propio de python
        #			motor->	Selector del motor:
        #					Con: Concentrado, Min: mineral Lev: levadura

        if (motor == 'Con'):
            if self.aConObj != 0:
                #Encendido motor Con
                velocidad = 99  #self.aConCrucero
                self.amConPWM.ChangeDutyCycle(velocidad)
                GPIO.output(self.amCon[0], 1)
            else:
                print("Masa es 0, concentrado no encendido")
            return

        if (motor == 'Min'):
            if self.aMinObj != 0:
                self.amMinPWM.ChangeFrequency(750)
                self.amMinPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, mineral no encendido")
            return

        if (motor == 'Lev'):
            if self.aLevObj != 0:
                self.amLevPWM.ChangeFrequency(750)
                self.amLevPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, levadura no encendido")
            return

        else:
            print("Motor no encontrado")

    def desacelerarMotores(self, motor):
        #Metodo que desacelera los motores
        if (motor == 'Con'):
            velocidad = self.aConMin
            self.amConPWM.ChangeDutyCycle(velocidad)
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(200)
            self.amMinPWM.ChangeDutyCycle(50)
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(200)
            self.amLevPWM.ChangeDutyCycle(50)
            return

        else:
            print("Motor no encontrado")
            return

    def apagarMotores(self, motor, condicion):
        #Detener motores
        #Entradas: motor: 	Seleccion del motor deseado
        #					Con -> Concentrado
        #					Min -> Mineral
        #					Lev -> Levadura
        #		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
        if (motor == 'Con'):
            GPIO.output(self.amCon[0], 0)
            self.amConPWM.stop()
            if condicion:
                print("Concentrado apagado")
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(50)
            self.amMinPWM.ChangeDutyCycle(0)
            if condicion:
                print("Mineral apagado")
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(50)
            self.amLevPWM.ChangeDutyCycle(0)
            if condicion:
                print("Levadura apagado")
            return

        else:
            print("Motor no encontrado")
            return

    def abrirCerrarValvulas(self, valvula, condicion):
        #Metodo de abrir y cerrar valvulas
        #Entradas:	valvula:
        #				Tolv -> Puerta de la tolva Romana
        #				Min	->	Compuerta del mineral
        #				Lev ->	Compuerta levadura
        #			condicion:
        #				0 	->	Valvula cerrada
        #				1	->	Valvula abierta

        if (valvula == 'Tolv'):
            GPIO.output(self.avTolva, condicion)
            return

        if (valvula == 'Min'):
            GPIO.output(self.avMineral, condicion)
            return

        if (valvula == 'Lev'):
            GPIO.output(self.avLevadura, condicion)
            return

        else:
            print("Valvula incorrecta")

    def cambiarSensibilidad(self, celda, sensibilidad):
        #Metodo para cambiar la sensibilidad de la celda de carga: (depuracion)
        #Formato de celda: 'Min','Lev','A','B'
        #Entradas: celda: A1, A2, B1, B2, Min, Lev
        print("Cambiando sensibilidad")
        if (celda == 'A1'):
            self.asA1 = sensibilidad
            self.axA.select_channel(channel='A')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'A2'):
            self.asA2 = sensibilidad
            self.axA.select_channel(channel='B')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B1'):
            self.asB1 = sensibilidad
            self.axB.select_channel(channel='A')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B2'):
            self.asB2 = sensibilidad
            self.axB.select_channel(channel='B')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Min'):
            self.asMin = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Lev'):
            self.asLev = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        else:
            print("Celda no encontrada")

    def leerMineral(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar

        #Mineral puerto A del sensor
        masaMin = -((self.ahxML.weighOnce()) - self.asZeroMin) / self.asMin
        return masaMin

    def leerLevadura(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        masaLev = (self.ahxML.weighOnce() - self.asZeroLev) / self.asLev
        return masaLev

    def leerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar

        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

        Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
        #Nota: De momento se estan leyendo solo las celdas de los puertos A del concentrado
        #		las celdas B presetan problemas de retardos en las lecturas
        return float(Conc)

    def cerrarSteppers(self):
        #Metodo para apagar puertos de velocidad de los motores
        self.amMinPWM.stop()
        self.amLevPWM.stop()
        self.amConPWM.stop()

    def leer4Concentrado(self):
        #Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)
        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def leer4ConcentradoRaw(self, lecturas):
        #Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
        Conc1 = self.ahxC1.weighOnce()

        Conc2 = self.ahxC2.weighOnce()

        Conc3 = self.ahxC3.weighOnce()

        Conc4 = self.ahxC4.weighOnce()

        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def tararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(80)

        self.asZeroC2 = self.ahxC2.weigh(80)

        self.asZeroC3 = self.ahxC3.weigh(80)

        self.asZeroC4 = self.ahxC4.weigh(80)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def tararMineral(self, printVal=False, lecturas=30):
        #Metodo para tarar mineral

        self.asZeroMin = self.ahxML.tare(lecturas, False)
        if printVal:
            print("\tTara del mineral %d" % (self.asZeroMin))

    def tararLevadura(self, printVal=False, lecturas=30):
        #Metodo para tarar levdura
        self.asZeroMin = self.ahxML.tare(lecturas, False)
        if printVal:
            print("\tTara de la levadura %d" % (self.asZeroMin))

    def filtradorTamizador(self, dato, alimento):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con tres periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / 5
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == 5:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.peso_k_1[1])) > self.razon[1]):
                datoT = self.peso_k_1[1]
                print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / 5
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == 5:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / 5
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == 5:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def inRangeCoerce(self, dato, minimo=0.0, maximo=100.0):
        #Metodo que limita los valores de una variable
        if dato > maximo:
            return maximo

        if dato < minimo:
            return minimo

        else:
            return dato

    def normalizarVelocidadConcentrado(self, dato):
        #Metodo para normalizar los valores del concentrado
        #Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
        #Fuera de esos valores comienza a presentarse comportamiento erratico.
        dato = self.inRangeCoerce(dato, 0, 100)
        dato = (self.maxCon - self.minCon) / 100 * dato + self.minCon
        return dato

    #Metodos para resumir bloques de la secuencia
    def tararCeldas(self):
        #Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
        self.leerMineral(80)
        print("________________\nTarando Concentrado\n________________\n")
        self.tararConcentrado(80, True)
        print("Zero A1 ", self.asZeroC1)
        print("Zero A2 ", self.asZeroC2)
        print("Zero B1 ", self.asZeroC3)
        print("Zero B2 ", self.asZeroC4)

        print("________________\nTarando Mineral\n________________\n")
        self.tararMineral(80)
        print("________________\nTarando Levadura\n________________\n")
        self.tararLevadura(80)

    def resetearCeldas(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

        print("Reseteando celdas de carga Mineral y Levadura")
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()
        time.sleep(0.5)

    def filtroButterWorth(self, xk):
        self.yk = (
            0.7769 * self.xk_1  #- 0.007079*self.xk_2
            + 0.2231 * self.yk_1)  #- 0.000002 * self.yk_2)
        #Retrasar muestras
        #self.xk_4	= self.xk_3
        #self.xk_3	= self.xk_2
        self.xk_2 = self.xk_1
        self.xk_1 = xk

        #self.yk_4	= self.yk_3
        #self.yk_3	= self.yk_2
        self.yk_2 = self.yk_1
        self.yk_1 = self.yk
        return self.yk

    def controlPD(self, yk):
        #Comienza algoritmo de control

        #Estimacion de la velocidad
        #vk = yk-self.aYk_1+0.6703*self.aVk_1

        #Estimacion del retardo

        #self.aInt_Retardo = self.inRangeCoerce(self.aInt_Retardo,0,10000)

        #Estimacion del error
        ek = (self.aMasaObj[0] - yk)
        #Estimacion del control PD
        pk = self.aKp[0] * (ek)
        dk = ((self.aKd[0] * self.aN[0]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[0] * 0.05)
        pidk = pk + dk + 0.4
        #Compensacion de componente no lineal
        pidk = 100 * self.inRangeCoerce(pidk, 0, 0.99)
        self.amConPWM.ChangeDutyCycle(pidk)
        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1 = yk
        self.aDk_1 = dk
        self.aEk_1 = ek
        self.aPWM[0] = pidk

    def generadorTrayectoria(self):
        print("Generando Trayectorias")
        a = self.aAceleracion[0]
        t2 = self.aMasaObj[0] / a - 2
        tf = t2 + 4
        k = 0
        m1 = 0.5 * a
        m2 = a * t2 + m1
        tiempo = np.arange(0.05, 30.05, 0.05)
        k = 0
        mk = np.zeros(600)

        #Inicia la generacion de la trayectoria
        t = 0
        while t < 30:
            t = tiempo[k]
            if t <= 1:
                mk[k] = (a / 2) * t * t
            if ((1 < t) and (t <= (t2 + 1))):
                mk[k] = a * (t - 1) + m1
            if ((t2 + 1 < t) and (t <= tf)):
                mk[k] = -a / 6 * (t - t2 - 1) * (t - t2 - 1) + a * (t - t2 -
                                                                    1) + m2
            elif (t > tf):
                mk[k] = self.aMasaObj[0]
            k += 1
        print("Trayectoria generada")
        self.aTrayectoriaCon = mk
Exemplo n.º 2
0
class Dosificadora:
    def __init__(self):
        #Crear el objeto de la clase dosificadora

        ##Convenciones: axxxx: a significa atributo
        #Con-> Concentrado
        #Min-> Mineral
        #Lev-> Levadura

        #Puertos de control para las valvulas
        self.avTolva = 2
        self.avMineral = 16
        self.avLevadura = 27

        #Puertos de asignacion de las celdas de carga
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        #Puertos control de motores
        self.amCon = (7, 8)  #Formato tupla: (Encendido, velocidad)
        self.amMin = (20, 21)  #Formato tupla: (velocidad, sentido)
        self.amLev = (25, 26)

        #Sensibilidades celdas de carga
        self.asMin = 1030.3320
        self.asLev = 2563.3821
        self.asC1 = 1
        self.asC2 = 1
        self.asC3 = 1
        self.asC4 = 1
        self.asConc = 53.2201

        #Valores de Tara para cada celda de carga
        self.asZeroMin = 0
        self.asZeroLev = 0
        self.asZeroC1 = 0
        self.asZeroC2 = 0
        self.asZeroC3 = 0
        self.asZeroC4 = 0

        #Masas objetivo
        self.aConObj = 1
        self.aMinObj = 1
        self.aLevObj = 1
        self.aMasaObj = [self.aConObj, self.aMinObj, self.aLevObj]

        #Parametros del filtro tamizador y media movil
        self.aPeso_kbuffer = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
                              [0.0, 0.0, 0.0, 0.0, 0.0,
                               0.0]]  #Formato lista [Con,Min,Lev]
        self.aSk = [0.0, 0.0, 0.0]  #Formato listas [Con,Min,Lev]
        self.aContador = [0, 0, 0]
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]
        self.aPeso_kbuffer = np.zeros((3, 20), dtype=np.float32)

        #Valores para algoritmo de control
        self.aMultiplo = [0.8, 0.8, 0.8]  #Formato listas [Con,Min,Lev]
        self.aDeltaRef = [0.0, 0.0, 0.0]

        self.aInt = [0.0, 0.0, 0.0]
        self.aKp = [0.00051743, 0.0051743, 0.0]
        self.aKi = [0.0, 0.0, 0.0]
        self.aKd = [0.00080848, 0.0080848, 0.0]
        self.aN = [0.3704, 0.0, 0.3704]

        self.aVk_1 = 0.0
        self.aEk_1 = 0.0
        self.aYk_1 = 0.0
        self.aDk_1 = 0.0
        self.aUk_1 = 0
        self.aRk_1 = 0

        self.aInt_Retardo = 0
        self.aTcontador = 0
        self.aDoCalcularKp = [True, True, True]
        self.aPWM = [0.0, 0.0, 0.0]

        self.aAceleracion = [300.0, 0.0, 0.0]
        #Otros atributos
        self.asText = "________________"  #Separador de Textos
        self.minCon = 39.0  #Menor ciclo de PWM permitido para el concentrado
        self.maxCon = 99.0  #Mayor ciclo de PWM permitido para el concentrado
        self.razon = [
            60.0, 50.0, 10.0
        ]  #Mayor tasa de cambio permitida por el filtro tamizador
        #Formato lista [Con,Min,Lev]
        self.aConCrucero = 70.0  #Velocidad crucero motor Con
        self.aConMin = 60.0  #Minima velocidad para mover el motor
        self.aEspacio = "_________________"

    def __del__(self):
        #Metodo destructor de objeto
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def inicializarPuertos(self):
        #Encargado de iniciar el estado de los puertos de RPi.

        print("\n________________\nIniciando puertos\n________________\n")
        #Configurar puertos
        #Valvulas
        GPIO.setup(self.avTolva, GPIO.OUT)
        GPIO.setup(self.avMineral, GPIO.OUT)
        GPIO.setup(self.avLevadura, GPIO.OUT)

        #Motores
        #Concentrado
        GPIO.setup(self.amCon[0], GPIO.OUT)
        GPIO.setup(self.amCon[1], GPIO.OUT)

        #Mineral
        GPIO.setup(self.amMin[0], GPIO.OUT)
        GPIO.setup(self.amLev[0], GPIO.OUT)

        #Levadura
        GPIO.setup(self.amMin[1], GPIO.OUT)
        GPIO.setup(self.amLev[1], GPIO.OUT)

        #Colocar todos los puertos en BAJO "LOW".
        GPIO.output(self.avTolva, 0)
        GPIO.output(self.avMineral, 0)
        GPIO.output(self.avLevadura, 0)

        GPIO.output(self.amCon[0], 0)
        GPIO.output(self.amCon[1], 0)

        GPIO.output(self.amMin[0], 0)
        GPIO.output(self.amMin[1], 0)

        GPIO.output(self.amLev[0], 0)
        GPIO.output(self.amLev[1], 0)

    def inicializarMotores(self):
        #Iniciar el estado de los motores
        #Frecuencia de PWM
        self.amMinPWM = GPIO.PWM(self.amMin[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amLevPWM = GPIO.PWM(self.amLev[0],
                                 300)  #Formato tupla: (velocidad, sentido)
        self.amConPWM = GPIO.PWM(self.amCon[1], 250)

        ##Iniciar PWM en valor 0
        self.amMinPWM.start(0)
        self.amLevPWM.start(0)
        self.amConPWM.start(0)

    def inicializarCeldas(self):
        #Inciar celdas de carga
        print(
            "\n________________\nIniciando celdas de carga\n________________\n"
        )
        #Formato tupla: self.alsensorA	=	(dt,sck)

        #Celda de carga Concentrado C1
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)
        #Celda de carga Levadura Mineral
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

        self.resetearCeldas()

    def encenderMotores(self, motor):
        #Metodo que activa los motores
        #Entrada: 	self-> 	Objeto propio de python
        #			motor->	Selector del motor:
        #					Con: Concentrado, Min: mineral Lev: levadura

        if (motor == 'Con'):
            if self.aConObj != 0:
                #Encendido motor Con
                velocidad = 99  #self.aConCrucero
                self.amConPWM.ChangeDutyCycle(velocidad)
                GPIO.output(self.amCon[0], 1)
            else:
                print("Masa es 0, concentrado no encendido")
            return

        if (motor == 'Min'):
            if self.aMinObj != 0:
                self.amMinPWM.ChangeFrequency(750)
                self.amMinPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, mineral no encendido")
            return

        if (motor == 'Lev'):
            if self.aLevObj != 0:
                self.amLevPWM.ChangeFrequency(750)
                self.amLevPWM.ChangeDutyCycle(50)
            else:
                print("Masa igual a 0, levadura no encendido")
            return

        else:
            print("Motor no encontrado")

    def desacelerarMotores(self, motor):
        #Metodo que desacelera los motores
        if (motor == 'Con'):
            velocidad = self.aConMin
            self.amConPWM.ChangeDutyCycle(velocidad)
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(200)
            self.amMinPWM.ChangeDutyCycle(50)
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(200)
            self.amLevPWM.ChangeDutyCycle(50)
            return

        else:
            print("Motor no encontrado")
            return

    def apagarMotores(self, motor, condicion):
        #Detener motores
        #Entradas: motor: 	Seleccion del motor deseado
        #					Con -> Concentrado
        #					Min -> Mineral
        #					Lev -> Levadura
        #		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
        if (motor == 'Con'):
            GPIO.output(self.amCon[0], 0)
            self.amConPWM.stop()
            if condicion:
                print("Concentrado apagado")
            return

        if (motor == 'Min'):
            self.amMinPWM.ChangeFrequency(50)
            self.amMinPWM.ChangeDutyCycle(0)
            if condicion:
                print("Mineral apagado")
            return

        if (motor == 'Lev'):
            self.amLevPWM.ChangeFrequency(50)
            self.amLevPWM.ChangeDutyCycle(0)
            if condicion:
                print("Levadura apagado")
            return

        else:
            print("Motor no encontrado")
            return

    def abrirCerrarValvulas(self, valvula, condicion):
        #Metodo de abrir y cerrar valvulas
        #Entradas:	valvula:
        #				Tolv -> Puerta de la tolva Romana
        #				Min	->	Compuerta del mineral
        #				Lev ->	Compuerta levadura
        #			condicion:
        #				0 	->	Valvula cerrada
        #				1	->	Valvula abierta

        if (valvula == 'Tolv'):
            GPIO.output(self.avTolva, condicion)
            return

        if (valvula == 'Min'):
            GPIO.output(self.avMineral, condicion)
            return

        if (valvula == 'Lev'):
            GPIO.output(self.avLevadura, condicion)
            return

        else:
            print("Valvula incorrecta")

    def cambiarSensibilidad(self, celda, sensibilidad):
        #Metodo para cambiar la sensibilidad de la celda de carga: (depuracion)
        #Formato de celda: 'Min','Lev','A','B'
        #Entradas: celda: A1, A2, B1, B2, Min, Lev
        print("Cambiando sensibilidad")
        if (celda == 'A1'):
            self.asA1 = sensibilidad
            self.axA.select_channel(channel='A')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'A2'):
            self.asA2 = sensibilidad
            self.axA.select_channel(channel='B')
            self.axA.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B1'):
            self.asB1 = sensibilidad
            self.axB.select_channel(channel='A')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'B2'):
            self.asB2 = sensibilidad
            self.axB.select_channel(channel='B')
            self.axB.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Min'):
            self.asMin = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        if (celda == 'Lev'):
            self.asLev = sensibilidad
            self.axML.select_channel(channel='A')
            self.axML.set_scale_ratio(sensibilidad)
            return

        else:
            print("Celda no encontrada")

    def leerMineral(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        mineral = 0.0
        for i in range(lecturas):
            masaMin = -(
                (self.ahxML.weighOnce()) - self.asZeroMin)  #/self.asMin
            mineral += masaMin
        mineral / lecturas
        return mineral

    def leerLevadura(self, lecturas):
        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        masaLev = (self.ahxML.weighOnce() - self.asZeroLev) / self.asLev
        return masaLev

    def leerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de promediar y devolver

        concentrado = 0.0
        for i in range(lecturas):
            Conc1 = self.ahxC1.weighOnce() - self.asZeroC1
            Conc2 = self.ahxC2.weighOnce() - self.asZeroC2
            Conc3 = self.ahxC3.weighOnce() - self.asZeroC3
            Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

            Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
            concentrado += Conc
        concentrado = concentrado / lecturas
        return float(concentrado)

    def cerrarSteppers(self):
        #Metodo para apagar puertos de velocidad de los motores
        self.amMinPWM.stop()
        self.amLevPWM.stop()
        self.amConPWM.stop()

    def leer4Concentrado(self):
        #Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
        Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

        Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

        Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

        Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)
        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def leer4ConcentradoRaw(self, lecturas):
        #Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
        Conc1 = self.ahxC1.weighOnce()

        Conc2 = self.ahxC2.weighOnce()

        Conc3 = self.ahxC3.weighOnce()

        Conc4 = self.ahxC4.weighOnce()

        print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4))

    def tararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(lecturas)

        self.asZeroC2 = self.ahxC2.weigh(lecturas)

        self.asZeroC3 = self.ahxC3.weigh(lecturas)

        self.asZeroC4 = self.ahxC4.weigh(lecturas)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def tararMineral(self, printVal=False, lecturas=30):
        #Metodo para tarar mineral
        self.asZeroMin = self.ahxML.weigh(lecturas)
        if printVal:
            print("\tTara del mineral %d" % (self.asZeroMin))

    def tararLevadura(self, printVal=False, lecturas=30):
        #Metodo para tarar levdura
        self.asZeroMin = self.ahxML.weigh(lecturas)
        if printVal:
            print("\tTara de la levadura %d" % (self.asZeroMin))

    def filtradorTamizador(self, dato, alimento, periodos=5):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con tres periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]

            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / periodos
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == periodos:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[1])) > self.razon[1]):
                datoT = self.aX_k_1[1]

            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / periodos
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == periodos:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]

            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / periodos
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == periodos:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def inRangeCoerce(self, dato, minimo=0.0, maximo=100.0):
        #Metodo que limita los valores de una variable
        if dato > maximo:
            return maximo

        if dato < minimo:
            return minimo

        else:
            return dato

    def normalizarVelocidadConcentrado(self, dato):
        #Metodo para normalizar los valores del concentrado
        #Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
        #Fuera de esos valores comienza a presentarse comportamiento erratico.
        dato = self.inRangeCoerce(dato, 0, 100)
        dato = (self.maxCon - self.minCon) / 100 * dato + self.minCon
        return dato

    #Metodos para resumir bloques de la secuencia
    def tararCeldas(self):
        #Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
        self.leerMineral(80)
        print("________________\nTarando Concentrado\n________________\n")
        self.tararConcentrado(80, True)
        print("Zero A1 ", self.asZeroC1)
        print("Zero A2 ", self.asZeroC2)
        print("Zero B1 ", self.asZeroC3)
        print("Zero B2 ", self.asZeroC4)

        print("________________\nTarando Mineral\n________________\n")
        self.tararMineral(80)
        print("________________\nTarando Levadura\n________________\n")
        self.tararLevadura(80)

    def resetearCeldas(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

        print("Reseteando celdas de carga Mineral y Levadura")
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()
        time.sleep(0.5)

    def filtroButterWorth(self, xk):
        self.yk = (
            0.7769 * self.xk_1  #- 0.007079*self.xk_2
            + 0.2231 * self.yk_1)  #- 0.000002 * self.yk_2)
        #Retrasar muestras
        #self.xk_4	= self.xk_3
        #self.xk_3	= self.xk_2
        self.xk_2 = self.xk_1
        self.xk_1 = xk

        #self.yk_4	= self.yk_3
        #self.yk_3	= self.yk_2
        self.yk_2 = self.yk_1
        self.yk_1 = self.yk
        return self.yk

    def controlPDCon(self, yk):
        #Comienza algoritmo de control
        #Calculo del error
        ek = (self.aMasaObj[0] - yk)
        #Estimacion del control PD
        pk = self.aKp[0] * (ek)
        dk = ((self.aKd[0] * self.aN[0]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[0] * 0.05)
        pidk = pk + dk + 0.3
        #Compensacion de componente no lineal
        pidk = 100 * self.inRangeCoerce(pidk, 0, 0.99)
        self.amConPWM.ChangeDutyCycle(pidk)
        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1 = yk
        self.aDk_1 = dk
        self.aEk_1 = ek
        self.aPWM[0] = pidk

    def controlPDLev(self, yk):
        #Comienza algoritmo de control
        #Calculo del error
        ek = (self.aMasaObj[2] - yk)
        #Estimacion del control PD
        pk = self.aKp[2] * (ek)
        dk = ((self.aKd[2] * self.aN[2]) *
              (ek - self.aEk_1) + self.aDk_1) / (1 + self.aN[2] * 0.05)
        pidk = pk + dk + 0.4
        #Compensacion de componente no lineal
        pidk = self.inRangeCoerce(pidk, 0, 0.99)
        pidk = self.escalar(pidk, 0, 1400)
        self.amLevPWM.ChangeFrequency(pidk)

        #Termina algoritmo de control

        #Retrasa las muestras
        self.aYk_1_Lev = yk
        self.aDk_1_Lev = dk
        self.aEk_1_Lev = ek
        self.aPWM[2] = pidk

    def escalar(self, dato, outMin, outMax, inMin=0, inMax=1):
        m = (outMin - outMax) / (inMin - inMax)
        b = outMin - inMin * m
        dato = dato * m + b
        return dato

    ##-----	Metodos para pruebas por separado ----- ##

    def InicializarCeldasTR(self):
        #Inicializa solamente las celdas de carga de la tolva romana
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)

    def InicializarCeldasML(self):
        #Inicializa las celdas de carga del mineral/Levadura
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

    def ResetearCeldasTR(self):
        #Resetea las celdas de carga del concentrado
        print("Reseteando celdas de carga concentrado")

        self.ahxC1.turnOff()
        self.ahxC2.turnOff()
        self.ahxC3.turnOff()
        self.ahxC4.turnOff()
        time.sleep(0.5)

        self.ahxC1.turnOn()
        self.ahxC2.turnOn()
        self.ahxC3.turnOn()
        self.ahxC4.turnOff()
        time.sleep(0.5)

    def resetearCeldasML(self):
        #Resetea las celdas de carga del Mineral/Levadura
        print("Reseteando celdas de carga Mineral-Levadura")
        #Celdas del Mineral/Levadura
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()

    ## --- Metodos para pruebas individuales --------
    def probarCeldasMinLev(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Mineral-Levadura")
        print(self.aEspacio)

        self.InicializarCeldasML()
        self.resetearCeldasML()
        self.tararMineral(False, 80)
        self.tararLevadura(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Min = self.leerMineral(80)

            #Calcular filtro de media movil en linea
            MinF = self.filtradorTamizador(Min, 'Min', 5)
            print("%f\t%f" % (Min, MinF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def probarCeldasTR(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Tolva Romana")
        print(self.aEspacio)

        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.tararConcentrado(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Con = self.leerConcentrado(80)

            #Calcular filtro de media movil en linea
            ConF = self.filtradorTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def obtenerFuncionRampaTR(self, maxSpeed=99):

        #metodo que se usa para obtener la rampa de velocidad del citrocom
        aceleracion = True
        print(self.aEspacio)
        print("Obteniendo rampa")
        print(self.aEspacio)
        self.inicializarPuertos()
        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.tararConcentrado()

        self.inicializarMotores()
        self.amConPWM.ChangeDutyCycle(99)
        print("Arrancado")
        self.encenderMotores('Con')

        c = 0
        self.amConPWM.start(0)

        while True:

            if aceleracion:
                for i in range(10, maxSpeed, 1):
                    masaConc = self.leerConcentrado(4)
                    masaConcF = self.filtradorTamizador(masaConc, 'Con', 5)
                    self.amConPWM.ChangeDutyCycle(i)
                    time.sleep(0.1)
                    print("%f\t%f\t%f" % (masaConc, masaConcF, i))
                for i in reversed(range(10, maxSpeed, 1)):
                    masaConc = self.leerConcentrado(4)
                    masaConcF = self.filtradorTamizador(masaConc, 'Con', 5)
                    self.amConPWM.ChangeDutyCycle(i)
                    time.sleep(0.1)
                    print("%f\t%f\t%f" % (masaConc, masaConcF, i))
            else:
                if (c == 0):
                    self.amConPWM.ChangeDutyCycle(maxSpeed)
                    c += 0
Exemplo n.º 3
0
class Testing:
    def __init__(self):
        #Atributos de las celdas de carga del concentrado
        self.alsensorC1 = (11, 9)  #Formato tupla: (dt,sck)
        self.alsensorC2 = (22, 10)
        self.alsensorC3 = (24, 23)
        self.alsensorC4 = (12, 6)
        self.alsensorML = (19, 13)

        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroC1 = 0
        self.asZeroMin = 0
        self.asZeroLev = 0

        self.asConc = 53.2201
        self.asMin = 1030.3320
        self.asLev = 2563.3821

        #Atributos del filtro
        self.aDato_k_1 = [0.0, 0.0, 0.0]
        self.razon = [60.0, 50.0, 10.0]
        self.aX_k_1 = [0.0, 0.0, 0.0]

        self.aSk = [0.0, 0.0, 0.0]
        self.aPeso_kbuffer = np.zeros((3, 20), dtype=np.float32)
        self.aContador = [0, 0, 0]

        #Otros atributos
        self.aEspacio = "_________________"

    def __del__(self):
        nombre = self.__class__.__name__
        print(nombre, "Destruido")

    def InicializarCeldasTR(self):
        #Inicializa las celdas de carga del concentrado
        self.ahxC1 = Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
        #Celda de carga Concentrado C2
        self.ahxC2 = Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
        #Celda de carga Concentrado C3
        self.ahxC3 = Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
        #Celda de carga Concentrado C4
        self.ahxC4 = Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)

    def InicializarCeldasML(self):
        #Inicializa las celdas de carga del mineral Levadura
        self.ahxML = Scale(self.alsensorML[0], self.alsensorML[1], 1, 80)

    def ResetearCeldasTR(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC1.turnOff()
        time.sleep(0.5)
        self.ahxC1.turnOn()
        time.sleep(0.5)

        self.ahxC2.turnOff()
        time.sleep(0.5)
        self.ahxC2.turnOn()
        time.sleep(0.5)

        self.ahxC3.turnOff()
        time.sleep(0.5)
        self.ahxC3.turnOn()
        time.sleep(0.5)

        self.ahxC4.turnOff()
        time.sleep(0.5)
        self.ahxC4.turnOn()
        time.sleep(0.5)

    def ResetearCeldasML(self):
        print("Reseteando celdas de carga concentrado")
        #Celdas del concentrado
        self.ahxML.turnOff()
        time.sleep(0.5)
        self.ahxML.turnOn()

    def LeerConcentrado(self, lecturas):
        #Leer el peso del concentrado en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        concentrado = 0
        for i in range(lecturas):
            Conc1 = self.ahxC1.weighOnce() - self.asZeroC1

            Conc2 = self.ahxC2.weighOnce() - self.asZeroC2

            Conc3 = self.ahxC3.weighOnce() - self.asZeroC3

            Conc4 = -(self.ahxC4.weighOnce() - self.asZeroC4)

            Conc = (Conc1 + Conc2 + Conc3 + Conc4) / (4 * self.asConc)
            concentrado += Conc
        concentrado = concentrado / lecturas
        return float(concentrado)

    def LeerMineral(self, lecturas):

        #Leer el peso del mineral en gramos.
        #Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
        #Mineral puerto A del sensor
        masaMin = -((self.ahxML.weighOnce()) - self.asZeroMin) / self.asMin
        return masaMin

    def FiltroMediaTamizador(self, dato, alimento, periodos=5):
        #Metodo para filtrar y tamizar los valores de las celdas de carga
        #Se aplica un filtro de media movil con n periodos,
        #luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
        if alimento == 'Con':

            #Tamizar
            if ((abs(dato - self.aDato_k_1[0])) > self.razon[0]):
                datoT = self.aX_k_1[0]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[0] = self.aSk[0] - self.aPeso_kbuffer[0][
                self.aContador[0]] + datoT
            concentrado = self.aSk[0] / periodos
            self.aPeso_kbuffer[0][self.aContador[0]] = datoT
            #Calcular filtro de media movil en linea
            self.aContador[0] += 1
            self.aDato_k_1[0] = dato
            self.aX_k_1[0] = datoT

            if self.aContador[0] == periodos:
                self.aContador[0] = 0
            return concentrado

        if alimento == 'Min':
            #Tamizar
            if ((abs(dato - self.peso_k_1[1])) > self.razon[1]):
                datoT = self.peso_k_1[1]
                print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[1] = self.aSk[1] - self.aPeso_kbuffer[1][
                self.aContador[1]] + datoT
            mineral = self.aSk[1] / periodos
            self.aPeso_kbuffer[1][self.aContador[1]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[1] += 1
            self.aDato_k_1[1] = dato
            self.aX_k_1[1] = datoT

            if self.aContador[1] == periodos:
                self.aContador[1] = 0
            return mineral

        if alimento == 'Lev':
            #Tamizar
            if ((abs(dato - self.aDato_k_1[2])) > self.razon[2]):
                datoT = self.aX_k_1[2]
                #print("Tamizado")
            else:
                datoT = dato

            #Filtrar
            self.aSk[2] = self.aSk[2] - self.aPeso_kbuffer[2][
                self.aContador[2]] + datoT
            levadura = self.aSk[2] / periodos
            self.aPeso_kbuffer[2][self.aContador[2]] = datoT

            #Mover el contador y retrasar las muestras
            self.aContador[2] += 1
            self.aDato_k_1[2] = dato
            self.aX_k_1[2] = datoT

            if self.aContador[2] == periodos:
                self.aContador[2] = 0
            return levadura

        else:
            print("Alimento no encontrado")

    def TararConcentrado(self, imprimir=False, lecturas=30):
        #Metodo para tarar los amplificadores del concentrado
        if imprimir:
            print("Tarando")

        self.asZeroC1 = self.ahxC1.weigh(lecturas)

        self.asZeroC2 = self.ahxC2.weigh(lecturas)

        self.asZeroC3 = self.ahxC3.weigh(lecturas)

        self.asZeroC4 = self.ahxC4.weigh(lecturas)

        if imprimir:
            print("Tara del concentrado\n%d\t%d\t%d\t%d\t" %
                  (self.asZeroC1, self.asZeroC2, self.asZeroC3, self.asZeroC4))

    def ProbarCeldasTR(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Tolva Romana")
        print(self.aEspacio)

        self.InicializarCeldasTR()
        self.ResetearCeldasTR()
        self.TararConcentrado(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Con = self.LeerConcentrado(4)

            #Calcular filtro de media movil en linea
            ConF = self.FiltroMediaTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break

    def ProbarCeldasMinLev(self, tiempo):
        #Obtiene muestras de la celda de carga por una cantidad determinada de tiempo
        print(self.aEspacio)
        print("Probando celdas de carga Mineral-Levadura")
        print(self.aEspacio)

        self.InicializarCeldasML()
        self.ResetearCeldasML()
        self.TararMinLev(False, 80)

        print("Sin filtro\tCon Filtro")
        tic = time.time()
        while True:
            Min = self.LeerMineral(80)

            #Calcular filtro de media movil en linea
            ConF = self.FiltroMediaTamizador(Con, 'Con', 5)
            print("%f\t%f" % (Con, ConF))
            toc = time.time()

            #Condicion de parada para el ciclo
            if ((toc - tic) >= tiempo):
                break
class Dosificadora:
	def __init__(self):
		#Crear el objeto de la clase dosificadora	

		##Convenciones: axxxx: a significa atributo
		 			#Con-> Concentrado
					#Min-> Mineral
					#Lev-> Levadura
						
		#Puertos de control para las valvulas
		self.avTolva	 	= 2
		self.avMineral 		= 16
		self.avLevadura 	= 27
		
		#Puertos de asignacion de las celdas de carga
		self.alsensorC1		= (11,9) 	#Formato tupla: (dt,sck)
		self.alsensorC2		= (22,10)	
		self.alsensorC3		= (24,23)
		self.alsensorC4		= (12,6)
		self.alsensorML		= (19,13)
		
		#Puertos control de motores
		self.amCon			= (7,8)		#Formato tupla: (Encendido, velocidad)
		self.amMin			= (20,21) 	#Formato tupla: (velocidad, sentido)
		self.amLev			= (25,26)
		
		#Sensibilidades celdas de carga
		self.asMin	 		= 1030.3320
		self.asLev		 	= 2563.3821
		self.asC1			= 1
		self.asC2			= 1
		self.asC3			= 1
		self.asC4			= 1
		self.asConc			= 53.2201
		
		#Valores de Tara para cada celda de carga
		self.asZeroMin		= 0
		self.asZeroLev		= 0
		self.asZeroC1		= 0
		self.asZeroC2		= 0
		self.asZeroC3		= 0
		self.asZeroC4		= 0
		
		#Masas objetivo
		self.aConObj		= 1
		self.aMinObj		= 1
		self.aLevObj		= 1
		
		#Parametros del filtro tamizador y media movil
		self.aPeso_kbuffer 	= [[0.0,0.0,0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0,0.0,0.0],[0.0,0.0,0.0,0.0,0.0,0.0]]	#Formato lista [Con,Min,Lev]
		self.aSk			= [0.0,	0.0,0.0	] 		#Formato listas [Con,Min,Lev]
		self.aContador		= [0,	0,	0	]
		self.aDato_k_1		= [0,	0,	0	]
		self.aX_k_1			= [0,	0,	0	]
		
		#Otros atributos
		self.asText			= "________________" 	#Separador de Textos
		self.minCon			= 39.0					#Menor ciclo de PWM permitido para el concentrado
		self.maxCon			= 99.0					#Mayor ciclo de PWM permitido para el concentrado
		self.razon			= [60.0,50.0,10.0]			#Mayor tasa de cambio permitida por el filtro tamizador
													#Formato lista [Con,Min,Lev]
		self.aConCrucero	= 70.0					#Velocidad crucero motor Con
		self.aConMin		= 60.0					#Minima velocidad para mover el motor

		#Parametros filtro Butterworth
		"""self.xk 			=  0.0
		self.xk_1 			=  0.0
		self.xk_2			=  0.0
		self.xk_3			=  0.0
		self.xk_4 			=  0.0	
		
		self.yk 			=  0.0
		self.yk_1 			=  0.0
		self.yk_2 			=  0.0
		self.yk_3 			=  0.0
		self.yk_4 			=  0.0	
		"""
		#Control PI
		self.ik				=  0.0
		self.kp				=  0.003625
		self.ki				=  0.0003 #0.00065
		self.ref		=  0.0
	def __del__(self):
	#Metodo destructor de objeto
		nombre = self.__class__.__name__
		print(nombre, "Destruido")
		
	def inicializarPuertos(self):
		#Encargado de iniciar el estado de los puertos de RPi.
		
		print("\n________________\nIniciando puertos\n________________\n")
	#Configurar puertos
		#Valvulas
		GPIO.setup(self.avTolva,GPIO.OUT)
		GPIO.setup(self.avMineral,GPIO.OUT)
		GPIO.setup(self.avLevadura,GPIO.OUT)
			
		#Motores
			#Concentrado
		GPIO.setup(self.amCon[0],GPIO.OUT)
		GPIO.setup(self.amCon[1],GPIO.OUT)
			
			#Mineral
		GPIO.setup(self.amMin[0],GPIO.OUT)
		GPIO.setup(self.amLev[0],GPIO.OUT)
		
			#Levadura
		GPIO.setup(self.amMin[1],GPIO.OUT)
		GPIO.setup(self.amLev[1],GPIO.OUT)
		
		#Colocar todos los puertos en BAJO "LOW".
		GPIO.output(self.avTolva,0)
		GPIO.output(self.avMineral,0)
		GPIO.output(self.avLevadura,0)
		
		GPIO.output(self.amCon[0],0)
		GPIO.output(self.amCon[1],0)
		
		GPIO.output(self.amMin[0],0)
		GPIO.output(self.amMin[1],0)
		
		GPIO.output(self.amLev[0],0)
		GPIO.output(self.amLev[1],0)
		
	def inicializarMotores(self):
	#Iniciar el estado de los motores 
		#Frecuencia de PWM
		self.amMinPWM	= GPIO.PWM(self.amMin[0],300) 	#Formato tupla: (velocidad, sentido)
		self.amLevPWM	= GPIO.PWM(self.amLev[0],300)	#Formato tupla: (velocidad, sentido)
		self.amConPWM	= GPIO.PWM(self.amCon[1],250)
		
		##Iniciar PWM en valor 0
		self.amMinPWM.start(0)
		self.amLevPWM.start(0)
		self.amConPWM.start(0)
		
	def inicializarCeldas(self):
	#Inciar celdas de carga
		print("\n________________\nIniciando celdas de carga\n________________\n")
			#Formato tupla: self.alsensorA	=	(dt,sck)
			
			#Celda de carga Concentrado C1
		self.ahxC1 	= Scale(self.alsensorC1[0], self.alsensorC1[1], 1, 80)
			#Celda de carga Concentrado C2
		self.ahxC2 	= Scale(self.alsensorC2[0], self.alsensorC2[1], 1, 80)
			#Celda de carga Concentrado C3
		self.ahxC3 	= Scale(self.alsensorC3[0], self.alsensorC3[1], 1, 80)
			#Celda de carga Concentrado C4
		self.ahxC4 	= Scale(self.alsensorC4[0], self.alsensorC4[1], 1, 80)		
			#Celda de carga Levadura Mineral
		self.ahxML	= Scale(self.alsensorML[0], self.alsensorML[1], 	1, 80)
		
		self.resetearCeldas()
		
	def encenderMotores(self,motor):
		#Metodo que activa los motores
		#Entrada: 	self-> 	Objeto propio de python
		#			motor->	Selector del motor: 
		#					Con: Concentrado, Min: mineral Lev: levadura
		
		if (motor=='Con'):
			if self.aConObj!=0:
				#Encendido motor Con
				velocidad = 99#self.aConCrucero
				self.amConPWM.ChangeDutyCycle(velocidad)
				GPIO.output(self.amCon[0],1)
			else:
				print("Masa es 0, concentrado no encendido")
			return
			
		if (motor=='Min'):
			if self.aMinObj!=0:
				self.amMinPWM.ChangeFrequency(750)
				self.amMinPWM.ChangeDutyCycle(50)
			else:
				print("Masa igual a 0, mineral no encendido")
			return
			
		if (motor=='Lev'):
			if self.aLevObj!=0:
				self.amLevPWM.ChangeFrequency(750)
				self.amLevPWM.ChangeDutyCycle(50)
			else:
				print("Masa igual a 0, levadura no encendido")
			return
			
		else:
			print("Motor no encontrado")
		
	def desacelerarMotores(self,motor):
		#Metodo que desacelera los motores
		if(motor=='Con'):
			velocidad = self.aConMin
			self.amConPWM.ChangeDutyCycle(velocidad)
			return
			
		if(motor=='Min'):
			self.amMinPWM.ChangeFrequency(200)
			self.amMinPWM.ChangeDutyCycle(50)
			return
			
		if(motor=='Lev'):
			self.amLevPWM.ChangeFrequency(200)
			self.amLevPWM.ChangeDutyCycle(50)
			return
			
		else:
			print("Motor no encontrado")
			return
			
	def apagarMotores(self,motor,condicion):
		#Detener motores
		#Entradas: motor: 	Seleccion del motor deseado
		#					Con -> Concentrado
		#					Min -> Mineral
		#					Lev -> Levadura
		#		Condicion:	Indica si el motor no fue apagado en la iteracion anterior
		if (motor=='Con'):
			GPIO.output(self.amCon[0],0)
			self.amConPWM.stop()
			if condicion:
				print("Concentrado apagado")
			return
			
		if (motor=='Min'):
			self.amMinPWM.ChangeFrequency(50)
			self.amMinPWM.ChangeDutyCycle(0)
			if condicion:
				print("Mineral apagado")
			return
			
		if (motor=='Lev'):
			self.amLevPWM.ChangeFrequency(50)
			self.amLevPWM.ChangeDutyCycle(0)
			if condicion:
				print("Levadura apagado")
			return
			
		else:
			print("Motor no encontrado")
			return
	
	def abrirCerrarValvulas(self,valvula,condicion):
		#Metodo de abrir y cerrar valvulas
		#Entradas:	valvula: 
		#				Tolv -> Puerta de la tolva Romana
		#				Min	->	Compuerta del mineral
		#				Lev ->	Compuerta levadura
		#			condicion:
		#				0 	->	Valvula cerrada
		#				1	->	Valvula abierta
		
		if (valvula=='Tolv'):
			GPIO.output(self.avTolva,condicion)
			return
		
		if (valvula =='Min'):
			GPIO.output(self.avMineral,condicion)
			return
		
		if (valvula =='Lev'):
			GPIO.output(self.avLevadura,condicion)
			return
			
		else:
			print("Valvula incorrecta")
	
	def cambiarSensibilidad(self,celda,sensibilidad):
	#Metodo para cambiar la sensibilidad de la celda de carga: (depuracion) 
		#Formato de celda: 'Min','Lev','A','B'
		#Entradas: celda: A1, A2, B1, B2, Min, Lev
		print("Cambiando sensibilidad")
		if (celda=='A1'):
			self.asA1 = sensibilidad
			self.axA.select_channel(channel='A')
			self.axA.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='A2'):
			self.asA2 = sensibilidad	
			self.axA.select_channel(channel='B')
			self.axA.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='B1'):
			self.asB1 = sensibilidad
			self.axB.select_channel(channel='A')
			self.axB.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='B2'):
			self.asB2 = sensibilidad
			self.axB.select_channel(channel='B')
			self.axB.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='Min'):
			self.asMin = sensibilidad
			self.axML.select_channel(channel='A')
			self.axML.set_scale_ratio(sensibilidad)
			return
			
		if (celda=='Lev'):
			self.asLev = sensibilidad
			self.axML.select_channel(channel='A')
			self.axML.set_scale_ratio(sensibilidad)
			return
			
		else:
			print("Celda no encontrada")
			
	def leerMineral(self,lecturas):
	#Leer el peso del mineral en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		
		#Mineral puerto A del sensor
		masaMin = -((self.ahxML.weighOnce())-self.asZeroMin)/self.asMin
		return masaMin
	
	def leerLevadura(self,lecturas):
	#Leer el peso del mineral en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		masaLev = (self.ahxML.weighOnce()-self.asZeroLev)/self.asLev
		return masaLev
	
	def leerConcentrado(self,lecturas):
	#Leer el peso del concentrado en gramos.
		#Entrada: lecturas -> Cantidad de veces que el sensor se lee antes de retornar
		
		Conc1 	= self.ahxC1.weighOnce()-self.asZeroC1
		
		Conc2 	= self.ahxC2.weighOnce()-self.asZeroC2
		
		Conc3 	= self.ahxC3.weighOnce()-self.asZeroC3
				
		Conc4	= -(self.ahxC4.weighOnce()-self.asZeroC4)
				
		Conc = (Conc1+Conc2+Conc3+Conc4)/(4*self.asConc)
		#Nota: De momento se estan leyendo solo las celdas de los puertos A del concentrado
		#		las celdas B presetan problemas de retardos en las lecturas
		return float(Conc)
	
	def cerrarSteppers(self):
	#Metodo para apagar puertos de velocidad de los motores 
			self.amMinPWM.stop()
			self.amLevPWM.stop()
			self.amConPWM.stop()
			
	def leer4Concentrado(self):
	#Metodo para leer por separado cada celda de carga del concentrado 	(depuracion)
		Conc1 	= self.ahxC1.weighOnce()-self.asZeroC1
		
		Conc2 	= self.ahxC2.weighOnce()-self.asZeroC2
		
		Conc3 	= self.ahxC3.weighOnce()-self.asZeroC3
				
		Conc4	= -(self.ahxC4.weighOnce()-self.asZeroC4)
		print("%d\t%d\t%d\t%d"%(Conc1,Conc2,Conc3,Conc4))
		
	def leer4ConcentradoRaw(self,lecturas):
	#Metodo para leer cada celda del concentrado sin restar tara 		(depuracion)
		Conc1 	= self.ahxC1.weighOnce()
		
		Conc2 	= self.ahxC2.weighOnce()
		
		Conc3 	= self.ahxC3.weighOnce()
				
		Conc4	= self.ahxC4.weighOnce()
				
		print("%d\t%d\t%d\t%d"%(Conc1,Conc2,Conc3,Conc4))
		
	def tararConcentrado(self,imprimir= False,lecturas=30):
		#Metodo para tarar los amplificadores del concentrado
		if imprimir:
			print("Tarando")
		
		self.asZeroC1 	= self.ahxC1.weigh(80)
		 
		self.asZeroC2 	= self.ahxC2.weigh(80)
		
		self.asZeroC3 	= self.ahxC3.weigh(80)
		
		self.asZeroC4 	= self.ahxC4.weigh(80)
		
		if imprimir:
			print("Tara del concentrado\n%d\t%d\t%d\t%d\t"%
			(self.asZeroC1,self.asZeroC2,self.asZeroC3,self.asZeroC4))

	def tararMineral(self,printVal=False,lecturas=30):
	#Metodo para tarar mineral
	
		self.asZeroMin = self.ahxML.tare(lecturas,False)
		if printVal:
			print("\tTara del mineral %d"%(self.asZeroMin))
	
	def tararLevadura(self,printVal=False,lecturas = 30):
	#Metodo para tarar levdura
		self.asZeroMin = self.ahxML.tare(lecturas,False)
		if printVal:
			print("\tTara de la levadura %d"%(self.asZeroMin))

	def filtradorTamizador(self,dato,alimento):
	#Metodo para filtrar y tamizar los valores de las celdas de carga
	#Se aplica un filtro de media movil con tres periodos,
	#luego se eliminan las lecturas que presenten cambios abruptos respecto de los valores predecesores.
		if alimento == 'Con':
			
			#Tamizar
			if ((abs(dato-self.aDato_k_1[0]))>self.razon[0]):
				datoT = self.aX_k_1[0]
				#print("Tamizado")
			else:
				datoT = dato
			
			#Filtrar
			self.aSk[0] 		= self.aSk[0]-self.aPeso_kbuffer[0][self.aContador[0]]+datoT
			concentrado 		= self.aSk[0]/5
			self.aPeso_kbuffer[0][self.aContador[0]] = datoT
			
			#Mover el contador y retrasar las muestras
			self.aContador[0] 	+=	1
			self.aDato_k_1[0]	= dato
			self.aX_k_1[0]		= datoT
			
			if self.aContador[0] 	== 5:
				self.aContador[0] 	= 0
			return concentrado
			
		if alimento == 'Min':
			#Tamizar
			if ((abs(dato-self.peso_k_1[1]))>self.razon[1]):
				dato = self.peso_k_1[1]
				print("Tamizado")
			#Filtrar
			mineral			 	= (dato+self.peso_k_1[1]+self.peso_k_2[1])/3
			self.peso_k_2[1]	= self.peso_k_1[1]
			self.peso_k_1[1]	= mineral
			return mineral
		
		if alimento == 'Lev':
			#Tamizar
			if ((abs(dato-self.peso_k_1[2]))>self.razon[2]):
				dato = self.peso_k_1[2]
				print("Tamizado")
			#Filtrar
			levadura 			= (dato+self.peso_k_1[2]+self.peso_k_2[2])/3
			self.peso_k_2[2]	= self.peso_k_1[2]
			self.peso_k_1[2]	= levadura
			return levadura
			
		else:
			print("Alimento no encontrado")
			
	def	inRangeCoerce(self,dato, minimo = 0.0, maximo = 100.0):
	#Metodo que limita los valores de una variable
		if dato > maximo:
			return maximo
			
		if dato < minimo:
			return minimo
		
		else:
			return dato
			
	def normalizarVelocidadConcentrado(self,dato):
	#Metodo para normalizar los valores del concentrado
		#Debido a la electronica, el valor de PWM permitido es entre 39 y 99.
		#Fuera de esos valores comienza a presentarse comportamiento erratico.
		dato = self.inRangeCoerce(dato,0,100)
		dato = (self.maxCon-self.minCon)/100 * dato + self.minCon
		return dato
	
	#Metodos para resumir bloques de la secuencia
	def tararCeldas(self):
	#Metodo para tarar todas las cedas de carga. Permite no hacerlo desde el main
		self.leerMineral(80)
		print("________________\nTarando Concentrado\n________________\n")
		self.tararConcentrado(80,True)
		print("Zero A1 ",self.asZeroC1)
		print("Zero A2 ",self.asZeroC2)
		print("Zero B1 ",self.asZeroC3)
		print("Zero B2 ",self.asZeroC4)
		
		print("________________\nTarando Mineral\n________________\n")
		self.tararMineral(80)
		print("________________\nTarando Levadura\n________________\n")
		self.tararLevadura(80)

	def resetearCeldas(self):
		print("Reseteando celdas de carga concentrado")
		#Celdas del concentrado
		self.ahxC1.turnOff()
		time.sleep(0.5)
		self.ahxC1.turnOn()
		time.sleep(0.5)
		
		self.ahxC1.turnOff()
		time.sleep(0.5)
		self.ahxC1.turnOn()
		time.sleep(0.5)
		
		self.ahxC2.turnOff()
		time.sleep(0.5)
		self.ahxC2.turnOn()
		time.sleep(0.5)
		
		self.ahxC3.turnOff()
		time.sleep(0.5)
		self.ahxC3.turnOn()
		time.sleep(0.5)
		
		self.ahxC4.turnOff()
		time.sleep(0.5)
		self.ahxC4.turnOn()
		time.sleep(0.5)		
		
		print("Reseteando celdas de carga Mineral y Levadura")
		self.ahxML.turnOff()
		time.sleep(0.5)
		self.ahxML.turnOn()
		time.sleep(0.5)
		
	def filtroButterWorth(self,xk):
		self.yk = (0.7769*self.xk_1 #- 0.007079*self.xk_2
			 + 0.2231*self.yk_1) #- 0.000002 * self.yk_2)
		#Retrasar muestras
		#self.xk_4	= self.xk_3
		#self.xk_3	= self.xk_2
		self.xk_2	= self.xk_1	  
		self.xk_1	= xk	
		
		#self.yk_4	= self.yk_3
		#self.yk_3	= self.yk_2
		self.yk_2	= self.yk_1	  
		self.yk_1	= self.yk
		return self.yk	
		
	def controlPI(self,xk):
		if ((xk +250) < self.aConObj):
			ek 	= self.aConObj-xk
			PI	= ek*self.kp+self.ki*self.ik
			PIl 	= self.inRangeCoerce(PI,0,99)
			self.amConPWM.ChangeDutyCycle(PIl)
			self.ik	= ek*0.1+self.ik
			return PI
		else:
			self.apagarMotores("Con",False)	
		#Anti Windup
		self.inRangeCoerce(self.ik,-100/self.ki,100/self.ki)