2021-03-29
Tacómetro
Problema
Los generadores de la central hidroeléctrica tienen sendos tacómetros que sirven para medir las RPM de la máquinas. Su función es doble: sirven como referencia para arrancar la máquina de forma manual y de protección ante la perdida o exceso de velocidad.
Ambos tacómetros son exactamente iguales, de marca y modelos sin especificar dado que la pegatina de identificación ha desaparecido y no existe ninguna referencia en la documentación.
Durante el funcionamiento normal se empezó a observar que las revoluciones de la máquina oscilaban mucho alrededor de la velocidad de trabajo (375 rpm):
En un principio se pensó en el mal funcionamiento del sensor, así que se procedió a limpiarlo y revisar las conexiones con el mismo resultado. Al apagar la electrónica y volverlo a encender, parecía que se volvía estable en la medida y no se le dio mas importancia.
Pero conforme iba pasando el tiempo, el fallo iba a peor y cuando se apagaba y encendía las revoluciones se disparaban a una cifra muy elevada que después iba decayendo.
Así que se decidió cambiar el tacómetro y hacerlo desde cero con la plataforma Arduino.
Funcionamiento del tacómetro
El tacómetro lee las revoluciones mediante un sensor de reluctancia variable. Este tipo de sensores se componen de una bobina y circuitos electrónicos que leen las alteraciones del campo magnético en la bobina.
Dicho campo magnético se altera cuando se acerca un metal a la bobina. Por eso para la lectura de las RPM las máquinas poseen un disco dentado en el eje.
El número de dientes de la rueda dentada es de 60. No es un número aleatorio, sino que tiene fundamento. Si calculamos la frecuencia de giro de la máquina, es decir cuantas vueltas da por segundo, obtendremos que a régimen de trabajo (375r.p.m) la frecuencia será de 6.25Hz. Eso significa que si solo tuviéramos un diente, la señal tendría 6.25Hz, pero si aumentamos el número de pulsos en 60 veces, obtendremos que cada segundo obtendríamos la velocidad en rpm de la máquina.
En un sensor de reluctancia variable cuando un diente se acerca o aleja, se produce algo parecido a una señal alterna muy parecida a un diente de sierra.
Este tipo de sensores son bastante baratos y precisos pero necesitan una circuitería adicional para poder trabajar a los niveles lógicos de un microcontrolador.
Por otro lado tenemos el tacómetro en si, donde se haya la electrónica que se encarga de medir las rpm y mostrarla en un display de 4 dígitos. Además posee de dos salidas a relé una para indicar máxima velocidad y otro para mínima velocidad. Ambos relés se programan mediante un selector en el frontal.
El tacómetro diseñado
El tacómetro ha sido realizado siguiendo los mismos principios pero cambiando algunos elementos.
Se sigue manteniendo en el frontal el display de 4 segmentos y los leds indicadores de máxima y de mínima, aunque se han sustituido los selectores de velocidad por unos pulsadores.
Debido a la necesidad de tener una electrónica más compleja se ha optado por elegir un sensor de tipo NPN para medir las RPM. Este sensor presenta un problema y es que los que sirven para medir altas revoluciones suelen tener un precio alto.
La solución menos costosa es la de usar un sensor NPN para detección de proximidad como el LJ2A43. Este sensor tiene una baja respuesta en frecuencia y no se pueden usar mas allá de los 100 Hz, así que la rueda dentada no sirve y se ha tenido que fabricar una nueva con un solo punto de detección.
Por otro lado, las salidas se siguen manteniendo, una de mínima y otra de máxima. Siguen estando disponibles tanto el contacto abierto como el cerrado de cada relé.
El Hardware
El tacómetro se ha construido sobre dos placas para poder minimizar el tamaño y utilizando componentes "trough hole" fácilmente adquiribles en cualquier tienda de electrónica.
La primera placa es la que contiene el display, los leds de indicación y los pulsadores.
El esquema es el siguiente:
Las resistencias R1 a R7 limitan la corriente que llega a los segmentos de cada display.
Los transistores Q1 a Q4 se encargan de realizar la conmutación de cada display.
Los diodos led D1 y D2 son los diodos de indicación de mínima y de máxima, se encienden a nivel bajo. Se ha hecho así porque va a ir conectados al pin D0 y D1 (RX y TX) del Arduino y de esa manera no interfieren con el bootloader.
Los pulsadores P1 a P4 son de 12x12mm, bastante grandes y además se les puede colocar capucha. Los condensadores C1 a C4 sirven para reducir el rebote que se pueda producir. No se utiliza resistencia de PULL UP ya que se usará la propia del pin correspondiente del microcontrolador.
La segunda placa es la que contiene los relés de salida, la fuente de de alimentación y la entrada del sensor.
Su esquema es el siguiente:
El microcontrolador elegido es el atmega328p. Con él tendremos las suficientes entradas y salidas para controlar todo el montaje.
El tacómetro se alimenta a 12 voltios, por lo que hay que bajar la tensión a 5 voltios. El encargado de esta función es el 7805. Alrededor de él se encuentran un conjunto de filtros RC para eliminar ruidos en la fuente así como un diodo supresos de transitorios de 18 voltios.
los relés elegidos son los HK4100F. Han sido elegidos más que otra cosa por su tamaño, aunque la potencia de sus contactos es más que suficiente para varias aplicaciones, incluida la de la protección de las máquinas. Ambos relés tienen una bobina de 12 voltios que se cogen de la misma fuente de alimentación. El control mediante Arduino se hace con sendos transistores BC547.
Para la programación y pruebas del tacómetro es necesario tener los pines del puerto serie, reset y GND por lo que en el conector J2 estarán disponibles.
Para la entrada del sensor se ha utilizado un optoacoplador PC817 que para la aplicación tiene una respuesta en frecuencia suficiente. Este se encarga de reducir la tensión del sensor a los 5 voltios del microcontrolador. No se usa resistencia PULL UP y se utiliza la interna del micro.
La configuración utilizada es no inversora, es decir, la señal del sensor tendrá la misma forma en la entrada del micro.
La salida del sensor es casi una señal cuadrada cuyo tiempo entre pulsos equivale al periodo de la señal (recordar que el periodo es la inversa de la frecuencia.
La parte baja de la señal es cuando el sensor detecta el metal. Curiosamente la duración de dicho pulso no es exactamente igual, quizás debido a la misma naturaleza del sensor o al balanceo que tiene la pieza en el eje. Como observamos en la imagen, la señal está a nivel alto y cuando el metal pasa cerca se vuelve nivel bajo.
La señal del sensor la leeremos con un pin de interrupción del microcontrolador, debido a que la detección la hacemos pasando de HIGH a LOW utilizaremos la detección del flanco de bajada (FALLING).
Debido a que el pulso no dura lo mismo siempre cuando está en la parte baja si usamos el flanco de subida (RISING) en la interrupción obtenemos como resultado que la velocidad fluctúa. En cambio usando el flanco de bajada la medición es perfecta.
El software
Para medir revoluciones hay dos técnicas básicas: lectura de pulsos en un intervalo de tiempo o leer tiempo entre pulsos.
Leer el número de pulsos es mejor cuando este número sea una cifra elevada. En cambio la lectura de tiempo entre pulsos es mejor cuando tenemos pocos pulsos.
En este caso se usa la lectura del tiempo entre pulsos. Para ello se hace uso de la función micros(), cuando la interrupción se produce observamos cuando se ha producido el pulso. Si le restamos el tiempo cuando ocurrió el pulso anterior obtenemos el tiempo entre pulsos.
Dicho tiempo es el periodo de la señal, con lo que basta con hacer la inversa para obtener la frecuencia (número de pulsos por segundo) y basta multiplicar por 60 para obtener las revoluciones.
El código es el siguiente:
/* TACOMETRO Autor: VictorJAM. Fecha: 22/03/2021. */ #include <SimpleButton.h> #include <EEPROM.h> #define RELEMINIMA 7 #define RELEMAXIMA 8 //--- VARIABLES CONTROL DISPLAY. // Contiene los segmentos que se han encender para cada digito. const uint8_t numeros[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x67 }; // Pines donde están conectados los segmentos. const uint8_t segmentos[7] = { A1, A3, 10, 11, 12, A2, 9 }; // Pines de control de cada digito. const uint8_t digitos[4] = { 13, A0, 5, 6 }; uint8_t digitoActual; uint32_t td; //--- Botones. SimpleButton btnSetMax(A5); SimpleButton btnSetMin(A4); SimpleButton btnUp(4); SimpleButton btnDown(3); //--- VARIABLES PARA LA MEDIDA DE PULSOS Y RPM volatile uint32_t tanterior; volatile uint32_t tactual; volatile uint32_t periodo; float f; uint32_t rpm; //--- VARIABLES DE CONTROL. int maxrpm; int minrpm; uint32_t t1; uint32_t t2; uint32_t t3; uint32_t t4; uint32_t t5; uint32_t t6; //--- RUTINA DE SERVICIO A LA INTERRUPCIÓN. void isr() { tactual = micros(); periodo = tactual-tanterior; tanterior = tactual; } // Ajusta un valor entre un valor minimo y un maximo. Si el valor 'value' es // mayor que el de 'imax', 'value' será 'imin'. De la misma forma, si 'value' // es menor de 'imin' su valor pasará a ser 'imax'. void trimvalue(int &value, int imin, int imax) { if ( value > imax ) value = imin; else if ( value < imin ) value = imax; } // Rutina de retraso al encendido. Su salida será uno cuando la condición sea // cierta y haya transcurrido el tiempo indicado en duración; será cero en caso // contrario. unsigned int onDelayTimer(bool condicion, unsigned long &timer, unsigned long duracion) { if (condicion == false) { timer=0; return 0; } else if (timer == 0) { timer = millis(); return 0; } else if (millis() - timer >= duracion ) return 1; else return 0; } // Dado un entero devuelve el valor decimal (o digito) especificado en la // posición p. int digitopos(int v, int p) { int d, r; d = v; int i=0; while (i<=p) { r = d % 10; d = d / 10; i++; } return r; } // Muestra/enciende los leds correspondientes a ese digito en el display. void printd(uint8_t v) { if ( v>=0 && v<=9 ) { for (int i=0; i<7; i++) { digitalWrite(segmentos[i], bitRead(numeros[v], i)); } } } // Muestra un digito de 4 cifras en un display de 4 dígitos. Usa la función // delayMicroseconds. Útil para pruebas. void actualizaDisplay1() { for (int d=0; d<4; d++) { printd(digitopos(valorDisplay, 3-d)); digitalWrite(digitos[d], HIGH); delayMicroseconds(800); digitalWrite(digitos[d], LOW); delayMicroseconds(200); } } // Muestra un digito de 4 cifras en un display de 4 digitos. Usa la función // micros() con lo que no interrumpe el programa. void actualizaDisplay2() { if ( micros()-td > 2000 ) { digitalWrite(digitos[digitoActual], LOW); digitoActual++; if ( digitoActual==4 ) digitoActual = 0; printd(digitopos(valorDisplay, 3-digitoActual)); digitalWrite(digitos[digitoActual], HIGH); td = micros(); } } //--- CONFIGURACIÓN DE PROGRAMA. void setup() { // Establece los segmentos como salida. for (int i=0; i<7; i++) { pinMode(segmentos[i], OUTPUT); } // Establece los pines de control de digitos como salida. for (int i=0; i<4; i++) { pinMode(digitos[i], OUTPUT); } // Leemos las posiciones 0 y 2 que contienen un int con el valor por defecto // de las variables minrpm y maxrpm. EEPROM.get(0, minrpm); EEPROM.get(2, maxrpm); // Ponemos los pones de relé como salida. pinMode(RELEMINIMA, OUTPUT); // RELE DE MINIMA pinMode(RELEMAXIMA, OUTPUT); // RELE DE MAXIMA digitalWrite(RELEMINIMA, 0); digitalWrite(RELEMAXIMA, 0); // Diodos de led indicadores de mínima y máxima. pinMode(0, OUTPUT); pinMode(1, OUTPUT); // Pin de interrupcion, no usamos una resistencia de PULL-UP en el opto ya // que podemos usar la interna. pinMode(2, INPUT_PULLUP); // IMPORTANTE!! La interrupción debe darse en el flanco de bajada. attachInterrupt(digitalPinToInterrupt(2), isr, FALLING); } //--- PROGRAMA PRINCIPAL. void loop() { // Actualizo el estado de los botones. btnSetMin.update(); btnSetMax.update(); btnUp.update(); btnDown.update(); // Rutina para que en el display se visualice cero, cuando hay pocas // revoluciones (máquina parándose o arrancándose). if ( periodo == 0 ) f=0; else { // Se guarda el tiempo actual y el valor de micros en sendas variables // auxiliares. Esto reduce el error que se produce cuando intentamos leer // las variables y se produce la interrupción, resultando que tactual // tiene valor 0 y provocando que la frecuencia sea 0. uint32_t a = tactual; uint32_t b = micros(); if ( b-a > 1000000UL) { f=0; } else { f = 1000000.0 / periodo; } } // La frecuencia se expresa en Hertzios, para convertirla a r.p.m. debemos // multiplicar por 60. Despues redondeamos el número obtenido. f = f*60.0; rpm = round(f); // Para activar los relés tenemos que tener en cuenta que la rutina no es // perfecta y da errores dando como resultado que la frecuencia es cero, aun // cuando es fija. Para ello se usa la función 'onDelayTimer' tanto en el // el encendido como en el activado. // Relé/Led de mínima. digitalWrite(RELEMINIMA, onDelayTimer(rpm<minrpm, t1, 500)); digitalWrite(1, !onDelayTimer(rpm<minrpm, t3, 20)); // Relé/Led de máxima. if ( onDelayTimer(rpm>=maxrpm, t2, 500) ) digitalWrite(RELEMAXIMA, HIGH); else if ( onDelayTimer(rpm<maxrpm, t5, 100 ) ) digitalWrite(RELEMAXIMA, LOW); if ( onDelayTimer(rpm>=maxrpm, t4, 20) ) digitalWrite(0, LOW); else if ( onDelayTimer(rpm<maxrpm, t6, 20) ) digitalWrite(0, HIGH); // Pequeño menú para interactuar con el display y poder elegir el número de // rpm de mínima y de máxima. if ( btnSetMin==Pressed ) { valorDisplay = minrpm; if ( btnUp==Press ) { minrpm++; trimvalue(minrpm, 0, 500); } if ( btnDown==Press ) { minrpm--; trimvalue(minrpm, 0, 500); } } else if ( btnSetMax==Pressed ) { valorDisplay = maxrpm; if ( btnUp==Press ) { maxrpm++; trimvalue(maxrpm, 0, 500); } if ( btnDown==Press ) { maxrpm--; trimvalue(maxrpm, 0, 500); } } else valorDisplay = rpm; if ( btnSetMin==Release ) { EEPROM.put(0, minrpm); } if ( btnSetMax==Release ) { EEPROM.put(2, maxrpm); } // Muestra el digito. actualizaDisplay2(); }
En los comentarios del código se puede observar el pequeño problema que tiene dicha rutina. Para evitar que cuando va muy lento la máquina y dado que las interrupciones se van a producir cada vez menos, hay que hacer que la frecuencia sea cero.
Dicho problema se resuelve dentro del loop, pero hasta cierto punto, ya que el compilador crea un código que genera un error al leer las variables haciendo que cuando haya velocidad, el resultado de la frecuencia sea 0.
En el display no es mayor problema porque es imperceptible, pero para las salidas de relé y display es peligroso.
Por ello se han añadido las rutinas de retraso al encendido de los relés y los led indicadores.
Programación del software
Primeramente hemos de haber subido el bootloader al atmega328p. Una vez subido podemos usar un Arduino UNO sin chip y hacer la siguiente conexión:
Debido al circuito de reset que se se ha incorporado a la placa, formado por la resistencia R1, el diodo D1 y el condensador C5, si intentamos programar el tacómetro directamente nos dará un error indicando que el microcontrolador no responde.
Para evitar esto hemos de estar atentos en el IDE y justo cuando termine de compilar el código, cuando aparecen las letras rojas del AVRDUDE debemos pulsar el botón de reset de la placa Arduino. Si lo hacemos correctamente el programa será subido y echará a andar.
Hemos usado un Arduino UNO porque simplifica la operación. Podríamos haber usado un convertidor USB a TTL y unido los pines RX y TX a la placa, pero además deberíamos haber añadido alguna forma, por ejemplo un pulsador, para provocar el reset llevándolo el pin reset a GND. No olvidar de que el GND del convertidor de la placa y del convertidor deben estar unidos.
Conexión y manejo del tacómetro
La conexión del tacómetro es simple: alimentar el circuito con 12 VDC, conectar el sensor y conectar las salidas de mínima y máxima al circuito de protección de las máquinas.
Los relés de mínima y de máxima van conectados al autómata de protecciones mecánicas de la máquina. El relé de mínima se corresponde con la entrada X7 (número de protección ANSI 14) y el relé de máxima lo hace con la entrada X8 (número de protección ANSI 12).
Hay que recordar que hay que usar los contactos cerrados, la condición de disparo se da cuando en la entrada no hay circulación de corriente.
El tacómetro permite la programación de una velocidad mínima y máxima mediante los botones del panel. La disposición de estos es la siguiente:
Para establecer la velocidad de disparo mínima hemos de apretar el botón de mínima y mantenerlo apretado, entonces en la pantalla aparece el valor de la velocidad y mínima, que se podrán ajustar apretando los botones de subir y bajar.
El mismo procedimiento se usa para ajustar el disparo de máxima, pero usando en este caso el botón de máxima.
Anexos
Conectar un display de 7 segmentos.
Conectar un display de 4 digitos.
16/10/2021
He descubierto un fallo tonto a la hora de grabar la información en la EEPROM. Usando la función update() solo se graba un byte de memoria. Como las variables minrpm y maxrpm son enteros, perdemos entonces la mitad de la información y hay problemas.
Se ha corregido tanto el programa en el artículo, como en el código fuente descargable.