Medidor de Potencia AC sin Amplificador Operacional

Esta es una versión simplificada del medidor de Potencia AC que he presentado anteriormente, esta versión no posee la etapa de amplificadores operacionales para realizar el rectificado de onda completa de precisión, lo cual reduce al máximo la calibración y complejidad del mismo, también eleva un poco el nivel de error ya que posee menos instancias de acondicionado de señal.

Esta es la versión con acondicionado de señal:
http://electgpl.blogspot.com.ar/2016/06/medidor-de-potencia-truerms-para-220vac.html

El método que utilizaremos para nuestro medidor sera basado en montar la señal alterna a medir en una continua a 2.5V (el punto medio de representación del ADC del MCU con 5V).
Para ello agregaremos el offset mediante un divisor resistivo y un atenuador de tensión para ingresar la alterna con offset al ADC con una amplitud reducida, ya que no podemos ingresar con los 220Vac.
Esto sera para el caso de la medición de tensión, ya que en corriente utilizaremos un circuito integrado dedicado ACS712 (en mi caso de 5A, pero los hay en 20 y 30A).

Esta es la nota donde hablamos de medición de señales simétricas:
https://electgpl.blogspot.com.ar/2016/06/medicion-de-senales-simetricas-con-adc.html

En esta otra nota vamos a encontrar diversas maneras de sensar corriente, una de ellas sera la del ACS712 que podremos leer para expandir y acompañarla del vídeo de youtube como en todas las notas:
http://electgpl.blogspot.com.ar/2017/01/sensado-de-corriente-alterna.html

El circuito empleado sera el siguiente:


El modulo ACS712 esta representado dentro del cuadro de linea punteada, en la realidad podremos comprar el modulo ya armado y solo hay que alimentarlo con 5V y conectar su salida directa a la entrada del ADC del microcontrolador.

El programa utilizado para este proyecto:


  1. #include <16F883.h>
  2. #device adc=10
  3. #use delay(int=4000000)
  4. #define LCD_ENABLE_PIN  PIN_B2
  5. #define LCD_RS_PIN      PIN_B0
  6. #define LCD_RW_PIN      PIN_B1
  7. #define LCD_DATA4       PIN_B4
  8. #define LCD_DATA5       PIN_B5
  9. #define LCD_DATA6       PIN_B6
  10. #define LCD_DATA7       PIN_B7
  11. #include <LCD.C>
  12. #include <math.h>
  13. #define CONST_CORRIENTE 5
  14. #define CONST_TENSION   310
  15. void main(){
  16.    setup_adc_ports(sAN0|sAN1|VSS_VDD);
  17.    setup_adc(ADC_CLOCK_DIV_2);
  18.    int16 i, adcZ=0;
  19.    float adc1=0, valorMax1=0, adc2=0, valorMax2=0;
  20.    float tension, corriente, potencia;
  21.    lcd_init();
  22.    while(true){
  23.       do{
  24.          set_adc_channel(0);
  25.          adcZ=read_adc()-512;
  26.          delay_us(20);
  27.       }while(adcZ<10);
  28.       for(i=0;i<300;i++){
  29.          set_adc_channel(0);
  30.          adc1=(read_adc()*5.0/1023.0)-2.5;
  31.          delay_us(33);
  32.          valorMax1=adc1*adc1+valorMax1;
  33.          set_adc_channel(1);
  34.          adc2=(read_adc()*5.0/1023.0)-2.5;
  35.          delay_us(33);
  36.          valorMax2=adc2*adc2+valorMax2;
  37.       }  
  38.       tension=sqrt(valorMax1/300)*CONST_TENSION;
  39.       corriente=sqrt(valorMax2/300)*CONST_CORRIENTE;
  40.       potencia=tension*corriente;
  41.       if(tension<50.0||corriente<0.03){
  42.          lcd_gotoxy(1,1);
  43.          printf(lcd_putc,"     W:000.0    ");
  44.          lcd_gotoxy(1,2);
  45.          printf(lcd_putc,"V:000.0   A:0.00");
  46.       }else{
  47.          lcd_gotoxy(1,1);
  48.          printf(lcd_putc,"     W:%3.1f    ",potencia);
  49.          lcd_gotoxy(1,2);
  50.          printf(lcd_putc,"V:%3.1f   A:%1.2f",tension, corriente);
  51.       }
  52.       valorMax1=0;
  53.       valorMax2=0;
  54.    }
  55. }



NOTA: se ha implementado una mejora al nivel de ruido del sensor ACS712 mediante un filtro RC de 1k y 1uF.
Podemos ver la diferencia notable en el osciloscopio, aunque el algoritmo de medición realiza una integración mediante la discretizacion de la señal, podemos mejorar aun mas la relación señal ruido del amperimetro, este reduce el ruido para niveles bajos de corriente.



Código para Arduino:

  1. #include <math.h>
  2. #include <LiquidCrystal.h>
  3. LiquidCrystal lcd(7, 8, 9, 10, 11, 12); //( RS, EN, d4, d5, d6, d7)
  4. #define CONST_CORRIENTE 2
  5. #define CONST_TENSION   530
  6. int i, adcZ=0;
  7. float adc1=0, valorMax1=0, adc2=0, valorMax2=0;
  8. float tension, corriente, potencia;
  9. void setup(){
  10.    lcd.begin(16, 2);
  11.    lcd.clear();
  12. }
  13. void loop(){
  14.    do{
  15.       adcZ=analogRead(A0)-512;
  16.       delayMicroseconds(20);
  17.    }while(adcZ<10);
  18.    for(i=0;i<500;i++){
  19.       adc1=(analogRead(A0)*5.0/1023.0)-2.5;
  20.       delayMicroseconds(33);
  21.       valorMax1=adc1*adc1+valorMax1;
  22.       adc2=(analogRead(A1)*5.0/1023.0)-2.5;
  23.       delayMicroseconds(33);
  24.       valorMax2=adc2*adc2+valorMax2;
  25.    }  
  26.    tension=sqrt(valorMax1/500)*CONST_TENSION;
  27.    corriente=sqrt(valorMax2/500)*CONST_CORRIENTE;
  28.    potencia=tension*corriente;
  29.    delay(2000);
  30.    if(tension<50.0||corriente<0.03){
  31.       lcd.setCursor(0,0);
  32.       lcd.print("     W:000.0    ");
  33.       lcd.setCursor(0,1);
  34.       lcd.print("V:000.0   A:0.00");
  35.    }else{
  36.       lcd.setCursor(5,0);
  37.       lcd.print("W:");
  38.       lcd.setCursor(7,0);
  39.       lcd.print(potencia);
  40.       lcd.setCursor(0,1);
  41.       lcd.print("V:");
  42.       lcd.setCursor(2,1);
  43.       lcd.print(tension);
  44.       lcd.setCursor(10,1);
  45.       lcd.print("A:");
  46.       lcd.setCursor(12,1);
  47.       lcd.print(corriente);
  48.    }
  49.    valorMax1=0;
  50.    valorMax2=0;
  51. }


Reemplazar los potenciometros por la etapa de sensado que se ve en el primer circuito, El resto corre igual se utilizan dos canales ADC al igual que en el primer circuito.
Se ha utilizado potenciometros para la simulación.
Aquí dejare el link a Circuits.io para la simulación del proyecto.

41 comentarios:

  1. Seba muy interesante tu proyecto, tendrías que pasarlo para un arduino uno. Una pregunta es mas preciso el de los operacionales?

    ResponderEliminar
    Respuestas
    1. Hola! como estas, si, voy a migrar el programa a Arduino también. Es mas preciso el que tiene operacionales, pero requiere mas calibración.
      Saludos!!

      Eliminar
  2. Hola Sebastián. Muy bueno el proyecto. Una consulta ¿Por qué un valor de 160Hz para el filtro RC?
    ¿Qué ocurriría si ponemos un valor mayor o menor?

    Gracias y saludos.

    ResponderEliminar
    Respuestas
    1. Hola, como estas?, lo hice así para atenuar hasta el tercer armónico, pero si pones un valor menor vas a atenuar menos armónicos y si pones un valor mayor podes hacer una caída de tensión mas grande que termina atenuando mucho la salida del sensor.
      En un diagrama de bode se vería mejor esto, después me hago un vídeo de eso.
      Saludos!!!

      Eliminar
    2. Gracias Sebastian. Esperaremos al vídeo.

      Saludos.

      Eliminar
  3. Seba, mas pretencioso me pongo, tenes que montarlo con medicion de coseno, haci queda completo. Muy bueno tu proyecto.

    ResponderEliminar
    Respuestas
    1. Hola! si es verdad quedo el FP para hacer, lo tengo que hacer, lo que pasa que llegue ayer de las vacaciones y bien tengo que ordenarme para retomar un monton de cosas!

      Eliminar
  4. Buenas noches amigo.. muy buen aporte, te felicito... aunque tengo un duda... me podrias explicar que haces en las lineas de código 23;24;25;26;27 para PIC.? agradezco tu ayuda para comprender mejor este codigo

    ResponderEliminar
    Respuestas
    1. Hola, ese bloque while espera el cruce por cero para iniciar la medición, es decir, cuando detecta cruce por cero comienza la medición para que no empiece en cualquier lado.
      saludos!

      Eliminar
  5. hola para encontrar la constante de tensión como le hago ?

    ResponderEliminar
    Respuestas
    1. Hola, la constante de tensión sale del divisor resistivo que pones afuera, ya que tenes la tensión a medir (220Vac) que pasa por un divisor resistivo por n veces, y luego ingresa al ADC, luego dentro del MCU se multiplica por ese valor al que reduciste la tensión para poder mostrar el valor. Lo ideal, ya que ningún divisor va a ser exacto al otro por el error de medición y demás. es que pruebes este circuito tal cual esta acá, y midas la tensión junto a un multimetro para comparar que tensión te da el multimetro y que tensión te da este circuito, así sabes el desvió de la medición y podes ajustarlo.
      Saludos.

      Eliminar
  6. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  7. Muy buenas, he visto las notas anteriores a esta y aún no entiendo de dónde sale la constante de tensión y la constante de corriente, podrías explicarme por favor?

    Muchas gracias.

    ResponderEliminar
    Respuestas
    1. Hola, la constante de tensión sale del divisor resistivo que pones afuera, ya que tenes la tensión a medir (220Vac) que pasa por un divisor resistivo por n veces, y luego ingresa al ADC, luego dentro del MCU se multiplica por ese valor al que reduciste la tensión para poder mostrar el valor. Lo ideal, ya que ningún divisor va a ser exacto al otro por el error de medición y demás. es que pruebes este circuito tal cual esta acá, y midas la tensión junto a un multimetro para comparar que tensión te da el multimetro y que tensión te da este circuito, así sabes el desvió de la medición y podes ajustarlo.
      Saludos.

      Eliminar
    2. Bueno, hay algo y es que no podría comprobar con 220V ya que soy de Colombia y acá hay 110V, he estado realizando muchas pruebas con el código, tengo ya el circuito armado acondicionando el divisor resistivo a mi voltaje, pero aún no logro comprender muy bien cómo sacas esa constante de tensión (por ahora sólo me interesa el voltaje), dices que sale del divisor de tensión que pongo afuera, es decir, el R3, R4 y R5 en este caso, pero bueno para que una medición me de 530 que es la constante de tensión, tendría que medirla con el arduino porque supongo que eso es un valor que da el ADC, entonces no comprendo muy bien lo que me dices.

      Saludos.

      Eliminar
    3. Hola, es correcto, tenes 100k+100k+1k, quedaría un divisor de 200k y 1k, de esa manera en 220Vac tenes Vo=Vi*(R2/(R1+R2))=220*(1k/(200k+1k))=1.094V, si vos ingresas 110V vas a tener Vo=110*(1k/(200k+1k))=0.547V, lo que se me ocurre que podes hacer es eliminar una de las resistencias de 100k, entonces te quedara Vo=110*(1k/(100k+1k))=1.089V lo cual esta mucho mas cerca de 1.094 y estaría ingresando el mismo valor. El tema es que tendrías que tocar igual el calculo del programa. Tene en cuenta que el circuito esta montando esta alterna de 1.094V en una continua de 2.5V para que los 2.5V sean el 0V y de ahí suba y baje.
      Entonces suponiendo 1.094V + 2.5V = 3.59V que en el ADC sera de aproximadamente 735. siendo el cero en 512 ya que son 2.5V.
      Entonces el programa lee 735 que sera: adc1=(read_adc()*5.0/1023.0)-2.5;
      adc1=(735*5.0/1023.0)-2.5; dará adc1=1.092 ya que en el calculo contempla los 2.5V del cero lo cual ya estaríamos midiendo lo que teníamos en la salida del divisor resistivo. Luego se realizan las 300 muestras se sacan los cuadrados, se realiza la sumatoria, etc.. para sacar el valor RMS.
      Por ultimo este valor RMS no el Pico, es multiplicado por CONST_TENSION que en este caso son 310, entonces tomamos Vp=1.092 y el valor RMS suponiendo que es una senoidal pura sera del 70.7% quedando Vrms=0.772 y luego multiplicarlo por 310 te dará aprox los 220Vac TRMS que mostraras en el LCD.
      Para el caso tuyo, usando 100k en lugar de 200k, vas a tener en la entrada Vp=1.089V que luego de pasar por la formula RMS serán aprox Vrms=0.769 que eso para vos deberían ser los 110Vac TRMS, por ende tu factor de multiplicación aproximado seria de 144, entonces si multiplicas 0.769 * 144, serán 110.7Vac en el LCD. En mi caso debería ser menos de 310 pero en la practica hay que compensar la tolerancia de los componentes, el desvió del ADC, etc... en tu caso sacaría una R de 100k y cambiaría 310 por 144.
      Coméntame después como funciono.
      Saludos.

      Eliminar
    4. Bueno he hecho las pruebas con una resistencia de 100k y una de 1k, eso me da 0.8V, he intentado realizar otros arreglos de resistencias para que me de 1V pero no lo he podido lograr, lo máximo que he logrado son 0.9 pero con una cantidad de resistencias en serie y paralelo que se vuelve un completo enredo, así que opté por dejarlo con los 0.8V, por lo tanto cuando realizo la fórmula del Vrms me da un valor aproximado de 1.26 lo cual eso serían mis 110V, pero por lo que veo el factor de multiplicación lo sacas simplemente buscando un número que multiplicado por el valor Vrms me de 110, corrígeme si no es así por fa. Si es así, ¿hay otra manera de realizarlo para que fuera más preciso? porque si es de esa forma al momento que el voltaje baje habrá un error en la medida, yo diría que podría hacerse como lo habías hecho anteriormente con el diac para así realizar una ecuación y que las medidas fueran bastante precisas.

      Otra cosa que no comprendo muy bien del código es la variable adcZ, para qué creaste otra variable en lugar de utilizar el mismo adc1? Tuviste algún problema que te hiciera hacer eso? y cuando lees el valor analógico le restas 510, esa resta a qué se debe?

      Saludos

      Eliminar
    5. Te anexo algunas imágenes de las pruebas, hasta ahora ha sido bastante estable la medida, pensé que haciendo la simple multiplicación no iba a ser tan preciso, pero hasta ahora ha ido bastante bien, aunque el multímetro que tengo es de los sencillos.

      http://prntscr.com/g415ab
      http://prntscr.com/g415fl

      Eliminar
    6. Si, puede que tengas desvió cuando los valores se acercan a los limites, es normal ya que no esta compensado con un auto-rango (seria lo ideal) para mantener la linealidad en toda la medición.
      Igual tene en cuenta que este medidor es comparable a un multimetro de muy bajo costo, ya que utiliza el ADC interno que no es de la mejor calidad y es de baja resolución, para algo mejor tendríamos que implementar algún ADC externo de mayor resolución y un PGA para el auto-rango.
      La medición contra tu multimetro esta bien, para el uso básico que tiene el ADC del MCU, creo que es correcto.
      Saludos.

      Eliminar
    7. Si, también considero que la medición está bien, trataré de realizarle algunas mejoras para que quede más preciso. En cuanto al código podrías responderme las últimas preguntas que te hice por favor? Disculpa tanta molestia. Hablo del siguiente párrafo

      Otra cosa que no comprendo muy bien del código es la variable adcZ, para qué creaste otra variable en lugar de utilizar el mismo adc1? Tuviste algún problema que te hiciera hacer eso? y cuando lees el valor analógico le restas 510, esa resta a qué se debe?

      Saludos

      Eliminar
    8. Hola, la variable adcZ es la variable que busca el cero de la señal, es decir el valor 0 que seria a 2.5V o 512.
      Ese loop espera a que sea cero para salir, ya que es el detector de cruce por cero, una vez que encuentra el cruce por cero comienza la medición, es solamente para que el comienzo de las cuentas del valor RMS sea al principio del periodo y no en cualquier momento.
      Saludos!

      Eliminar
    9. Ok entiendo, muchas gracias por responder a todas mis dudas y disculpa las molestias.

      Saludos.

      Eliminar
  8. Mmm, hay un error en los divisores para medir tensión. No está sumando el voltaje del segundo diviso, por ende ña muestra va con ciclos negativos

    ResponderEliminar
  9. Mmm, hay un error en los divisores para medir tensión. No está sumando el voltaje del segundo diviso, por ende ña muestra va con ciclos negativos

    ResponderEliminar
    Respuestas
    1. Hola, en la programación esta contemplando -2.5, o -512 ya que es el centro del ADC para el montaje de continua en el divisor de tierra virtual.
      Fíjate bien, ya lo he probado varias veces el proyecto funcionando ok.
      Saludos.

      Eliminar
  10. Hola Sebastian. Con ese montaje y parte del código parace posible hallar la frecuencia de la red, midiendo el tiempo entre los cruces por cero, que sería el periodo y se haría el inverso para calcular la frecuencia. puedes confirmar esa hipotesis, y en ese caso puedes ayudarme con el código? por favor.

    ResponderEliminar
    Respuestas
    1. Hola, correcto, sabiendo el cruce por cero de cada señal podes saber que tiempo transcurre entre ambas y obtener el PF, es uno de los metodos mas comunes para hallar el PF en MCU. Saludos.

      Eliminar
  11. Hola Sebastian. soy nuevo en electrónica y arduino, queria consultarte si tiene el esquema no en pic si no en arduino todo solo para guiarme y probar.
    es muy interesante tu proyecto y deseo hacerlo en arduino, porfavor ayuame en esto.

    ResponderEliminar
    Respuestas
    1. Hola, no lo tengo echo tipo diagrama, solo en el pic, pero fíjate en el diagrama del pic donde en la parte izquierda están los circuitos de sensado de corriente y tensión y luego salen dos conexiones a los pines RA0 y RA1 del PIC, bueno en arduino el circuito es igual solo que en lugar de ir a RA0 y RA1 va a A0 y A1, el resto es lo mismo es lo único que cambia, después la conexión del LCD es la que esta en el dibujo de Arduino, solo sacas los dos potenciometros que están en el arduino y pones el circuito que esta en el pic. Saludos.

      Eliminar
  12. Que tal amigo muy bueno tu proyecto, en mi caso tengo 127v ca, queria saber que valor se puede poner en const de corriente o ese no cambia?

    ResponderEliminar
    Respuestas
    1. Hola, el de corriente no debería cambiar porque es el sensor ACS712 el que envía un valor proporcional a la corriente que lo atraviesa, de todas formas si pones otro sensor de otro valor vas a tener que ajustarlo. El divisor de tensión va a cambiar para 127V, pero el de corriente si no cambias el ACS, no debería cambiar. Saludos.

      Eliminar
    2. ok ahora entiendo, gracias por la ayuda.

      Eliminar
  13. EN LA PARTE DEL ESQUEMÁTICO DEBE SER MAS CLARO YA QUE CUANDO SE CONECTA ASI HAY UN PROBLEMA QUE EN EL SENSOR ACS712 DETECTA COMO CARGA LAS RESISTENCIAS. NO ME FUNIONO ASI TAL CUAL COMO ESTA EN ESE ESQUEMA. BUSQUE INF POR OTRA FUENTE Y HICE LA CORRECCIÓN Y FUNCIONA BIEN. QUISIERA HACER UNA AÑADIDURAS. POTENCIA REAL ACTIVA Y FACTOR DE POTENCIA EN UNA LCD 20x4. ME AYUDAN

    ResponderEliminar
    Respuestas
    1. Hola, es verdad, lo que pasa que el proyecto se hizo para PIC principalmente, y se realizo todo el circuito para este, entonces esta dedicado por completo a PIC. Luego por un pedido de un amigo migre el código a Arduino para que se pueda realizar ahí también, y el circuito quedo como simulación con dos potenciometros representando a la tensión y la corriente, pero bueno, creí que existiendo el circuito del PIC detallado podían portarlo a Arduino ya que solo se usan dos ADC tanto en PIC como en Arduino.
      Saludos.

      Eliminar
  14. #include
    #include
    LiquidCrystal lcd(7, 8, 9, 10, 11, 12); //( RS, EN, d4, d5, d6, d7)
    #define CONST_CORRIENTE 0.067
    #define CONST_TENSION 205
    int i, adcZ=0;
    float adc1=0, valorMax1=0, adc2=0, valorMax2=0;
    float tension, corriente, potencia;
    float Amp;

    void setup(){
    lcd.begin(16, 2);
    lcd.clear();
    }
    void loop(){
    do{
    adcZ=analogRead(A0)-512;
    delayMicroseconds(20);
    }while(adcZ<10);
    for(i=0;i<700;i++){
    adc1=(analogRead(A0)*5.0/1023.0)-2.5;
    delayMicroseconds(33);
    valorMax1=adc1*adc1+valorMax1;
    adc2=(analogRead(A1)*5.0/1023.0)-2.5;
    delayMicroseconds(356);
    valorMax2=adc2*adc2+valorMax2;
    }
    tension=sqrt(valorMax1/700)*CONST_TENSION;
    corriente=(sqrt(valorMax2/700)/CONST_CORRIENTE);


    if(corriente<4.0){
    Amp=corriente-0.3;
    }
    else{
    Amp=corriente+0.82;
    }


    potencia=tension*Amp;
    if(tension<50.0){
    lcd.setCursor(0,0);
    lcd.print(" W:000.0 ");
    lcd.setCursor(0,1);
    lcd.print("V:000.0 A:0.00");
    delay(3000);
    }else{
    lcd.setCursor(0,1);
    lcd.print("V:");
    lcd.setCursor(2,1);
    lcd.print(tension);
    delay(700);
    }
    if(Amp<0.07){
    lcd.setCursor(0,0);
    lcd.print(" W:000.0 ");
    lcd.setCursor(10,1);
    lcd.print("A:");
    lcd.setCursor(12,1);
    lcd.print("0.00");
    }else{
    lcd.setCursor(5,0);
    lcd.print("W:");
    lcd.setCursor(7,0);
    lcd.print(potencia);
    lcd.setCursor(10,1);
    lcd.print("A:");
    lcd.setCursor(12,1);
    lcd.print(Amp);
    delay(700);
    }
    valorMax1=0;
    valorMax2=0;
    }

    ResponderEliminar
    Respuestas
    1. ESTA PRUEBA DESDE COLOMBIA 110 AC ESTA PRECISA Y TAMBIEN. ME TOCO HACER UNOS AJUSTES PARA LA PARTE DEL CODIGO SOBRE EL SENSOR NO LOGRE TENER UNA EXACTITUD SOBRE LOS VALORES. SI ALGUIEN TIENE UNA IDEA ME COLABORA

      Eliminar
  15. HOLA AMIGO, VERAS SOY NUEVO EN ARDUINO Y ESTOY INTENTANDO HACER EL CIRCUITO UTILIZANDO UN SENSOR NO INVASIVO sct-013, MI PROBLEMA ES QUE NO SE COMO CONECTARLO A LOS PUERTOS DE ENTRADA ANALOGICA DEL ARDUINO. AGRADECERIA MUCHO SI ME PUEDES ORIENTAR ACERCA DE COMO HACER ESTA MODIFICACION. DE ANTEMANO GRACIAS!

    ResponderEliminar
    Respuestas
    1. Hola, no lo use nunca ese transformador de corriente, pero supongo que podes ingresarlo directamente al ADC del Arduino, lo que sucede es que habría que analizar que proporción de tensión te entrega respecto a la corriente que lo atraviesa, ya que en base a eso sera el calculo que hay que hacer dentro del programa para que la corriente que te muestra en la pantalla sea la correcta que se esta midiendo.
      Saludos.

      Eliminar
    2. Posees el SCT-013-000 o el SCT-013-030?

      Eliminar
    3. Hola, la verdad que no tengo ningún sensor de corriente, solo el ACS712.
      Saludos.

      Eliminar