Grundsätzliche Frage zur Berechnung von Funktionsvorschriften

Hallo,
Ich brauche eine Funktion die bei Eingabe Wert 720 das Maximum 255 hat und bei 360 und 1080 den Wert 10.
Bei 0 und 1439 eine 5.

Wertebereich von 0 bis 1439 ( Also die Minuten eines Tages)
Ich habe also 5 Punkte die den Graphen definieren sollen.
Für die Interpolationsformel braucht es also eine Gleichung vierter Ordnung.

Ich habe also die Y Werte für 0/5 und 1439/5 ,360/10 ,720/255 ,1080/10 angegeben und berechnen lassen.
http://www.arndt-bruenner.de/mathe/scripts/gleichungssysteme.htm

a = 3,6e-9
 b = -0,0000104417
 c = 0,0089127224
 d = -2,010656471
 e = 5

Erwartet hatte ich einen Graphen der sich im ersten Quadranten befindet, im Nutzbereich um die 720 ist es quasi eine nach unten geöffnete Parabel würde ja fast passen, nur bei 356 bzw 1093 wandert er in den 4 Quadranten.

Klar könnte man jetzt sagen wenn ich im Sourcecode alles was < 5 ist zu 5 mache passt es fast.
Nur hätte da eine Formel für eine Parabel (ax²+bx+c) mit Scheitel 702/255 auch gereicht.

Wo mache ich den Denkfehler, brauche ich mehr Punkte in den beiden Bereichen also eine Grundform 5 oder 6 Ordnung ?

Ich denke, der Arduino mit seinen 4-byte float wird hier Grundsätzlich Schwierigkeiten haben.
Selbst im Excel sind ja schon abweichungen zu sehen.

Generell sieht mir das aber eher nach einer Glockenkurve aus als nach einer polinomischen, also y = exp(-x²)
wobei du x natürlich normieren mußt und y dann auch.

wenn also dein x von 0 bis 1440 läuft, und wir definieren
b als Breite der Glockenkurve
m als den Mittelpunkt der Glockenkurve und
h als die Höhe der Glockekurve
g grundhöhe der Kurve

dann gibt das die Formel:
y = exp(-1 *( (x - m)/b)²)*h+g

mit
b = 180
m = 720
h = 250
g = 5

gibt das eine ganz gut passende Kurve:

Es stellt sich aber doch die Frage, ob es nicht sinnvoller wäre eine Wertetabelle zu definieren und diese zwischen den Stützstellen linear zu interpolieren.

Besten Dank. 8)
Mit der Glockenkurve funktioniert dies ohne Einschränkung des Wertebereichs im Code.

Mit meinem Ansatz geht das natürlich nicht, wenn es ins Negative geht muss ich eingreifen, und den Default Min-Wert setzen
Die Steigungswerte (Ableitung) um den oberen Wendepunkt sind bei der Glockenkurve kleiner, und dies war entscheidend.

Es handelt sich um eine zeitabhängige Beleuchtung für ein Aquarium, der "Kunde" wollte diese Soll-Wertepaare.
Und um die Mittagszeit ist die Glocke für seinen Geschmack etwas zu schmal.

Gesehen ich das nicht, ich schreibe nur den Code und Poste es ihm zum Testen.

Ah, ok.
Mit diesem Hintergrund ist die Glockenfunktion aber ein mathematischer Overkill.
Es bietet sich natürlich eine sinusfunktion an. Schliesslich handelt es sich beim Sonnenumlauf um einen Kreis.

Aber mach dir einfach eine Tabelle mit Stützstellen, zwischen denen linear interpoliert wird.
Das ist viel einfacher und du kannst beliebige Kurvenformen darstellen.

In dem Beispiel ist die Tabelle für die Kurve “Sinus”:

min	pwm
0	5
360	10
400	18
440	39
520	111
600	194
640	226
680	248
720	255
760	248
800	226
840	194
920	111
1000	39
1040	18
1080	10
1439	5

und für die Kurve “breiter”:

min	pwm
0	5
360	10
380	18
410	39
460	111
540	194
580	226
640	248
720	255
800	248
860	226
900	194
980	111
1030	39
1060	18
1080	10
1439	5

Du siehst, du kannst hier beliebige Kurven basteln.
Streng genommen könntest du dir sogar die halbe Tabelle sparen, da symetrisch.

Eine ganz grundsätzliche Frage neben dem mathematischen Overkill ist noch, dass das Verhältnis zwischen PWM-Wert, Helligkeit und wahrgenommener Helligkeit sehr nichtlinear ist.

Am "interessantesten" sind die Zeitpunkte des Übergangs von "finsterster Nacht" nach PWM=1, 2, 3, ... . Danach wird es immer stufenloser, bis man ( und was ist mit fisch ??? ) ab ca. 200 praktisch gar keinen Unterschied mehr sieht ... :wink:

Statt PWM etwas zu bauen, das anstelle der Sprünge (0, 1/255, 2/255) tatsächlich stufenlos arbeitet, das wäre Overkill nach meinem Geschmack :wink:

Klar ist das der mathematische Overkill für diese Aquariumgeschichte.
Ist auch nicht für mich.
Ich habe ihm einen Testsketch gemacht, und mit der Lichtstärke und Ausgaben der PWM Werte und er hat mir gesagt wann er was haben wollte.

Mit diesen Werte habe ich dann gerechnet, mit der Erkenntnis das der Weg über ein Polynom nicht der Richtige ist.
Obwohl es schon funktioniert hat, wenn man den Wertebereich eingrenzt und das Maxium auf 255 begrenzt hat.

Die Lösung von Gunther mit den Tabellen und linearer interpolation zwischen den Stützwerten ist einfach und genial zugleich…
Ich habe den Thread nicht nur wegen dem Aquariumprojekt gestartet, sondern hauptsächlich auch deswegen wie man solche Aufgaben generell pragmatisch lösen kann. (Daher auch der Titel des Threads)

Und dies hat dieser Thread dank Gunter sehr gut gezeigt ! Karma++
Betrifft z.b. alle Sensoren mit nicht linearer Kennlinie und unbekannter Formel.

Kannst du bitte, nur zur vollstädnigkeit den Code reinhängen, den ich dir geschickt habe?
ich habe ihn nämlich nicht mehr :blush:

Kein Problem.
Wobei ich den Code live noch nicht getestet habe.
Aber verstanden was dort gemacht wird.
Nur das hier verstehe ich nicht:
Mit dem Spiegeln, also wenn der Graph Symetrisch sein soll und das Array deswegen nur halb so groß zu sein braucht.
Diese Zeile :

_MoD = 1440 - _MoD;  // Werte oberhalb 720 nach unten spiegeln, wegen Symetrie

Berechnet für _MoD <= 720 meiner Meinung nach den falschen Index, da fehlt wohl ein If ( _MoD >=720) ??

int LED_PWM (int _MoD){              // _MoD = Minute of Day
  const int Tablength = 9;
  const int Tab[Tablength][2]={ {  0 ,   5 },
				{360 ,  10 },
				{400 ,  18 },
				{440 ,  39 },
				{520 , 111 },
				{600 , 194 },
				{640 , 226 },
				{680 , 248 },
				{720 , 255 }};

      _MoD = 1440 - _MoD;  // Werte oberhalb 720 nach unten spiegeln, wegen Symetrie


      // In Tabelle das passende Wertepaar suchen
      int i = 0;
      while (i < Tablength && _MoD > Tab[i][0])  i++;  
      
      // aus dem ermittelten Wertepaar und dem nächsten darüber den korrekten Wert interpolieren
      int _out = (   ((Tab[i][1] - Tab[i-1][1])*( _MoD - Tab[i-1][0] ))
      		 	  / (Tab[i][0] - Tab[i-1][0] ))
     		 + Tab[i-1][1];    
     return _out;
}

rudirabbit:
Berechnet für _MoD <= 720 meiner Meinung nach den falschen Index, da fehlt wohl ein If ( _MoD >=720) ??

Ähem, ja, da hast natürlich recht. Das ist mit durchgerutscht.
Wobei du das ja nur brauchst, wenn du symetrische Kennfelder hast und nur die Hälfte ablegst um Speicher zu sparen.