Go Down

Topic: Measuring speed of a motorbike wheel (Read 1 time) previous topic - next topic

Markosec

Hi everyone,

I'm working on a small project, among sensing other stuff, i'm trying to get the speed of a motorcycle, based on the number of turns of the front wheel.
To do this, i've managed to put my hands on a small bicycle speedometer, chopped the magnet / sensor and connected it to an arduino mega (i believe it's a hall sensor, but who knows, the thing is heavily packed in plastic, with only 2 wires coming out)
The arduino 5+v goes to the sensor, and the input goes to pin 18(wich should be an interrupt if i'm not wrong) and is tied to ground via a 3.9k. Everytime the magnet reaches the sensor, i'm getting an interrupt. So far so good.
I'll try to add more detail:
*/Not necessarily useful stuff
Refreshing the speed in the screen every 500 ms sounds great to me, so i'm counting how many times the interrupt gets triggered in that amount of time.
I've measured the wheel circumference, and it's 1.88 meters.
So, let's say i count 3 turns in 500ms, then this should be something like:
3 turns * 1.88 mts = 5.64 mts
If in 500 ms the wheel does 3 turns, then in 3600000(1hour) it would be...21600 turns... based on that, i can assume speed.
/* End of blablah
Well, the problem actually is that the wheel circumference is too big, and the speedometer "resolution" is really poor, it jumps from 0 to 13 to 27 to 40, and so on...
I know adding more magnets would give me more accuracy, but i had to debounce the sensor, because it was giving me two readings every time the magnet was passing by.
From some basic(random)  calculations, i've used 50 ms as debouncing. If the last pulse was read at least 50 ms ago, it's counted, if not, i discard it (do nothing).
Adding more magnets means less time between pulses, wich could be impossible to debounce.
Searching different methods, i've found an optic mouse-like sensor.
I'm thinking on using the brake disc to count revolutions, using the small refrigeration holes. But i'll have to deal with interference from the sun, cars headlights, road water. rain,  and lots more...
I've seen rotary encoders, but i'm not sure i can mount that, and price here in my country is a bit too high. Can it be mounted as the old bike generators? (a little wheel spinning with the main wheel) Maybe using one of those little electric engines from toy cars? can it be measured somewhat proportional to the number of turns?
Is there a better method of doing this? It doesn't need to be 1km accurate, but the more, the merrier  :D
I can add more detail, but might be even more confusing.
Any idea helps!

Thanks in advance!
I'll do it, just because i can do it

JavaMan

Hi Markosec,

If you count the number of revolutions in 500ms, your error can be as large as 1.88 * 2 = 3.76 meters per second.  That's 13.5 kilometers per hour!

Why don't you just measure the time of each revolution instead? 

Markosec

Thanks JavaMan, your idea is just what i need :)
Doing all the other stuff, i ended up confused and trying to do it the wrong way. :smiley-roll-blue:
I'll change the code and post results :D

Again, thanks!!
I'll do it, just because i can do it

Markosec

Hi again,
I am trying to implement that solution, but there must be something (again) i'm missing.
First of all, the code:
In the setup:
Code: [Select]
  attachInterrupt(5, contar, RISING);
The interrupt routine:
Code: [Select]

void contar()
{
  //Count only if pulses are at least 40 ms from each other...
  if ( (millis() - ultimo )  > 40)
  {
    tiempos[puntero] = millis() - ultimo; //Save the difference in an array
    puntero++;                            //advance the array pointer
    if ( puntero == 4 )                   //don't fall from the array!
      puntero = 0;
    m_totales += 1.88;                    //count total distance
    ultimo = millis();                    //remember when this pulse happened
  }
}


Then, the processing loop (a loop inside the main loop, because of the menu):

Code: [Select]

while( !myTouch.dataAvailable())  //Repeat until there is a touch on the screen, anywhere...
  {
    if ( ( millis() - ultima_vez) > 500 )  //refresh every 500 ms
    {
      //Check if nothing changed since last time...
      igual = true;   //turn on flag...
     
      for (i = 0; i<4 ; i++)
        if ( verificacion[i] != tiempos[i] )
          igual = false;                //Something has changed
      if (igual)                        //If nothing has changed
        for (i = 0; i<4 ; i++)          //empty everything
          tiempos[i] = 0;
      promedio = 0;                     //clean average time..
     
      if (!igual)                       //Only calculate average if there is something to calculate
        for (i = 0 ; i<4 ; i++)
        {
          promedio = promedio + tiempos[i]; 
          if ( tiempos[i] != 0)         //sum up every non-zero item
            contador++;               
        }   

      if ( promedio != 0)
      {       
        promedio = promedio / contador;
        //           1 hour   wheel   average     km
        promedio = 3600000UL * 1.88 /  promedio / 1000 ;
      }                 
      myGLCD.printNumI(promedio, 10 , 140,3,'0');  //Write the calculated speed
      for (i = 0 ; i<4 ; i++)                      //Additionally
      {                                            //write every measured time
        myGLCD.printNumI(tiempos[i],50,(i*16+40),10,'0');       
      }
      for (i = 0 ; i<4 ; i++)                      //copy everything for the next turn
        verificacion[i] = tiempos[i];           
      ultima_vez = millis();                       //remember time for the next 500ms
    }
  }

I've placed some comments, but the general idea, is to show  4 positions of timings. The four positions in the display, should be the four positions of the array "tiempos". The array "verificacion" it's a general idea to check for changes (when i brake to 0 the bike, it shouldn't be waiting for another wheel turn)
The lcd library is taken from http://www.henningkarlsen.com/electronics/library.php?id=51, and seems to be working fine.
All of this, to get an average speed every 500 ms, counting the average of the array "tiempos".
Code: [Select]
promedio = 3600000UL * 1.88 /  promedio / 1000 ; is an approach to measure speed, 1.88 is the wheel perimeter ,and i'm trying to show it in KM/H
The behaviour, is that only in the first turns, the positions are filled correctly, but then the first 3 positions are never updated, and the last one begins to show random numbers (i guess are random, because it changes without noticeable pattern, and even some negative numbers appear).
I've tried not clearing the array "tiempos", but didn't change behaviour at all.
Any idea about what could be wrong? Anything helps!
Thanks!

I'll do it, just because i can do it

dc42

1. Please post the code where you declare the arrays and other variables used in the ISR, so we can see what types they have, and whether they are declared 'volatile'.

2. It's better to call millis() just once at the start of the ISR rather than 3 times, like this:

Code: [Select]

void contar()
{
  //Count only if pulses are at least 40 ms from each other...
  unsigned long now = millis();
  if ( (now - ultimo )  > 40)
  {
    tiempos[puntero] = now - ultimo; //Save the difference in an array
    puntero++;                            //advance the array pointer
    if ( puntero == 4 )                   //don't fall from the array!
      puntero = 0;
    m_totales += 1.88;                    //count total distance
    ultimo = now;                    //remember when this pulse happened
  }
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Go Up