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
doublei=0.5;
2
voidsetup(){
3
Serial.begin(9600);
4
pinMode(5,OUTPUT);
5
}
6
7
voidloop(){
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
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.
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.
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.
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.
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.
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.
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
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.
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.
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?
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.
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 :)
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;
}