Rundungsfehler beim lösen einer quadratischer Gleichung?

Hallo ich bin neu hier und das ist mein erster Thread im Arduino Forum. Ich hoffe ich mache alles soweit richtig. Ich verwende einen Arduino Uno R3 und die Arduino IDE.

Bei meinem aktuellen Projekt geht es mir darum mithilfe eines Magnetventils eine bestimmte Menge Wasser durch Schwerkraft aus einem zylindrischen Gefäß auszulassen.
Dafür habe ich mir für ein beispielhaftes Gefäß eine lineare Funktion gesucht, die der des Volumenstroms über die Zeit ausreichend genau entspricht.
In diesem Fall ist das: f(t) = -0,463t + 21,5.

Nun möchte ich das mir der Arduino für ein beliebiges Volumen, welches ich über den seriellen Monitor eingebe, mithilfe der Mitternachtsformel, den zweiten Grenzwert des Integrals und damit delta t ausgibt. Delta t möchte ich dann verwenden um das Magnetventil anzusteuern und die gewünschte Menge Wasser abzulassen.
Da die Mitternachtsformel jeweils zwei Ergebnisse hat muss der Arduino das richige wählen (b1 oder b2) und davon dann den ersten Grenzwert (t) abziehen.

Das funktioniert soweit auch halbwegs, nur gibt es zwei Probleme:

Das erste ist das ich im seriellen Monitor b1 und b2 ausgegeben bekomme und delta t, wenn t null ist keinem der beiden Werte entspricht. Ich denke es könnte eventuell ein Rundungsfehler sein, verstehe allerdings nicht wie das bei (b1-0) oder (b2-0) passiert.

Die Ausgabe sieht z.b. so aus:

Bitte auszugebendes Volumen eingeben:
Vol (ml): 499.19
A = -0.46
B = 43.00
C = -998.38
discriminant = 0.0
b1 = 46.42
b2 = 46.45
delta t: 46.31 s
gesamt t: 0.00 s

Das berechnete Ergebnis an dieser Stelle wäre 46.44 s.

Das zweite Probleme ist die Auswahl der korrekten Lösung im Moment prüft er nur ob b > t,
also der errechnete zweite Grenzwert größer als der erste Grenzwert ist. Bei größeren Eingaben sind die Werte sehr nah bei einander und er nimmt einfach den ersten.

Leider ist das für mich alles noch Neuland und mein Sketch sicher nicht toll. Bisher habe ich den Sketch in Tinkercad mit einer LED am Pin 9 getestet. Wenn mir jemand weiterhelfen kann freue mich sehr.

float inVol = 0.0; // Globale Variable zum Speichern der Eingabe
const int valvePin = 9;  // Pin, an dem das Ventil angeschlossen ist

//f(t) = (-0.463)*t+21.5 lineare Annäherung des Volumenstroms

float t = 0.0;  // Erster Grenzwert bzw. aktueller x-Wert der Funktion
float m = (-0.463);  // Steigung der linearen Funktion
float c = 21.5;  // y-Achsenabschnitt der linearen Funktion
float maxT = 46.436; // Nullstelle der Funktion


void setup() {
  pinMode(valvePin, OUTPUT);
  Serial.begin(9600); // Startet die serielle Kommunikation

  Serial.println("Bitte auszugebendes Volumen eingeben:");
}

void loop() {
  // Überprüfen, ob neue serielle Daten verfügbar sind
  if (Serial.available() > 0 && t < maxT) {
    String inputString = Serial.readStringUntil('\n'); // Lesen der Eingabe bis zum Zeilenende-Zeichen
    inVol = inputString.toFloat(); // Konvertieren der Eingabe in einen float

    Serial.print("Vol (ml): ");
    Serial.println(inVol, 2); // Ausgabe mit 2 Nachkommastellen
    
    float additionalTime = calculateDeltaT(calculate_b(inVol, t, m, c));
    
    Serial.print("delta t: ");
    Serial.print(additionalTime);
    Serial.println(" s");
    Serial.print("gesamt t: ");
    Serial.print(t);
    Serial.println(" s");

    activateValve(additionalTime);
    t += additionalTime;

    Serial.println("Bitte auszugebendes Volumen eingeben:");
  }
}


void activateValve(float duration) {
  digitalWrite(valvePin, HIGH);  // Ventil öffnen
  delay(duration*1000);  // Ventil für die berechnete Dauer offen halten
  digitalWrite(valvePin, LOW);  // Ventil schließen
}

float calculateDeltaT(float b) {
  b-t;
}

float calculate_b(float i, float t, float m, float c) {
  // Koeffizienten der quadratischen Gleichung
  float A = m;
  Serial.print("A = ");
  Serial.println(A);
  float B = 2.0 * c;
  Serial.print("B = ");
  Serial.println(B);
  float C = -(2.0 * i + m * sq(t) + 2.0 * c * t);
  Serial.print("C = ");
  Serial.println(C);

  // Diskriminante der quadratischen Gleichung
  float discriminant = sq(B) - 4.0 * A * C;
  Serial.print("discriminant = ");
  Serial.println(discriminant, 1);
  
  //if (discriminant < 0) {
  //  Serial.println("Keine reale Lösung vorhanden.");
  //  return NAN;
  //}
  
  // Lösungen der quadratischen Gleichung
  float sqrtDiscriminant = sqrt(discriminant);
  float b1 = (-B + sqrtDiscriminant) / (2 * A);
  float b2 = (-B - sqrtDiscriminant) / (2 * A);
  Serial.print("b1 = ");
  Serial.println(b1);
  Serial.print("b2 = ");
  Serial.println(b2);
  
  // Wähle die gültige Lösung (b muss größer als t sein)
  if (b1 > t) {
    return b1;
  } 
  else if (b2 > t) {
    return b2;
  } 
  else {
    Serial.println("Keine gültige Lösung gefunden.");
    return NAN;
  }
}

Fließkommazahlen haben auf einem 8 bit Microcontroller eine Genauigkeit von 6-7 signifikante Stellen.
Grüße Uwe

Das ist nicht der Voumenstrom, das ist die enthaltene Füllmenge im Quellgefäß.

eines Magnetventils eine bestimmte Menge Wasser durch Schwerkraft aus einem zylindrischen Gefäß auszulassen.

Poste ein Foto oder eine Zeichnung des Geräts. Es erscheint unwahrscheinlich, dass die Abflussrate linear zur Zeit ist.

Bei nicht so ganz altem AVR Compilern kann man auf 64 Bit double umstellen
z.B. so:

#compiler.c.elf.extra_flags=        -mdouble=64     

Wobei die Ausgaben das noch nicht können.
Aber für die ganzen Zwischenrechungen durchaus hilfreich.

Ist bisher nur ein theoretischer Zylinder. Ich habe auch die Schaltzeit des Ventils nicht beachtet. Geht erstmal darum es auszuprobieren.

Aber wie werden aus 46.42 - 0.0 plötzlich 46.31? Ich ziehe ja nur "float t" von "float b1" ab

DIe Flussrate bei einem simplen Loch im Boden ist abhängig vom Druck und der hängt von der Flüssigkeitshöhe über dem Loch ab --> v'dt != const.

1 Like

Wie sehr man sich doch irren kann.
Leider verbirgt die Irrung dann auch gerne den wahren Fehler.

Aktiviere bitte mal alle Warnungen!
Und natürlich auch lesen/beheben....

E:\Programme\arduino\portable\sketchbook\sketch_aug07b\sketch_aug07b.ino: In function 'float calculateDeltaT(float)':
E:\Programme\arduino\portable\sketchbook\sketch_aug07b\sketch_aug07b.ino:52:4: warning: statement has no effect [-Wunused-value]
   52 |   b-t;
      |   ~^~
E:\Programme\arduino\portable\sketchbook\sketch_aug07b\sketch_aug07b.ino:53:1: warning: no return statement in function returning non-void [-Wreturn-type]
   53 | }
      | ^

Du meinst sicher:

float calculateDeltaT(float b) {
  return b-t; 
}
2 Likes

oh man, ich habs mehrmals gelesen und nich gesehen :sweat_smile:, Danke

Ja genau

Wo aktiviere ich die Warnungen?

Gerne doch:

1 Like