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).
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.
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:
¿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.
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

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.
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.

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.

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:
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):
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
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)
= 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.
ESTA EXELENTE
ResponderEliminarDISCULPA 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.
ResponderEliminarY LOS CODIGOS EN MATLAB EN QUE VERSION LOS CORRIERON ?? ASI PARA DESCRGARR MATLAB PLORFA
ResponderEliminarsaludos, soy estudiante de electronica, me interesa este proyecto, y me gustaria si tu me puedes pasar el proyecto completo desde ya muchas gacias
ResponderEliminarNo 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.
EliminarTambié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
Hola Antonio, genial tu blog y aportación.
ResponderEliminartengo 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.
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).
ResponderEliminarBasicamente 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
Hola Antonio,
ResponderEliminarEn 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.
Hola Antonio,
ResponderEliminarEmpiezo 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.
Interesante. Con las aportaciones de todos, cada vez se irá mejorando. Saludos. Eduardo Castro.
ResponderEliminarBuenas 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?
ResponderEliminarhe 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, !!?
ResponderEliminarImpresionante articulo.
ResponderEliminarMi pregunta es porque in pic 18? No se podria con un pic16 pequeño?
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
Eliminarinteresante 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
ResponderEliminarbuenas 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
ResponderEliminarHola Antonio tengo una duda que compilador utilizaste para este proyecto?
ResponderEliminarGracias por tu atención, saludos.
Excelente felicitaciones
ResponderEliminar