Czujniki ruchu

Platforma Android udostępnia kilka czujników, które umożliwiają monitorowanie ruchu urządzenia.

Czujniki możliwe architektury różnią się w zależności od typu czujnika:

  • Grawitacja, przyspieszenie liniowe, wektor obrotu, znaczny ruch, krok licznika i wykrywacza kroków są oparte na sprzęcie opartych na oprogramowaniu.
  • Akcelerometr i żyroskop są zawsze oparte na sprzęcie.

Większość urządzeń z Androidem ma akcelerometr, a wiele z nich ma też żyroskop. Dostępność czujników programowych jest większa ponieważ często wykorzystują one co najmniej jeden czujnik sprzętowy do określenia i skalowalnych danych. W zależności od urządzenia czujniki oparte na oprogramowaniu mogą wykrywać z akcelerometru i magnetometru lub z żyroskopu.

Czujniki ruchu przydają się do monitorowania ruchu urządzenia, np. przechylenia, wstrząsów, obrotu lub machanie zębów. Ruch jest zwykle odzwierciedleniem bezpośrednich działań użytkownika (np. kierowania samochodem w grze lub sterowania piłką w grze), ale może też być odzwierciedleniem środowiska fizycznego, w którym znajduje się urządzenie (np. poruszanie się razem z użytkownikiem podczas jazdy samochodem). W pierwszym przypadku monitorujesz ruch w układzie odniesienia urządzenia lub aplikacji, a w drugim – w układzie odniesienia świata. Czujniki ruchu same w sobie nie są zwykle używane do monitorowania urządzenia, ale można ich używać z innymi czujnikami, takimi jak czujniki pola magnetycznego, określić położenie urządzenia względem światowego punktu odniesienia (zobacz Czujniki położenia, aby dowiedzieć się więcej ).

Wszystkie czujniki ruchu zwracają wielowymiarowe tablice wartości czujników dla każdego elementu SensorEvent. Na przykład w przypadku pojedynczego zdarzenia czujnika akcelerometr powraca dane o siłach przyspieszenia dla trzech osi współrzędnych oraz szybkość obrotu powrotnego żyroskopu dla trzech osi współrzędnych. Te wartości danych są zwracane w tablicy float (values) razem z innymi: SensorEvent . W tabeli 1 podsumowano dane z czujników ruchu dostępnych na platformie Android.

Tabela 1. Czujniki ruchu obsługiwane przez platformę Android.

Czujnik Dane zdarzenia z czujnika Opis Jednostki miary
TYPE_ACCELEROMETER SensorEvent.values[0] Siła przyspieszenia wzdłuż osi X (w tym grawitacja). m/s2
SensorEvent.values[1] Siła przyspieszenia wzdłuż osi Y (w tym grawitacja).
SensorEvent.values[2] Siła przyspieszenia wzdłuż osi Z (w tym grawitacja).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] Zmierzone przyspieszenie wzdłuż osi X bez kompensacji odchylenia. m/s2
SensorEvent.values[1] Zmierzone przyspieszenie wzdłuż osi Y bez kompensacji odchylenia.
SensorEvent.values[2] Zmierzone przyspieszenie wzdłuż osi Z bez kompensacji odchylenia.
SensorEvent.values[3] Zmierzone przyspieszenie wzdłuż osi X z szacowaną kompensacją stronniczości.
SensorEvent.values[4] Zmierzone przyspieszenie wzdłuż osi Y z szacowaną kompensacją stronności.
SensorEvent.values[5] Zmierzone przyspieszenie wzdłuż osi Z z szacowaną kompensacją odchylenia.
TYPE_GRAVITY SensorEvent.values[0] Siła grawitacji wzdłuż osi x. m/s2
SensorEvent.values[1] Siła grawitacji wzdłuż osi y.
SensorEvent.values[2] Siła grawitacji wzdłuż osi Z.
TYPE_GYROSCOPE SensorEvent.values[0] Prędkość obrotu wokół osi X. rad/s
SensorEvent.values[1] Prędkość obrotu wokół osi y.
SensorEvent.values[2] Szybkość obrotu wokół osi Z.
TYPE_GYROSCOPE_UNCALIBRATED SensorEvent.values[0] Szybkość obrotu (bez kompensacji dryfu) wokół osi X. rad/s
SensorEvent.values[1] Szybkość obrotu (bez kompensacji dryfu) wokół osi Y.
SensorEvent.values[2] Szybkość obrotu (bez kompensacji dryfu) wokół osi Z.
SensorEvent.values[3] Szacowany dryft wokół osi x.
SensorEvent.values[4] Szacunkowa odchyłka wzdłuż osi Y.
SensorEvent.values[5] Szacowany dryf wokół osi Z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] Siła przyspieszenia wzdłuż osi X (z wyłączeniem grawitacji). m/s2
SensorEvent.values[1] Siła przyspieszenia wzdłuż osi y (z wyłączeniem grawitacji).
SensorEvent.values[2] Siła przyspieszenia wzdłuż osi z (z wyłączeniem grawitacji).
TYPE_ROTATION_VECTOR SensorEvent.values[0] Składnik wektora obrotu wzdłuż osi X (x * sin(θ/2)). bez jednostek
SensorEvent.values[1] Składnik wektora obrotu wzdłuż osi y (y * sin(θ/2)).
SensorEvent.values[2] Składnik wektora obrotu wzdłuż osi z (z * sin(θ/2)).
SensorEvent.values[3] Składnik skalarny wektora obrotu ((cos(θ/2)).1
TYPE_SIGNIFICANT_MOTION Nie dotyczy Nie dotyczy Nie dotyczy
TYPE_STEP_COUNTER SensorEvent.values[0] Liczba kroków wykonanych przez użytkownika od ostatniego ponownego uruchomienia, gdy czujnik był włączony. Kroki
TYPE_STEP_DETECTOR Nie dotyczy Nie dotyczy Nie dotyczy

1 Komponent skalarny jest wartością opcjonalną.

Czujnik wektora obrotu i czujnik grawitacji to najczęściej używane czujniki ruchu ich wykrywanie i monitorowanie. Czujnik wektorowy obrotu jest szczególnie wszechstronny i może być używany do wykonywania wielu zadań związanych z ruchy, takich jak wykrywanie gestów, monitorowanie zmiany kąta i monitorowanie względnych zmian orientacji. Czujnik wektorów obrotowych jest idealny, jeśli tworzą grę, aplikację rzeczywistości rozszerzonej, dwu- lub trójwymiarowy kompas, lub aplikacji do stabilizacji aparatu. W większości przypadków lepiej jest używać tych czujników niż przy użyciu akcelerometru i czujnika pola geomagnetycznego lub czujnika orientacji.

Czujniki projektu Android Open Source

Projekt Android Open Source (AOSP) udostępnia 3 oparte na oprogramowaniu czujniki ruchu: czujnik przyspieszenia grawitacyjnego, czujnik przyspieszenia liniowego i czujnik wektora obrotu. Te czujniki zostały zaktualizowane za System Android 4.0 i korzysta teraz z żyroskopu urządzenia (oprócz innych czujników), aby zwiększyć stabilność skuteczność reklam. Jeśli chcesz wypróbować te czujniki, możesz je zidentyfikować za pomocą metod getVendor() i getVersion() (dostawcą jest Google LLC, a numer wersji to 3). Identyfikowanie tych czujników według dostawcy numer wersji jest niezbędny, ponieważ system Android uznaje te trzy czujniki za dodatkowe i czujników. Jeśli na przykład producent urządzenia ma własny czujnik grawitacji, AOSP czujnik grawitacji jest wyświetlany jako dodatkowy czujnik grawitacji. Wszystkie 3 czujniki działają żyroskop: jeśli urządzenie nie jest wyposażone w żyroskop, czujniki nie są widoczne i nie są; dostępnych do użytku.

Korzystanie z czujnika grawitacyjnego

Czujnik grawitacji udostępnia trójwymiarowy wektor wskazujący kierunku i wielkości grawitacji. Zwykle czujnik ten służy do określania orientacji względnej urządzenia w przestrzeni. Poniższy kod pokazuje, jak uzyskać wystąpienie domyślnego czujnika przyspieszenia ziemskiego:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);

Jednostki są takie same, jak używane przez przyspieszenie (m/s2), a układ współrzędnych jest taki sam jak czujnik przyspieszenia.

Uwaga: gdy urządzenie jest nieruchome, dane wyjściowe z czujnika grawitacyjnego powinny być identyczne z danymi z akcelerometru.

Użyj akcelerometru liniowego

Czujnik przyspieszenia liniowego dostarcza 3-wymiarowego wektora przyspieszenia wzdłuż każdej osi urządzenia, z wyjątkiem grawitacji. Możesz użyć tej wartości do wykrywania gestów. Wartość może też służyć jako dane wejściowe dla systemu nawigacji bezwładnościowej, który wykorzystuje metodę szacowania. Ten kod wyświetla się jak pobrać instancję domyślnego czujnika przyspieszenia liniowego:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);

Zasadniczo ten czujnik dostarcza dane o przyspieszeniu w następujący sposób: relacja:

linear acceleration = acceleration - acceleration due to gravity

Zwykle używasz tego czujnika, gdy chcesz uzyskać dane o przyspieszeniu bez wpływu grawitacji. Czujnik może na przykład sprawdzić, jak szybko jedziesz samochodem. Model liniowy czujnik przyspieszenia zawsze ma przesunięcie, które należy usunąć. Najprostszym sposobem jest wbudowanie w aplikację etapu kalibracji. Podczas kalibracji możesz poprosić użytkownika o ustawienie urządzenie na stole i odczytać przesunięcia dla wszystkich trzech osi. Następnie możesz odjąć tę wartość odsunięcie od bezpośrednich odczytów z czujnika przyspieszenia, by uzyskać rzeczywistą jego przyspieszenie.

System współrzędnych czujnika jest taki sam jak w przypadku czujnika przyspieszenia, podobnie jak jednostki miary (m/s2).

Użyj czujnika wektorów obrotu

Wektor obrotu przedstawia orientację urządzenia jako kombinację kąta i oś, na której urządzenie obróciło się o kąt Φ wokół osi (x, y lub z). Poniżej pokazuje, jak pobrać wystąpienie domyślnego czujnika wektorów obrotu:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);

3 elementy wektora obrotu wyraża się w ten sposób:

x*sin(Możliwość kliknięcia/2), y*sin(Conversions/2), z*sin(Conversions/2)

Gdzie siła wektora obrotu jest równa sin(zmienne) i kierunek wektora jest równy kierunkowi osi obrotu.

Rysunek 1. System współrzędnych używany przez czujnik wektora obrotu.

3 elementy wektora obrotu są równe 3 ostatnim elementom jednostkowego kwaterniony (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). Elementy wektora obrotu są bez jednostek. Osie x, y i z są definiowane tak samo jak w przypadku czujnika przyspieszenia. Plik referencyjny układ współrzędnych jest zdefiniowany jako bezpośrednia podstawa ortonormalna (zobacz ilustrację 1). Ten układ współrzędnych ma te cechy:

  • X jest zdefiniowane jako iloczyn wektorowy Y x Z. Jest styczna do funkcji w bieżącej lokalizacji urządzenia i w kierunku wschód.
  • Y jest dotykiem gruntu w bieżącej lokalizacji urządzenia i wskazuje na geomagnetyczny biegun północny.
  • Z wskazuje niebo i jest prostopadły do płaszczyzny horyzontu.

Przykładowa aplikacja, która pokazuje, jak używać czujnika wektorowego obrotu, znajduje się w pliku RotationVectorDemo.java.

Użyj ważnego czujnika ruchu

Znaczny czujnik ruchu aktywuje zdarzenie za każdym razem, gdy zostanie wykryty znaczny ruch, wtedy sam się wyłączy. Znaczący ruch to ruch, który może prowadzić do zmiany lokalizacja użytkownika; np. spacerowanie, jazda na rowerze czy siedzenie w poruszającym się samochodzie. Poniższy kod pokazuje, jak uzyskać instancję domyślnego czujnika ruchu i jak zarejestrować listenera zdarzeń:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val mSensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
val triggerEventListener = object : TriggerEventListener() {
    override fun onTrigger(event: TriggerEvent?) {
        // Do work
    }
}
mSensor?.also { sensor ->
    sensorManager.requestTriggerSensor(triggerEventListener, sensor)
}

Java

private SensorManager sensorManager;
private Sensor sensor;
private TriggerEventListener triggerEventListener;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);

triggerEventListener = new TriggerEventListener() {
    @Override
    public void onTrigger(TriggerEvent event) {
        // Do work
    }
};

sensorManager.requestTriggerSensor(triggerEventListener, mSensor);

Więcej informacji: TriggerEventListener.

Korzystanie z czujnika krokomierza

Czujnik licznika kroków pokazuje liczbę kroków zrobionych przez użytkownika od ostatniego restartu gdy czujnik był aktywny. Licznik kroków ma większą latencję (do 10 sekund), ale jest dokładniejszy niż czujnik krokomierza.

Uwaga: aby aplikacja mogła korzystać z tego czujnika na urządzeniach z Androidem 10 (poziom interfejsu API 29) lub nowszym, musisz zadeklarować uprawnienie ACTIVITY_RECOGNITION.

Poniższy kod pokazuje, jak uzyskać instancję domyślnego czujnika licznika kroków:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);

Aby oszczędzać baterię na urządzeniach z Twoją aplikacją, użyj klasy JobScheduler, aby pobierać bieżącą wartość z czujnika krokomierza w określonym odstępie czasu. Chociaż różne typy aplikacji wymagają różnych interwałów odczytu z czujnika, ustaw ten interwał jako tak długo, jak to możliwe, chyba że aplikacja wymaga danych z czujnika w czasie rzeczywistym.

Używaj czujnika kroków

Detektor kroków wyzwala zdarzenie za każdym razem, gdy użytkownik wykona krok. Czas oczekiwania wynosi powinien być poniżej 2 sekund.

Uwaga: musisz zadeklarować ACTIVITY_RECOGNITION uprawnienia, by aplikacja mogła używać tego czujnika na urządzeniach z Android 10 (poziom interfejsu API 29) lub nowszy.

Poniższy kod pokazuje, jak uzyskać instancję domyślnego czujnika wykrywania kroków:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);

Praca z danymi nieprzetworzonymi

Te czujniki dostarczają aplikacji surowych danych o siłach liniowych i siłach obrotowych działających na urządzenie. Aby efektywnie korzystać z wartości z tych czujników, musisz odfiltrowywać czynniki środowiskowe, takie jak grawitacja. Konieczne może być też zastosowanie algorytmu wygładzania do trendu. wartości, aby zredukować szum.

Korzystanie z akcelerometru

Czujnik przyspieszenia mierzy przyspieszenie urządzenia, w tym siłę drgań grawitacji. Poniższy kod pokazuje, jak pobrać instancję domyślnego czujnika przyspieszenia:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

Java

private SensorManager sensorManager;
private Sensor sensor;
  ...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

Uwaga: jeśli aplikacja jest kierowana na Androida 12 (poziom interfejsu API 31) lub jest wyższy, ograniczone do stawek.

W ujęciu teoretycznym czujnik przyspieszenia określa przyspieszenie zastosowane do urządzenia (Ad) przez pomiar sił działających na sam czujnik (Fs) przy użyciu następującego związku:

A_D=-(1/mass)∑F_S

Siła grawitacji zawsze wpływa jednak na zmierzone przyspieszenie zgodnie z tym wzorem:

A_D=-g-(1/masa)∑F_S

Z tego powodu, gdy urządzenie leży na stole (i nie przyspiesza), akcelerometr wskazuje natężenie g = 9,81 m/s2. Podobnie, gdy urządzenie jest w wolnym spadku i szybko przyspiesza w kierunku ziemi z przyspieszeniem 9,81 m/s2, jego akcelerometr rejestruje wartość g = 0 m/s2. Dlatego, aby zmierzyć rzeczywiste przyspieszenie urządzenia, z danych akcelerometru należy usunąć wpływ siły grawitacji. Można to osiągnąć, stosując filtr górnoprzepustowy. Z drugiej strony, filtr dolnoprzepustowy może być użyty do wyizolowania siły grawitacji. Ten przykład pokazuje, jak to zrobić:

Kotlin

override fun onSensorChanged(event: SensorEvent) {
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    val alpha: Float = 0.8f

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0]
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1]
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2]

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0]
    linear_acceleration[1] = event.values[1] - gravity[1]
    linear_acceleration[2] = event.values[2] - gravity[2]
}

Java

public void onSensorChanged(SensorEvent event){
    // In this example, alpha is calculated as t / (t + dT),
    // where t is the low-pass filter's time-constant and
    // dT is the event delivery rate.

    final float alpha = 0.8;

    // Isolate the force of gravity with the low-pass filter.
    gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
    gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
    gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];

    // Remove the gravity contribution with the high-pass filter.
    linear_acceleration[0] = event.values[0] - gravity[0];
    linear_acceleration[1] = event.values[1] - gravity[1];
    linear_acceleration[2] = event.values[2] - gravity[2];
}

Uwaga: dane z czujnika możesz filtrować za pomocą wielu różnych metod. Powyższy przykładowy kod używa prostej stałej filtra (alfa) do utworzenia filtra dolnoprzepustowego. Ten filtr jest określana na podstawie stałej czasowej (t), która jest przybliżoną reprezentacją czasu oczekiwania filtr jest dodawany do zdarzeń z czujnika oraz do częstotliwości dostarczania zdarzeń przez czujnik (dt). W tym przykładzie kodu na potrzeby demonstracyjne użyto wartości alfa 0,8. Jeśli używasz tej metody filtrowania, może być konieczne aby wybrać inną wartość alfa.

Akcelerometry używają standardowego systemu współrzędnych czujnika. W praktyce oznacza to, że podczas kładzenia urządzenia obowiązują następujące warunki na stole w naturalnej orientacji:

  • Jeśli naciśniesz urządzenie po lewej stronie (czyli przesuniesz je w prawo), wartość przyspieszenia x będzie dodatnia.
  • Jeśli popchniesz urządzenie na dół (tak się oddala), wartość przyspieszenia Y wynosi pozytywnym.
  • Jeśli pchniesz urządzenie w stronę nieba z przyspieszeniem A m/s2, wartość przyspieszenia Z jest równa A + 9,81, co odpowiada przyspieszeniu urządzenia (+A m/s2) pomniejszone o siłę grawitacji (-9,81 m/s2).
  • Wartość przyspieszenia urządzenia nieruchomego to +9,81, co odpowiada przyspieszeniu urządzenia (0 m/s2 minus siła grawitacji, która wynosi -9,81 m/s2).

Ogólnie rzecz biorąc, akcelerometr jest dobrym czujnikiem do monitorowania ruchu urządzenia. Prawie każdy telefon i tablet z Androidem ma akcelerometr, który zużywa około 10 razy mniej energii niż inne czujniki ruchu. Jednym z mankamentów jest to, że może być konieczne wdrożenie filtrów dolnoprzepustowych i górnoprzepustowych, aby wyeliminować siły grawitacyjne i zredukować szum.

Korzystanie z żyroskopu

Żyroskop mierzy szybkość obrotu w rad/s wokół punktów x, y urządzenia, i osi Z. Poniższy kod pokazuje, jak uzyskać wystąpienie domyślnego żyroskopu:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

Uwaga: jeśli aplikacja jest kierowana na Androida 12 (poziom interfejsu API 31) lub jest wyższy, ograniczone do stawek.

Układ współrzędnych czujnika jest taki sam jak w przypadku czujnika przyspieszenia. Rotacja jest dodatnia w w lewo; czyli obserwatora patrzącego na ekran z pewnego dodatniego położenia na osi x, y lub z na urządzeniu umieszczonym na punkcie początkowym. dodatni obrót, jeśli wydaje się, że urządzenie obraca się w lewo. To jest standardowa definicja matematyczna rotacji dodatniej i różni się od definicji dla używane przez czujnik orientacji.

Zazwyczaj dane z żyroskopu są integrowane w czasie, aby obliczyć obrót opisujący zmianę kątów w interwale czasowym. Na przykład:

Kotlin

// Create a constant to convert nanoseconds to seconds.
private val NS2S = 1.0f / 1000000000.0f
private val deltaRotationVector = FloatArray(4) { 0f }
private var timestamp: Float = 0f

override fun onSensorChanged(event: SensorEvent?) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0f && event != null) {
        val dT = (event.timestamp - timestamp) * NS2S
        // Axis of the rotation sample, not normalized yet.
        var axisX: Float = event.values[0]
        var axisY: Float = event.values[1]
        var axisZ: Float = event.values[2]

        // Calculate the angular speed of the sample
        val omegaMagnitude: Float = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ)

        // Normalize the rotation vector if it's big enough to get the axis
        // (that is, EPSILON should represent your maximum allowable margin of error)
        if (omegaMagnitude > EPSILON) {
            axisX /= omegaMagnitude
            axisY /= omegaMagnitude
            axisZ /= omegaMagnitude
        }

        // Integrate around this axis with the angular speed by the timestep
        // in order to get a delta rotation from this sample over the timestep
        // We will convert this axis-angle representation of the delta rotation
        // into a quaternion before turning it into the rotation matrix.
        val thetaOverTwo: Float = omegaMagnitude * dT / 2.0f
        val sinThetaOverTwo: Float = sin(thetaOverTwo)
        val cosThetaOverTwo: Float = cos(thetaOverTwo)
        deltaRotationVector[0] = sinThetaOverTwo * axisX
        deltaRotationVector[1] = sinThetaOverTwo * axisY
        deltaRotationVector[2] = sinThetaOverTwo * axisZ
        deltaRotationVector[3] = cosThetaOverTwo
    }
    timestamp = event?.timestamp?.toFloat() ?: 0f
    val deltaRotationMatrix = FloatArray(9) { 0f }
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

Java

// Create a constant to convert nanoseconds to seconds.
private static final float NS2S = 1.0f / 1000000000.0f;
private final float[] deltaRotationVector = new float[4]();
private float timestamp;

public void onSensorChanged(SensorEvent event) {
    // This timestep's delta rotation to be multiplied by the current rotation
    // after computing it from the gyro sample data.
    if (timestamp != 0) {
      final float dT = (event.timestamp - timestamp) * NS2S;
      // Axis of the rotation sample, not normalized yet.
      float axisX = event.values[0];
      float axisY = event.values[1];
      float axisZ = event.values[2];

      // Calculate the angular speed of the sample
      float omegaMagnitude = sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);

      // Normalize the rotation vector if it's big enough to get the axis
      // (that is, EPSILON should represent your maximum allowable margin of error)
      if (omegaMagnitude > EPSILON) {
        axisX /= omegaMagnitude;
        axisY /= omegaMagnitude;
        axisZ /= omegaMagnitude;
      }

      // Integrate around this axis with the angular speed by the timestep
      // in order to get a delta rotation from this sample over the timestep
      // We will convert this axis-angle representation of the delta rotation
      // into a quaternion before turning it into the rotation matrix.
      float thetaOverTwo = omegaMagnitude * dT / 2.0f;
      float sinThetaOverTwo = sin(thetaOverTwo);
      float cosThetaOverTwo = cos(thetaOverTwo);
      deltaRotationVector[0] = sinThetaOverTwo * axisX;
      deltaRotationVector[1] = sinThetaOverTwo * axisY;
      deltaRotationVector[2] = sinThetaOverTwo * axisZ;
      deltaRotationVector[3] = cosThetaOverTwo;
    }
    timestamp = event.timestamp;
    float[] deltaRotationMatrix = new float[9];
    SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
    // User code should concatenate the delta rotation we computed with the current rotation
    // in order to get the updated rotation.
    // rotationCurrent = rotationCurrent * deltaRotationMatrix;
}

Standardowe żyroskopy dostarczają danych bez filtrowania ani korygowania szumów i błędów (błędu systematycznego). W praktyce szum i drift żyroskopu powodują błędy, które trzeba skompensować. Dryf (odchylenie) i szum możesz zwykle określić, monitorując inne czujniki, takie jak takich jak czujnik grawitacji czy akcelerometr.

Korzystanie z nieskalibrowanego żyroskopu

Nieskalibrowany żyroskop działa podobnie do żyroskopu, z tym że nie stosuje się do niego kompensacji dryftu. Kalibracja fabryczna i kompensacja temperatury są nadal stosowane do szybkości obrotu. Nieskalibrowany żyroskop jest przydatny do postprocessingu i zszywania danych orientacji. Ogólnie rzecz biorąc, gyroscope_event.values[0] będzie zbliżone do uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]. To znaczy,

calibrated_x ~= uncalibrated_x - bias_estimate_x

Uwaga: nieskalibrowane czujniki dostarczają więcej nieprzetworzonych wyników i mogą uwzględnia pewną odchylenie, ale ich pomiary zawierają mniej skoków po korekcie kalibracji. Niektóre aplikacje mogą preferować te nieskalibrowane wyniki, ponieważ są one płynniejsze i bardziej niezawodne. Jeśli na przykład aplikacja próbuje przeprowadzić własną wprowadzanie kalibracji może zniekształcić wyniki.

Oprócz szybkości obrotu nieskalibrowany żyroskop podaje też szacowany przesunięcie wokół każdej osi. Ten kod pokazuje, jak pobrać instancję domyślnej nieskalibrowany żyroskop:

Kotlin

val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensor: Sensor? = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED)

Java

private SensorManager sensorManager;
private Sensor sensor;
...
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED);

Dodatkowe przykłady kodu

W przykładzie BatchStepSensor dowiesz się więcej o tym, wykorzystanie interfejsów API omówionych na tej stronie.

Przeczytaj też