Monday, February 21, 2011

Arduino Stroboscope

I was watching TV a bit ago and playing with my model wind turbine from my fluids class and was having a good time playing with aliasing. From there, the obvious answer to my problem that I didn't really have was to build a stroboscope. This little tangent has resulted in a stroboscope capable of measuring rotational speeds from 577 to 30,000 rpm.



Before I get too far into the code and such, a brief description of a stroboscope is in order. A stroboscope is essentially a pulsed light at a known frequency. when the frequency of a rotating object matches that of the stroboscope, it appears to stand still. It is possible for the object to be moving at a multiple of the strobe frequency as well. for more info check out the interwebs or something.


My Arduino was already set up with 6 LEDs connected to ports 3, 5, 6, 9, 10, and 11 so I used that as a starting place. Since I don't have any drivers or expertise yet in dealing with led/lcd displays, I decided that the serial out would work for taking measurements.first I started trying to use the tone function, however, the 50% duty cycle was not adequate for my needs. The next step was to use a time based funtion that would be independent of the time it took to run the code. This was solved using millis(); however this did not produce adequate results as will be discussed later.

A potentiometer was used to control the strobe rate running off pin A0. all 6 LEDs were used as brighter is better. The basic circuit is about a follows:
What really makes it tick is the code. Its split into 2 sections because if you use only millis(), resolution at high speeds is terrible. I then tried using only micros() but then the lowest frequency was 31hz or about 1800 rpm. I split the code so that for high speed, it uses a version based on mircos() and low speed uses millis(). This split allowed for good resolution to about from 9.7Hz to 300Hz.

In terms of displaying the speed, I used a simple serial output to read the strobe rate as well as the rotational speed in rpm. To avoid delaying the program unnecessarily, the speeds are only reported 5 times per second. I had also noticed that having the serial port set to 9600 baud would cause errors in code execution. In order to handle data transfer, the baud rate was set to 115200. With the higher baud rate, everything seems to run smoothly.

Overall it was a neat little project that could prove to be very useful later on. Here is a little video that kind of shows it working, though that is a bit difficult to convey. It would have been nice to have an accurate stroboscope for some of my other projects, and now that I do essentially have one, I'm sure it will find some more interesting uses. 

I have included a video  that somewhat shows it working, but for best results, you would actually be using it. That and the video that my camera takes is horrid...


Its pretty cool, It even has a variable delay for how long to leave the LEDs on so it always will produce a sharp image with a constant brightness.I was quite surprised at how well the arduino was handling the math for how sensitive it was.

If anyone has an idea of something else cool to use it for, don't hesitate to mention it =]

And obviously some code =]

**EDIT: This is the newer code.. The new code uses micros()exclusively using an unsigned long for 17-2,500Hz so 1,020rpm to 150,000rpm The older code can be found here**
/*
  Analog input, serial output
 
 Reads an analog input pin, uses this value as a time delay for a stroboscope.
 Also sends frequency info back via serial.
 
 The circuit:
 * potentiometer connected to analog pin 0.
 Center pin of the potentiometer goes to the analog pin.
 side pins of the potentiometer go to +5V and ground
 * LEDs connected from digital pins 3,5,6,9,10,and 11 to ground
 
 created 2/20/2011
 modified 2/21/2011 
 by Steve Krave
 .
 
 */

//sets constants
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to

// Variables etc...
float sensorValue = 1;       // value read from the pot
float hold = 1;              // hold time between iterations (sort of)
float holdFrequency = 1;
unsigned long microsPrev = 0;       // set up value for timing       
unsigned long microsCurrent = 0;    // used for timing purposes
unsigned long millisSerial =0;      //used for serial output timing
float frequency = 1;         // stores frequency
float rpm = 0;              // rpm

void setup() {
  // initialize serial communications at 115200 bps
  // lower speeds can delay execution
  Serial.begin(115200); 

  //initialize pins as outputs
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
}

void loop() {
  // this while is a workaround for timer limitations using micros vs millis
  sensorValue = analogRead(analogInPin);   //take potentiometer reading
  hold = sensorValue*55 + 400;             //add some time to scale and for code execution
  microsCurrent = micros();                // collect current time
  if (microsCurrent > microsPrev + hold){  //set up timing loop
    frequency = 1000000/(microsCurrent - microsPrev);    
    microsPrev = microsPrev + hold;            //set up millis for delay stuff

    //set all 6 LEDs high (they were already plugged in)
    digitalWrite(3,HIGH);
    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(9,HIGH);
    digitalWrite(10,HIGH);
    digitalWrite(11,HIGH);
    delayMicroseconds(.02*hold);  //leave LEDs on long enough to see

    //set all LEDs low
    digitalWrite(3,LOW);
    digitalWrite(5,LOW);
    digitalWrite(6,LOW);
    digitalWrite(9,LOW);
    digitalWrite(10,LOW);
    digitalWrite(11,LOW);

    if(millis() > millisSerial + 200){ 
      millisSerial=millis();    //reset timer
      holdFrequency = 1000000/hold;    
      //do some math to find values
      rpm = frequency * 60;

      // print the results to the serial monitor:
      //      Serial.print("sensor  " );      //print sensor data for debug
      //      Serial.print(sensorValue);
      //Serial.print("holdFreq = " );                       
      //Serial.print(holdFrequency);
      Serial.print("  freq = " );                       
      Serial.print(frequency);      
      Serial.print("  rpm = ");      
      Serial.println(rpm);
    }
  }
}

8 comments:

  1. Hey! I dig your site, just wanted to let you know that your code has been put to good use measuring the speed of a gyro. Let's hear it for open source. Cheers from Boston!
    -AH

    ReplyDelete
  2. Hi, I need measure rotation at 380.000 can I use this code? where I can modify to measure this rotation?
    Tks

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. I'm curious why you switched to only use micros(). I do want to measure shafts rotating at less than 1,020rpm does the older code cover this?

    ReplyDelete
  5. Did you calibrate or verify its operation? Did you find something with a known RPM and see if the Arduino was reporting this speed correctly?

    ReplyDelete
  6. In my previous comment, I suggested using Timer 1 to generate the signal, in order to have cycle-accurate timings. I just tried this approach, here it is: https://gist.github.com/edgar-bonet/ec86ab7c65e7661b2f3dc4e3e053b0fb

    ReplyDelete
    Replies
    1. Seems my previous comment (quite long, with lots of suggestions) got somehow deleted. :-( Did you receive a copy via blogspot?

      Delete
  7. Dear Mista,

    Thanks for your amazing code. however, I found the older code better, cause the rpm range changing in fewer values ( at least in lower rpms). The rpm interval in the new code is 60 (cause of the cross product to turn the frequency to rpm). How can we decrease the interval (60)?

    With regards,

    ReplyDelete