En el capítulo anterior……
Si recordais habíamos obtenido un medidor que comprarado con uno comercial nos daba valores bastante parecidos (del orden de un 1%), pero que tenía un problema en el valor devuelto sin carga provocado por el ruido eléctrico del sensor.
Decía que el problema era estético porque por el tipo de producto no se van a medir potencias de 3 ó 4 W, pero cuando no estás utilizando una salida esperas que las lecturas de consumo sean cero y no tengan un comportamiento errático. De hecho creo que es mejor que para unos pocos Watios marque cero que no que por el ruido vaya dando lecturas de potencias negarivas ni cosas por el estilo.
Los sensores de corriente que hemos utilizado tienen dos patillas a las que se les pone un condensador de filtro (FILTER en el esquema), internamente antes del último amplificador operacional del chip se inserta este condensador, eso permite poner el filtro con un condensador más pequeño y sin utilizar la resistencia en serie, porque utilizaremos la resistencia de salida de un operacional. A continuación el esquema interno del sensor:
//********************************************************************************** //PDU gestionable por carlos Rodríguez //http://www.zigbe.net //El Sketch mide voltaje y corriente y calcula los valores de consumo de potencia //Basado en los algoritmos del la app note de Atmel AVR465 con inspiración en el código de openenergymonitor //********************************************************************************** //********************Variables de configuración******************** int NumeroDeMuestras = 3000; //Pines de entrada de voltaje y corriente int PinV = 1; int PinI1 = 2; int PinI2 = 3; int PinI3 = 4; int PinI4 = 5; int PinI; //********************Coeficientes de calibración******************** double CalV = 1.62; double CalI1 = 0.043; double CalI2 = 0.043; double CalI3 = 0.043; double CalI4 = 0.043; double CalI; double CalFase1 = 0.7; double CalFase2 = 0.7; double CalFase3 = 0.7; double CalFase4 = 0.7; double CalFase; //********************Variables para las muestras******************** int UlMuestraV; int UlMuestraI; int MuestraV; int MuestraI; int MuestraI0; int MuestraI1; int MuestraI2; int MuestraI3; //********************Variables filtradas******************** double UlFiltradoV; double UlFiltradoI; double FiltradoV; double FiltradoI; double filterTemp; //********************Almacena el voltahe instantaneo con la fase calibrada******************** double calibratedV; //********************Variables de cálculo de potencia******************** double sqI,sqV,instP,sumI,sumV,sumP; //********************Variables de salida******************** double PotenciaReal1, PotenciaAparente1, FactorPotencia1, Vrms1, Irms1; double PotenciaReal2, PotenciaAparente2, FactorPotencia2, Vrms2, Irms2; double PotenciaReal3, PotenciaAparente3, FactorPotencia3, Vrms3, Irms3; double PotenciaReal4, PotenciaAparente4, FactorPotencia4, Vrms4, Irms4; double PotenciaReal, PotenciaAparente, FactorPotencia, Vrms, Irms; //********************Variables de medida de la energía******************** //Calculation of kwh //time taken since last measurment timems = tmillis - ltmillis; unsigned long ltmillis, tmillis, timems; //time when arduino is switched on... is it 0? unsigned long startmillis; //kwhTotal is cumulative kwh today, the other 3 are historCalIX over the last 3 days for comparison. double kwhTotal =0.0; //********************Variables de medida de la frecuencia******************** //Tiempo en microsegundos desde el último paso del voltaje por cero unsigned long vLastZeroMsec; unsigned long vPeriod; //Suma de vPeriod para obtener el promedio unsigned long vPeriodSum; //Número de periodos unsigned long vPeriodCount; //Frequencia float freq; //Se utiliza para filtrar lecturas erróneas de vPeriod //50Hz -> 20000, 60 Hz -> 16666 unsigned long expPeriod = 20000; unsigned long filterWidth = 2000; void setup() { Serial.begin(115200); //*******************Setup de la medida de energía******************* tmillis = millis(); startmillis=tmillis; pinMode (13, OUTPUT); //Para aproximar el primer valor //CalcularEnergia(); } void loop() { //Primer puerto digitalWrite(13,1); //Enciende el led de la placa PinI=PinI1; //asigna el pin analógico de I del puerto1 CalI=CalI1; //asigna los valores de calibración del puerto1 CalFase=CalFase1; //Asigna los valores de calibración de fase del puerto1 //CalcularEnergia(); //Calcula la energía CalcularEnergia(); //Calcula la energía CalculoRMSPot(); //Calculos de potencia Vrms1=Vrms; //Asigna resultados a variables Irms1=Irms; PotenciaReal1=PotenciaReal; PotenciaAparente1=PotenciaAparente; FactorPotencia1=FactorPotencia; CalculoFrecuencia(); //Calcula la frecuencia ResetearAcumuladores(); //resetea los acumuladores //Segundo puerto digitalWrite(13,0); //Documentado en el puerto1 PinI=PinI2; CalI=CalI2; CalFase=CalFase2; CalcularEnergia(); CalculoRMSPot(); Vrms2=Vrms; Irms2=Irms; PotenciaReal2=PotenciaReal; PotenciaAparente2=PotenciaAparente; FactorPotencia2=FactorPotencia; CalculoFrecuencia(); ResetearAcumuladores(); //tercer puerto digitalWrite(13,1); //Documentado en el puerto1 PinI=PinI3; CalI=CalI3; CalFase=CalFase3; CalcularEnergia(); CalculoRMSPot(); Vrms3=Vrms; Irms3=Irms; PotenciaReal3=PotenciaReal; PotenciaAparente3=PotenciaAparente; FactorPotencia3=FactorPotencia; CalculoFrecuencia(); ResetearAcumuladores(); //Cuarto puerto digitalWrite(13,0); //Documentado en el puerto1 PinI=PinI4; CalI=CalI4; CalFase=CalFase1; CalcularEnergia(); CalculoRMSPot(); Vrms4=Vrms; Irms4=Irms; PotenciaReal4=PotenciaReal; PotenciaAparente4=PotenciaAparente; FactorPotencia4=FactorPotencia; CalculoFrecuencia(); ResetearAcumuladores(); //--ENERGY MEASURMENT CALCULATION---------------- //Calculate amount of time since last PotenciaReal measurment. ltmillis = tmillis; tmillis = millis(); timems = tmillis - ltmillis; //Calculate number of kwh consumed. // REVISAR____>//kwhTotal = kwhTotal + ((PotenciaReal/1000.0) * 1.0/3600.0 * (timems/1000.0)); //Reset accumulators //Muestra la salida por el puerto serie Serial.print ("Puerto 1 -> "); Serial.print(PotenciaReal1); Serial.print(" W / "); Serial.print(PotenciaAparente1); Serial.print(" VA / "); Serial.print(FactorPotencia1); Serial.print(" Factor P / "); Serial.print(Vrms1); Serial.print(" V rms / "); Serial.print(Irms1); Serial.print(" A rms / "); Serial.print(kwhTotal); Serial.print(" KWh / "); Serial.print(freq); Serial.println(" Hz"); Serial.print ("Puerto 2 -> "); Serial.print(PotenciaReal2); Serial.print(" W / "); Serial.print(PotenciaAparente2); Serial.print(" VA / "); Serial.print(FactorPotencia2); Serial.print(" Factor P / "); Serial.print(Vrms2); Serial.print(" V rms / "); Serial.print(Irms2); Serial.print(" A rms / "); Serial.print(kwhTotal); Serial.print(" KWh / "); Serial.print(freq); Serial.println(" Hz"); Serial.print ("Puerto 3 -> "); Serial.print(PotenciaReal3); Serial.print(" W / "); Serial.print(PotenciaAparente3); Serial.print(" VA / "); Serial.print(FactorPotencia3); Serial.print(" Factor P / "); Serial.print(Vrms3); Serial.print(" V rms / "); Serial.print(Irms3); Serial.print(" A rms / "); Serial.print(kwhTotal); Serial.print(" KWh / "); Serial.print(freq); Serial.println(" Hz"); Serial.print ("Puerto 4 -> "); Serial.print(PotenciaReal4); Serial.print(" W / "); Serial.print(PotenciaAparente4); Serial.print(" VA / "); Serial.print(FactorPotencia4); Serial.print(" Factor P / "); Serial.print(Vrms4); Serial.print(" V rms / "); Serial.print(Irms4); Serial.print(" A rms / "); Serial.print(kwhTotal); Serial.print(" KWh / "); Serial.print(freq); Serial.println(" Hz"); Serial.println("************************************************************************"); } void CalcularEnergia() { UlMuestraV=607; UlMuestraI=607; MuestraV=607; MuestraI=607; UlFiltradoV=2.5; UlFiltradoI=2.5; FiltradoV=2.5; FiltradoI=2.5; for (int n=0; n<NumeroDeMuestras; n++) { //Variables utilizadas para eliminar el ofset de tensión // MuestraI0=MuestraI1; // MuestraI1=MuestraI2; // MuestraI2=UlMuestraI; UlMuestraV=MuestraV; UlMuestraI=MuestraI; //Lee las muestras de corriente y voltaje MuestraV = analogRead(PinV); MuestraI = analogRead(PinI); if (MuestraI <=610 and MuestraI >=604) {MuestraI=607; } //Serial.println (MuestraI); // MuestraI3= (MuestraI0+MuestraI1+MuestraI2+UlMuestraI+MuestraI)/5; //Serial.print (MuestraI); //Serial.print (";"); //Serial.println (MuestraI3); // MuestraI=MuestraI3; //Variables utilizadas para eliminar el ofset de tensión UlFiltradoV = FiltradoV; UlFiltradoI = FiltradoI; //Filtro digital de paso alto para eliminar el Ofset de 2,5V en las entradas FiltradoV = 0.996*(UlFiltradoV+MuestraV-UlMuestraV); FiltradoI = 0.996*(UlFiltradoI+MuestraI-UlMuestraI); //Calibración de fase (para compensar el retraso en el conversor AD) calibratedV = UlFiltradoV + CalFase * (FiltradoV - UlFiltradoV); //Calculo del voltaje RMS sqV= calibratedV * calibratedV; //Eleva al cuadrado sumV += sqV; //Añade al acumulador //Calculo de la corriente RMS sqI = FiltradoI * FiltradoI; //Eleva al cuadrado //Serial.println (sqI); sumI += sqI; //Añade al acumulador //Cálculo de la potencia instantanea instP = calibratedV * FiltradoI; //Multiplica tensión por corriente sumP +=instP; //Añade al acumulador //*******************MEDICIÓN DE FRECUENCIA************************** if (n==0) vLastZeroMsec = micros(); if (UlFiltradoV < 0 && FiltradoV >= 0 && n>1) //Chequea el paso por cero { vPeriod = micros() - vLastZeroMsec; //Periodo de la forma de onda del voltaje if (vPeriod > (expPeriod-filterWidth) && vPeriod<(expPeriod+filterWidth)) //Filtra las medidas de periodo erróneas { vPeriodSum += vPeriod; vPeriodCount++; } vLastZeroMsec = micros(); } //******************************************************************** } } void ResetearAcumuladores() { sumV = 0; sumI = 0; sumP = 0; } void CalculoRMSPot() { Vrms = CalV*sqrt(sumV / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio del voltaje elevado al cuadrado (RMS) / Aplica los coeficientes de calibración Vrms= double(int(Vrms*10))/10; //Reduce a uno el número de decimales Irms = CalI*sqrt(sumI / NumeroDeMuestras); //Cálculo de la raiz cuadrada del promedio de la corriente elevada al cuadrado (RMS) / Aplica los coeficientes de calibración Irms= double(int(Irms*20))/20; //Dos decimales, pero la segunda cifra solo puede ser 0 ó 5 //calcula los valores de potencia PotenciaReal = CalV*CalI*sumP / NumeroDeMuestras; PotenciaReal = double(int(PotenciaReal*10))/10; //Reduce el número de decimales a uno PotenciaAparente = double(int((Vrms * Irms -4)*10))/10;//El cuatro es una calibración para compensar el algoritmo de reducción de ruido if (PotenciaAparente<PotenciaReal) {PotenciaAparente=PotenciaReal;} //En alguna ocasión "rara" Ha dado algún valor inferior que da factor de potencia negativo if (PotenciaReal<4) // No se detecta una potencia menor de 4W, para evitar los errores que quedan del filtro pasa bajos {PotenciaReal=0;} if (PotenciaAparente<4)// No se detecta una potenciao menor de 4VA, para evitar los errores que quedan del filtro pasa bajos {PotenciaAparente=0;} FactorPotencia = PotenciaReal / PotenciaAparente; } void CalculoFrecuencia() { //******************Cáculos de frecuencia***************** freq = (1000000.0 * vPeriodCount) / vPeriodSum; //divido por 1000 crc freq= double(int(freq*20))/20; //El segundo decimal es cero o 5 vPeriodSum=0; vPeriodCount=0; }