Array und lineare Interpolation

Hallo.

Ich habe mir einen Sketch gebastelt, um mal mit Array zu arbeiten und es besser zu verstehen.
Soweit ist mir mittlerweile auch alles klar, nur bei der linearen Interpolation steh ich etwas auf dem Schlauch.
Am Ausgang habe ich wieder eine LED angeschlossen, die beim drehen des Potis in Stufen gedimmt wird, genau so wie es auch im Serialmonitor angezeigt wird.

Ich brauche in meinem Sketch für die Formale zur interpolation also theoretisch ein "i" mit Kommastelle, nur auch gleichzeitig das "i" als nicht-Kommastelle, um die Werte zwischen die Array-Zeilen zu errechnen.
Ich habe schon mehrere Ansätze gehabt, mit z.B. float oder einen zweiten Buchstaben (j) als i-Ersatz mit Kommastelle, nur alles brachte nicht das gewünschte Ergebnis und nun sind mir die Ideen ausgegangen.

int arrayX[21]                        //X-Werte aus potiX/50
{  
   0,  // 0 =    0     
  10,  // 1 =   50   
  20,  // 2 =  100   
  30,  // 3 =  150   
  40,  // 4 =  200   
  50,  // 5 =  250   
  60,  // 6 =  300   
  70,  // 7 =  350   
  80,  // 8 =  400  
  90,  // 9 =  450  
 100,  //10 =  500  
  90,  //11 =  550  
  80,  //12 =  600  
  70,  //13 =  650  
  60,  //14 =  700  
  50,  //15 =  750  
  40,  //16 =  800  
  30,  //17 =  850  
  20,  //18 =  900  
  10,  //19 =  950  
   0,  //20 = 1000
};
int i;                                  //Platzhalter für arrayX[] (0-21)
const int potiX = A0;                   //Poti zum wählen von i
int wertX;                              //Wert von potiX, 10bit = 0-1023
const byte OUTpin = 11;                 //Ausgangspin
int wertOUT;                            //Ausgangswert, 8bit = 0-255

void setup() 
{  Serial.begin(9600);}

void loop() 
{ wertX = analogRead(potiX);
  i = wertX/50;                                                                 //Wert vom Poti wird durch 50 geteilt um auf die jeweilige arrayX[]-Zeile zu errechnen
  
  arrayX[i] = arrayX[i-1] + (i-(i-1))*(arrayX[i]-arrayX[i-1]) / (i-(i-1));      //Formel für lineare Interpolation
  
  wertOUT = arrayX[i]*255/100;
  analogWrite(OUTpin,wertOUT);
  Serial.print("arrayX[i]: "); Serial.print(arrayX[i]); Serial.print("\t");
  Serial.print("i: "); Serial.print(i); Serial.print("\t");
  Serial.print("potiX: "); Serial.print(wertX); Serial.print("\t");
  Serial.print("wertOUT: "); Serial.println(wertOUT);}

Ich habe schon seit langen keine Mathematik in der schule gehabt, aber ist
(i-(i-1))
für alle i aus R nicht 1?

Lineare interpolation heißt daß man die Differenz 2-er Arraywerte in 10 Teile teilt und dann mit der Dezimalstelle (zehntel) multipliziert?

Grüße Uwe

Ja so ähnlich ist es fast, dies trifft auch bei (arrayX*-arrayX[i-1]) zu, wenn sich die Array-Zeilen jeweils nur in 1-er Schritten erhöhen, denn könnte man diesen Teil der formel weglassen.*
Deswegen fehlt mir auch ein "i" mit Kommastelle bzw. ein zweiter Buchstabe, damit diese Rechnung aufgeht.
Die Formel müsste also genaugenommen so aussehen:
arrayX[i,?] = arrayX[i-1] + (i,?-(i-1))(arrayX-arrayX[i-1]) / (i-(i-1))*
Wobei "i" auf die nächste ganze höhere Zahl aufgerundet ist und "i-1" auf die nächste ganze Zahl abgerundet.
Mit Werten aus dem Array sieht es dann so aus, angenommen das Poti steht auf 625:
arrayX[12.5] = arrayX[12] + (12.5-(12))*(arrayX[13]-arrayX[12]) / (13-(12))
vereinfacht heißt es dann:
arrayX[12.5] = 80 + (0.5) * (10) / (1)
arrayX[12.5] = 85
Das ist soweit nicht schwer, nur wie muss der Sketch dazu aussehen, das der Arduino damit rechnen kann?
Ich habe schon versucht mit float-Zahlen zu rechnen, aber das funktioniert nicht.

Ohne jetzt deinen sketch im Detail geprüft zu haben:

ich hatte das mal so umgesetzt:

double _OUT;
const byte StuetzZU  = 8; 
const int ServoZuTab[StuetzZU][2]={    {0 , 0 },   // Kennlinie für Servo Zuluftsteuerung
                                         {10 , 28 },  //  PID_Out, Servowinkel
                                         {20 , 38 },
                                         {40 , 50 },
                                         {60 , 59 },
                                         {100 , 73 },
                                         {170 , 93 },
                                         {255 , 120 }};



      // Aus Servo Zulufttabelle den korrekten Wert für den Winkel interpolieren
      while (i < StuetzZU && _OUT > ServoZuTab[i][0]){i++;}     // Passenden Eintrag in der Tabelle suchen
          // den Wert zwischen den Stützstellen interpolieren
      _out = (((ServoZuTab[i][1] - ServoZuTab[i-1][1]) * ( _OUT - ServoZuTab[i-1][0] ))/ (ServoZuTab[i][0] - ServoZuTab[i-1][0] )) + ServoZuTab[i-1][1];

Nachtrag: ich habe mit jetzt doch deinen Code mal angesehen.
Ich glaube, du hast da ein prinzipielles Verständnisproblem. Wenn du mit genau 20 möglichen Werten in eine Tabelle mit 21 Stützstellen reingehts, dann brauchst du nichts zu interpolieren.

Inpolieren musst du, wenn du viele mögliche Eingangswerte hast (z.B. 1024), aber nur eine Tabelle mit z.B. 16 Stützstellen anlegen willst.

Deshalb muss ein Array als Basis für eine Interpolation immer auch mindestens 2 dimensional sein.

Für dein Problem könnte das in etwa so aussehen:

int arrayX[7][2] = { {   0 ,   0},
                     { 100 ,   2},
                     { 200 ,   9},
                     { 350 ,  29},
                     { 500 ,  60},
                     { 700 , 119},
                     {1023 , 255}};

const int potiX = A0;                   //Poti zum wählen von i
int wertX;                              //Wert von potiX, 10bit = 0-1023
const byte OUTpin = 11;                 //Ausgangspin
int wertOUT;                            //Ausgangswert, 8bit = 0-255

void setup()
{  Serial.begin(9600);}

void loop(){ 
  wertX = analogRead(potiX);

  byte i = 0;
  while (i < 7 && wertX > arrayX[i][0]){i++;}  // passenden Eintrag im Array suchen

  wertOUT = (((arrayX[i][1] - arrayX[i-1][1]) * ( wertX - arrayX[i-1][0] ))/ (arrayX[i][0] - arrayX[i-1][0] )) + arrayX[i-1][1];    

 
  analogWrite(OUTpin,wertOUT);
  Serial.print("arrayX[");  Serial.print(i);  Serial.print("]:  "); Serial.print(arrayX[i][0]); Serial.print("  ,  ");Serial.print(arrayX[i][1]); Serial.print("\t");
  Serial.print("potiX: "); Serial.print(wertX); Serial.print("\t");
  Serial.print("wertOUT: "); Serial.println(wertOUT);
}

Ungetesteter Code, nur zur Veranschaulichung

Als Basis für die Kennlinie habe ich eine einfache quadratische Kennlinie genommen, nur um das Beispield zu verdeutlichen:
Zwischenablage01.jpg
Wie du siehtst kannst du mit nur 7 (als Beispiel) Stützpunkten die Kennlinie recht gut abbilden. Das reicht für LED ansteuerung völlig.

int arrayX[21] //X-Werte aus potiX/50
{
0, // 0 = 0
10, // 1 = 50
20, // 2 = 100
30, // 3 = 150
40, // 4 = 200
50, // 5 = 250
60, // 6 = 300
70, // 7 = 350
80, // 8 = 400
90, // 9 = 450
100, //10 = 500
90, //11 = 550
80, //12 = 600
70, //13 = 650
60, //14 = 700
50, //15 = 750
40, //16 = 800
30, //17 = 850
20, //18 = 900
10, //19 = 950
0, //20 = 1000
};

Wenn ich das richtig verstanden habe, sollen dass deine Stützpunkte zur Interpolation sein? Der Verlauf der Werte scheint linear zu sein (erst eine steigende Gerade, dann eine identische fallende Gerade) demnach glaube ich brauchst du eigentlich nur drei dieser Werte. Und zwar:

0, // 0 = 0
100, //10 = 500
0, //20 = 1000

Mit diesen Wertepaaren solltest du jegliche Werte zwischen 0 und 20 errechnen können. Für kommerzahlen, natürlich dann ein passenden Datentypen verwenden (den Fehler hab ich vor einigen Tagen gemacht).

Wichtig!
Funktioniert nur wenn du auch wirklich eine Gerade interpolieren möchtest. Für Funktionen höheren Grades braucht man dann doch ein paar Informationen mehr wie HP/TP/WP.

guntherb:
Ich glaube, du hast da ein prinzipielles Verständnisproblem. Wenn du mit genau 20 möglichen Werten in eine Tabelle mit 21 Stützstellen reingehts, dann brauchst du nichts zu interpolieren.

Inpolieren musst du, wenn du viele mögliche Eingangswerte hast (z.B. 1024), aber nur eine Tabelle mit z.B. 16 Stützstellen anlegen willst.

Es sind doch 1024 Eingangswerte, bzw. 0-1023, die durch 50 geteilt werden, um die Zeile oder auch Stützstellen, m.M.n. die "zweite Dimension", zu ermitteln.
Zu den Stützstellen stehen die anderen Werte, was denn die erste Dimension sein dürfte.

Wenn ich das richtig sehe, habe ich genau das selbe wie du in deinem Sketch.
Ich muss den mal ausprobieren und gucken was passiert und ob die LED damit keine Sprünge macht, wie in meinem.

Oh_Ce:
Wenn ich das richtig verstanden habe, sollen dass deine Stützpunkte zur Interpolation sein? Der Verlauf der Werte scheint linear zu sein (erst eine steigende Gerade, dann eine identische fallende Gerade) demnach glaube ich brauchst du eigentlich nur drei dieser Werte. Und zwar:

0, // 0 = 0
100, //10 = 500
0, //20 = 1000

Das sind nur Beispiele, um es besseren nachvollziehen zu können, die könnten auch quer durcheinander sein.
Dazu vermute ich mal, das die LED mit nur diesen drei Zeilen, aus - an - aus sein wird.

normo:
Es sind doch 1024 Eingangswerte, bzw. 0-1023, die durch 50 geteilt werden, um die Zeile oder auch Stützstellen, m.M.n. die "zweite Dimension", zu ermitteln.
Zu den Stützstellen stehen die anderen Werte, was denn die erste Dimension sein dürfte.

Wenn du deinen Eingangswert durch 50 teilst, dann hast du keine 1023 Werte mehr, sondern nur noch 21.
Und die Information dazwischen geht dir verloren.

Lass es mich an obigem Beispiel nochmal erklären.
Ich habe diesen Graf mit den Stützstellen aus obigem Beispiel.
Zwischenablage02.jpg

Und ich möchte den Y-Wert für den X-Wert "640" wissen.

Dann such zunächst das passende Wertepaar zu meiner Eingangsgrösse:
für 640 ist sind die Nachbarn { 500 , 60} und { 700 , 119}. Damit kann ich nun rechnen:
Mit der Formel

wertOUT = (((arrayX[i][1] - arrayX[i-1][1]) * ( wertX - arrayX[i-1][0] ))/ (arrayX[i][0] - arrayX[i-1][0] )) + arrayX[i-1][1]; 
ergibt das:
101.3 = (((119 - 60) * (640 - 500)) / (700-500)) + 60.

oder, anders ausgedrückt: ich ermittle die Geradengleichung zwischen den beiden Punkten und errechne mir damit den zu meinem X-Wert gehörigen Y-Wert.

Die Information, wo genau dein Eingangswert zwischen den Stützstellen liegt, verlierst du, wenn du vorher durch 50 teilst, die brauchst du aber, um die genaue Lage zwischen den Stützstellen zu kennen.

Sieh es dir auf der Grafik an, da ist es leichter zu verstehen.

Aaaaaaaah, ich glaube verstanden zu haben was du meinst, wenn ich noch ein klein wenig weiter denke.

Liegt es daran, das ich zwischen den Zeilen sonst eine Kommastelle hätte und nur mit ganze Zahlen verarbeitet werden können?

Ich werde es ausprobieren und berichten.

ES FUNKTIONIERT :slight_smile:

Nun habe ich es verstanden wo es läuft und ich ein wenig mit den Werten rumgespielt habe.

Das einzige was mich daran noch störte, war wenn potiX auf 0 stand, leuchtete die LED und im Monitor wurde wertOUT mit -1 angezeigt.
Habe dagegen einfach

if (wertOUT <=0)
{
wertOUT = 0;

drunter geschrieben und schon lief es.

Super, danke für die Hilfe

Kann es sein, dass dann der linke Wert zum Interpolieren fehlt?

Arduino, oder genauer avr-gcc, greift hemmungslos auch ausserhalb definierter Arrays zu.
Wenn das sicher zu deinem beobachteten -1 führt, ist es ja gut. Aber das ist schwer abzuschätzen.

Könntest dein arrayX bei -1 anfangen lassen oder bei 1024 aufhören lassen, und/oder die Grenz-Prüfung genau checken (was ist richtig: <  <=  >=  > ? )

ES FUNKTIONIERT :slight_smile:
Nun habe ich es verstanden wo es läuft und ich ein wenig mit den Werten rumgespielt habe.

:slight_smile: GLÜCKWUNSCH, spiel ein wenig weiter und versteh noch mehr ...