Forum: Mikrocontroller und Digitale Elektronik Arduino 'überspringt' Bedingung


von 12V DC (Gast)


Lesenswert?

Ich habe folgendes Problem. An meinem Arduino würde ich gerne einen 
Servo betrieben, der sich immer hin und her dreht. Das klappt jetzt auch 
ganz gut. Wenn ich jetzt aber möchte, das er in der mitte stehenbleibt, 
tut er das nicht.
Code:
1
double i = 0.5;
2
void setup(){
3
  Serial.begin(9600); 
4
  pinMode(5, OUTPUT);
5
}
6
7
void loop(){
8
  if(i > 3.0){
9
    Serial.println("3.0 ERREICHT!");
10
    i = 0.5;
11
  }
12
  if(i==1.50){        //<----besagte Stelle
13
    Serial.println("PAUSE");
14
    delay(500);
15
  }
16
  else{
17
    Serial.println(i);
18
    digitalWrite(5, HIGH);
19
    delay(i);
20
    digitalWrite(5, LOW);
21
    delay(20 - i + 50);
22
  }
23
  i += 0.1;
24
}
Ich bedanke mich jezt schon mal für alle Antworten

von decimad (Gast)


Lesenswert?

http://de.wikipedia.org/wiki/Gleitkommazahl

insbesondere "Prüfung auf Gleichheit"

von Peter II (Gast)


Lesenswert?

12V DC schrieb:
> Ich bedanke mich jezt schon mal für alle Antworten

man kann float/double nicht auf Gleichheit prüfen!

denn 1.50 kann auch 1.49999999999999 sein.

von k. affe (Gast)


Lesenswert?

Hast du schonmal von analogWrite bzw. PWM gehört?

von Patrick C. (pcrom)


Lesenswert?

Wahrscheinlich wegen abrundungs-fehler im double.
Vergleich : 2.5 ist nicht gleich an 2.500000001

Daneben guk mal die Arduino beispielen und libraries, es gibt Servo 
libraries die einfach zu benutzen sind.

von PittyJ (Gast)


Lesenswert?

Man könnte auch i*10 nehmen, und dann ganz ohne double auskommen, 
sondern nur mit Integer rechnen.
Ich wusste auch gar nicht, dass delay() überhaupt mit doubles arbeitet.
Lauf Doku
http://arduino.cc/de/Reference/Delay
ist das ein unsigned long.

von Markus (Gast)


Lesenswert?

siehe Vergleiche von Fließkommazahlen:
http://www.mpdvc.de/artikel/FloatingPoint.htm

von 12V DC (Gast)


Lesenswert?

Patrick C. schrieb:
> Daneben guk mal die Arduino beispielen und libraries, es gibt Servo
> libraries die einfach zu benutzen sind.

Geht nicht, ich hab dami mehr Freiheiten und die brauche ich. Aber das 
ist ja auch egal...

PittyJ schrieb:
> Man könnte auch i*10 nehmen, und dann ganz ohne double auskommen,
> sondern nur mit Integer rechnen.

Wäre ne Idee, und dann wieder durch 10 teilen, oder wie meinst du das, 
da der Servo HIGH-Impulse von 0,5 bis 3 ms länge erwartet.

von Maxx (Gast)


Lesenswert?

12V DC schrieb:
> da der Servo HIGH-Impulse von 0,5 bis 3 ms länge erwartet.

Umformuliert:

da der Servo HIGH Impulse von 5 bis 30 zehntel-ms Länge erwartet.

von test (Gast)


Lesenswert?

Das wird so eh nix. "delay" kann laut Doku eh nur ms-Auflösung, du musst 
für die Servos aber viel feiner auflösen. Sowas macht man direkt mit nem 
Timer.

von Michael A. (micha54)


Lesenswert?

Patrick C. schrieb:
> Wahrscheinlich wegen abrundungs-fehler im double.
> Vergleich : 2.5 ist nicht gleich an 2.500000001
>

Hallo,

Vermutlich wird hier wegen float == float nicht einmal gerundet.

VisualBasic geht double = 3 meistens gut (bei kg, m, sec usw.), weil 
float zu integer gerundet wird. Gehts um Euros, gehts meistens schief 
und irgendein Kontroller findet es heraus.

Insgesamt ist das aber kein Rundungsfehler sondern einfach 
unprofessionell.

Gruß,
Michael

von Karl H. (kbuchegg)


Lesenswert?

Michael Appelt schrieb:
> Patrick C. schrieb:
>> Wahrscheinlich wegen abrundungs-fehler im double.
>> Vergleich : 2.5 ist nicht gleich an 2.500000001
>>
>
> Hallo,
>
> Vermutlich wird hier wegen float == float nicht einmal gerundet.

Warum soll da irgendwas gerundet werden.
2.5 ist ein double
i ist ein double

Der Vergleich auf Gleichheit besteht einfach darin, ob alle Bytes der 
beiden double übereinstimmen.


Das eigentliche Problem und Ausgangspunkt der ganzen Sache, mit dem man 
in diesem Code kämpft, sitzt hier
1
  i += 0.1;

0.1 ist in IEEE 754 nicht exakt darstellbar. Als Fliesskommazahl ist das 
eine "periodische Zahl". Egal wieviele Mantissenbits die Variable hätte, 
es gelingt nicht, da exakt 0.1 zu addieren. Das im Rechner erhaltene 
Ergebnis wird nie exakt mit dem übereinstimmen, das ein Mathematiker 
herausbringt. Und damit liefert die Aufsummierung nach n Summier-Stufen 
dann eben nicht das exakte Ergebnis i_0+n*0.1, sondern eine Näherung 
davon, die mit größer werdendem n immer mehr vom korrekten Wert 
abweichen wird, weil hier keine Rücksicht auf Fehlerfortpflanzung 
genommen wurde.

Wie schon geschrieben: Wer Floating Point Zahlen, mit denen gerechnet 
wurde, mittels == auf Gleichheit vergleicht, hat noch viel über Arbeiten 
mit Floating Point zu lernen. Die Nicht-Berücksichtigung der 
Fehlerfortpflanzung und naive Aufsummieren ist da dann nur ein 
Teilaspekt davon.

Rechnen mit Floating Point
ist wie das Schaufeln von Sand
von einem Haufen zu einem anderen.
Jedes mal, wenn man es tut,
hat man ein bisschen weniger Sand
und ein bisschen mehr Schmutz.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> Warum soll da irgendwas gerundet werden.
> 2.5 ist ein double

2.5 wird auf das nächste darstellbare float gerundet.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz schrieb:
>> Warum soll da irgendwas gerundet werden.
>> 2.5 ist ein double
>
> 2.5 wird auf das nächste darstellbare float gerundet.

Seit wann?
Im ganzen Programm gibt es keinen float. Auch 2.5 ist keiner. 2.5F wäre 
einer. Aber das steht ja nicht dort.

Das auf einem AVR double und float dasselbe sind, spielt hier keine 
Rolle. Das sind alles double Operationen.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> Das auf einem AVR double und float dasselbe sind, spielt hier keine
> Rolle. Das sind alles double Operationen.

warum sollte das keine rolle spielen? Wenn double = float dann musst 
doch die 2.5 als float abgelegt werden oder nicht?

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz schrieb:
>> Das auf einem AVR double und float dasselbe sind, spielt hier keine
>> Rolle. Das sind alles double Operationen.
>
> warum sollte das keine rolle spielen?

Du verwechselst das was ein Compiler laut Sprachdefinition machen muss 
mit dem wie es implementiert wird.

> Wenn double = float dann musst
> doch die 2.5 als float abgelegt werden oder nicht?

Es wird als double abgelegt. Auch wenn double und float dieselbe Anzahl 
an Bytes benutzen (auf einem AVR), so ist das doch ein double.

Im ganzen Code gibt es keine einzige Konvertierung, die auf eine 
Vermischung der beiden Datentypen double und float zurückzuführen wäre. 
Das ist alles so weit in sich konsistent. Also sollte man das Kind auch 
beim Namen nennen. Wenn etwas ein double ist, dann sollte man das auch 
als double bezeichnen. Selbst dann, wenn es durch die konkrete 
Implementierung keinen Unterschied zwischen den beiden gibt.

> Wenn double = float dann musst doch die 2.5 als float abgelegt werden oder 
nicht?

Wo ist der Unterschied, wenn double und float sowieso auf einem AVR 
dasselbe sind.
Was der Compiler nämlich NICHT machen darf: Er darf nicht einfach nur 
hergehen und beim Parsen des Quelltextes in seinem eigenen System einen 
double erzeugen und den dann auf 4 Bytes zurecht stutzen. Wenn ein 
Compiler Constant-Folding betreibt und
1
  kl = 3.45 * 8.67;
zur Compile-Time ausrechnet, dann muss das so geschehen, als ob die 
Zielhardware das selbst gerechnet hätte! Und zwar in allen Aspekten! 
Früher, als es noch wesentlich mehr unterschiedliche Floating Point 
Systeme gab, bedeutete das, dass der Compiler das Floating Point System 
der Zielhardware unter Umständen emulieren musste um rauszukriegen, 
welches Ergebnis die rausbringen würde. Und das konnte sich von dem 
unterscheiden, was sein eigener Floating-Koprozessor rausgebracht hätte.

: Bearbeitet durch User
von hinzkunz123 (Gast)


Lesenswert?

Du willst die Impulslänge in Bruchteilen von Millisekunden verändern, 
benutzt dafür aber eine Funktion deren grösste Auflösung 1ms ist. Warum 
verwendest du nicht die hübsche Funktion "delayMicroseconds"?
Dein Code würde dann folgendermassen aussehen:

unsigned int i = 500;
void setup(){
  Serial.begin(9600);
  pinMode(5, OUTPUT);
}

void loop(){
  if(i > 3000){
    Serial.println("3.0 ERREICHT!");
    i = 500;
  }
  if(i==1500){        //<----besagte Stelle
    Serial.println("PAUSE");
    delay(500);
  }
  else{
    Serial.println(i);
    digitalWrite(5, HIGH);
    delayMicroseconds(i);
    digitalWrite(5, LOW);
    delayMicroseconds(20000 - i);
  }
  i += 100;
}

Sollte funktionieren :)

von 12V DC (Gast)


Lesenswert?

Wenn ich deinen Code nehme Zuckt der Servo nur von einer zur anderen 
Stelle.

von 12V DC (Gast)


Lesenswert?

Mit dem Code(jaja ich weiß ich soll µS verwenden) geht es zwar, er 
pausiert aber die eine Sekunde am ende der Bewegung. Ich glaube bei 0,5,
1
int i = 5;
2
void setup(){
3
  Serial.begin(9600); 
4
  pinMode(5, OUTPUT);
5
}
6
7
void loop(){
8
  if(i > 30){
9
    i = 5;
10
  }
11
  if(i == 15){    //<-----STELLE
12
    Serial.println("15 ERREICHT");
13
    delay(1000);
14
  }
15
  else{
16
    Serial.println(i);
17
    digitalWrite(5, HIGH);
18
    delay(i/10);
19
    digitalWrite(5, LOW);
20
    delay(20 - i/10 + 50);
21
  }
22
  i += 1;
23
}

von hinzkunz123 (Gast)


Lesenswert?

Okay, ich hatte übersehen, das wir dem Servo ja auch ein wenig Zeit 
geben müssen um seine Position anzufahren.
Die Servo Endstellungen sind mit 500uS und 3000uS auch etwas viel. 
Standard ist glaube ich 750 - 2500. Das hängt aber vom Servo ab.
Ich habe das jetzt mal getestet und dieser Code funktioniert zumindest 
bei mir:

unsigned int i = 800;
void setup(){
  Serial.begin(9600);
  pinMode(5, OUTPUT);
}

void loop(){
  if(i > 2000){
    Serial.println("2.0 ERREICHT!");
    i = 800;
  }
  if(i == 1500){        //<----besagte Stelle
    Serial.println("PAUSE");
    delay(1000);
  }
  Serial.println(i);
  digitalWrite(5, HIGH);
  delayMicroseconds(i);
  digitalWrite(5, LOW);
  delayMicroseconds(20000 - i);
  delay(10);    // Pause damit der Servo seine Position anfahren kann
  i += 10;
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.