Translate

viernes, 7 de junio de 2013

Proyecto: Medidor de potencia

En este proyecto vamos a construir un medidor de potencia, un dispositivo que monitoriza la potencia (de la que se puede deducir fácilmente el consumo) de un electrodoméstico o cualquier otro aparato conectado a la red.

Para obtener la potencia consumida tendremos que monitorizar (muestrear) la intensidad y el voltaje de la red. Como siempre que manipulemos la red, debemos ser muy cuidadosos con nuestros montajes. Existen varias formas de monitorizar el voltaje e intensidad de la red. En este montaje he escogido la que me ha parecido más segura: un montaje en el que existe un aislamiento galvánico entre la red y las señales que el micro monitoriza.

Una vez que tenemos las medidas de intensidad y voltaje a lo largo de un ciclo de trabajo (20 milisegundos si nuestra red va a 50 Hz) es muy sencillo para el micro estimar valores como la intensidad RMS, potencia RMS y potencia real consumida y volcar los resultados por el puerto serie o (si queremos un sistema más autónomo) presentarlos en un LCD.

Como en proyectos anteriores, se adjunta un programa MATLAB para visualizar los datos capturados (intensidad, potencia instantánea, etc.) a lo largo de un ciclo, permitiéndonos ver gráficamente aspectos como el desfase entre Intensidad y Voltaje, forma de onda de la intensidad, etc.  En el video adjunto se muestran los resultados para el caso de una pequeña batidora, mientras cambiamos su velocidad, ponemos el "turbo", etc:


Código asociado:  power_meter.c  (código PIC)
                         18f4520_g.lkr    (linker script modificado) 
                         power_meter.m (programa MATLAB de interfaz con el PC).  



---------------------------------------------------------------------------------------------


1. Fundamentos (Voltaje, Intensidad, Potencia)

La potencia consumida por una carga es el producto del potencial (voltaje) entre sus bornes y la intensidad que la atraviesa:

                                               P = I V

Si estamos manejando corriente continua (donde el voltaje se mantiene esencialmente constante) no hay mucha más complicación. Sin embargo, en el caso de cargas conectadas a la red la cosa se complica un poco (aunque no mucho). En la red tenemos un voltaje alterno, esencialmente una sinusoide cuya amplitud V0 y frecuencia (f) dependen del país o zona donde nos encontremos.

En España, por ejemplo, la frecuencia es de 50 Hz y el voltaje de 230 V (rms). Veis que el voltaje tiene un "adjetivo". Esto es consecuencia de tener una corriente alterna. Podríamos pensar que el voltaje declarado corresponde a la amplitud máxima de la sinusoide (sería entonces V0=230), pero esto no es correcto. El voltaje declarado (rms, o root mean square) corresponde al voltaje continuo que haría el mismo trabajo que el voltaje alterno usado.

Como el voltaje alterno sube y baja, pasará por el cero de tanto en tanto (100 veces por segundo) y en esas zonas no hará demasiado trabajo. Si de media tiene que comportarse como un voltaje de 230V está claro que durante su máximo tendrá que compensar el poco esfuerzo que hizo cuando estaba cerca del cero. Eso quiere decir que el  máximo (amplitud V0) del voltaje debe ser mayor que el Vrms declarado. ¿Cuánto mayor? El voltaje y la intensidad  RMS están definidos como:                                 

Notad que ahora V(t) e I(t) dependen del tiempo, al contrario que en la primera ecuación. Los valores rms (al cuadrado) son esencialmente una media del cuadrado de la señal durante un periodo. De ahí su nombre: son la raíz (Root) de la media (Mean) del cuadrado (Square) de la señal.

Consideremos en primer lugar la relación entre V0 (amplitud) y Vrms:
En España (Vrms=230) tendremos un voltaje con una amplitud máxima (V0) de 325V.

Una vez que conocemos las especificaciones de nuestro voltaje, imaginemos una carga puramente resistiva (R) enchufada a la red (una bombilla incandescente podría ser una buena aproximación). En cada momento la intensidad que atraviesa la bombilla será I(t) = V(t)/R.


La potencia instantánea consumida será por lo tanto: 




Notad que en este caso la potencia instantánea siempre es positiva (una carga resistiva está consumiendo energía durante todo el ciclo). Veremos que esto no es siempre así.

¿Cuál es la potencia media durante un ciclo? Basta promediar P(t) sobre un periodo:

A la cantidad anterior se le denomina potencia media o potencia real consumida. La potencia media se relaciona directamente con la energía consumida (si no hubiésemos dividido por T en la fórmula anterior habríamos obtenido la energía consumida en el periodo). Para obtener energía bastaría volver a multiplicar <P> por el periodo T.

Se observa que la energía consumida en esta situación es la mitad de la que se consumiría si hubiésemos aplicado un voltaje V0 constante. De ahí que el voltaje alterno equivalente (rms) tuviera ese factor de raíz de 2 respecto a V0.

Si calculamos Vrms e Irms para el caso anterior (carga resistiva) obtendríamos:

Al producto de estas dos cantidades se le denomina P_rms:


y en este caso verificamos que coincide con la potencia real consumida.

Cuando usamos un polímetro para medir el voltaje de una corriente alterna nos da V(rms). Si medimos la intensidad obtenemos I(rms). Multiplicando ambos obtenemos P(rms). El problema es que P(rms) sólo coincide con la potencia real <P> para una carga resistiva como hemos visto en el ejemplo anterior.

Imaginad por ejemplo una carga con un componente inductivo o capacitivo que cambia la fase de la intensidad respecto al voltaje. En este caso


donde phi es el desfase entre I y V. ¿Cuál es la potencia real consumida  en este caso?


En este caso la potencia real es menor que la potencia RMS por un factor (cos(phi)) que se denomina factor de potencia. Lo que está pasando es que durante una parte del ciclo la carga está devolviendo energía a la red, por lo que su consumo total es menor que el de una carga equivalente resistiva.

Otra razón por la que <P> puede ser diferente de Prms es cuando tengamos un comportamiento no lineal de la carga, lo que hará que la intensidad se aleje de la forma sinusoidal. 

Con nuestro dispositivo medidor de potencia queremos conocer la potencia real consumida. Lo que tenemos que hacer es monitorizar (muestrear) el voltaje y la intensidad en la red. El muestreo tiene que ser lo suficientemente "fino" para poder ver que está pasando dentro de un ciclo. Una vez que tengamos dichas muestras podremos aproximar las integrales anteriores para calcular la potencia media, Irms, factor de potencia, etc.


2. Hardware, sensores, montaje:

Como hemos comentado necesitamos monitorizar la intensidad y voltaje de la línea. Veamos como hacerlo de la forma más segura y menos "invasiva" posible.


Medición de la Intensidad:

Para medir la intensidad se ha usado el sensor mostrado en la figura adjunta. (adquirido en http://www.seeedstudio.com/depot/grove-electricity sensor-p-777.html?cPath=25_28 por unos $5). 

           



Un hilo (no los dos, eso cancelaría la medida) de la red se pasa por el eje de la bobina (roja). Una corriente alterna que circule por el hilo provocará una corriente proporcional en los bornes de la bobina. Mirando las especificaciones vemos que la corriente inducida resulta ser 1/2000 veces la intensidad original. La corriente inducida se hace pasar por una resistencia de 800 ohmios (ver diagrama adjunto), provocando un voltaje:

    V = 800 x (I / 2000)  = I / 2.5

En el pantallazo adjunto se muestra una captura en el osciloscopio del voltaje observado en la bobina con una bombilla incandescente de 60W. Vemos que es básicamente una sinusoide pura con un Vrms de unos 104 mV. Eso equivale a una Irms (según la fórmula anterior) de unos 260 mA. Como la carga es resistiva tenemos que Prms = P = Vrms x Irms = 230 x 0.26 = 59.8 W, lo que concuerda muy bien con lo esperado. 



Si bien el voltaje obtenido es proporcional a la intensidad que circula por el hilo, nada nos garantiza que esté en fase con dicha intensidad. Las especificaciones hablan de un desfase de unos 5º. Como veremos no tendremos que fiarnos de las especificaciones, ya que un proceso de calibración en software tendrá en cuenta ese factor.

¿Cuál es la intensidad máxima que podemos medir? La especificaciones indican un máximo de unos 5/6 A. Con las especificaciones dadas esos 5 amperios provocarán un voltaje de +/- 2V, lo que corresponde a un rango de unos 4-5 V, perfecto para ser monitorizado con el ADC de un micro trabajando a 5V.

En principio la intensidad podría ser mayor, lo único que el voltaje a monitorizar podría exceder el rango de 5V y tendríamos que usar un divisor de tensión o similar en la interfaz con el PIC. No se si la recomendación de no pasar de 5A viene de ahí o si tendríamos algún otro problema. Incluso si nos vamos a 20A la intensidad inducida no pasaría de 10 mA que no creo que vaya a quemar la bobina. Podría ser que pasados los 5A se pierda la linearidad entre corriente de entrada y voltaje de salida.

En cualquier caso no hecho pruebas para verificar los límites del sensor. En mi caso con un voltaje Vrms de 230V puedo monitorizar cargas poco más de 1000W   (230V x 5A).

Nota: si en alguna otra aplicación estáis interesados en aumentar la sensibilidad de la medida (porque las intensidades a medir son pequeñas) siempre podéis pasar varias vueltas del hilo cuya corriente queremos medir por la bobina. De esta forma multiplicando la intensidad inducida y por lo tanto el voltaje a detectar.


Medición de Voltaje:

La medición de la intensidad anterior tiene la gran ventaja que aísla galvanicamente el circuito del PIC (que solo está conectado a la bobina) de la red. Esto lo hace muy seguro.

Para la medición del voltaje me gustaría algo similar. Me he decidido por una solución de compromiso que me permite contar también con aislamiento galvánico en la medida del voltaje. Eso me supondrá tener que hacer ciertas asunciones, pero como veremos en los resultados no parecen funcionar mal.

La idea es conectar simplemente un pequeño trafo o transformador a la red y monitorizar el voltaje en el secundario (ver diagrama adjunto). El trafo usado lo saqué de una vieja radio. Tiene la ventaja de que el voltaje en el secundario es de unos 1.5 V (rms). Como sabemos ahora eso corresponde a un voltaje máximo de unos +/- 2 V, de nuevo un rango ideal para monitorizarlo con el micro sin tener que añadir un divisor de tensión.

El problema obvio es que lo que estaremos viendo no es el voltaje de la red (primario) sino el del bobinado secundario, con todas las distorsiones y desfases introducidas por el trafo. En el pantallazo adjunto vemos el voltaje del trafo al que tendríamos acceso desde el PIC. El rango (+/- 2V) es ideal, pero la forma no es la sinusoide esperada.

La idea es que al fin y al cabo el voltaje en la red lo conozco muy bien: una sinusoide de amplitud 325V (para Vrms=230) y periodo de 20 MSEC (50 Hz). Lo único que me hace falta es conocer su fase, esto es, saber cuándo arranca un nuevo ciclo de voltaje. Y podemos obtener dicha información a partir de nuestro trafo. De nuevo, la salida del trafo no estará en fase con el verdadero voltaje. El pantallazo anterior fue obtenido con el trigger en modo RED: vemos como la salida del trafo se adelanta al voltaje un poquito (del orden de un milisegundo aproximadamente). Tendremos que hacer algún proceso de calibración previa para determinar dicho valor. Veremos los detalles en la parte del software.


Diagrama completo:

El esquema adjunto muestra como conectaríamos la salida de ambos sensores con el micro:



Hemos unido ambos sensores en un punto común para tener una referencia común. No hay problema en hacerlo al estar ambos sensores "en el aire". Como los voltajes a medir son alternos, para poder medirlos con el ADC del PIC (0-5V) ato el punto común a un divisor de voltaje con dos resistencias iguales lo que lo pone a 2.5V. A partir de ahí los otros dos hilos de los sensores van a los canales ADC 0 y 1 del PIC (RA0 y RA1). También conecto al canal RA2 el punto común para medirlo también (por si no fuese exactamente 2.5V) y descontarlo de las medidas de RA0 y RA1.

Como los voltajes de ambos sensores oscilaban en +/-2V, sumados a los 2.5V de referencia darán valores cubriendo bastante bien el rango del ADC.

Nota: el esquema anterior es lo mejor que se me ha ocurrido con mis escasos, tendiendo a  nulos, conocimientos de electrónica. Supongo que hay formas mucho más acertadas, eligiendo R para ajustar impedancias, metiendo algún condensador por ahí, etc. Si a alguien  se le ocurre un montaje mejor (no será difícil) o simplemente puede decirme por qué el anterior es una malísima idea, se agradecerán todas las sugerencia.



3. Software:

El programa es muy sencillo. Programaremos un timer (TMR0) para que tome muestras de un periodo de la señal (o de varios periodos para reducir ruido) a intervalos predefinidos. Dentro de la interrupción del timer se harán sendas capturas del ADC correspondientes al voltaje e intensidad monitorizados. Los valores leídos del ADC se guardarán en sendos arrays. Cuando se complete la fase de toma de datos  (1 o varios ciclos) se parará el TMR0 (interrumpiendo la toma de datos) y se pondrá a 1 una bandera. Una vez que detectemos dicha bandera procesaremos los datos desde el programa principal, calcularemos los resultados y los enviaremos por el puerto serie (si se desea un medidor más portatil podrían aparecer en un LCD).

Tras terminar de procesar los datos reactivaremos el timer TMR0 y volverá a empezar un nuevo ciclo de toma de datos. Dependiendo del número de periodos que se usen para tomar datos podremos refrescar los resultados con mayor o menor frecuencia. Con 32 periodos para la medida (32/50 = 0.65 seg), más el tiempo empleado en procesar los datos, actualizaremos los datos cada segundo, aproximadamente.


Ajustar timing del sampling:

La variable SAMPLES_PERIOD determina el número de muestras que se toman en un ciclo de la red. He elegido tomar 128 muestras por periodo. Si solo queremos presentar datos de consumo podría valer con menos, pero como también quiero presentar algunas gráficas he sobremuestreado los datos.

Para mi frecuencia de 50 Hz un periodo corresponde a 20 msec = 20000 usec. La separación entre muestras será de 20000/128 = 156.25 microsec. En este proyecto estoy usando un cristal de 8 MHz con un multiplicador PLLx4, lo que equivale a una Fosc = 32 MHz. Eso quiere decir que un ciclo de instrucción se ejecuta en 1/8 usec, por lo que la separación entre muestras corresponde a 156.25  x 8 = 1250 ciclos. La fórmula a usar sería:

   delay (ciclos de instrucción)  =  (F_osc/4)  / (F_red x SAMPLES_PERIOD ) 

Si programamos el TMR0 sin pre-scaler y en modo 16 bits, debemos programar el contador TMR0 (cada vez que entremos en la interrupción de reloj) para que vuelva a rebosar en 1250 "clocks" (TMR0 = 65536 – delay)

En realidad el valor a usar será un poco mayor, ya que para cuando llegamos a la ISR (y fijamos el siguiente tiempo de re-entrada) ya ha pasado un tiempo (mientras guardamos datos imprescindibles, saltamos a la rutina de interrupción, etc). Normalmente eso tarda unos pocos microsegundos, por lo que deberíamos aumentar la estimación anterior por dicho tiempo (en ciclos).

En la tabla adjunta se dan varios valores de partida para varias frecuencias de red y del micro usado (todas ellas para 128 puntos de muestreo en el ciclo) 

Fosc
4
8
10
16
20
32
50 Hz
65410
65255
65175
64940
64785
64315
60 Hz
65435
65305
65240
65245
64915
64525


Si solo vamos a muestrear 1 periodo la elección del delay no es demasiado crítica. Sin embargo, para reducir ruido, voy a promediar los valores tomados en una serie de periodos. En particular por defecto usaré N_PERIOD = 32 periodos. En ese caso si que es más critico determinar exactamente el delay para que 128 muestras correspondan exactamente a un periodo. En caso contrario los ciclos se irían "corriendo" respecto a nuestras muestra y terminaría promediando valores que se han tomado en diferentes instantes del ciclo.

Para ver cual es exactamente el valor más conveniente a usar como delay voy a generar (usando el timer TMR0) una onda cuadrada, intentando que esté sincronizada con la red. Para verificarlo iré probando varios delay (en la vecindad de TMR0=64300) y visualizaré la señal obtenida en el osciloscopio (usando la RED como trigger). Cuando vea a señal lo más estacionaria posible será la señal de que el valor que estamos usando es el más ajustado. En el corto video adjunto se muestra el proceso, donde se observa que el mejor valor es TMR0=64315. Valores inmediatamente inferiores o superiores causan una lenta deriva en la señal (hacia uno u otro lado según que el timer se adelante o retrase respecto a la red.)



Una vez establecido el timing ideal para el TMR0 este es el código de la interrupción, donde todo el proceso de toma (ADC) y almacenamiento de los datos tiene lugar:

#define set_TMR0(x) {TMR0H=(x>>8); TMR0L=(x&0x00FF);}
#define start_TMR0 T0CONbits.TMR0ON=1;
#define stop_TMR0 T0CONbits.TMR0ON=0;

#define select_ADC(ch) { ADCON0 &= 0b11000011;  ADCON0 += (ch<<2); }

uint16 delay=64315;

// High priority interruption
#pragma interrupt high_ISR
void high_ISR (void)
{
 int16 res,ref;
 if (TMR0_flag) //ISR de la interrupcion de TMR0
  {
   set_TMR0(delay);

  // Reference voltage
  select_ADC(2); ADCON0bits.GO=1; while (ADCON0bits.GO); 
  ref=ADRESH; ref<<=8; ref+=ADRESL;

  // Voltage (chan 0)
  select_ADC(0); ADCON0bits.GO=1; while (ADCON0bits.GO); 
  res=ADRESH; res<<=8; res+=ADRESL; res-=ref; V[sample]+=res;
   
  // Intensity (chan 1) 
  select_ADC(1); ADCON0bits.GO=1; while (ADCON0bits.GO); 
  res=ADRESH; res<<=8; res+=ADRESL; res-=ref; I[sample]+=res;
   
  sample++;

   if (sample==SAMPLES_PERIOD) // PERIOD SAMPLED
    {
     sample=0;
     period++; if (period==N_PERIOD) { data_ready=1; stop_TMR0; }  // End of ADQ process
     LATBbits.LATB0=~LATBbits.LATB0;
    }
  
   TMR0_flag=0;           // clear flag
  }
}

Nada más entrar en la interrupción reprogramamos el contador para volver a entrar en el siguiente instante de muestreo.

A continuación se muestrean los tres canales: primero el voltaje de referencia y luego el voltaje del trafo y de la bobina del sensor de intensidad. El voltaje de referencia se resta a las dos mediadas y los resultados se acumulan en los arrays V e I. Dichos arrays son de enteros con signo (+/- 32768). Como cada medida está en el rango +/- 512 podría acumular hasta 64 medidas sin causar un rollover. Ese será el número máximo de periodos a promediar. En el código uso N_PERIOD=32 periodos por defecto.

Tras acumular los valores medidos se incrementa una variable global que indica en que muestra dentro del periodo estamos. Si llegamos a SAMPLES_PERIOD (128) se resetea a 0 y seguimos acumulando datos durante otro periodo. Si el indicador de periodo (period) llega a N_PERIOD (32) se detiene el timer y se pone a 1 la bandera para pasar a procesar los datos adquiridos. 

Los arrays donde se acumulan los datos son de tipo int16 y de tamaño 128 cada uno. Ocupan 128x2x2=512 bytes, por lo que será necesario reservar un par de bancos de la memoria del PIC para guardarlos. Hemos de usar un script del linker modificado como se explicó en entradas anteriores (el script .lkr modificado se enlaza al principio de esta entrada). En nuestro programa crearemos dos punteros I y V apuntando a esa zona:  

#pragma udata big  // Memory needed for two int16 arrays (128 elements)
byte tmp[512];     // located in specific section
#pragma udata
int16* V=(int16*)&tmp[0];   // Pointer to first array (V measurements)
int16* I=(int16*)&tmp[256]; // Pointer to second array (I measurements)



En el programa principal es sólo cuestión de configurar el ADC, timer TMR0 y la UART para eventualmente enviar datos:


OpenADC(ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_4_TAD,
         ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS, 7);

TMR0L=0; OpenTimer0(T0_16BIT&T0_SOURCE_INT&T0_PS_1_1);
stop_TMR0;
enable_global_ints; enable_TMR0_int;

// USART @ 19200 (32MHz)
 OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF &USART_ASYNCH_MODE
          & USART_EIGHT_BIT & USART_BRGH_HIGH,103); 

 printf("--  Power Meter  ------"); salto_linea;
 
 start_TMR0;

Luego, entramos en un bucle sin fin:

 while(1)
  {
   reset_datos();         // Resets arrays, initializes samples=period=0, restarts TMR0; 
   while(data_ready==0);  // Wait until data is ready (while capturing data)
   procesar_resultados(); // Process captured data
   Delay10KTCYx(216);     // Delay
 }

La función reset_data inicializa los contadores sample y period, pone a cero los arrays donde se acumularan los datos y pone en marcha el timer TMR0, provocando el inicio de un proceso de captura. El bucle se bloquea hasta terminar la captura (data_ready=1) y entonces se procesan los resultados. Opcionalmente se puede añadir un delay entre captura y captura.

void reset_datos(void)
{
 uint8 k;
 k=0; while(k<SAMPLES_PERIOD) { V[k]=0; I[k]=0; k++;}
 period=0; data_ready=0; sample=0; start_TMR0;
}


Modo Calibración  (determinación del desfase para una carga resistiva)

Dentro del procesado podemos hacer dos cosas (dependiendo de unos #define). Si tenemos:

#define CALIB

el programa lleva a cabo una calibración previa (necesaria para compensar los desfases que tanto el voltaje del trafo como el de la bobina que monitoriza la intensidad pueden tener un desfase con respecto al verdadero voltaje o intensidad).

La idea es conectar una carga resistiva (bombilla incandescente) y detectar el paso por cero de la señal procedente del trafo (esencialmente asociada con el voltaje V) y de la señal de la bobina del sensor de intensidad. Si no existiesen los desfases de los que hemos hablado ambos pasos por cero deberían coincidir ya que con una carga resistiva Intensidad y Voltaje deberían ir en fase. En nuestro caso no coincidirán. Dicho desfase lo anotaremos y lo codificaremos en la parte del programa que lleve a cabo las mediciones (cuando ya no estemos seguros de si la carga monitorizada es o no resistiva).

Veamos el código usado para detectar los pasos por cero:

// Detects zero crossing (index + fraction)
float zero_crossing(int16* data)
{
 uint8 k,kk;
 float frac;
     
 for(k=0;k<SAMPLES_PERIOD;k++)
  {
   kk=k+1; kk&=(SAMPLES_PERIOD-1);
   if ( (data[k]<=0) && (data[kk]>0) ) break// ZC in [k,k+1]
  }

 frac = (data[kk]-data[k]); if (frac==0) frac=0.5; else frac = -data[k]/frac; //Fraction within interval
 frac+=k;
 return frac;
}


Simplemente se recorre el array data[] buscando un cambio de signo (de - a +) entre los índices k y k+1. Dentro del intervalo se hace una interpolación lineal para determinar la fracción dentro del intervalo donde ocurriría el corte con el cero (que no vemos debido al carácter discreto de los datos).

Dentro de la función procesa_datos llamamos 2 veces a zero_crossing, una para los datos de V y otra para los de I, calculamos la diferencia y volcamos los resultados por el puerto serie.

#ifdef CALIB  // Calibration of initial phase shift

  zc1=zero_crossing(V);
  zc2=zero_crossing(I);

  dt = zc2-zc1; if (dt<0) dt+=SAMPLES_PERIOD;

  k=float2str(txt,zc1,2); txt[k++]=32;
  k+=float2str(txt+k,zc2,2); txt[k++]=32;
  k+=float2str(txt+k,dt,2);
  send_txt(txt); send_CR; send_LF;

  return;
#endif


A la izquierda podemos ver un típico volcado de resultados. Los cruces por el cero no se mantienen de una mediada a otra (el timer TMR0 está parado entre una serie de medidas y la siguiente y no hay ninguna garantía de que el siguiente ciclo se empiece a muestrear en el mismo punto que el anterior). Sin embargo la diferencia entre ambas medidas si que es consistente, ya que ambas señales están sincronizadas entre ellas (a través de la red).

Vemos que la intensidad va retrasada 5.33 (de media) muestras con respecto al voltaje. Y no debería ser así. Lo que haremos cuando estemos en modo medida es correr las medidas de la intensidad 5.33 muestras hacia atrás, antes de empezar a operar con ellas de cara a medir potencia, etc.

Este desfase es del orden de 1msec y proviene en su mayor parte del desfase introducido por el trafo, aunque también el sensor de intensidad descoloca las cosas un poco.


Modo Medición de potencia.

Una vez que hemos averiguado el desfase entre I e V cuando no debería haber ninguno podemos usarlo para corregir la fase de medida.

En esta fase se vuelve a medir el paso por cero del voltaje del trafo. Al valor encontrado le sumo el desfase antes encontrado (p.e. 5.33). En ese punto debería encontrarse el cero de intensidad (y por lo tanto de voltaje) si tuviésemos una carga resistiva pura:


// Measurements
 zc1=zero_crossing(V);  // zero crossing for V
 zc1+=5.33;             // Add phase found during calibration
 if (zc1>=SAMPLES_PERIOD) zc1-=SAMPLES_PERIOD;  // Correct for rollover
 ZC = (uint8)zc1;  frac = zc1-ZC;  // Extracts index and fraction 


En ZC guardo la parte entera del desfase detectado y en frac la fracción. 

A continuación desplazo (ZC muestras) e interpolo (por la fracción frac) los datos de intensidad, para colocarlos con referencia al arranque del ciclo de voltaje. Para hacer dicho reordenamiento uso el array V[], cuyos valores ya no necesito para nada (solo lo uso como base de tiempos):


 // Array V is not needed anymore, will use it to store array I shifted (by ZC)
 for(k=0,kk=ZC;k<SAMPLES_PERIOD;k++,kk++)  // Shift data ZC indexes
  {
   kk &= (SAMPLES_PERIOD-1);
   V[k]=I[kk];
  }

 for(k=0,kk=1;k<SAMPLES_PERIOD;k++,kk++) // Interpolate I between k,k+1 using frac
  {
   kk &= (SAMPLES_PERIOD-1);
   ii = (V[kk]-V[k]); ii*=frac; ii+=V[k];
   ii*=0.3815;  // INtensity in mA
   I[k]=(int16)ii;
  }



El factor 0.3815 relaciona el valor en el array donde hemos acumulado las medidas y la intensidad en la red (mA). Sabemos que:

V_bob = I /2.5  , I = intensidad deseada (en A) , luego I(ma) = 2500 V_bob

V_bob está relacionada con el resultado del ADC de la forma siguiente:

    V_bob = (ADC/1024)*5V

Finalmente, el valor contenido en el array I[k] es en realidad la suma de N_period (32) valores del ADC, por lo que:

I = 2500 V_bob = 2500 (ADC/1024)*5  = 2500 ((I[k]/32)/1024)*5  
  = 0.3815 I[k] (en mA)

Si usais otro voltaje (5V) en el micro, un número diferente de niveles (1024) o un número distinto de periodos (32) tendréis que cambiar esta constante. Por supuesto si usáis un sensor de intensidad distinto, la relación inicial (I = 2500 Vbob) también habrá que cambiarla.

Una vez que tenemos la intensidad medida alineada con el voltaje es sólo cuestión de hacer unas pocas cuentas para calcular Irms, <P> real, Prms y factor de potencia.
Las integrales que aparecían antes se aproximan por sumatorios. La potencia queda p.e.:
 
Los valores del voltaje ideal (para Vrms = 230) han sido precalculados (en 128 muestras/ciclo) y guardados previamente en un array en memoria de programa:

// Computes <P> and Irms using measured intensity + ideal voltage
 Preal=0;  Irms=0;
 for(k=0;k<SAMPLES_PERIOD;k++)
  {
   vv = Vteo[k];           // Ideal voltage (230 rms) sampled @ 128 samples/cycle
   ii = (float)I[k]/1000;  // I measurement (Amps)
   Irms  += (ii*ii);       // Computes Irms
   Preal += (ii*vv);       // Computes <P>
  }
 Irms=sqrt(Irms/SAMPLES_PERIOD);
 Preal=Preal/SAMPLES_PERIOD;
 Vrms=230;
 Prms = Vrms*Irms; 
 factor = Preal/Prms;


Sólo queda volcar los datos por el puerto serie. Si se define la opción TEXTO los resultados (Irms, Prms, Preal y factor de potencia) se vuelcan en formato texto usando las rutinas que vimos para formatear números reales:

#define TEXTO

#ifdef TEXTO   // Dumps results as text through the serial port 
 float2str(txt,Irms,3); send_txt(txt); send_byte(32);
 float2str(txt,Prms,1); send_txt(txt); send_byte(32);
 float2str(txt,Preal,1); send_txt(txt); send_byte(32);
 float2str(txt,factor+5e-4,3);  send_txt(txt);
 send_CR; send_LF;
#else          // Dumps binary data (I) to be used by a MATLAB program (power_meter.m)
  ptr=(uint8*)I; // Pointer to array
  send_byte(65); // 'A'
  for(temp=0;temp<256;temp++) send_byte(ptr[temp]);
  send_byte(msg_cont++);
  send_byte(90); // 'Z'
#endif

El resultado, cuando tenemos conectada la bombilla que usamos de calibración es:

+0.254 +58.6 +58.6 +1.000

obteniendo una intensidad rms de 0.25A y una potencia medida de unos 59W. El factor de potencia sale justo 1, pero eso se esperaba puesto que es la bombilla usada en la calibración. Sin embargo los 59W que vemos nos indica que el factor (0.3815) deducido para la relación entre la medida tomada del sensor y la verdadera intensidad es correcto.


Programa MATLAB (gráficos):

Si la opción TEXTO no está definida lo que hace el programa es mandar (en su formato nativo) los 128 datos de intensidad (en mA) por el puerto serie. Dichos datos estaban alineados con un ciclo del voltaje. El programa adjunto (power_meter.m) de MATLAB recibe los datos, y hace unas gráficas con el voltaje ideal (Vrms=230V), la intensidad medida y la potencia instantánea en un ciclo. Como tiene todos los datos puede también presentar los resultados de Irms, Prms, factor de potencia, etc.

En las siguientes gráficas se adjuntan algunos ejemplos: arriba una lampara incandescente etiquetada como 60W. La medida de potencia son 59W y la intensidad sigue fielmente la forma del voltaje, por lo que el factor de potencia=1.00

Abajo una lámpara de tipo CFL etiquetada como 20W. se observa una gran diferencia entre la Preal y la Prms, debido a que la forma de onda de la intensidad dista mucho de una sinusoide. En este caso la potencia declarada (20W) parece estar entre medias de la potencia rms (25W) y la potencia real (15W).




En las siguientes gráficas se muestra el consumo de una fuente de alimentación de un portatil sin (arriba) y con (abajo) el PC conectado:





Se observa que aunque la potencia RMS es de unos 5W en ausencia de carga, la potencia real es mucho menor. Con carga se consumen unos 70W. La fuente de alimentación está etiquetada como de 65W. De nuevo, debido a la fuerte no linealidad de la intensidad con respecto al voltaje el factor de potencia se aleja mucho de la unidad.

18 comentarios:

  1. DISCULPA POR CASUALIDAD ME PODEIS MANDAR LOS CIRCUITOS PORFA ESQ ESTOY CONTRA EL TIEMPO Y DEBO ENTREGAR UNA TAREA ASI COMO ESTO PERO PARA EL MARTES DE LA PROXIMA SEMANA Y ESTOY CON OTRAS MATERIAS DE LA U Q NO ME DEJAN POR EL MOMENTO TRABAJAR DE LLENO EN ESA TAREA.. GRACIAS POR LA COMPRENSION Y AYUDA.

    ResponderEliminar
  2. Y LOS CODIGOS EN MATLAB EN QUE VERSION LOS CORRIERON ?? ASI PARA DESCRGARR MATLAB PLORFA

    ResponderEliminar
  3. saludos, soy estudiante de electronica, me interesa este proyecto, y me gustaria si tu me puedes pasar el proyecto completo desde ya muchas gacias

    ResponderEliminar
    Respuestas
    1. No entiendo muy bien a lo que te refieres por el proyecto completo. El código usado en este proyecto (power_meter.c) lo tienes enlazado al principio del artículo, junto con el fichero lkr modificado para poder usar los buffers de medida con un tamaño de 256 bytes cada uno.
      También usamos un par de ficheros .h con definiciones que puedes encontrar también por estas páginas. Te dejo los enlaces:
      http://artico.lma.fi.upm.es/numerico/antonio/blog/tipos.h
      http://artico.lma.fi.upm.es/numerico/antonio/blog/int_defs_C18.h

      Un saludo, Antonio

      Eliminar
  4. Hola Antonio, genial tu blog y aportación.

    tengo una pregunta que te agradecería me orientases, necesito aumentar el banco de memoria del PIC a través del .lkr como tu has hecho en este proyecto, pero en mi caso el microcontrolador es el PIC 18F4550, como he de actuar para hacer las modificaciones oportunas?, o conoces alguna web donde pueda encontrar información?.

    Gracias tu tiempo.

    ResponderEliminar
  5. En la entrada del levitador magnético (http://picfernalia.blogspot.com.es/2013/02/proyecto-levitador-magnetico.html) lo explico con algo más de detalle (apartado de Interrupción TX del puerto serie).

    Basicamente se trata de hacer una copia del fichero lkr (en tu caso el 18f4550_g.lkr) en el directorio de tu proyecto y modificarlo ligeramente (declarando uno o varios bancos de 256 bytes como PROTECTED y asignando un nombre a esa sección de memoria). En el programa C, usando #pragma udata puedes inicializar un array en esa sección. A partir de ahí, en el resto del programa puedes usar esa variable normalmente.

    No olvidar indicar en tu proyecto que se use el nuevo fichero (sección Linker Scripts) para evitar que use la copia original (que no deberías modificar).

    Antonio

    ResponderEliminar
  6. Hola Antonio,

    En primer lugar agradecerte la ayuda, me ha sido de muchísima utilidad, tanto para mi proyecto actual como para futuros.

    Tengo otra duda que como siempre agradecería si me pudieses ayudar, es una situación que querría compartir contigo para saber si alguna vez te ha ocurrido igual. En resumen, estoy trabajando con MPLAB C18 PICF4550 y me esta ocurriendo lo siguiente, normalmente cuando voy a montar un gran programa, voy haciendo pequeños bloques, sencillos de comprobar, y finalmente los uno, generando el programa completo, pues bien, mi problema esta en que cuando uso una llamada a una función, pasándole el parámetro por valor, no tengo ningún problema si lo hago sobre uno de estos programitas ya mencionados, pero al incluir ese trozo de codigo en el programa final (junto con otras muchas llamadas de función), es como si ahora esa función no recibiera el parámetro por valor correctamente, recibiendo otra info totalmente diferente.

    Espero haberme explicado lo suficiente.

    Un saludo y gracias.

    ResponderEliminar
  7. Hola Antonio,
    Empiezo felicitandote y agradeciéndote por la clara y detallada exposición de tu proyecto. Leyéndolo he recordado las nociones básicas de electricidad que aprendí en la universidad.
    El caso es que llevo tiempo bichera do por la red buscando sistemas para medir la potencia real instantánea que se consume una vivienda. Algo parecido a lo que miden los contadores nuevos.
    He contemplado dos opciones: productos que ya se comercialicen y que realicen esta función (ya he encontrado alguno interesante en efimarket) o proyectos que lo fabriquen.

    En esta última opción es donde apareció tu blog. El caso es que lo he releído varias veces y creo que se ajusta perfectamente a lo que busco sí fuese capaz de combinarlo con una raspberry pi o un arduino para instalarlo en el cuadro de la luz de mi casa.

    Por casualidad has contemplado tú esa opción? Crees que sería factible?

    Aprovecho para hacerte más preguntas técnicas sobre tu proyecto anticipandote mi ignorancia en electrónica:
    - el sensor que muestras sería suficiente para medir la potencia de una casa?
    - qué transformador sugieres. Tienes algún modelo de ejemplo como el mostrado para el sensor?

    Un saludo gracias de nuevo.

    ResponderEliminar
  8. Interesante. Con las aportaciones de todos, cada vez se irá mejorando. Saludos. Eduardo Castro.

    ResponderEliminar
  9. Buenas que tal, tengo un 16F1939, en el que solo el timer1 es de 16 bits, como hago en este caso? requiero cristal externo? En el ejemplo usas el pre-scaler de 256, lo haces para tener mayor resolución en la variable delay? En mi caso quiero efectuar 200 muestras para 20.000 us tendría 100us x muestra. Y por último, como haría en mi caso para sincronizar los 50Hz con la frecuencia de interrupción del timer que use? hice un simple ejemplo con un output_toggle en ccs, pero tengo entendido que en el código del medidor tendré mas instrucciones dentro de la interrupción a atender. Como podría tomar certeza de que he logrado el sincronismo si luego voy a agregar/quitar instrucciones?

    ResponderEliminar
  10. he estado viendo informacion, y segun veo lo que cobran las empresas de suministro electrico es al final la potencia aparente, por ello al parecer el calculo del F.P. swe puede obviar en el diseño de un medidor, !!?

    ResponderEliminar
  11. Impresionante articulo.
    Mi pregunta es porque in pic 18? No se podria con un pic16 pequeño?

    ResponderEliminar
    Respuestas
    1. hice pruebas con un pic16f88 y va bien, el problema es que solo tiene 368bytes de ram con lo cual no te alcanza para los arrays y solo pude tomar 40 muestras, creo que el pic adecuado seria el 18F2550 que tiene 2k

      Eliminar
  12. interesante proyecto, tengo una duda respecto a los codigos,si yo quisiera implementarlos en un chip . ese codigo en que compilador lo ingresas? y como enlazas el scrip modificado y los demas codigos , disculpa la ignorancia soy nuevo en esto de micro controladores. gracias

    ResponderEliminar
  13. buenas noches no me aparecen los codigos no se llega a cargar nunc ala pagina no se si es porque ya no existe me los podrias pasar a mi correo jevelasquez45@hotmail.com o volverlos a subir y facilitarme el link

    ResponderEliminar
  14. Hola Antonio tengo una duda que compilador utilizaste para este proyecto?
    Gracias por tu atención, saludos.

    ResponderEliminar
  15. Excelente felicitaciones

    ResponderEliminar