Geschwindigkeit messen mit Arduino           

von Gerd Sinning                       

Elektronik-Labor   Projekte   AVR    


Was man immer schon wissen wollte.. Wie schnell sind die kleinen Rennautos. Mit einem Arduino und wenigen Bauteilen kann man die Geschwindigkeit messen. Die Bauteile sind praktisch in der Bastelkiste oder im Starter-Kit und kosten wenig, allerdings muss man etwas sägen und hämmern um es zu bauen. Man braucht einen Laserpointer und eine Fotodiode wie etwa BPW34 (LDR geht sicher auch), dann ein paar Oberflächenspiegel, die sind aus einer alten Harddisk ausgebaut und sehen so aus wie eine glänzende CD. Mit der Blechschere werden daraus zwei Segmente geschnitten, das sind die Spiegel zum Umlenken des Laserstrahls. Zwei Bretter braucht man auch noch um das zu befestigen.

Das Schema vom Versuchsaufbau, mit Laserpointer, Spiegel und Fotodiode

Wenn ein Messobjekt den Laserstrahl unterbricht, dann sieht man ein Oszillogramm wie unten in grün. Das Signal der Fotodiode geht auf den Input Capture Pin des Arduino und die fallende Flanke triggert und speichert die Startzeit. Dann wird der triggerlevel umgedreht und wenn das Messobjekt den Laserstrahl wieder freigibt dann triggert die steigende Flanke und speichert die Endzeit1 für T1. Wieder wird der Triggerlevel umgedreht und die fallende Flanke triggert und speichert die Endzeit2 für Tgesamt. Jetzt ist die Zeit bekannt und die Geschwindigkeit v lässt sich berechnen, v ist gleich Weg (25cm) durch die Zeit Tgesamt. Gemessen wird in beiden Richtungen, von links nach rechts oder umgekehrt.

Man sieht, das Messobjekt muss kürzer als der Weg sein, das kann ein Nachteil sein, aber man kann dann die Wegstrecke von 25 cm auf z. B. 100 cm verlängern bis es passt. Der Aufbau hat ein paar Vorteile, es gibt nur eine Fotodiode für die Messung, so dass die Abfallzeiten der Elektronik keine Rolle spielen, sie sind jeweils gleich. Die gesamte Verdrahtung ist auch nur auf einer Seite, recht angenehm im Aufbau.

Der Versuchsaufbau, links unten der Laserpointer, oben die Spiegel und rechts unten die Fotodiode, in der Mitte unten ein Verteilerboard und der Arduino

Erst mal braucht man zwei Bretter, in den Spiegelhalter werden zwei 45 Grad Schlitze für die Spiegel gesägt (mit einer Stchsäge). Auf dem zweiten Brett wird der Laserpointer mit 3 Nägeln befestigt und die Fotodiode mit 2 Nägeln und einer Schraube. Erstaunlich, wie gut und exakt das funktioniert, es hilft, wenn man die Löcher vorbohrt. Dann werden der Arduino und die kleine Platine angeschraubt. Der Laserpointer hat zwei Vorwiderstände von je 33 Ohm in Serie und läuft kontinuierlich mit 20 mA bei 5 V, das ist recht hell. Nur ein Tip, den Laserpointer nicht auseinandernehmen, für die Masseverbindung wird eine Holzschraube in die Spiralfeder gedreht, die Plusleitung wird ans Gehäuse gelötet. Die Kathode der BPW34 ist an +5V angeschlossen und geht über 100 k an Masse, die Mitte geht an den Input Capture Pin PB0, pin8 vom Arduino.

Justieren ist wichtig, mit einer Schraubzwinge und einem Brett bleibt das parallel und man kann die Spiegel justieren. Der Abstand soll 25 cm betragen.

In der Software gibt es dazu eine kleine Hilfe, wenn man Pin 9 auf Masse legt dann wird der TIMER1 abgeschaltet und die Led 13 on board leuchtet, wenn der Laserstrahl die Fotodiode trifft. Man verbiegt die Spiegel solange, bis die Led leuchtet. Wenn pin9 offen ist wird gemessen.

Zum Test habe ich mal eine Kugel durchrollen lassen, von einem Aluwinkel als schiefe Ebene. Sie rollt dann mit konstanter Geschwindigkeit durch die Lichtschranke, hier das Ergebnis. Die Ausgabe ist über die serielle Schnittstelle, LCD Anzeige ist in der Software definiert aber hier nicht verwendet.

.PW low= 794837 PWtotal= 6483229 PW % = 12.26
Time= 405201.81 usec Speed= 61.698 cm/sec
PW low= 577742 PWtotal= 4643579 PW % = 12.44
Time= 290223.69 usec Speed= 86.140 cm/sec
PW low= 496057 PWtotal= 3974245 PW % = 12.48
Time= 248390.31 usec Speed= 100.648 cm/sec

Die PW (pulse width in counts) sind mehr fürs Debugging und kann man löschen, dann kommt die Laufzeit in Mikrosekunden und die Geschwindigkeit in cm/sec. Interessant ist vielleicht noch PW %, das Verhältnis von .PW low zu PWtotal, daraus kann man die Länge des Messobjekts berechnen. Hier ist das ziemlich konstant bei ca 12 %, das sind 3 cm von gesamt 25 cm. Stimmt recht gut mit der Kugel überein.

Die Geschwindigkeitsmessung mit Arduino funktioniert gut, mit geringen Kosten und etwas Arbeit . Viel Spaß beim Basteln!

Download: Speedy.zip

/*
Arduino Speedy
measure time and calculate speed
uses a laserpointer, 2 mirrors and a photodiode for light barrier

photodiode signal goes to Input Capture Pin on PB0, Arduino pin8
measures time from falling edge to next falling edge = total time
first measures time from falling edge to next rising edge = time1 (low time)
useful to abt 50 us, minimum low time is 4 us

serial output and LCD defined/not used

GS 3-2014

This is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
*/


//include the library code:
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
/*
* LCD RS pin to digital pin 7
* LCD Enable pin to digital pin 6
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
*/

#ifndef F_CPU
#define F_CPU 16000000L
#endif

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

int pinLed=13; // on board Led
int pinStart=9; // HIGH to measure, LOW to adjust
int pinTrig=8; // ICP pin input

volatile unsigned char p_mlt = 0; // Timer1 Overflows total
volatile unsigned char p_mlt1 = 0; // Timer1 Overflows low time
volatile unsigned char p_ready; // Flag
volatile unsigned int StartTime = 0; // ICP pin 1st edge falling
volatile unsigned int EndTime1 = 0; // ICP pin next edge rising
volatile unsigned int EndTime2 = 0; // ICP pin last edge falling

ISR( TIMER1_CAPT_vect )
{
static unsigned char edge = 0;

if( p_ready ) return; // wait until display done

if( edge == 0 ) // 1st edge falling
{
StartTime = ICR1;
TIMSK1 = 0; // stop Interrupts , Capture & Overflow
p_mlt = 0;
p_mlt1 = 0;
TCCR1B |= (1<<ICES1); // then change to rising
++edge; //
TIMSK1 = (1<<ICIE1) | (1<<TOIE1); // enable Interrupts , Capture & Overflow
}

else if( edge == 1 ) // 2nd edge rising
{
EndTime1 = ICR1; // time high
TIMSK1 = 0; // stop Interrupts , Capture & Overflow
p_mlt1 = p_mlt;
TCCR1B &= ~(1<<ICES1); // then change ICES1 to falling
++edge; //
TIMSK1 = (1<<ICIE1) | (1<<TOIE1); // enable Interrupts , Capture & Overflow
}

else if( edge == 2 ) // 3rd edge falling
{
EndTime2 = ICR1; // total time
p_ready = TRUE; // p_ready to display
edge = 0;
}
}

ISR( TIMER1_OVF_vect ) // count overflow
{
p_mlt++;
}

void setup() {
pinMode(pinLed, OUTPUT); // on board led
pinMode(pinStart, INPUT_PULLUP); // start Input Capture, pin9
pinMode(pinTrig, INPUT); // Input Capture Pin on PB0, pin8
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.print("Speed Counter");
Serial.begin(9600); // prepare for serial out
Serial.println("Pulsewidth to Speed");

TCCR1A = 0; //
TIMSK1 = (1<<ICIE1) | (1<<TOIE1); // 2 Interrupts: Capture & Overflow
TCCR1B &= ~(1<<ICES1); // ICES1 trigger on falling
TIMSK2 &= ~(1<<OCIE2A); // disable Timer2 Interrupt, millis
sei();
}

void loop()
{
char FString[12];
double myTime = 0.0;
double pwpercent = 0.0;
unsigned long pwidth;
unsigned long pwidth1;

if (digitalRead(pinStart) == HIGH) // start ISR else adjust
{

if( p_ready )
{
TCCR1B = 0; // stop Input Capture

pwidth1 = (p_mlt1 * 65536) + EndTime1 - StartTime;
Serial.print("PW low= ");
Serial.print(pwidth1); //PW1 in counts

pwidth = (p_mlt * 65536) + EndTime2 - StartTime;
Serial.print(" PWtotal= ");
Serial.print(pwidth); //PW total in counts
Serial.print(" PW % = ");
pwpercent = pwidth1*100.0/pwidth;
dtostrf( pwpercent, 5, 2, FString ); // 2 digits
Serial.println(FString); //PW in counts

myTime = pwidth/16.0 ; // in usec
dtostrf( myTime, 8, 2, FString ); // 2 digits

Serial.print("Time= ");
Serial.print(FString);
Serial.print(" usec ");
lcd.print(FString);

myTime = (p_mlt * 65536) + EndTime2 - StartTime;
myTime = (F_CPU *25.0)/ myTime; // 25cm / t
dtostrf( myTime, 8, 3, FString ); // 3 digits

Serial.print("Speed= ");
Serial.print(FString);
Serial.println(" cm/sec");
lcd.print(FString);
//
p_ready = FALSE;
digitalWrite(pinLed,!digitalRead(pinLed)); // blink Led 13 on board
TCCR1B = (1<<ICES1) | (1<<CS10); // enable Input Capture Edge, PreScale 1
TCCR1B &= ~(1<<ICES1); // ICES1 trigger on falling

} //if( p_ready )
}
else // stop to adjust
{
TCCR1B = 0;
digitalWrite(pinLed,digitalRead(pinTrig)); // Led 13 on board shows status
}

// delay(50);

}


Elektronik-Labor   Projekte   AVR