Czujniki ruchu

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

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

  • Czujniki grawitacji, przyspieszenia liniowego, wektora obrotu, istotnego ruchu, licznika kroków i detektora kroków są oparte na sprzęcie lub oprogramowaniu.
  • Akcelerometr i żyroskop są zawsze oparte na sprzęcie.

Większość urządzeń z Androidem ma akcelerometr, a wiele z nich jest teraz w postaci żyroskopu. Dostępność czujników opartych na oprogramowaniu jest bardziej zmienna, ponieważ często korzystają one z co najmniej jednego czujnika sprzętowego. W zależności od urządzenia te oparte na oprogramowaniu czujniki mogą pobierać dane z akcelerometru i magnetometru lub z żyroskopu.

Czujniki ruchu są przydatne do monitorowania ruchu urządzenia, np. pochylania, potrząsania, obracania lub kołysania. 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 pozycji urządzenia, ale można ich używać wraz z innymi czujnikami, takimi jak czujnik pola geomagnetycznego, do ustalania pozycji urządzenia względem światowego układu odniesienia (więcej informacji znajdziesz w sekcji Czujniki pozycji).

Wszystkie czujniki ruchu zwracają wielowymiarowe tablice wartości czujnika dla każdego SensorEvent. Podczas pojedynczego zdarzenia związanego z czujnikiem akcelerometr zwraca dane o sile przyspieszenia dla 3 osi, a żyroskop – dane o prędkości obrotu dla 3 osi. Te wartości danych są zwracane w tablicy float (values) razem z innymi parametrami SensorEvent. W tabeli 1 podsumowano dane z czujników ruchu dostępnych na platformie Android.

Tabela 1. Czujniki ruchu obsługiwane na platformie Android.

Czujnik Dane o zdarzeniach 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 żadnej kompensacji błędów. m/s2
SensorEvent.values[1] Zmierzone przyspieszenie wzdłuż osi Y bez żadnej kompensacji błędów.
SensorEvent.values[2] Zmierzone przyspieszenie wzdłuż osi Z bez żadnej kompensacji błędów.
SensorEvent.values[3] Zmierzone przyspieszenie wzdłuż osi X z szacowaną kompensacją odchylenia.
SensorEvent.values[4] Zmierzone przyspieszenie wzdłuż osi Y z szacowaną kompensacją przesunięcia.
SensorEvent.values[5] Zmierzone przyspieszenie wzdłuż osi Z z szacowaną kompensacją przesunięcia.
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 na osi z.
TYPE_GYROSCOPE SensorEvent.values[0] Szybkość 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] Prędkość obrotu (bez kompensacji dryfu) wokół osi Y.
SensorEvent.values[2] Szybkość obrotu (bez kompensacji dryfu) wokół osi z.
SensorEvent.values[3] Szacowana zmiana wokół osi x.
SensorEvent.values[4] Przewidywany dryft 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 (bez grawitacji).
TYPE_ROTATION_VECTOR SensorEvent.values[0] Składowa wektora obrotu na osi x (x * sin(Możliwość kliknięcia/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 Składnik skalarny jest wartością opcjonalną.

Czujnik wektora obrotu i czujnik przyspieszenia są najczęściej używane do wykrywania i monitorowania ruchu. 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. Na przykład czujnik wektorowy obrotu jest idealny, jeśli tworzysz grę, aplikację rozszerzonej rzeczywistości, dwu- lub trójwymiarowy kompas albo aplikację do stabilizacji kamery. W większości przypadków użycie tych czujników jest lepszym wyborem niż użycie akcelerometru, 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. Czujniki te zostały zaktualizowane w Androidzie 4.0 i korzystają teraz z żyroskopu urządzenia (oprócz innych czujników), aby zwiększyć stabilność i wydajność. Jeśli chcesz wypróbować te czujniki, możesz je zidentyfikować, używając metody getVendor() i metody getVersion() (dostawcą jest Google LLC; numer wersji to 3). Identyfikacja tych czujników według dostawcy i numeru wersji jest niezbędna, ponieważ system Android uznaje te 3 czujniki za czujniki dodatkowe. Jeśli na przykład producent urządzenia ma własny czujnik grawitacji, czujnik grawitacji AOSP będzie się wyświetlać jako dodatkowy czujnik grawitacji. Wszystkie te czujniki działają na podstawie żyroskopu: jeśli urządzenie nie ma żyroskopu, te czujniki nie są widoczne i nie można ich używać.

Użyj czujnika grawitacji

Czujnik grawitacji udostępnia trójwymiarowy wektor wskazujący kierunek i siłę grawitacji. Zwykle czujnik ten służy do określania orientacji względnej urządzenia w przestrzeni. Poniższy kod pokazuje, jak uzyskać instancję 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 w przypadku czujnika przyspieszenia (m/s2), a układ współrzędnych jest taki sam jak w przypadku czujnika przyspieszenia.

Uwaga: gdy urządzenie jest w spoczynku, odczyt z czujnika grawitacji powinien być taki sam jak z akcelerometru.

Korzystanie z liniowego akcelerometru

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. Poniższy kod pokazuje, jak uzyskać 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);

Ten czujnik dostarcza danych przyspieszenia zgodnie z tym wzorem:

linear acceleration = acceleration - acceleration due to gravity

Zwykle używasz tego czujnika, gdy chcesz uzyskać dane o przyspieszeniu bez wpływu grawitacji. Możesz na przykład użyć tego czujnika, aby sprawdzić, jak szybko jedzie Twój samochód. Czujnik przyspieszenia liniowego ma zawsze 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ądzenia na stole, a następnie odczytać przesunięcia dla wszystkich 3 osi. Następnie możesz odjąć to przesunięcie od bezpośrednich odczytów czujnika przyspieszenia, aby uzyskać rzeczywiste przyspieszenie liniowe.

Układ współrzędnych czujnika jest taki sam jak używany przez czujnik przyspieszenia, a także jednostki miary (m/s2).

Korzystanie z czujnika wektora obrotu

Wektor obrotu reprezentuje orientację urządzenia jako kombinację kąta i osi, w której urządzenie zostało obrócone o kąt θ wokół osi (x, y lub z). Poniższy kod pokazuje, jak uzyskać wystąpienie domyślnego czujnika wektorowego 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(θ/2), y*sin(θ/2), z*sin(θ/2)

gdzie moduł wektora obrotu jest równy sin(θ/2), a kierunek wektora obrotu 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ą bezwymiarowe. Osie x, y i z są definiowane tak samo jak w przypadku czujnika przyspieszenia. Współrzędnościowy system odniesienia jest zdefiniowany jako bezpośrednia baza ortonormalna (patrz rysunek 1). Ten układ współrzędnych ma te cechy:

  • X jest zdefiniowane jako iloczyn wektorowy Y x Z. Jest ona styczna do gruntu w bieżącej lokalizacji urządzenia i wskazuje w przybliżeniu na wschód.
  • Y jest stycznie względem ziemi przy bieżącej lokalizacji urządzenia i wskazuje 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.

Korzystanie z czujnika ruchu znaczącego

Czujnik ruchu wyzwala zdarzenie za każdym razem, gdy wykryje znaczący ruch, a następnie wyłącza się. Znaczący ruch to ruch, który może spowodować zmianę lokalizacji użytkownika, np. chodzenie, jazda na rowerze lub siedzenie w ruchu. Poniższy kod pokazuje, jak uzyskać wystąpienie domyślnego czujnika ruchu i zarejestrować detektor 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 znajdziesz w artykule TriggerEventListener.

Korzystanie z czujnika krokomierza

Czujnik licznika kroków zawiera liczbę kroków wykonanych przez użytkownika od ostatniego ponownego uruchomienia, gdy czujnik był aktywowany. Licznik kroków ma większe opóźnienie (do 10 sekund), ale większą dokładność niż czujnik kroków.

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.

Ten 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 czujnika, należy je robić tak długie, jak to możliwe, chyba że aplikacja wymaga danych z czujnika w czasie rzeczywistym.

Korzystanie z czujnika krokomierza

Detektor kroków wyzwala zdarzenie za każdym razem, gdy użytkownik wykona krok. Opóźnienie powinno wynosić mniej niż 2 sekundy.

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 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 nieprzetworzone dane o siłach liniowych i obrotowych oddziałujących na urządzenie. Aby efektywnie korzystać z wartości z tych czujników, musisz odfiltrowywać czynniki środowiskowe, takie jak grawitacja. Aby zmniejszyć szum, konieczne może być również zastosowanie algorytmu wygładzania do trendu wartości.

Korzystanie z akcelerometru

Czujnik przyspieszenia mierzy przyspieszenie stosowane do urządzenia, w tym siłę 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 Twoja aplikacja jest kierowana na Androida 12 (poziom API 31) lub nowszego, ten czujnik jest ograniczony szybkością.

Sensor przyspieszenia określa przyspieszenie zastosowane do urządzenia (Ad) przez pomiar sił działających na sam czujnik (Fs) przy użyciu tego 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

Dlatego gdy urządzenie znajduje się na stole (i nie przyspiesza), akcelerometr odczytuje wartość 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. Z przykładu poniżej dowiesz się, 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: do filtrowania danych z czujników możesz używać wielu różnych technik. W przykładowym kodzie powyżej użyto prostej stałej filtra (alfa) do utworzenia filtra dolnoprzepustowego. Ta stała filtra jest obliczana na podstawie stałej czasowej (t), która jest przybliżoną wartością opóźnienia dodanego przez filtr do zdarzeń czujnika, oraz współczynnika dostarczania zdarzeń przez czujnik (dt). W tym przykładzie kodu na potrzeby demonstracji użyto wartości alfa 0,8. Jeśli używasz tej metody filtrowania, być może trzeba będzie wybrać inną wartość alfa.

Akcelerometr wykorzystuje standardowy układ współrzędnych czujnika. W praktyce oznacza to, że gdy urządzenie leży na stole w naturalnej orientacji, obowiązują te warunki:

  • Jeśli naciśniesz urządzenie po lewej stronie (czyli przesuniesz je w prawo), wartość przyspieszenia x będzie dodatnia.
  • Jeśli naciśniesz urządzenie od dołu (czyli odsuniesz je od siebie), wartość przyspieszenia w osi Y będzie dodatnia.
  • Jeśli pchniesz urządzenie w stronę nieba z przyspieszeniem A m/s2, wartość przyspieszenia z będzie równa A + 9,81, co odpowiada przyspieszeniu urządzenia (+A m/s2) pomniejszonemu 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).

Przy monitorowaniu ruchu urządzenia zwykle warto używać akcelerometru. Niemal każdy telefon i tablet z Androidem ma akcelerometr, który zużywa mniej więcej 10 razy mniej energii niż pozostałe 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ół osi X, Y i Z urządzenia. 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 Twoja aplikacja jest kierowana na Androida 12 (poziom API 31) lub nowszego, ten czujnik jest ograniczony szybkością.

Układ współrzędnych czujnika jest taki sam jak w przypadku czujnika przyspieszenia. Obrót jest dodatni w kierunku przeciwnym do ruchu wskazówek zegara, czyli obserwator patrzący z jakiegoś dodatniego miejsca na osi x, y lub z urządzenia umieszczonego w początku układu współrzędnych odnotowałby obrót dodatni, gdyby urządzenie obracało się w kierunku przeciwnym do ruchu wskazówek zegara. Jest to standardowa definicja matematyczna pozytywnego obrotu i nie jest to to samo co definicja przewijania używana przez czujnik orientacji.

Zazwyczaj dane z żyroskopu są integrowane w czasie, aby obliczyć rotację opisującą 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 dryf żyroskopu powodują błędy, które należy wyrównać. Zwykle określa się dryft (błąd systematyczny) i szum, monitorując inne czujniki, takie jak czujnik grawitacji lub akcelerometr.

Korzystanie z nieskalibrowanego żyroskopu

Nieskalibrowany żyroskop jest podobny do żyroskopu, z tym że nie stosuje się do niego kompensacji dryftu. kalibracja fabryczna i kompensacja temperatury nadal mają wpływ na szybkość obrotów; Nieskalibrowany żyroskop jest przydatny przy przetwarzaniu i meldowaniu 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].

calibrated_x ~= uncalibrated_x - bias_estimate_x

Uwaga: nieskalibrowane czujniki zapewniają bardziej surowe wyniki i mogą zawierać pewne odchylenia, ale ich pomiary zawierają mniej skoków wynikających z poprawek wprowadzonych podczas kalibracji. Niektóre aplikacje mogą preferować te nieskalibrowane wyniki jako płynniejsze i bardziej wiarygodne. Jeśli na przykład aplikacja próbuje przeprowadzić własną fuzje czujników, wprowadzenie kalibracji może zniekształcić wyniki.

Oprócz prędkości obrotu nieskalibrowany żyroskop podaje też szacowany dryf wokół każdej osi. Poniższy kod pokazuje, jak uzyskać instancję domyślnego nieskalibrowanego żyroskopu:

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

Przykład BatchStepSensor pokazuje dodatkowe zastosowania interfejsów API opisanych na tej stronie.

Przeczytaj też