Forum: Mikrocontroller und Digitale Elektronik Linearisierung klappt nicht


von Abderrahim (Gast)


Angehängte Dateien:

Lesenswert?

Hallo alle,
habe ein nicht lineares Signal(5%-Nichtlinearität) von einem Hallsensor.
Muss es linearisieren, nach der Berechnung der linearen Regression durch 
Excel habe ich folgende Gleichung bekommen: Ysoll = 1,2311x + 111.
so schreibe ich sie im Prog.: Lin_current_value_x =((val_x*123110L)/10)
und es lässt sich rechnen.
Problem jetzt, dass obwohl ich dies gemacht habe wird nichts 
linearisiert!
in Anhang findet ihr Ergebnisse vor und nach Linearisierung.
Danke im Voraus!

von Karl H. (kbuchegg)


Lesenswert?

Bei dem Signalverlauf wirst du auch mit einer linearen Regression
nicht weit kommen. Der Verlauf ist noch nicht mal annähernd eine
Gerade.

Eine Parabel (also eine Kurve 2. Grades) passt schon eher,
aber wahrscheinlich wird es auf ein Polynom 3. Grades hinauslaufen.

von Pad S. (padesmos)


Angehängte Dateien:

Lesenswert?

entschuldigt, das was ich dargestellt habe war die Abweichung,
so sehen meine U ist und soll:

von Pad S. (padesmos)


Lesenswert?

oder weiß jemand wie ich es durch eine look up Tabelle linearisieren 
kann?
Ich weiß grob daß man den Interval in kleinen Intervale, wo es gleiche 
Steigung herrscht, teilen muß. und dann werden sie einfach aufgerufen in 
einem Array.
Aber  genau um dies zu realisieren brauche ich eine C- Code.
Kann jemand vielleicht helfen?

von 900ss (900ss)


Lesenswert?

Mit Excel kannst Du Dir das Polynom generieren lassen. Du must die
Werte deine Kurve alle in eine Tabelle hacken und eine Kurve daraus 
generieren. So wie ich das sehe, hast Du genau das schon gemacht.
Im Kontextmenu der Grafik (oder war es doch in einem anderen Menu?) dann 
mal nach Trendlinie schauen. Die wird dann angelegt und die Formel dazu 
ist genau das Polynom, das Du für Deine Kurve benötigst. Funktioniert 
prima. Nur genauer beschreiben kann ich es aus dem Kopf gerade nicht.

von 900ss (900ss)


Lesenswert?

So ich hab es nochmal ausprobiert.
Du must den Grafen mit der Maus markieren (anklicken). Dann dazu das 
Kontextmenu aufmachen (rechter Mausklick). Dort "Trendlinie hinzufügen" 
wählen. Da dann "Polynomisch" (Reihenfolge ist der Grad des Polynoms 2 
oder 3 sollte passen) und unter Optionen "Gleichung im Diagramm 
darstellen" anwählen. Danach hast Du alles was Du brauchst.

von Abderrahim (Gast)


Lesenswert?

Hi,
Danke für die nette Hilfe!
das ist nicht Frage der Erstellung der Gleichung.
ich will eigentlich einen c-Code der mir die Sache
erledigt.
Look- up table zu erstellen ist mein eigentliches Problem:
wie erstellen, welche werte zuweisen, woher weiß ich welche werte bei 
mir passen werden..uzw.

von Karl H. (kbuchegg)


Lesenswert?

Abderrahim wrote:
> Hi,
> Danke für die nette Hilfe!
> das ist nicht Frage der Erstellung der Gleichung.
> ich will eigentlich einen c-Code der mir die Sache
> erledigt.
> Look- up table zu erstellen ist mein eigentliches Problem:
> wie erstellen, welche werte zuweisen, woher weiß ich welche werte bei
> mir passen werden..uzw.

Lookup Tabelle willst du ja gerade umgehen. Das ist ja der
Sinn der linearen Regression: Die ganze Tabellengeschichte
bzw. die Daten durch eine Gerade ersetzen, die den gleichen
Zusammenhang möglichst gut wiedergibt.

In deinem Fall hast du den y Wert von Sensor und möchtest
wissen welchem x-Wert der entspricht. Also gehst du mit
dem Sensor Wert in die Umkehrung deiner Gleichung rein
und erhältst den x-Wert dazu.
Natürlich stimmt der nicht exakt mit deinem gemessenen Wert
überein. Die Ausgleichsgerade, die du mittels linearer Regr.
erhalten hast, ist ja nur eine 'Best Fit' Gerade. Aber dafür
hast du einen einfachen Rechengang, der dir aus dem y-Wert
einen x-Wert berechnet, ohne dass du viel Platz für irgendwelche
Tabellen benötigst.

von Abderrahim (Gast)


Lesenswert?

>In deinem Fall hast du den y Wert von Sensor und möchtest
>wissen welchem x-Wert der entspricht. Also gehst du mit
>dem Sensor Wert in die Umkehrung deiner Gleichung rein
>und erhältst den x-Wert dazu.
nein, das ist nicht der Fall. ich möchte eigentlich y-Werte
(entsprechen Hallspannung) zu einer Funktion geben, damit
ich am Ende einen Winkel genau berechne.

von Karl H. (kbuchegg)


Lesenswert?

Was willst du genau erreichen?
Beschreib doch mal!

Ich komme immer mehr zu der Überzeugung, dass du mit dem
falschen Werkzeug versuchst ein Problem zu lösen, von dem
niemand weiß, was denn das eigentliche Problem ist.


Das ist jetzt nicht böse gemeint: Versuche möglichst deutsche
Sätze zu verwenden. Aus einem Kauderwelsch wird niemand schlau.

von Abderrahim (Gast)


Lesenswert?

ich möchte eine hochauflösende Positionserfassung mit meinem 
ratiometrischen Hallsensor(Allegro 1381) erreichen.
Direkt von meinem Hallsensor bekomme ich ein analoges Signal.
Dieses Signal ist nicht 100% linear. deshalb versuche ich diese 
Nichtlinearität(momentan 5%) mit einem uC zu verbessern.
Ich hoffe ich konnte dir ein klares Bild über mein Ziel geben.
Gruß

von blog.coldtobi.de (Gast)


Lesenswert?

Also, wie bereits von anderen erwähnt: Die Linearisierung mit Regression 
ist eigentlich dafür, dass Du Dir eine Lookup-Table sparen kannst.
Du implementierst stattdessen die Gleichung x = m*y + t.
(x = Winkel, y=Eingabewert)

Ne Lookup-Table schaut z,.B so aus:

uint8_t lookup[255] { 1, 2, 3, 5, 6, 8, 9 , 10 , ... };

und Verwendet so:

x  = lookup[y];

Vorteil der ersten Methode ist, dass die klein ist, die zweite ist sehr 
groß braucht aber kaum Rechenzeit.

Eine lineare Regression wird den Fehler größtenteils umverteilen. Die 
Fehlerfläche wird kleiner, jedoch nicht optimal. Probier mal ne 
quadratische oder kubische aus.

PS
www.zunzun.com
Hier kannst du mit den einzelenen Ausgleichsrechnungen experimentieren, 
und der gibt auch noch nette Abschätzungen zu den Fehlern ab.

PPS:

von blog.coldtobi.de (Gast)


Lesenswert?

PPS [cont]
Du hast doch schon Werte aufgenommen. Diese wären doch die Grundlage für 
die Loouptable, oder?

von Abderrahim (Gast)


Lesenswert?

Ja, ich habe Werte aufgenommen, aber sie zeigen keine gute Linearität.
Bild davon siehst du oben.
ich sage dir was ich so von der look up Table verstehe, und korrigiere 
mich bitte wenn ich mich irre:
Ich schreiben in einem Array die schlechten Werte(z.B. 71 Wert), die 
grosse Abweichung verursachen, und werden sie von Werten in einem 
anderen Array gewechselt:

dword Array-soll[],Array-ist[];
for(i=0;i<71;i++)
{Array-soll[i]=Array-ist[i];}

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Abderrahim wrote:
> Ich schreiben in einem Array die schlechten Werte(z.B. 71 Wert), die
> grosse Abweichung verursachen, und werden sie von Werten in einem
> anderen Array gewechselt:
>

Nein.

Es gibt 2 Möglichkeiten

Möglichkeit 1:

Du hast nur 1 Array.
Dein schlechter Wert ist Index in dieses Array und aus dem
Array kriegst du an dieser Position den guten Wert.

Bsp:
Deine Werte seien:   10, 20, 30

Jetzt weist du, dass du bei einem Wert von 10 eigentlich 8
haben müsstest. Bei einem Wert von 20 wäre der korrekte Wert
21 und bei 30 sollte eigentlich 29 rauskommen.

Das nächste Problem ist, dass du eine Umrechnung von deinen
gemessenen Werten zu Array-Indizes brauchst. Du musst also
die Werte 10, 20, 30 irgendwie auf 0, 1, 2 umrechnen.
Durch scharfes hinschauen erkennst du, dass das genau dann der
Fall ist, wenn du vom Wert 10 abziehst und durch 10 dividierst
(Man kann das auch mathematisch ableiten :-)

Also:
1
  int GoodValues[] = { 8, 21, 29 };
2
3
  Value = GoodValues[ ( BadValue - 10 ) / 10 ];

Das Problem an dieser Version ist, dass du eine Umrechnungsformel
von den schlechten Werten zu den Array Indizes brauchst. Das
kann der Fall sein, es kann aber auch sein, dass es keine
vernünftige Formel gibt.

Möglichkeit 2:
Wenn Möglichkeit 1 wegen einer fehlenden vernünftigen Index-Umrechnung
ausfällt, kann man immer noch Plan B anwenden.

Angenommen deine bekannten Umrechnungswerte sehen so aus

   Messwert         linearisierter Wert
  --------------------------------------
      10                   8
      23                  24
      38                  35

Man baut sich eine Struktur, die den schlechten Wert mit dem
guten Wert gruppiert
1
struct Value {
2
  int Bad;
3
  int Good;
4
};

Daraus wird jetzt das Umrechnungsarray generiert
1
struct Value Table[] = 
2
  {
3
    { 10,   8 },
4
    { 23,  24 },
5
    { 38,  35 }
6
  };

Wie wird jetzt die Umrechnung gemacht? Ganz einfach: Du gehst
mit dem gemessenen Wert in das Array und suchst nach der korrekten
Zeile.
1
int Correct( int Value )
2
{
3
  int i;
4
5
  for( i = 0; i < 3; ++i )
6
    if( Table[i].Bad == Value )
7
      return Table[i].Good;
8
9
  return 0;
10
}

Das bisherige funktioniert nur dann, wenn es für jeden möglichen
schlechten Messwert eine entsprechende Zeile in der Tabelle gibt.
Im Beispiel wäre das: Es können nur die Messwerte 10, 23 und 38
auftreten. Das wird man unter Umständen so nicht haben. Was macht
man dann? Nun dann kann man immer noch linear interpolieren.

Wenn der korrekte Wert bei 10 8 lautet, und der korrekte Wert bei
23 auf 24 kommt, dann berechnet sich der korrekte Wert für zb 18
zu:

   y = k * x + d       // die allgemeine lineare Gleichung
                       // du musst k und d bestimmen

                       // also wird mal für die bekannten Werte
                       // eingesetzt

   8 = k * 10 + d
  24 = k * 23 + d

                       // daraus kann man nun k und d bestimmen
                       // erste Gleichung von der 2.ten abziehen
  16 = k  23 - k  10
                       // k herausheben
  16 = k * ( 23 - 10 )
                       // und durchdividieren
   k = 16 / 13
   k = 1.2307          // damit kennen wir k

                       // dieses k in eine der beiden Ursprungs-
                       // gleichungen einsetzen und d bestimmen

  8 = 1.2307 * 10 + d
  8 = 12.307 + d
  8 - 12.307 = d
      -4.307  = d

Die Umrechnungsformel in diesem Abschnitt ( 10 -> 8, 23 -> 24 )
lautet also: y = 1.2307 * x - 4.307

(Zur Kontrolle: für 10:   y = 1.2307 * 10 - 4.307 = 8      stimmt
                für 23:   y = 1.2307 * 23 - 4.307 = 23.991 stimmt auch
)

Für einen Messwert von 18 ergibt sich also ein korregierter Wert
von
    y = 1.2307 * 18 - 4.307 = 17.8456

Die Strategie sieht dann so aus:
Mit deinem gemessenen Wert, suche in der Tabelle nach dem unmittelbar
kleinerem Bad-Wert, dem unmittelbar größeren Bad-Wert und interpoliere
dazwischen linear. Da die Tabelle nach aufsteigendem Bad-Wert
sortiert ist, genügt es, den Eintrag zu finden, der gerade
kleiner als der gemessene Wert ist. Der nächst größere ist
dann einfach der nächste Index.
1
int Correct( int Value )
2
{
3
  int i;
4
  int Index = -1;
5
  double k, d;
6
7
  for( i = 0; i < 3; ++i )   // 3, weil 3 Tabelleneinträge vorhanden sind
8
    if( Table[i].Bad < Value )
9
      Index = i;
10
11
  if( Index == -1 )
12
    return Table[0].Good;   // Messwert kleiner als alle Tabellenwerte
13
14
  if( Index == 2 )          // Messwert größer als alle Tabellenwerte
15
    return Table[2].Good;
16
17
  k = (double)( Table[Index+1].Bad - Table[Index].Bad ) /
18
      (double)( Table[Index+1].Good - Table[Index].Good );
19
  d = Table[Index].Good - Table[Index].Bad * k;
20
21
  return (int)( Value * k + d + 0.5 );
22
}

Ich hoffe mal, dass ich mich jetzt bei der Umsetzung der speziellen
Formel von oben in eine allgemeine Form im Programm nicht vertan habe.
Du solltest das am Papier nochmal prüfen.

von Abderrahim (Gast)


Lesenswert?

Hallo Karl,
herzlischen Dank für die ausführliche Erklärung.
Viel Respekt!

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.