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
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)
# logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) display = Display() print("[0/2] Starting setup...") display.print("[0/2] Setup\n\rstarted") file_upload = FileUpload() snipe_it = SnipeIT() reader = Scanner() scale = Scale() print("[0/2] Taring the scale. Remove everything and press Enter.") _ = input() print("[0/2] Taring the scale...") scale.tare() print("[1/2] Scale taring complete.") # initialize the video stream and allow the camera sensor to warm up print("[1/2] Starting video stream...") camera = Camera() print("[2/2] Video stream started...") print("[2/2] Setup complete") display.print("[2/2] Setup\n\rcomplete") try: while True: asset_id = None display.print("Place kit\n\runder camera") print("Waiting for QR Code")
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 = 50.380 #Valores de Tara para cada celda de carga self.asZeroMin = 0 self.asZeroLev = 0 self.asZeroA1 = 0 self.asZeroA2 = 0 self.asZeroB1 = 0 self.asZeroB2 = 0 #Masas objetivo self.aConObj = 1 self.aMinObj = 1 self.aLevObj = 1 #Parametros del filtro de media movil self.peso_k_1 = [0, 0, 0] #Formato de lista [Con,Min,Lev] self.peso_k_2 = [0, 0, 0] #Formato lista [Con,Min,Lev] #Otros atributos self.asText = "________________" #Separador de Textos self.minCon = 39 #Menor ciclo de PWM permitido para el concentrado self.maxCon = 99 #Mayor ciclo de PWM permitido para el concentrado self.razon = [ 800, 50, 10 ] #Mayor tasa de cambio permitida por el filtro tamizador #Formato lista [Con,Min,Lev] self.aConCrucero = 70 #Velocidad crucero motor Con self.aConMin = 60 #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], selfalsensorML[1], 1, 80) #Inicializa, resetea y tara A print( "\n________________\nConfigurando Amplificador C1\n________________\n" ) #Resetear amplificador C1 self.ahxA.reset() self.ahxA.set_gain(gain=64) #Configurar ganancia para el canal A self.ahxA.select_channel(channel='A') self.ahxA.set_reference_unit(1) #Resetear calibracion amplificador C1 self.ahxA.select_channel(channel='B') self.ahxA.set_offset(0) self.ahxA.select_channel(channel='A') print('\tConfigurado\n') # -------------------------------------------------------------# ##Configurar amplificador C2 #Inicializa, resetea y tara amplificador C2 print( "\n________________\nConfigurando Amplificador C2\n________________\n" ) print("\tReseteando...") self.ahxA.reset() #Resetear amplificador C2 self.ahxB.set_gain(gain=64) #Configurar ganancia para el canal A self.ahxB.select_channel(channel='A') self.ahxB.set_reference_unit(1) #Resetear calibracion amplificador C2 self.asZeroB1 = self.ahxA.tare(1) #Tarar celda de carga self.ahxB.select_channel(channel='B') self.asZeroB2 = self.ahxA.tare(1) self.ahxB.set_offset(0) self.ahxB.select_channel(channel='A') print('\tConfigurado\n') ##Configurar amplificador Min Lev #Inicializa, resetea y tara Min Lev print( "\n________________\nConfigurando Amplificador Min Lev\n________________\n" ) print("\tReseteando...") self.ahxML.reset() #Resetear amplificador print('\tConfigurado') self.ahxML.set_gain(gain=64) #Configurar ganancia para el canal A self.ahxML.select_channel(channel='A') self.ahxML.set_reference_unit(1) #Calibrar celda A self.asZeroMin = self.ahxML.tare(1) #Tarar celda de carga self.ahxML.select_channel(channel='B') self.asZeroLev = self.ahxML.tare(1) self.ahxML.set_offset(0) self.ahxML.select_channel(channel='A') 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 = 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 self.ahxML.select_channel(channel='A') masaMin = -( (self.ahxML.get_value(lecturas)) - 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 self.ahxML.select_channel(channel='B') masaLev = (self.ahxML.get_value(lecturas) - 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 self.ahxA.select_channel(channel='A') Conc1 = (self.ahxA.get_value(lecturas) - self.asZeroA1) self.ahxA.select_channel(channel='B') Conc2 = (self.ahxA.get_value(lecturas) - self.asZeroA2) self.ahxB.select_channel(channel='A') Conc3 = (self.ahxB.get_value(lecturas) - self.asZeroB1) self.ahxB.select_channel(channel='B') Conc4 = (self.ahxB.get_value(lecturas) - self.asZeroB1) Conc = (Conc1 + Conc2 + Conc3 + Conc4) / 2 * 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 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) self.ahxA.select_channel(channel='A') Conc1 = (self.ahxA.get_value(1) - self.asZeroA1) self.ahxA.select_channel(channel='B') Conc2 = (self.ahxA.get_value(1) - self.asZeroA2) self.ahxB.select_channel(channel='A') Conc3 = (self.ahxB.get_value(1) - self.asZeroB1) self.ahxB.select_channel(channel='B') Conc4 = (self.ahxB.get_value(1) - self.asZeroB1) 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) self.ahxA.select_channel(channel='A') Conc1 = (self.ahxA.get_value(lecturas)) self.ahxA.select_channel(channel='B') Conc2 = (self.ahxA.get_value(lecturas)) self.ahxB.select_channel(channel='A') Conc3 = (self.ahxB.get_value(lecturas)) self.ahxB.select_channel(channel='B') Conc4 = (self.ahxB.get_value(lecturas)) print("%d\t%d\t%d\t%d" % (Conc1, Conc2, Conc3, Conc4)) return def tararConcentrado(self, lecturas=30): #Metodo para tarar los amplificadores del concentrado self.ahxA.select_channel(channel='A') self.asZeroA1 = self.ahxA.get_value(lecturas) self.ahxA.select_channel(channel='B') self.asZeroA2 = self.ahxA.get_value(lecturas) self.ahxB.select_channel(channel='A') self.asZeroB1 = self.ahxB.get_value(lecturas) self.ahxB.select_channel(channel='B') self.asZeroB2 = self.ahxB.get_value(lecturas) def tararMineral(self, lecturas=30): #print self.ahxML.GAIN #Metodo para tarar mineral self.ahxML.select_channel(channel='A') self.asZeroMin = self.ahxML.get_value(lecturas) print("\tTara del mineral %d" % (self.asZeroMin)) def tararLevadura(self, lecturas=30): #Metodo para tarar levdura self.ahxML.select_channel(channel='B') self.asZeroMin = self.ahxML.get_value(lecturas) 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.peso_k_1[0])) > self.razon[0]): dato = self.peso_k_1[0] print "Tamizado" #Filtrar concentrado = (dato + self.peso_k_1[0] + self.peso_k_2[0]) / 3 self.peso_k_2[0] = self.peso_k_1[0] self.peso_k_1[0] = concentrado 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(30) print("________________\nTarando Concentrado\n________________\n") self.tararConcentrado(30) print("Zero A1 ", self.asZeroA1) print("Zero A2 ", self.asZeroA2) print("Zero B1 ", self.asZeroB1) print("Zero B2 ", self.asZeroB2) print("________________\nTarando Mineral\n________________\n") self.tararMineral(30) print("________________\nTarando Levadura\n________________\n") self.tararLevadura(30) self.tararMineral(30) def resetearCeldas(self): print("Reseteando celdas de carga concentrado") #Celdas del concentrado self.hxC1.turnOff() time.sleep(0.5) self.hxC1.turnOn() time.sleep(0.5) self.hxC1.turnOff() time.sleep(0.5) self.hxC1.turnOn() time.sleep(0.5) self.hxC2.turnOff() time.sleep(0.5) self.hxC2.turnOn() time.sleep(0.5) self.hxC3.turnOff() time.sleep(0.5) self.hxC3.turnOn() time.sleep(0.5) self.hxC4.turnOff() time.sleep(0.5) self.hxC4.turnOn() time.sleep(0.5) print("Reseteando celdas de carga Mineral y Levadura") self.hxML.turnOff() time.sleep(0.5) self.hxML.turnOn() time.sleep(0.5)
def main(): """ Read in settings from AMW_config.jsn. If we don't find the file, make it. Note that we save the file with new line as a separator, but can't load it with a non-standard separator, so we replace new lines with commas, he default separator character """ try: with open('AMW_config.jsn', 'r') as fp: data = fp.read() data = data.replace('\n', ',') configDict = json.loads(data) fp.close() # Constants for saving data # cage name, to tell data from different cages kCAGE_NAME = configDict.get('Cage Name') # path where data from each day will be saved kCAGE_PATH = configDict.get('Data Path') # rollover time, 0 to start the file for each day at 12 midnight. Could set to 7 to synch files to mouse day/night cycle kDAYSTARTHOUR = configDict.get('Day Start Hour') # size of array used for threaded reading from load cell amplifier kTHREADARRAYSIZE = configDict.get('Thread Array Size') # cuttoff weight where we stop the thread from reading when a mouse steps away kMINWEIGHT = configDict.get('Minimum Weight') # GPIO pin numbers and scaling for HX711, adjust as required for individual setup kDATA_PIN = configDict.get('GPIO Data Pin') kCLOCK_PIN = configDict.get('GPIO Clock Pin') kGRAMS_PER_UNIT = configDict.get('Grams Per Unit') # RFID Reader. Note that code as written only works with ID tag readers not RDM readers because of reliance on Tag-In-Range Pin kSERIAL_PORT = configDict.get('Serial Port') kTIR_PIN = configDict.get('GPIO Tag In Range Pin') #whether data is saved locally 1 or, not yet supported, sent to a server 2, or both, 3 kSAVE_DATA = configDict.get('Data Save Options') # a dictionary of ID Tags and cutoff weights, as when monitoring animal weights over time kHAS_CUTOFFS = configDict.get('Has Cutoffs') if kHAS_CUTOFFS: kCUT_OFF_DICT = configDict.get('Cutoff Dict') else: kCUT_OFF_DICT = None # can call get day weights code and email weights, needs extra options kEMAIL_WEIGHTS = configDict.get('Email Weights') if kEMAIL_WEIGHTS: kEMAIL_DICT = configDict.get('Email Dict') else: kEMAIL_DICT = None except (TypeError, IOError, ValueError) as e: #we will make a file if we didn't find it, or if it was incomplete print( 'Unable to load configuration data from AMW_config.jsn, let\'s make a new AMW_config.jsn.\n' ) jsonDict = {} kCAGE_NAME = input( 'Enter the cage name, used to distinguish data from different cages:' ) kCAGE_PATH = input( 'Enter the path where data from each day will be saved:') kDAYSTARTHOUR = int( input( 'Enter the rollover hour, in 24 hour format, when a new data file is started:' )) kTHREADARRAYSIZE = int( input( 'Enter size of array used for threaded reading from Load Cell:' )) kMINWEIGHT = float( input( 'Enter cutoff weight where we stop the thread from reading:')) kDATA_PIN = int( input( 'Enter number of GPIO pin connected to data pin on load cell:') ) kCLOCK_PIN = int( input( 'Enter number of GPIO pin connected to clock pin on load cell:' )) kGRAMS_PER_UNIT = float( input( 'Enter the scaling of the load cell, in grams per A/D unit:')) kSERIAL_PORT = input( 'Enter the name of serial port used for tag reader,e.g. serial0 or ttyAMA0:' ) kTIR_PIN = int( input( 'Enter number of the GPIO pin connected to the Tag-In-Range pin on the RFID reader:' )) kSAVE_DATA = int( input( 'To save data locally, enter 1; to send data to a server, not yet supported, enter 2:' )) tempInput = input('Track weights against existing cutoffs(Y or N):') kHAS_CUTOFFS = bool(tempInput[0] == 'y' or tempInput[0] == 'Y') if kHAS_CUTOFFS: kCUT_OFF_DICT = {} while True: tempInput = input( 'Enter a Tag ID and cuttoff weight, separated by a comma, or return to end entry:' ) if tempInput == "": break entryList = tempInput.split(',') try: kCUT_OFF_DICT.update({entryList[0]: float(entryList[1])}) except Exception as e: print('bad data entered', str(e)) else: kCUT_OFF_DICT = None jsonDict.update({ 'Has Cutoffs': kHAS_CUTOFFS, 'Cutoff Dict': kCUT_OFF_DICT }) tempInput = input('Email weights every day ? (Y or N):') kEMAIL_WEIGHTS = bool(tempInput[0] == 'y' or tempInput[0] == 'Y') if kEMAIL_WEIGHTS: kEMAIL_DICT = {} kFROMADDRESS = input( 'Enter the account used to send the email with weight data:') kPASSWORD = input( 'Enter the password for the email account used to send the mail:' ) kSERVER = input( 'Enter the name of the email server and port number, e.g., smtp.gmail.com:87, with separating colon:' ) kRECIPIENTS = tuple( input( 'Enter comma-separated list of email addresses to get the daily weight email:' ).split(',')) kEMAIL_DICT.update({ 'Email From Address': kFROMADDRESS, 'Email Recipients': kRECIPIENTS }) kEMAIL_DICT.update({ 'Email Password': kPASSWORD, 'Email Server': kSERVER }) else: kEMAIL_DICT = None jsonDict.update({ 'Email Weights': kEMAIL_WEIGHTS, 'Email Dict': kEMAIL_DICT }) # add info to a dictionay we will write to file jsonDict.update({ 'Cage Name': kCAGE_NAME, 'Data Path': kCAGE_PATH, 'Day Start Hour': kDAYSTARTHOUR, 'Thread Array Size': kTHREADARRAYSIZE }) jsonDict.update({ 'Minimum Weight': kMINWEIGHT, 'GPIO Data Pin': kDATA_PIN, 'GPIO Clock Pin': kCLOCK_PIN }) jsonDict.update({ 'GPIO Tag In Range Pin': kTIR_PIN, 'Grams Per Unit': kGRAMS_PER_UNIT, 'Serial Port': kSERIAL_PORT }) jsonDict.update({ 'Data Save Options': kSAVE_DATA, 'Email Weights': kEMAIL_WEIGHTS }) with open('AMW_config.jsn', 'w') as fp: fp.write( json.dumps(jsonDict, sort_keys=True, separators=('\r\n', ':'))) """ Initialize the scale from variables listed above and do an initial taring of the scale with 10 reads. Because pins are only accessed from C++, do not call Python GPIO.setup for the dataPin and the clockPin """ scale = Scale(kDATA_PIN, kCLOCK_PIN, kGRAMS_PER_UNIT, kTHREADARRAYSIZE) scale.weighOnce() scale.tare(10, True) """ Setup tag reader and GPIO for TIR pin, with tagReaderCallback installed as an event callback when pin changes either from low-to-high, or from high-to-low. """ tagReader = TagReader('/dev/' + kSERIAL_PORT, doChecksum=False, timeOutSecs=0.05, kind='ID') tagReader.installCallBack(kTIR_PIN) """ A new binary data file is opened for each day, with a name containing the current date, so open a file to start with """ now = datetime.fromtimestamp(int(time())) startDay = datetime(now.year, now.month, now.day, kDAYSTARTHOUR, 0, 0) if startDay > now: # it's still "yesterday" according to kDAYSTARTHOUR definition of when a day starts startDay = startDay - timedelta(hours=24) startSecs = startDay.timestamp( ) # used to report time of an entry through the weighing tube nextDay = startDay + timedelta(hours=24) filename = kCAGE_PATH + kCAGE_NAME + '_' + str( startDay.year) + '_' + '{:02}'.format( startDay.month) + '_' + '{:02}'.format(startDay.day) if kSAVE_DATA & kSAVE_DATA_LOCAL: print('opening file name = ' + filename) outFile = open(filename, 'ab') from OneDayWeights import get_day_weights """ Weight data is written to the file as grams, in 32 bit floating point format. Each run of data is prefaced by metadata from a 32 bit floating point metaData array of size 2. The first point contains the last 6 digits of the RFID code, as a negative value to make it easy for analysis code to find the start of each run. The second point contains the time in seconds since the start of the day. Both data items have been selected to fit into a 32 bit float. """ metaData = array('f', [0, 0]) while True: try: """ Loop with a brief sleep, waiting for a tag to be read or a new day to start, in which case a new data file is made """ while RFIDTagReader.globalTag == 0: if datetime.fromtimestamp(int(time())) > nextDay: if kSAVE_DATA & kSAVE_DATA_LOCAL: outFile.close() print('save data date =', startDay.year, startDay.month, startDay.day) try: get_day_weights(kCAGE_PATH, kCAGE_NAME, startDay.year, startDay.month, startDay.day, kCAGE_PATH, False, kEMAIL_DICT, kCUT_OFF_DICT) except Exception as e: print('Error getting weights for today:' + str(e)) startDay = nextDay nextDay = startDay + timedelta(hours=24) startSecs = startDay.timestamp() filename = kCAGE_PATH + kCAGE_NAME + '_' + str( startDay.year) + '_' + '{:02}'.format( startDay.month) + '_' + '{:02}'.format( startDay.day) if kSAVE_DATA & kSAVE_DATA_LOCAL: outFile = open(filename, 'ab') print('opening file name = ' + filename) else: sleep(kTIMEOUTSECS) """ A Tag has been read. Fill the metaData array and tell the C++ thread to start recording weights """ thisTag = RFIDTagReader.globalTag startTime = time() print('mouse = ', thisTag) #scale.turnOn() metaData[0] = -(thisTag % 1000000) metaData[1] = startTime - startSecs scale.threadStart(scale.arraySize) nReads = scale.threadCheck() lastRead = 0 """ Keep reading weights into the array until a new mouse is read by the RFID reader, or the last read weight drops below 2 grams, or the array is full, then stop the thread print the metaData array and the read weights from the thread array to the file """ while ((RFIDTagReader.globalTag == thisTag or (RFIDTagReader.globalTag == 0 and scale.threadArray[nReads - 1] > kMINWEIGHT)) and nReads < scale.arraySize): if nReads > lastRead: print(nReads, scale.threadArray[nReads - 1]) lastRead = nReads sleep(0.05) nReads = scale.threadCheck() nReads = scale.threadStop() if kSAVE_DATA & kSAVE_DATA_LOCAL: metaData.tofile(outFile) scale.threadArray[0:nReads - 1].tofile(outFile) if kSAVE_DATA & kSAVE_DATA_REMOTE: # modify to send : Time:UNIX time stamp, RFID:FULL RFID Tag, CageID: id, array: weight array response = requests.post( kSERVER_URL, data={ 'tag': thisTag, 'cagename': kCAGE_NAME, 'datetime': int(startTime), 'array': str((metaData + scale.threadArray[0:nReads - 1]).tobytes(), 'latin_1') }).text if response != '\nSuccess\n': print(reponse) #scale.turnOff() except KeyboardInterrupt: #scale.turnOn() event = scale.scaleRunner('10:\tQuit AutoMouseWeight program\n') if event == 10: if kSAVE_DATA & kSAVE_DATA_LOCAL: outFile.close() GPIO.cleanup() return except Exception as error: print("Closing file...") outFile.close() GPIO.cleanup() raise error