Pages: [1] 2   Go Down
Author Topic: Errechneter Wert ist falsch. Programmfehler? Kann mal jemand drüberschauen?  (Read 1850 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo Leute,

ich errechne über die Ticks des Tachosignals meines Motorrads die gefahrene Geschwindigkeit. Mir fällt auf, dass diese im Bereich um die 90-100km/h sehr stark vom Tachowert abweicht (teilweise bis 15km/h). Die errechnete Geschwindigkeit ist zu gering.

Bei niedrigeren Geschwindigkeiten habe ich aber höhere errechnete Geschwindigkeiten, die besser zur Realität passen.

Wenn ich z.B. anhand der Drehzahl vergleiche stelle ich fest, dass ich bei Verdoppelung der Drehzahl nicht die errechnete Geschwindigkeit verdopple.
Bei deutlich höheren Geschwindigkeiten (150km/h) passt es dann auf einmal wieder wesentlich besser und die errechnete Geschwindigkeit scheint wieder zu stimmen.

Ich hänge mal den entsprechenden Code an, vielleicht tritt ja irgendwo ein Überlauf oder ein dummer Rundungsfehler auf und ich sehe das einfach nicht.

Code:
//Geschwindigkeit
unsigned long speedTicks = 0;
int actualSpeed = 0;

//Konstanten für Tachosignal auswertung (Distanz + Geschwindigkeit)
const int tickPerRotation = 72;
const int rotationLength = 1950;

void getSpeed() {
   static unsigned long speedMillis = 0;
   static const unsigned int intervall = 100;
   static boolean beschlMessung = false;
   static unsigned long beschlMessMillis = 0;
   static int speeds[] = {0, 0, 0, 0, 0};
  static int speedIdx = 0;
  static int mySpeed = 0;

   long actualMillis = millis();
//alle 100ms
   if (actualMillis > speedMillis + intervall) {
       unsigned long ticks = speedTicks;  //ticks merken, da speedTicks durch interrupt erhöht wird
       speedTicks = 0; //speedTicks resetten
       int diff = actualMillis - speedMillis; //tatsächliches intervall
       speedMillis = actualMillis; //zeitpunkt für nächstes intervall speichern
       int calcSpeed = 0;
       if (ticks > 10) {
//nur wenn genügend ticks gezählt sind, geschwindigkeit berechnen, ansonsten speed = 0
         unsigned long divisor = (unsigned long)tickPerRotation * 1000 * diff;
         calcSpeed = (ticks * rotationLength * 3600) / divisor;
       } else { //keine ticks abziehen, das moped steht (wird nur im stand leicht bewegt)
         ticks = 0;
       }

//geschwindigkeit mit den 4 vorangegangenen Werten glätten (sonst schwankt es stark)
       mySpeed -= speeds[speedIdx];
       mySpeed += calcSpeed;
       speeds[speedIdx] = calcSpeed;
       speedIdx++;
       if (speedIdx == 5) {
           speedIdx = 0;
       }
       
       int theSpeed = mySpeed / 5;

       //errechnete geschwindigkeit übernehmen
       actualSpeed = theSpeed;
}

//interrupt für das Tachosignal
void speedTrigger() {
   speedTicks++;
}


actualSpeed ist die globale Variable, in der die errechnete Geschwindigkeit landet, um dann später aufs Display zu gelangen.

speedTrigger() wird über interrupt gerufen und dient dazu, die Ticks zu zählen. Es sind 72 Ticks pro Radumdrehung und jede Radumdrehung hat 1,95m
100km/h sind 27,7m/s also 1025 Ticks pro Sekunde. Ich denke das sollte der Chip schaffen, die alle korrekt zu zählen.

Woran könnte jetzt mein Problem liegen?
Gehen mir eventuell Ticks verloren, weil sie nicht gezählt werden?
Verpasse ich im Code irgendwo Ticks bei der Geschwindigkeit?
Oder habe ich irgendwo einen Überlauf (unwahrscheinlich, da müsste dann totaler Käse rauskommen) oder blöde Rundungsfehler?

Hat jemand eine Idee?
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 49
Posts: 2751
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
1025 Ticks pro Sekunde. Ich denke das sollte der Chip schaffen, die alle korrekt zu zählen.

Eigentlich schon.
Ganz generell solltest du aber
volatile unsigned long speedticks;
definieren. Und
unsigned long ticks = speedTicks;  //ticks merken, da speedTicks durch interrupt erhöht wird
       speedTicks = 0;      //speedTicks resetten


und am besten auch schon
     actualMillis = millis();
mit geschlossenen Interrupts machen, damit immer alles zusammenpasst.
( nointerrupts(); )
Logged

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3420
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Die Variable muss auf jeden Fall "volatile" deklariert werden.  Lesezugriffe auf eine 16 Bit Variable kann ein 8 Bit Prozessor normalerweise nicht atomar abarbeiten. Deshalb müssen für die Dauer des Lesezugriffs auf die Variable (Vergleichsoperation im IF statement) auch die Interrupts gesperrt werden.
Logged

Check out my experiments http://blog.blinkenlight.net

Forum Moderator
BZ (I)
Offline Offline
Brattain Member
*****
Karma: 236
Posts: 20273
+39 349 2158303
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich würde nicht messen wieviele Meßimpulse pro Zeiteinheit gelesen werden sondern die Zeit zwischen 2 Impulsen messen und aus dieser die Geschwindigkeit berechnen.
Grüße Uwe
Logged

0
Offline Offline
Faraday Member
**
Karma: 19
Posts: 3420
20 LEDs are enough
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Genaugenommen ist das Teil ein Frequenzzähler mit einer auf Umdrehungen pro Minute oder km/h umgerechneten Ausgabe. Ich würde mir anschauen wie andere Leute Frequenzzähler für den gewünschten Frequenzbereich optimieren. Auf jeden Fall würde ich mich bei Frequenzzählerprojekten bedienen. Also http://arduino.cc/en/Reference/pulseIn und http://interface.khm.de/index.php/lab/experiments/arduino-frequency-counter-library/.
Logged

Check out my experiments http://blog.blinkenlight.net

Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Schonmal danke für die Hinweise, das hilft ein wenig weiter.

Genaugenommen ist das Teil ein Frequenzzähler mit einer auf Umdrehungen pro Minute oder km/h umgerechneten Ausgabe. Ich würde mir anschauen wie andere Leute Frequenzzähler für den gewünschten Frequenzbereich optimieren. Auf jeden Fall würde ich mich bei Frequenzzählerprojekten bedienen. Also http://arduino.cc/en/Reference/pulseIn und http://interface.khm.de/index.php/lab/experiments/arduino-frequency-counter-library/.

pulseIn zählt die Dauer eines Zustands. Das Hilft mir nicht, da hier gemessen würde, wie lange bei einem Tick das Signal HIGH ist. Ich will aber nicht wissen wie lange es HIGH ist, sondern wie lange es dauert, bis es von HIGH wieder auf HIGH schaltet.

Bei der zweiten Variante müsste ich testen, ob hier der Programmablauf unterbrochen wird. Das kann ich nämlich nicht brauchen, weil ja auch noch andere Sachen laufen.

Egal wie, ich muss die Ticks auf jeden Fall alle zählen, weil ich auch die gefahrene Strecke brauche.


Ich will mal kurz auf volatile und noInterrupt zurück kommen:
Ich hole mir zuerst die aktuellen millis. Danach erst die Anzahl an Ticks. Wenn die Ticks zwischenzeitlich erhöht wurden, dann müsste mir eine höhere Geschwindigkeit errechnet werden, weil ich dann zuviele Ticks in meinem Intervall habe. Genau das passiert aber nicht.
Nach der Zuweisung setze ich die Ticks auf 0 zurück. Wenn hier zwischenzeitlich Ticks erhöht wurden, dann gehen die verloren. Das müsste dann doch aber auch bei deutlich höherer Geschwindigkeit auch so sein. Oder spielt mir da vielleicht die Physik einen Streich und der Schlupf wird so groß, dass das Rad tatsächlich eine deutlich höhere Geschwindigkeit hat? Eigentlich dachte ich, dass das nur beim Beschleunigen oder bei richtig hoher Geschwindigkeit signifikant wird.

Wenn ich jetzt mit nointerrupts arbeite, dann gehen mir doch sicherlich Ticks verloren oder nicht? Das würde ich gerne vermeiden.

Die Idee einfach die Zeit zwischen den Ticks zu zählen ist auch so ne Sache. Schließlich haben wir ja bereits bei 100km/h eine Frequenz von 1000Hz. Bisher berechne ich die Geschwindigkeit alle 100ms also mit 10Hz. Ich frage mich, ob mein Programm dann noch ordentlich läuft oder ob da dann nicht doch der IC irgendwann an seine Grenzen stößt. Aber wahrscheinlich unterschätze ich da den 16Mhz Chip.
Davon unabhängig kann ich dann aber nicht mehr mit millis arbeiten, sondern muss mikros nehmen.

Wäre folgendes eine Lösung (achtet nicht auf die syntax, ich kritzel das nur so hin)?
Code:

unsigned long speedMicros
unsigned long speedTicks

speedTrigger() #wird per interrupt gerufen
{
  static unsigned long lastMicros
  unsigned long actualMicros = micros()
  unsigned int diff = actualMicros - lastMicros
  lastMicros = actualMicros
  speecMicros += lastMicros
  speedTicks++
}

calculateSpeed()
{
  unsigned long mySpeedMicros = speedMicros
  unsigned long mySpeedTicks = speedTicks
  speedMicros -= mySpeecMicros
  speedTicks -= mySpeedTicks

... geschwindigkeit berechnen und kram machen
}

So sorgt der Interrupt selbst dafür, dass die Anzahl Ticks und die gemessene Zeit zusammen passen. Dennoch kann ich kontrolliert in einem definierten Intervall die Geschwindigkeit bestimmen und meinen restlichen Kram damit machen, ohne den Interrupt damit zu belasten.
Ticks verlieren kann ich nicht. Falls der Interrupt weiter gezählt hat, bleibt das erhalten, da ich nur den alten Wert abziehe.


Nachdem ich diesen Code eben so geschrieben habe fällt mir auf, dass ich auch den bestehenden Code dementsprechend anpassen könnte (statt speedTicks = 0 müsste ich ticks davon abziehen).
Zusammen mit volatile könnte das vielleicht schon die Lösung sein und ich muss den Interrupt nicht aufblähen.
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 49
Posts: 2751
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Wenn ich jetzt mit nointerrupts arbeite, dann gehen mir doch sicherlich Ticks verloren oder nicht? Das würde ich gerne vermeiden.
smiley-wink Da bist du nicht der einzige, der das vermeiden will.
Wenn die Zeit unter nointerrupts nicht länger ist als was du auch in einem IR Handler machen würdest, geht nichts verloren, sondern wird für später gemerkt.

D.h. delay(),  Serial.write und andere Funktionen mit delay ( pulseIn ) sind natürlich tabu, aber eine handvoll long Variable umkopieren geht bei 16 MHz in unter 4 µs. ( Die  micros() Auflösung )

pulseIn() ist nicht das was du brauchst, das stimmt.

Quote
Zusammen mit volatile könnte das vielleicht schon die Lösung sein und ich muss den Interrupt nicht aufblähen.
... nointerrupts während des Umkopierens ist trotzdem nötig, und bläht nicht !
 
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sollte ich dann besser die bestehende Logik einfach mit volatile und nointerrupt erweitern oder sollte ich die Zeitmessung zusätzlich in den Interrupt packen?
Logged

Wien
Offline Offline
Edison Member
*
Karma: 23
Posts: 1664
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi,

vielleicht komplett falsch, aber mir scheints einfach (wenn auch nichtr so elegant):

warum läßt Du nicht einen kleinen ATirgendwas abwechseln messen und die werte an den arduino schicken? würde doch einige der probleme lösen...

gruß stefan
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Grundsätzlich gute Idee, die Geschwindigkeit z.B. mit einem zweiten IC zu messen. Da ich die Steuerung aber bereits im Einsatz habe und ungern nochmal umlöten möchte, ist das keine praktikable Lösung für mich.
Davon abgesehen wäre es mit Kanonen auf Spatzen geschossen. Ich bin nicht auf eine absolut exakte Geschwindigkeit angewiesen, es wäre aber schön, wenn sie dennoch stimmen würde (daher hier ja meine Suche nach einer programmatischen Lösung).

Wenn GPS Module nicht so scheiß teuer wären, würde ich eines verbauen und einfach damit die Geschwindigkeit errechnen. Dann hätte ich auch nicht mehr die Probleme mit dem Schlupf (der macht sich nämlich tatsächlich bemerkbar und ich weiß nicht, wie ich den rausrechnen sollte).
Logged

Wien
Offline Offline
Edison Member
*
Karma: 23
Posts: 1664
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi,

Quote
Davon abgesehen wäre es mit Kanonen auf Spatzen geschossen.

naja, wenn die kanone nur 80 Cent kostet...
aber das argument mit umlöten versteh' ich schon.

eine frage zu GPS: laut wikipedia hat GPS eine genauigkeit von 5-8 metern. wird da die ungenauigkeit durch kurven nicht größer als die durch den schlupf ?

gruß stefan
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

GPS hat im Stillstand eine schlechte Genauigkeit, das ist korrekt. In Bewegung lässt sich die Position aber sehr genau berechnen (etwa 50cm genau).

Du hast aber Recht, in Kurven kann man per GPS keine gescheite Geschwindigkeit errechnen, das geht nur auf der Geraden.
Aber schaust du in der Kurve auf den Tacho? Besonders beim Motorrad? Eigentlich nicht ;-)

Ich würde die Lösung mit GPS dann sowieso nur für eine zusätzliche Anzeige nutzen. Die Geschwindigkeit des Hinterrads muss ich trotzdem weiterhin korrekt berechnen (also incl. Schlupf), da die Geschwindigkeit bzw. die gefahrene Strecke dazu benutzt wird, um die Kette zu ölen. Und die Kette hat nunmal mehr Strecke zurückgelegt, als das Moped (wegen Schlupf).
Logged

Germany
Offline Offline
Faraday Member
**
Karma: 49
Posts: 2751
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
warum läßt Du nicht einen kleinen ATirgendwas abwechseln messen und die werte an den arduino schicken? würde doch einige der probleme lösen...
Welche ? Ist es denn so , dass der Arduino zu viel zu tun hat, um alle 100 ms die die Geschwindigkeit der letzten 5 Zehntelsekunden zu mitteln ?
Den Schlupf (oder Messprobleme oder einen Programmierfehler ?) kann man doch nicht durch einen zweiten Controller beheben ?
Die letzte der 3 Fehler-Möglichkeiten wird dadurch eher erweitert.

Wenn der Schlupf so groß ist, wie groß ist denn dann die Abnahme des Rad-Durchmessers während einer Fahrt ? smiley-wink


Wer sagt denn übrigens, dass alle theoretischen Impulse, und nur die, einen Interrupt auslösen. 100% störungsfreie Messtechnik und "hurra, es geht, Impulse kommen" sind leider zwei verschiedene Sachen.
Evtl. mal die Interruptroutine nach Ausreissern fahnden lassen ? Die Zeit zwischen 2 Impulsen sollte sich nur langsam ändern, einzelne Lücken oder Störpulse sollten zumindest erkennbar sein   
Logged

Wien
Offline Offline
Edison Member
*
Karma: 23
Posts: 1664
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hi, michael,

und genau darum geht's bei einem zweiten atirgendwas: der kann in ruhe messen, weil andere aufgaben hat er nicht. danach kann er in ruhe korrekturen von wegen schlupf usw. vornehmen, weil andere aufgaben hat er nicht. und wenn er nach 0,1 sek fertig ist, kann er in ruhe die daten zum arduino senden, weil andere aufgaben hat er nicht. eben nicht elegant, aber effizient. der arduino kann ohne durch interrupts unterbrochen zu werden, sein programm ausführen, weil das ist seine aufgabe.
es ist halt ein anderer ansatz...

Quote
Den Schlupf (oder Messprobleme oder einen Programmierfehler ?) kann man doch nicht durch einen zweiten Controller beheben ?

doch, genau das kann man:
den schlupf kann man wegrechnen, weil man die zeit dazu hat.
messprobleme werden minimiert, weil der kleine at nur auf daten wartet und sonst nichts zu tun hat.
programmierfehler werden ohne interrupts viel einfachen eingrenzbar.

aber stimmt schon, kostet ja auch viel geld, und wie gesagt, die eleganz...

gruß stefan
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Also mit Messfehlern wegen Störungen hatte ich bisher nicht zu kämpfen, da scheint das Tachosignal ganz ordentlich zu sein. Oder es liegt daran, wie gemessen wird (interner Pullup und über Diode an das Tachosignal - somit triggert der Massedurchgang).

Ich habe jetzt mal volatile auf dem Zähler mit reingenommen und noInterrupts für das Auslesen der Messzeit und der gezählten Ticks eingebaut. Weiter werden die Ticks nicht mehr auf 0 gesetzt sondern der zuvor ausgelesene Wert wird abgezogen.

Testen kann ich die Lösung leider erst in ein paar Tagen. Gestern abend wars schon zu spät und heute bin ich dann bis Sonntag unterwegs.
Mal schnell ausprobieren ist leider nicht, da ich den Sitz runternehmen und die hintere Seitenverkleidung abnehmen muss. Das dauert ein wenig.
Logged

Pages: [1] 2   Go Up
Jump to: