Picos en la señal del pulseIn() al calcular los Hz

Hola amigos,
Desde hace tiempo, estoy intentando calcular las revoluciones por minuto de unos motores de corriente contínua de 210 rpm con encoders (del tipo hall). El motor es como éste: Motor dc 6V 210 rpm
Para hallar las revoluciones primero calculo los Hz y lo hago con la función pulseIn(). El problema es que de tanto en tanto, la señal tiene unos "megapicos" que no sé como cortarlos. Y he probado de todo....Por ejemplo he probado con el filtro paso bajo, con librerías de suavizado, etc, etc, etc.
Es posible que a ustedes les haya sucedido algo parecido con otro tipo de señales digitales.

La idea principal, creo que es sencilla. Se trata de calcular el periodo. Para ello sumo el tiempo del pulso en estado HIGH y luego el tiempo en estado LOW. Luego, la frecuencia es la inversa del periodo: f = 1/T. Finalmente, convertiría los Hz en RPM.

Lo que creo que sucede es que, por alguna razón, de vez en cuando T tiende a 0, y por tanto la frecuencia toma valores muy elevados.

const byte INPIN = 2;
unsigned long PulsoAlto;
unsigned long PulsoBajo;
double Periodo;
double Frequency;
int timeOut = 20000;


void setup ()
{
  Serial.begin (9600);
  pinMode(INPIN, INPUT);

}

void loop ()
{

  PulsoAlto = pulseIn(INPIN, HIGH, timeOut);
  PulsoBajo = pulseIn(INPIN, LOW, timeOut);
  Periodo = PulsoAlto + PulsoBajo;
  Periodo = Periodo / 1000000; //En segundos
  Frequency = 1 / Periodo; //En Hz

  if (Periodo == 0) {
    Frequency = 0;
  }

  Serial.print(Frequency);
  Serial.println("");
}

Luego tambien probé con una librería muy buena que se llama FreqCount de Paul Stoffregen pero sólo funciona con un único PIN y yo tengo dos motores...
Por favor, ¿tienen alguna idea de como evitar los "picos"? Llevo mucho tiempo dándole vueltas y no sé que más hacer.
Muchísimas gracias,

Estimado, pero no queda claro la amplitud de la señal de entrada y el valor pico de esta, tienes algún osciloscopio o similar a la salida del encoder? Hay que ir revisando desde la causa raíz, sin el instrumento es ir probando a ciegas

Hola peabass.
Por desgracia no tengo ningún osciloscopio. El encoder se alimenta con 3.3V y es la señal que le llega a un Arduino Mega 2560.
Gracias,

Tienes algunos erores de formato de variables y constantes, que pueden generar errores y cálculos extraños como , Periodo = Periodo / 1000000
Es mejor colocar; Periodo = Periodo / 1000000.0 (Trabaja todo en formato float)
Igual para Frecuency = 1.0 / Periodo.

Hi,
Ese motor parece que tiene escobillas por lo tanto si tienen escobillos van producir ruidos electromagneticos. Especialmente si el sensor es de hall. Trata de anadirle al vcc que alimenta el motor un condesador de .1 ufd para que elimine las altas frecuencia y uno de 10ufd para que elimine las de baja frecuencia. Creo que el ruido se esta produciendo en las escobillas del motor.

o uno u otro los dos no tienen sentido,

Si tienes encoders para que usar PulseIN? Por que no usar interrupciones como generalmente se mide cualquier medidor de RPM.

Amigos.
Muchísimas gracias. He cambiado las variables, tal como me habéis indicado, todas por un float. Luego lo de los condesadores, es totalmente cierto. He puesto uno en la propia alimentación del motor tal como indicastéis. Sin embargo, también he añadido un condensador de 10 uF en serie en el propio pin 2 de Arduino. La señal no tiene ningún pico. Han desaparecido!!. Sin embargo, para la alimentación del Pin de entrada digital, ¿recomendáis algún otro valor del condensador? Lo digo por si pudiera ser posible suavizar aun más la señal. ¿Quizás añadiendo alguna resistencia?
¡Muchísimas gracias de nuevo!

Estimados amigos,

Creo que lo he conseguido y lo quiero compartir con vosotros. Ahora el código va genial. Mide las revoluciones del motor tanto en un sentido de giro como en el otro sentido. Además, también mide los pasos, es decir, los tics que hacen los imanes al pasar por el sensor Hall (por si fuera necesario).

Dicho esto, como dice Surbyte hay varias maneras de plantear el problema, pero hace tiempo yo quería hacerlo de otra manera diferente. La forma más clásica, es contar los pasos que se producen en un tiempo determinado dentro de una función "if". Pasado dicho tiempo se deja nuevamente el número de pasos a 0. Lo que sucede es que para obtener el valor, debe pasar este margen de tiempo. Es decir, hasta que no se termina el "if" el programa no avanza y no se pueden hacer otras tareas. Este método que aquí expongo es muchísimo más inmediato, evitando que otras instrucciones se queden a la espera.

Tiene la desventaja que es necesario un condensador en serie y una resistencia para eliminar los picos y la mayor parte del ruido. Ver también el tema del motor dc.

Dicho esto, deseo que os guste y que os pudiera ser útil.

Un abrazo.

const byte Encoder_C1 = 2; //Cable verde pin 2 digital
const byte Encoder_C2 = 3; // Cable amarillo pin 3 digital

float PulsoAlto;
float PulsoBajo;
float Periodo;
float Frequency;
int timeOut = 20000;

byte Encoder_C1Last;
int paso;
boolean direccion;

void setup ()
{
  Serial.begin (9600);
  pinMode(Encoder_C1, INPUT);
  attachInterrupt(digitalPinToInterrupt(3), calculapulso, CHANGE);
}

void loop ()
{

  PulsoAlto = pulseIn(Encoder_C1, HIGH, timeOut);
  PulsoBajo = pulseIn(Encoder_C1, LOW, timeOut);
  Periodo = PulsoAlto + PulsoBajo;
  Periodo = Periodo / 1000000.0; //En segundos
  Frequency = 1.0 / Periodo; //En Hz

  if (Periodo == 0) {
    Frequency = 0;
  }

  if (direccion) {
    Frequency = Frequency;
  } else {
       Frequency *= -1;
  }

  Serial.print(Frequency);
  Serial.println("");
}

void calculapulso()
{
  int Lstate = digitalRead(Encoder_C1);
  if ((Encoder_C1Last == LOW) && Lstate == HIGH)
  {
    int val = digitalRead(Encoder_C2);
    if (val == LOW && direccion)
    {
      direccion = false; //Reverse
    }
    else if (val == HIGH && !direccion)
    {
      direccion = true;  //Forward
    }
  }
  Encoder_C1Last = Lstate;

  if (!direccion)  paso++;
  else  paso--;
}

Te corrijo algo, perdón.

  if (direccion) {
    Frequency = Frequency; // esto no tiene sentido
  } else {
       Frequency *= -1;
  }

haz así

  if (!direccion) {  
       Frequency *= -1;
  }

Saludos

1 Like

Hay otra forma y es usar el INPUT CAPTURE pin dedicado específicamente para esto. Hay un tutorial excelente de Nick Gammon que lee los dos anchos de pulsos HIGH y LOW perfectamente usando el TIMER y sin comprometer al microcontrolador.
Supongo que es la idea que persigues.

1 Like

Muchas Gracias Surbyte por tu comentario. Cierto, la idea es no comprometer al microcontrolador. Además, acabo de descubrir que la librería FreqCount de Paul Stoffregen, debe basarse en los Timers de Nick Gammon. El problema en ambos casos, es que sólo se puede usar para el pin 47 en el Arduino Mega y el pin 5 para el Atmega328. El caso es que yo tengo dos ruedas. Por lo tanto necesito dos pins de entrada. Intenté hacer algo con un multiplexor pero me di cuenta que eso no era posible. Si se pudiera añadir un pin de lectura más, sería ideal porque tanto la librería como el tutorial de Nick Gammon muestran la señal a la perfección.

Si usas un enconder, tienes dos señales, okay. No me percaté del detalle aunque está bien explicado. Usa entonces la librería encoder para contar pulsos. Lo hace con interrupciones.
Verás que no perderas pulsos.
La libreria esta en el Administrador de librerìas.