viernes, 16 de octubre de 2015

[Arduino 12] Velocímetro de bicicleta (2/3)

Continuando con la segunda parte del Velocímetro para bicicleta ya hemos armado el circuito principal de este sistema. hemos logrado determinar la cantidad de vueltas que da la rueda y verlo en el puerto serial de arduino IDE, ademas de programar correctamente el led que nos indica cuando hemos dado una vuelta a la rueda.

En esta segunda etapa haremos que el sistema nos diga que distancia hemos recorrido aproximadamente, cada vez que la rueda de una vuelta completa el sistema se actualizara y nos mostrará que distancia hemos recorrido.


Sketch "Calculo de distancia recorrida"
En el siguiente código calcularemos la distancia que hemos recorrido con cada giro de la rueda. En el código solo se ha agregado la función distancia(); esta funciona se ejecuta cada vez que detecte una vuelta y calcula la distancia que hemos recorrido. Si la distancia recorrida es menor o igual a 999 metros entonces mostrara la distancia en metros y si la distancia es mayor a 999 entonces mostrara la distancia calculada en kilómetros.

int estadoActual1=0;
int estadoActual2=0;
int estadoUltimo=0;
int contador=0;
float radioEnCm=30.0;   //ingresar radio de la rueda en cm
float pi=3.1416;
float perimetroRueda=2*pi*(radioEnCm/100);  //Calcula Perimetro en metros
float distRecorrida=0;
float distKM=0;

void setup(){
        pinMode(4,OUTPUT);
        pinMode(3,INPUT);
        Serial.begin(9600);
}

void loop(){
        estadoActual1=digitalRead(3);
        delay(10);
        estadoActual2=digitalRead(3);
//Si los estados no son iguales, el sketch no hace nada
        if (estadoActual1 == estadoActual2) {
              if (estadoActual1 != estadoUltimo){
                    if (estadoActual1 == HIGH) {
                        contador = contador + 1;
                        Serial.print ("Vueltas ");
                        Serial.println(contador);
                        distancia();
                    }
              }
        }
        estadoUltimo= estadoActual1;
                
        if (contador%2 == 0 ) {
              digitalWrite(4, LOW);
        }
        else {
              digitalWrite(4, HIGH);
        }
}

void distancia(){
                        distRecorrida=perimetroRueda*contador;
                        distKM=distRecorrida/1000;
                        if(distRecorrida<=999){
                                Serial.print("Distancia recorrida en m= ");
                                Serial.println(distRecorrida);
                        }
                        else{
                                Serial.print("Distancia recorrida en Km= ");
                                Serial.println(distKM);
                        }
                        }

Resultado "Calculo de distancia"
Lo que podremos ver en pantalla sera el numero de vueltas y la distancia recorrida, ahora que hemos comprobado que funciona correctamente. podemos pasar a la siguiente etapa donde calcularemos la velocidad de desplazamiento. 

Sketch "Cálculo de Velocidad"
Ahora que ya tenemos el calculo de la distancia recorrida podemos proceder a esta nueva etapa calculando la velocidad de desplazamiento. Añadiremos la nueva funcion que llamaremos VEL(); y en ella nos encargaremos de medir los tiempos y los calculos necesarios para la velocidad. Para ello mediremos el tiempo con millis(), de esta manera será medido el tiempo de cada vuelta que ha dado la rueda con respecto a cuando se inicio el código.

La clave para poder calcular la velocidad de desplazamiento es medir el tiempo en que se ejecuta cada vuelta es como si comenzáramos a andar en bicicleta y con un reloj midiéramos cuanto tiempo transcurre entre cada revolución. Luego con este tiempo que hemos medido (tiempo3=|tiempo2-tiempo1|) y la distancia recorrida (perimetro=2*pi*r) podemos calcular la velocidad.

Un ejemplo claro de lo anterior es el siguiente medimos el tiempo que trasncurrecon un reloj: vuelta1 a los 2seg, vuelta2 a los 6seg, vuelta3 a los 9seg, vuelta4 a los 12seg, luego hacemos los calculos, |6seg-2seg|=4seg, luego |6seg-9seg|=3seg, luego |12seg-9seg|=3seg. Cada vez que se obtiene uno de estos resultados dividimos la distancia en Km/(tiempo en hrs)

La velocidad la queremos en Km/hr por lo que es importante transformar las unidades, por ejemplo el tiempo3 que calculamos anteriormente esta en milisegundos (1 seg tiene 1000 milisegundos), para llevarlo a hrs, primero vamos a tener que dividir los milisegundos por 1000, ahora la unidad es segundos y esto lo tenemos que llevar a minutos, dividiendo por 60. Finalmente lo llevaremos a horas dividiendo una vez mas por 60, para de este modo obtener tiempo4=(((tiempo3/1000.0)/60.0)/60.0)

Con la distancia tenemos que hacer lo mismo, solo que sera una distancia fija (el perímetro de la rueda). Nosotros tenemos el perimetro en metros perimetroRueda=2*pi*(radioEnCm/100), esto lo llevaremos a kilómetros dividiendo por 1000, ya que un Km=1000 metros. Finalmente la velocidad va a ser velocidad=((perimetroRueda/1000)/tiempo4) y su unidad de medida estará en Km/hr.

int estadoActual1=0;
int estadoActual2=0;
int estadoUltimo=0;
int contador=0;
float radioEnCm=30.0;   //ingresar radio de la rueda en cm
float pi=3.1416;
float perimetroRueda=2*pi*(radioEnCm/100);  //Calcula Perimetro en metros
float distRecorrida=0;
float distKM=0;
int tiempo1=0;
int tiempo2=0;
int tiempo3=0;
float tiempo4=0;
float velocidad=0;

void setup(){
        pinMode(4,OUTPUT);
        pinMode(3,INPUT);
        Serial.begin(9600);
}

void loop(){
        estadoActual1=digitalRead(3);
        delay(10);
        estadoActual2=digitalRead(3);
//Si los estados no son iguales, el sketch no hace gran cosa
        if (estadoActual1 == estadoActual2) {
              if (estadoActual1 != estadoUltimo){
                    if (estadoActual1 == HIGH) {
                        contador = contador + 1;
                        Serial.print ("Vueltas ");
                        Serial.println(contador);
                        distancia();
                        VEL();
                    }
              }
        }
        estadoUltimo= estadoActual1;
              
        if (contador%2 == 0 ) {
              digitalWrite(4, LOW);
        }
        else {
              digitalWrite(4, HIGH);
        }
}


void distancia(){
                        distRecorrida=perimetroRueda*contador;
                        distKM=distRecorrida/1000;
                        if(distRecorrida<=999){
                                Serial.print("Distancia recorrida en m= ");
                                Serial.println(distRecorrida);
                        }
                        else{
                                Serial.print("Distancia recorrida en Km= ");
                                Serial.println(distKM);
                        }
                        }

void VEL(){
        if (contador%2 == 0 ) {
              tiempo1=millis();
        }
        else {
              tiempo2=millis();
        }
        tiempo3=abs(tiempo2-tiempo1); //hay que pasar el tiempo a hrs
        tiempo4=(((tiempo3/1000.0)/60.0)/60.0);
        velocidad=((perimetroRueda/1000)/tiempo4);
        Serial.print(" tiempo1= ");
        Serial.print(tiempo1);
        Serial.print("tiempo2= ");
        Serial.print(tiempo2);
        Serial.print(" tiempo3= ");
        Serial.print(tiempo3);
        Serial.print(" tiempo4= ");
        Serial.println(tiempo4);
        Serial.print("velocidad= ");
        Serial.println(velocidad);        
}

Resultado "Calculo Velocidad"
Como resultado de este sketch podremos apreciar el numero de vuelta que ha realizado la rueda, la distancia recorrida, los tiempos en los que toma el tiempo (tiempo1 y tiempo 2), veremos el calculo de tiempo (tiempo3) y veremos el tiempo en hrs (tiempo4 simpre marcara, 0.00 ya que es un numero muy pequeño, pero no afecta en el calculo final)  y finalmente veremos en el puerto serial la velocidad que ha sido calculada entre cada revolución.

Obviamente es innecesario mostrar todos los tiempos en el puerto serial, pero los pueden borrar del sketch, yo solo los agregue para que pudieran notar las mediciones y cálculos realizados por el sketch.


        Serial.print(tiempo1);
        Serial.print("tiempo2= ");
        Serial.print(tiempo2);
        Serial.print(" tiempo3= ");
        Serial.print(tiempo3);
        Serial.print(" tiempo4= ");
        Serial.println(tiempo4);


Ahora solo resta agregar una pantalla LCD para montarlo en una bicicleta y ver la velocidad y distancia recorrida, esto lo que veremos en el próximo tutorial obteniendo el sketch final y definitivo.






No olvides visitar la pestaña Tutoriales arduino y también Lenguaje arduino, donde podrás encontrar la lista de tutoriales que he hecho hasta el momento. Como siempre muchas gracias por visitar mi blog y si tienen alguna consulta o consejo puede comentarlo por medio de este mismo blog.

6 comentarios:

  1. Hola Seba: Muy Bueno el proyecto. Quería contarte de que lo estuve probando y me gustaría saber como podemos hacer de que la velocidad vuelva a cero cuando la rueda se detiene ya que actualmente mantiene el valor de la ultima vuelta registrada.
    Saludos!

    ResponderBorrar
    Respuestas
    1. Jaja eso lo había dejado oculto, es muy fácil, tienes que hacer que cuando pase un tiempo suficiente como para considerar que no te estas desplazando y hacer que muestre velocidad cero.

      En el código final que se muestra en la continuación de este tutorial

      http://sebalabs.blogspot.cl/2015/11/arduino-12-parte-3-velocimetro-de.html

      Ahora vamos a modificar el void PantallaLCD() Aquí tendrás que agregar un "if" y poner la condición ejemplo: "si el tiempo t4 es mayor que 20 seg" que muestre en pantalla 0km/seg, si el tiempo es menor (else) entonces que muestre lo que ya mostraba por defecto.

      Si necesitas ayuda subo el código a modo de actualización, Saludos :)

      Borrar
    2. HOLA esta muy interesante tu proyecto, lo estuve implementando y realizando pruebas para que vuelva a cero pero no lo he logrado; si es posible me puedes ayudar con esa parte del codigo ?

      Borrar
    3. HOLA esta muy interesante tu proyecto, lo estuve implementando y realizando pruebas para que vuelva a cero pero no lo he logrado; si es posible me puedes ayudar con esa parte del codigo ?

      Borrar
  2. Buenas Sebastián,
    Pues me sumo a la pregunta del comentario anterior, precisaría conocer Distancia, Tiempo y Velocidad. pero digamos 'datos por tramo', de cada vez que se arranca hasta que se detiene.
    Mi intención es enviarlos en cada parada al servidor de ThingSpeak con ESP8266.
    No, no es la rueda de una bicicleta, pero tu codigo es aplicable a la rueda de mi hamster y podría graficar su actividad nocturna. Según el ordenador de bici que ahora le tengo puesto, corre unos 10-12 Km durante 3-4 horas cada noche!!!

    Un saludo desde España y MILGRACIES!!!

    ResponderBorrar
  3. Siendo novato en Arduino, ya me perdía al encontrarme con "millis()". Pero he de felicitarte por tus detalladas explicaciones ¡¡¡y en castellano!!!.

    Indagando un poco en el tema, y con la ayuda del traductor de Google he encontrado algo de ¿interrupciones?. Y tras dejarme las pestañas en las 7 páginas de éste tema:

    https://forum.arduino.cc/index.php?topic=345156.90

    creo que voy a intentarlo con el método del siguiente sketch:

    // wheel radius is 15mm
    // wheel radius in mm * 100 * 2 * ( pi * 10000 ) = 94.248000 mm perimeter.
    // 6 0's were used in scaling up radius and pi, 6 places are divided in the end
    // and the units work out. You can use integers more accurate than float on
    // Arduino at greatly faster speed. Both type of long can hold any 9-digits.
    // Arduino variable type long long can hold any 19 digits is 19 place accuracy.
    // if you work in Small Units and scale back later, integers are plenty accurate.
    // remember, this value has to be divided by microseconds per turn.
    const unsigned long wheel_perimeter = 1500UL * 2UL * 31416UL; // = 94,248,000 //radius * 2 * pi, 'UL' to force the constant into an unsigned long constant
    // wheel perimeter gets divided by microseconds, 1,000,000/sec (usec or us).
    // wheel turns once for 94248000 mm/100 in 1000000 usecs =

    unsigned long Speed = 0;
    unsigned long PrevSpeed = 0;

    volatile byte hall_rising = 0; // interrupt flag
    volatile unsigned long irqMicros;

    unsigned long startMicros;
    unsigned long differenceTimeMicros;

    unsigned long displayStartMillis;
    const unsigned long displayWaitMillis = 200;
    unsigned long ledFlashStartMillis;
    const unsigned ledFlashWaitMillis = 100;

    unsigned long hallEffectCount = 0;
    unsigned long distance = 0;


    void wheel_IRQ()
    {
    irqMicros = micros();
    hall_rising = 1;
    hallEffectCount++;
    }

    void setup()
    {
    pinMode( 2, INPUT ); // pin # is tied to the interrupt
    Serial.begin( 9600 ); // can this be faster? faster would be better

    attachInterrupt( 0, wheel_IRQ, FALLING ); // pin 2 looks for LOW to HIGH change
    }

    void loop()
    {
    while(hall_rising == 1){
    differenceTimeMicros = irqMicros - startMicros;
    startMicros = irqMicros;
    hall_rising = 0;
    }

    distance = (wheel_perimeter * hallEffectCount)/1000000000; //distance in meters
    if( differenceTimeMicros != 0 ){
    Speed = wheel_perimeter / differenceTimeMicros; //speed = distance / time
    Speed = (Speed*3600)/1000000; // this converts the speed from mm/s to Km/h

    if ( Speed != PrevSpeed )
    {
    Serial.print( distance );
    Serial.println( "mm " );
    Serial.print( Speed );
    Serial.print( "KM/H " );
    Serial.println( " " );
    }

    }else {
    Speed = 0;
    Serial.print( Speed );
    Serial.println( "KM/H " );
    }

    PrevSpeed = Speed;
    }

    ResponderBorrar