Einzelnen Beitrag anzeigen

Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
734 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Orientationsensor, Floatanimation

  Alt 11. Mai 2017, 16:06
Wie im anderen Thread erwähnt: Geh in die System.Android.Sensors.pas und setze einen Breakpoint bei function TNativeSensor.LastValue: ASensorEvent;. Dann siehst du, was alles an Events ankommt. (Siehe auch die anderen Posts mit Hinweis auf das Embarcadero Beispiel Programm, welches dir alle Sensoren deines Geräts auflistet.)

Gut nutzbar sind alle Vektoren welche nicht schief zu unserem Standort zeigen. Der Sensor, welche dir einen Vektor auf "magnetisch Nord" zurückgibt ist keine gute Wahl (zeigt schief zu unserer Ebene; Umrechnen ist angesagt). Der Schwerkraftsensor zeigt senkrecht nach unten und ist eine gute Wahl.

Das Ruckeln kannst du (siehe deine andere Frage) mit Glättung/Regression verhindern oder zumindest vermindern.
Glättung/Regression zum Beispiel bei Wikipedia. Falls du Maple oder Mathematica hast, dann kannst du mit diesen Paketen rumspielen.

Eine sehr, sehr einfache Glättung ist zum Beispiel:

Wert(neu) := 1/(n+1)*(Messwert + n*Wert) wobei für kleine n der Wert natürlich rascher in Richtung Messwerte läuft, aber dafür anfälliger ist für "Schwankungen/Ruckeln".

Du könntest auch lineare Regression verwenden. Du würdest dabei zum Beispiel die letzten 10 Messwerte betrachten und eine Gerade g:y=ax+b suchen, für welche die 10 Messwerte möglichst nahe* bei g liegen.
[*nahe=Du forderst zum Beispiel, dass die Summe über alle Quadrate von (Messwert(x)-g(x)) minimal sein soll.]

Nach dem Glätten:
Du kannst zusätzlich einen Wert c definieren, welcher bestimmt, ob du den neuen geglätteten Wert w ausgibst, oder den zuletzt ausgegebenen Wert v stehen lässt: Wenn abs(w-v) < c, dann lässt du v stehen. [Deine Kugel würde sich also nicht bewegen, solange die neuen Glättungswerte maximal c vom momentanen angezeigten Wert abweichen.]
(Du musst dir aber bewusst sein, dass der angezeigte Wert dann jeweils maximal c vom "effektiven" Wert abweichen kann. Du könntest hier leicht ein "Feinjustieren" einbauen, wenn du das benötigst.)


So könntest du mittels linearer Regression aus IN Messwert den OUT Anzeigewert ermitteln.

Delphi-Quellcode:
 type
    TGlaetten = class // Lineare Regression
      private
        FMesswerteAnzahl : integer;
        FMesswerte : array of double;
        FZeitStempel : array of TDateTime;
        FAusgabeWert : double;
        FBand : double;
      public
        Constructor Create( MesswerteAnzahl : integer; Band : double );
        function wert( neuerwert : double ) : double;
    end;

var glaettenX, glaettenY, glaettenZ: TGlaetten;


constructor TGlaetten.Create( MesswerteAnzahl : integer; Band : double );
begin
  FBand := Band;
  FMesswerteAnzahl := MesswerteAnzahl;
  setlength( FMessWerte, FMesswerteAnzahl );
  setlength( FZeitStempel, FMesswerteAnzahl );
end;


function TGlaetten.wert(neuerwert: Double) : double;
var anzahlmesswerte, imband, i : integer;
    wert_regression, b1, b2, sumx, sumy, sumxx, sumxy, x, y, glw, sum : double;

    // Infos zum Beispiel auf: https://de.wikipedia.org/wiki/Lineare_Regression

begin
  if ( length( FMessWerte ) = FMesswerteAnzahl ) then
  begin
    Move( FZeitStempel[1], FZeitStempel[0],sizeof(double)*(FMesswerteAnzahl-1));
    Move( FMessWerte[1], FMessWerte[0],sizeof(double)*(FMesswerteAnzahl-1));

    

    FMessWerte[ FMesswerteAnzahl-1 ] := neuerwert;
    FZeitStempel[ FMesswerteAnzahl-1 ] := now;

    sumxy := 0;
    sumxx := 0;
    sumx := 0;
    sumy := 0;

    for i := 0 to FMesswerteAnzahl-1 do
    begin
      x := ( FZeitStempel[i] - FZeitStempel[0] )*24*60*60*10;
      y := FMesswerte[i];
      sumxy := sumxy + x*y;
      sumxx := sumxx + x*x;

      sumx := sumx + x;
      sumy := sumy + y;
    end;

    // y = b1 + b2x
    b2 := ( FMesswerteAnzahl*sumxy - sumx*sumy )/( FMesswerteAnzahl*sumxx - sumx*sumx );
    b1 := ( sumxx*sumy - sumx*sumxy )/( FMesswerteAnzahl*sumxx - sumx*sumx );

    x := ( FZeitStempel[FMesswerteAnzahl-1] - FZeitStempel[0] )*24*60*60*10;
    wert_regression := b1 + b2*x;

    if abs( FAusgabeWert - wert_regression ) > FBand then FAusgabeWert := wert_regression;
  end;
  Result := FAusgabeWert;
end;


Zum Beispiel im FormCreate:
10 = du willst die letzten 10 Messwerte beachten für die lineare Regression
2 = wenn der zuletzt ausgegebene Wert und der neu errechnete Wert näher als 2 beieinander liegen, dann soll TGlaetten.wert(.) den bisherigen Wert zurückgeben.

Delphi-Quellcode:
procedure TOrientationSensorForm.FormCreate(Sender: TObject);
begin
  glaettenX := Tglaetten.Create( 10, 2 );
  glaettenY := Tglaetten.Create( 10, 2 );
  glaettenZ := Tglaetten.Create( 10, 2 );
end;

Zum Beispiel Tilt glätten:
Du übergibst IN Sensorwert und erhältst OUT den anzuzeigenden Wert.

Delphi-Quellcode:
...
  lbTiltX.Text := floattostr(glaettenX.wert(OrientationSensor1.Sensor.TiltX));
  lbTiltY.Text := floattostr(glaettenY.wert(OrientationSensor1.Sensor.Tilty));
  lbTiltZ.Text := floattostr(glaettenZ.wert(OrientationSensor1.Sensor.Tiltz));
...
Michael Gasser

Geändert von Michael II (11. Mai 2017 um 18:27 Uhr)
  Mit Zitat antworten Zitat