Pages: [1]   Go Down
Author Topic: Increasing RPM accuracy with IR sensor  (Read 702 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 2
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi Everyone,

This is my first post to the Forum, so please bear with me. I am trying to increase the accuracy of my rpm reading from 2 IR sensors that are measuring the rpm of a 8.5" diameter rotating shaft. I have placed an encoder strip on each shaft with 100 "tick" marks on it and I am using the attachInterrupt function to record the data. In order to be able to compare the to rotating shafts, I am recording the rpm every 100 ms. I am using time to be abel to compare the 2 shafts so that my data arrays are the same length.

The problem I am having is that if I reduce the accuracy number (recording data more often), I get erratic rpm readings. I do not know if this is because I am trying to take data too often or if it is an alignment issue. Any help or suggestions on this matter would be greatly appreciated.

IR Sensors:  SEN-00246 - Optical Detector / Phototransistor - QRD1114

Code:
#include <SdFat.h>
#include <String.h>

const int chipSelect = 8;

//Change these variables
int accuracy = 100;   //This is the number of ms between data points
int targetRPM = 900;
int num = 1;        // of our file.

//Naming our Files

String frontFile = "Fron";    //Create an array that contains this name
String rearFile = "Rear";      //Crears an arrya that contains the name of the rear rpm
String timeFile = "Time";
String text = ".txt";
char fFile[10];
char rFile[10];
char tFile[10];
char contents[100];


//SD Card Setup
Sd2Card card;
SdVolume volume;
SdFile root;
SdFile test;
SdFile file;
int i = 0;

//MOTOR
int motorPin = 10;

//Solenoid
int solenoidPin = 9;

//BUTTON
int switchPin = 5;
boolean lastButton = LOW;
boolean ledOn = false;
boolean currentButton = LOW;

//Front RPM Variables
int fRpmPin = 0;       //Digital pin 2
unsigned long rpmFront = 0;
volatile byte frontCount = 0;
unsigned long deltaT = 0;
unsigned long lastRpm_F = 0;
SdFile front;

//Rear RPM Variables
int rRpmPin = 1;    //Digital pin 3
unsigned long rpmRear = 0;
volatile byte rearCount = 0;
unsigned long deltaT_Rear = 0;
SdFile rear;

//Other Variables
unsigned long rpmFactor = 60000;    // millisec*sec = 1000*60
boolean motorOff = false;
boolean changeMotor = false;
unsigned long lastTime = 0;

//Time Variables
unsigned long plotTime = 0;
SdFile time;

void setup() {
  Serial.begin(9600);

  //SD CARD SETUP
  pinMode(10, OUTPUT);
  card.init(SPI_FULL_SPEED, 8);
  volume.init(card);
  root.openRoot(volume);

  //Switch SETUP
  pinMode(switchPin, INPUT);
  pinMode(solenoidPin, OUTPUT);
  pinMode(motorPin, OUTPUT);

  //IR Sensors
  attachInterrupt(fRpmPin, frontISR, CHANGE);
  attachInterrupt(rRpmPin, rearISR, CHANGE);

  NAME_FILE();

}

void loop() {
  currentButton = debounce(lastButton);

  if (lastButton == LOW && currentButton == HIGH) {
    changeMotor = !changeMotor;
    digitalWrite(motorPin, changeMotor);
  }
  NEXT_FILE();
  RPM();

  lastButton = currentButton;
}


void RPM() {
  deltaT = millis() - lastTime; 

  if (deltaT >=accuracy) {
    lastRpm_F = rpmFront;
    rpmFront = rpmFactor*frontCount/(99*deltaT);
    rpmRear = rpmFactor*rearCount/(102*deltaT);  //3 more ticks on this rotor thus the 102 vs 99
    frontCount = 0;
    rearCount = 0;

    Serial.println(rpmFront);

    lastTime = millis();

    if (rpmFront >= targetRPM | rpmRear >= targetRPM | motorOff == true) {

      //Serial.println("writing data");
      front.open(root, fFile, O_CREAT | O_APPEND | O_WRITE);
      sprintf(contents, "%d\n", rpmFront);
      front.print(contents);
      front.close();
      // Serial.println(rpmFront);

      rear.open(root, rFile, O_CREAT | O_APPEND | O_WRITE);
      sprintf(contents, "%d\n", rpmRear);
      rear.print(contents);
      rear.close();

      //Records time for ploting later
      plotTime = plotTime + accuracy;
      time.open(root, tFile, O_CREAT | O_APPEND | O_WRITE);
      sprintf(contents,"%d\n", plotTime);
      time.print(contents);
      time.close();

      if (lastRpm_F >= targetRPM |motorOff == true) {
        //Turns motor off at target RPM
        motorOff = true;
        digitalWrite(motorPin, LOW);
        digitalWrite(solenoidPin, HIGH);
      }


    }
  }
}



//This code read whether the switch is on or off
boolean debounce(boolean last)
{
  boolean current = digitalRead(switchPin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}

//creates a new file and restarts the cycle
void NEXT_FILE() {
  if (rpmFront <=40 && motorOff == true) {
    num++;
    rpmFront = 0;
    rpmRear = 0;
    digitalWrite(solenoidPin, LOW);
    delay(7000);
    digitalWrite(motorPin, HIGH);

    motorOff = false;
    frontCount = 0;
    rearCount = 0;
    plotTime = 0;

    frontFile = "Fron";    //Create an array that contains this name
    rearFile = "Rear";      //Crears an arrya that contains the name of the rear rpm
    timeFile = "Time";
    NAME_FILE();
  }
}

void NAME_FILE() {
  frontFile = frontFile + num;
  frontFile.concat(text);
  frontFile.toCharArray(fFile, 14);
  Serial.println(fFile);

  rearFile = rearFile + num;
  rearFile.concat(text);
  rearFile.toCharArray(rFile,14);
  Serial.println(rFile);

  timeFile = timeFile + num;
  timeFile.concat(text);
  timeFile.toCharArray(tFile,14);
  Serial.println(tFile);
 
}

//Front interrupt Servie routine
void frontISR() {
  frontCount++;
}

//Rear interrupt service routine
void rearISR() {
  rearCount++;
}








Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13479
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset



Quote
The problem I am having is that if I reduce the accuracy number (recording data more often), I get erratic rpm readings. I do not know if this is because I am trying to take data too often or if it is an alignment issue. Any help or suggestions on this matter would be greatly appreciated.

quick look
* I would change  volatile byte frontCount = 0;    to   volatile unsigned long frontCount = 0;    to keep overflows out ...(now occurs every 256 pulses
* I would change attachInterrupt(....CHANGE) to RISING , otherwise you get 2 interrupts per tick mark  (causes the byte to overflow twice as fast ...)

might help

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I can't see why you would need any more than 10 segments per revolution or even 6 or 4 or 2!

That's because you are assuming something like an electric motor driving a constant load. Imagine a four cylinder engine or a motor driving a few compressor cylinders. If you plot instantaneous shaft velocity versus time you can easily tell what the load is doing just from that plot. That may not be the application here, but that is an example of why it could matter.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13479
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Or you just want to know where (which angle)  it stopped ... Most of the time there are other mechanical thingies connected to the wheel measured, the might need to know that angle (e.g. valves)
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Dallas, Texas
Offline Offline
God Member
*****
Karma: 0
Posts: 861
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Or maybe you want to sense the relative positions of both ends of the same shaft to measure torque.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13479
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Then 100 tick marks may be a minimum I guess - I can imagine it working perfectly for that too -
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

United Kingdom
Offline Offline
Tesla Member
***
Karma: 223
Posts: 6593
Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I would implement both of robtillaart's suggestions. Also, in RPM() don't call Serial.println between clearing frontCount and backCount and assigning lastTime - you want to reset the counts as close to assigning lastTime as possible. You might even need to disable interrupts during these 3 operations. Something like this:

Code:
void RPM() {
  deltaT = millis() - lastTime;  

  if (deltaT >=accuracy) {
    noInterrupts();
    unsigned long currentTime = millis();
    unsigned int lastFrontCount = frontCount;
    unsigned int lastRearCount = rearCount;
    frontCount = 0;
    rearCount = 0;
    interrupts();
    deltaT = currentTime - lastTime;
    lastTime = currentTime;

    lastRpm_F = rpmFront;
    rpmFront = rpmFactor*lastFrontCount/(99*deltaT);
    rpmRear = rpmFactor*lastRearCount/(102*deltaT);  //3 more ticks on this rotor thus the 102 vs 99
    Serial.println(rpmFront);
...
« Last Edit: August 17, 2011, 03:02:49 am by dc42 » Logged

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.

Pages: [1]   Go Up
Jump to: