Society of Robots - Robot Forum

Software => Software => Topic started by: DTM22 on November 19, 2011, 05:45:26 PM

Title: Rate of Fire using Arduino
Post by: DTM22 on November 19, 2011, 05:45:26 PM
Im attempting to have my Arduino Duemilanova output the Rate of Fire of a paintball marker using two optical sensors.  I have the sensors working properly and I am able to check when they are triggered however I am having trouble figuring out how to have the program check the shotcount every second.  Ive read some tutorials on Timer Interrupts but am not 100% sure I could use them as I have no external trigger and my program is extremely time sensitive.  I simply need to check the value of ShotCount every second.

Right now, I have my program increment the shotcount("ShotCount++;") every time a ball triggers both sensors.  I need to be able to determine(With an accuracy of 1 decimal place) the ROF in Balls/Second. Anyone have any ideas?
Title: Re: Rate of Fire using Arduino
Post by: joe61 on November 19, 2011, 10:29:09 PM
So, you're getting a shot count, you just want to look at it every second? I'm not sure why you would need an external trigger for that. Timers are able to wake up at periodic intervals. Just setup an ISR to read the count, set the value in a variable, and maybe a flag, for the main routine to check.

Be sure to declare the variable volatile since you're sharing it between interrupt and non-interrupt code.

Also be careful about which timer you use. Arduino claims some or all of them, depending on what else is going on.

Edit:
Quote
Right now, I have my program increment the shotcount("ShotCount++;")

If you're really time constrained, using the preincrement operator is a bit faster because there's no need to save the current value before incrementing it

Joe
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 21, 2011, 12:29:36 PM
So I've got the interrupt working, however, I'm not getting any decimal precision for the Rate of Fire which is standard on your average paintball Chrono...looking at my code, I'm not sure it's been written to do so and it might require a re-write of certain sections.   Anyone know how I could go about having the BPS displayed with an accuracy of 1 decimal place?

My code:
Code: [Select]
#include <LiquidCrystal.h> //Liquid Crystal Library
#include <TimerOne.h>      //Timer 1 Library
#define pin6 (1 << PORTD6)
#define pin7 (1 << PORTD7)       
             
        int Flag;                         
        unsigned long StartTime;         
        unsigned long StopTime;           
        unsigned long Duration;           
        unsigned long FPS;                 
        volatile unsigned long ShotCount; 
        volatile unsigned long BPS;       
   
        LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

        void setup()
        {
         DDRD &= ~ ((1 << PORTD6) | (1 << PORTD7));  // Sets Pins 6 and 7 as INPUTS
         Timer1.initialize(1000000UL);               // 1000000 microseconds (unsigned long) is 1 second
         Timer1.attachInterrupt(OneSecInterrupt);   
         lcd.begin(16, 2);             
         Duration=0;                   
         FPS=0;                         
         BPS=0;                         
         Flag=0;                         
        }

        void loop()
        {
            if (PIND & (1 <<PORTD6) && Flag==0)   
            {
              StartTime=micros();                   
              Flag=1;                               
            } 
           
            if (PIND & (1 <<PORTD7) && Flag==1)
            {
              StopTime=micros();                   
              ++ShotCount;                         
              Flag=0;                             
              Duration=StopTime-StartTime;         
              FPS=(0.5*(1000000))/Duration;         
            }
           
           if(BPS<=3)
           {
            PrintVelocity();
           } 
           if(BPS>3)
           {
            PrintROF();
           }     
        }
       
       
        void OneSecInterrupt()
        {
        BPS = ShotCount;
        ShotCount = 0;
        }
       
        void PrintVelocity()
        {
        lcd.clear();                   
        lcd.print("  Ball Velocity");   
        lcd.setCursor(0, 1);     
        lcd.print(FPS, DEC);             
        lcd.print("  Feet/Second");     
        }

        void PrintROF()
        {
        lcd.clear();                   
        lcd.print("    Fire Rate");     
        lcd.setCursor(0, 1);           
        lcd.print(BPS, DEC);           
        lcd.print("   Balls/Second");   
        }       
Title: Re: Rate of Fire using Arduino
Post by: joe61 on November 21, 2011, 05:10:07 PM
Integer arithmetic is used by default. You'd have to link in the math libraries and use floating point arithmetic. For example, instead of 10 / 3 use 10.0 / 3.0.

However, that's going to significantly increase code size, and hurt performance. Maybe you could do something like multiply values by 10 before doing arithmetic and then using integer division and modulus to simulate the floating point value.

Joe
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 22, 2011, 07:10:06 AM
So what your saying is I should continue to measure the shotcount every second, but before calculating the BPS(Balls/Second), I should multilply shotcount by 10. Then how do I go about using integer division and the modulus operator to simulate the point value?
Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 22, 2011, 09:13:10 AM
BPS / 10 would give you everything to the left of the decimal point, BPS % 10 would give you the digit to the right.

But that's mostly irrelevant--I think your problem is that you're using the interrupt to do the less time-sensitive task (printing out the data) instead of the more time-sensitive task (counting the number of balls going through).

Try something like this (untested): You'll need to move your sensors from PD6 and PD7 to PD2 and PD3, since that's where the hardware interrupts are connected to on the ATMega328P
Code: [Select]
#include <LiquidCrystal.h>

const uint8_t FIRST_INTERRUPT = 0;
const uint8_t FIRST_INTERRUPT_PIN = 2;

const uint8_t SECOND_INTERRUPT = 1;
const uint8_t SECOND_INTERRUPT_PIN = 3;

volatile uint32_t shot_count;
volatile uint32_t first_interrupt_time;
volatile uint32_t second_interrupt_time;
volatile uint8_t flag;

float rof;
float fps;
uint32_t last_process;

LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

void first_isr() {
  first_interrupt_time = micros();
  flag |= (1 << FIRST_INTERRUPT);
}

void second_isr() {
  second_interrupt_time = micros();
  ++shot_count;
  flag |= (1 << SECOND_INTERRUPT);
}

void process_rof() {

  uint32_t process_time = millis();
  rof = shot_count / ((process_time - last_process) * 1.0e-3);

  shot_count = 0;
  last_process = process_time;  
}

void process_fps() {
  uint32_t duration = second_interrupt_time - first_interrupt_time;
  fps = 0.5 / (duration * 1.0e-6);
}

void print_fps() {
  lcd.clear();                    
  lcd.print("  Ball Velocity");    
  lcd.setCursor(0, 1);      
  lcd.print(fps, DEC);            
  lcd.print("  Feet/Second");    
}

void print_rof() {
  lcd.clear();                    
  lcd.print("    Fire Rate");    
  lcd.setCursor(0, 1);            
  lcd.print(rof, DEC);            
  lcd.print("   Balls/Second");    
}

void setup() {
  // set pins to input
  pinMode(FIRST_INTERRUPT_PIN, INPUT);
  pinMode(SECOND_INTERRUPT_PIN, INPUT);
  // interrupt when pin2 goes from LOW to HIGH
  attachInterrupt(FIRST_INTERRUPT, first_isr, RISING);
  // interrupt when pin3 goes from LOW to HIGH
  attachInterrupt(SECOND_INTERRUPT, second_isr, RISING);

  // initialize to zero
  shot_count = first_interrupt_time = second_interrupt_time = flag = last_process = 0;
}

void loop() {
  // if more than a second has elapsed
  if (millis() - last_process > 1e3) {
    process_rof();
    if (rof > 3) {
      print_rof();
    }
    else {
      print_fps();
    }
  }
  // if both interrupts have occurred
  if (flag & (1 << FIRST_INTERRUPT) && flag & (1 << SECOND_INTERRUPT)) {
    process_fps;
    flag = 0;
  }
}
Title: Re: Rate of Fire using Arduino
Post by: joe61 on November 22, 2011, 01:48:01 PM
FWIW, GCC does support fixed point math. http://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/Fixed_002dPoint.html (http://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/Fixed_002dPoint.html)

Don't know if it's any faster than floating point though.

Joe
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 23, 2011, 03:40:04 PM
What if I have the interrupt check the shotcount every 3 seconds instead of every second, than I divide that value by 3 to get the BPS, you think that would be an effective way of going about it?
Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 23, 2011, 04:37:40 PM
It doesn't solve the problem.

Anyways, one of the things you're missing is that you're storing FPS as an unsigned long--if you want decimal precision, you need to use a float or a double.
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 25, 2011, 11:41:55 AM
I put FPS as an unsigned long because I dont need decimal precision for it, its for BPS where i need the decimal precision.   So how do i use float or double to get my decimal precision?
Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 25, 2011, 12:51:26 PM
Instead of doing
Code: [Select]
volatile unsigned long bps;
you do
Code: [Select]
volatile float bps;
Float and double are the same size in avr-gcc, so it doesn't really matter which one you use.

Nevertheless, your methodology will never give you a value of BPS that isn't an integer.
Think about it this way: your algorithm says:

1) I will count every ball that passes
2) Each second I will print my count as the rate of fire
3) I will thereafter reset the count to zero

Except that you can only count in integers...

If you want ROF as a decimal, you need to use a more mathematical approach, forex:

1) I will count the time between each ball that passes
2) I will invert that time to find the number of balls that should pass each second
3) I will print this calculated ROF each second
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 26, 2011, 03:17:50 PM
Quote
f you want ROF as a decimal, you need to use a more mathematical approach, forex:

1) I will count the time between each ball that passes
2) I will invert that time to find the number of balls that should pass each second
3) I will print this calculated ROF each second

Doesn't that assume your ROF is constant?
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 27, 2011, 12:20:29 PM
I found this in another thread, it seems to be relevant as he states floating point is too slow and ads 2-3k with the required libraries which would considerably slow down my program. Couldn't I use division as he states to get the value to the left and then the modulus to give me the remainder?
 
Quote
Yea, I avoid floating point if at all possible, except when I'm too lazy to do an alternative. Its better to multiply everything by 10^x. For example:

slow:
5.5/5.5 = 1

faster:
55/55 = 1

Also maybe of interest to you is modulus:
5/2 = 2
5%2 = 0.5

Or to avoid floats for a 5/2 calculation:
5/2 = 2
5%2*10 = 5

(the solution is 2.5)

Title: Re: Rate of Fire using Arduino
Post by: joe61 on November 27, 2011, 12:55:24 PM
That was suggested earlier in this thread.

Joe
Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 27, 2011, 01:55:39 PM
Quote
f you want ROF as a decimal, you need to use a more mathematical approach, forex:

1) I will count the time between each ball that passes
2) I will invert that time to find the number of balls that should pass each second
3) I will print this calculated ROF each second

Doesn't that assume your ROF is constant?

If you take an average of, say, three consecutive ROFs, it should be fairly accurate.

I found this in another thread, it seems to be relevant as he states floating point is too slow and ads 2-3k with the required libraries which would considerably slow down my program. Couldn't I use division as he states to get the value to the left and then the modulus to give me the remainder?
 
Quote
Yea, I avoid floating point if at all possible, except when I'm too lazy to do an alternative. Its better to multiply everything by 10^x. For example:

slow:
5.5/5.5 = 1

faster:
55/55 = 1

Also maybe of interest to you is modulus:
5/2 = 2
5%2 = 0.5

Or to avoid floats for a 5/2 calculation:
5/2 = 2
5%2*10 = 5

(the solution is 2.5)

This isn't your problem.
Your problem is this:
If I were to count the number of balls passing, it will always be an integer (you can't measure a half ball)
If I divide that by 1.000000000000 seconds, I get the same integer (possibly with many zeros following it)
Thus when I print it out it's always an integer value.

You have two ways to solve this:
1) measure time intervals
2) change the time interval of your measurement to 10 seconds, such that you can get another significant figure of accuracy. Then, you can use (either floating point or modulus/divison) to get the final value to print.

In this case, floating point's "too slow" will still finish well within the time you have to calculate the ball's path. Your current limitation is that you're using a free-running loop to check for the presence of the paintball, and so slowing that loop (even a little) causes problems. As referenced in the code example I posted earlier, you're much better off using interrupts to check for the ball's presence, and doing the calculation in the loop instead.
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 27, 2011, 06:57:41 PM
I tried running the example code you gave me and changing my inputs to pins 2 and 3 and I couldn't get it to work...ill see if I can't figure it out.  Im going to try and revisit my code with the recommendations you gave me.

I do have some questions regarding some of the terminology you used your example code:

Code: [Select]
#include <LiquidCrystal.h>

const uint8_t FIRST_INTERRUPT = 0;
const uint8_t FIRST_INTERRUPT_PIN = 2;

const uint8_t SECOND_INTERRUPT = 1;
const uint8_t SECOND_INTERRUPT_PIN = 3;

volatile uint32_t shot_count;
volatile uint32_t first_interrupt_time;
volatile uint32_t second_interrupt_time;
volatile uint8_t flag;

float rof;
float fps;
uint32_t last_process;


I'm unfamiliar with the way you declared your variables..Is "volatile uint8_t flag;" the equivalent of
"volatile unsigned int flag;" and in that case, what is the difference with
"volatile uint32_t second_interrupt_time;"?

Code: [Select]
void loop() {
  // if more than a second has elapsed
  if (millis() - last_process > 1e3) {
    process_rof();
    if (rof > 3) {
      print_rof();
    }
    else {
      print_fps();
    }
  }


In the line " if (millis() - last_process > 1e3)", what is "1e3"? 1000?
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 29, 2011, 01:55:58 PM
If you could elaborate on your use of "last process" and "process time" it would be much appreciated. Meanwhile Ive revisited my code using your recommendations and example and have tested it with my paintball marker.  It does give me a very accurate Velocity measurement however regardless of how fast I'm shooting(Balls/Second), it never prints the ROF.  Any ideas?

Code: [Select]
void process_rof() {

  uint32_t process_time = millis();
  rof = shot_count / ((process_time - last_process) * 1.0e-3);

  shot_count = 0;
  last_process = process_time; 
}

Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 29, 2011, 05:50:23 PM
last_process is the time that the ROF was last processed, where as process_time is the current time (of processing).
More semantically, it might be better to use last_process_time and process_time instead of last_process and process_time.

uint8_t is an unsigned int, yes, but it explicitly shows how wide the integer is (the '8' denotes an 8-bit integer, and the 'u' that is unsigned).
uint32_t, therefore, is a 32-bit unsigned integer, which can also be referred to as an unsigned long. Since millis()/micros() returns values which are unsigned longs, and can grow to be quite high, it is important to use a variable that is at least as wide as what the function returns to avoid errors.

The usage of 1en is scientific notation, such that 1en = 1 * 10n. In the process_rof method, since it is called (approximately) every second, the value dt = process_time - last_process should be just about 1 second, give or take a little. Unit conversion means that it needs to be divided by 1000 to get time in seconds, thus the multiplication by 1.0e-3 (0.001), and rof is shots per second, so all of those are combined into a single statement.

In terms of debugging, since I don't have your setup, it may be easier just to have it only print fps, to see what values are being reported.
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on November 30, 2011, 03:55:15 PM
Thanks for the explanation, so Ive been troubleshooting why I'm unable to get the ROF to display. I found out that when I place my shot Increment within the second interrupt function as you showed in your example code:
B
Code: [Select]
void second_isr()
{
  second_interrupt_time = micros();
  ++shot_count;
  flag |= (1 << SECOND_INTERRUPT);
}
the shotcount increments by random and sometimes fairly large amounts...
to corrreect this I move the shot increment within my loop as such
Code: [Select]
void loop()
{
  // if more than a second has elapsed
  if (millis() - last_process > 1e3)
   {
    [b]++shotcount;[/b]
    process_rof();
    if (rof > 3) {
      print_rof();
    }
}
I don't know if this will cause further problems but it seems to work for the time being. However,  I've now been stuck on trying to figure out how I will reset the shotcount to 0.
Ive tried printing the BPS value and it constantly reads 0.... is it possible there is a problem with the BPS calculation or possibly the millis() function?  My loop looks identical to the one in your example code other than the addition of the shotcount increment..
Title: Re: Rate of Fire using Arduino
Post by: rbtying on November 30, 2011, 05:05:56 PM
If you just randomly increment shot_count in the loop, the values you read aren't going to have much to do with the actual rate of fire--perhaps you might want to make the time interval between calculations of BPS larger, so that shot_count can get to higher values.

Also, you probably need to debounce your sensor inputs--each interrupt should fire only once per ball. Your modification basically makes the bps calculation pointless, since shot_count will always be one when the calculation occurs.

You should carefully debug your sensor setup, or add a delay between subsequent detections of the ball (software debouncing).
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on December 01, 2011, 02:05:18 PM
Sorry, I made a mistake in the quote I put above, what I meant to say was that I put the shotcount increment within this part of the loop:
Code: [Select]

void loop() {
 
  // if both interrupts have occurred
  if (flag & (1 << FIRST_INTERRUPT) && flag & (1 << SECOND_INTERRUPT))
   {
    process_fps;
    ++shotcount;
    flag = 0;
  }
}

therefore, the shotcount only increments when a ball has triggered both interrupts.  When I had my shotcount printing on my LCD screen, this seemed to work fine.  But, as I mentioned above, the BPS still remained 0.
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on December 02, 2011, 12:29:52 PM
So I figured out how to debounce my inputs, which allowed me to put the shotcount increment back into the second ISR.  I had the shotcount print on my LCD and it now properly increments by 1 after each ball passes.  My ROF also is now mesasured properly.  I just have one question relating to the decimal precision.  Im having my program multiply the BPS values by 10 and then use integer division and the modulus operator to give me the remainder as mentioned above.  My question  is how to have the value displayed in the proper form?  right now I have it displaying in the format (x.0.x) when I want to have (x.x).   
Title: Re: Rate of Fire using Arduino
Post by: rbtying on December 02, 2011, 03:39:52 PM
Congrats on getting the sensors working--that's the hard part done and over with.

For printing, does this not work:
Code: [Select]
  lcd.print(rof/10);
  lcd.print(".");
  lcd.print(rof%10);
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on December 03, 2011, 01:26:53 PM
That does give the proper format, Thanks.  What I don't understand is why Im dividing by 10.  If I divide my current ROF by 10, it will print 1/10 of the actual ROF.  If I multiply the ROF by 10 before hand, then use integer division and the modulus operator.  The remainder will always be 0.  I don't understand exactly how this works?
Title: Re: Rate of Fire using Arduino
Post by: rbtying on December 04, 2011, 01:34:57 AM
You can't get any more precision than you already have--if your calculation (that is to say, the calculation I used in the example) calculates it in floating-point, then changing it directly to integer math loses everything after the decimal point. On the other hand, if you change the calculation to instead give 10 * the original value (in the original calculation, not by multiplying afterwards), then you can get an additional digit of precision.

Otherwise, you get this:

Code: [Select]
int a = 2; // initial value
int leftofdecimal = 2 * 10 / 10;
int rightofdecimal = 2 * 10 % 10;

A cursory glance at that will show you why multiplying by 10 doesn't give you 10x more precision.
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on December 04, 2011, 01:51:43 PM
So if I understand correctly, I need to multiply my shotcount by 10 before calculating my ROF, then divide by 10 and use the modulus operator to give me my actual ROF with an accuracy of 1 decimal place??
Title: Re: Rate of Fire using Arduino
Post by: rbtying on December 04, 2011, 05:23:45 PM
Yes, but be well aware that this will NOT (by itself) keep you from using any floating-point operations, and in fact will most likely slow down (though imperceptibly) the operation of your device.

Note that the usage of ANY floating-point operation incurs a performance penalty, and trying to resolve that for a performance gain that you don't need is pointless and a general waste of effort. You are, by an large, better off using floating-point calculations in this application.

In short, this is what you're currently planning on doing:
1. count shots (and multiply by 10)
2. do floating-point calculation to get ROF
3. cast it to a fixed-width int and throw away the precision gained from the previous floating-point calculation in step 2
4. print it out using standard fixed-width techniques (division/modulus).

You would need to do quite some hackery to get it all to work well with pure integer manipulation, primarily because millis() and micros() are in 1000ths and 1000000ths of a second respectively, and you're trying to get things in terms of seconds (remember that ints overflow, too).
Title: Re: Rate of Fire using Arduino
Post by: DTM22 on December 05, 2011, 12:42:43 PM
Ok, so I should use floating point calculations instead despite the fact that it will slow my program(imperceptibly). What libraries do I need to add and how exactly are these calculations preformed?
Title: Re: Rate of Fire using Arduino
Post by: rbtying on December 06, 2011, 04:54:54 AM
You don't need to add any libraries, and they're done by using floating-point variables and numbers in your calculations (aka the ones you've been using). That is, add a .0 to the end of your integers, and declare them floats instead.