Czujniki ruchu

Platforma Android ma 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.
  • Czujniki akcelerometru i żyroskopu 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 programowych jest bardziej zmienna, ponieważ do pobierania danych często używają one co najmniej jednego czujnika sprzętowego. W zależności od urządzenia czujniki programowe pobierają dane z akcelerometru i magnetometru lub z żyroskopu.

Czujniki ruchu przydają się do monitorowania ruchu urządzenia, np. przechylenia, potrząsania, obrotu lub zamachu. Ruch jest zwykle odzwierciedleniem bezpośrednich działań użytkownika (np. gdy steruje samochodem w grze lub piłką w grze), ale może też odzwierciedlić warunki otoczenia, w których siedzi urządzenie (np. poruszanie się razem z Tobą podczas jazdy). W pierwszym przypadku monitorujesz ruch względem układu odniesienia urządzenia lub układu odniesienia aplikacji, a w drugim – w odniesieniu do światowego układu odniesienia. Czujniki ruchu same w sobie nie są zwykle używane do monitorowania pozycji urządzenia, ale można ich używać razem 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 czujników dla każdego elementu SensorEvent. Na przykład w przypadku pojedynczego zdarzenia czujnika akcelerometr zwraca dane o sile przyspieszenia dla 3 osi współrzędnych, a żyroskop zwraca dane o szybkości obrotu dla 3 osi współrzędnych. 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 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ą odchylenia.
SensorEvent.values[4] Zmierzone przyspieszenie wzdłuż osi Y z szacowaną kompensacją odchylenia.
SensorEvent.values[5] Zmierzone przyspieszenie wzdłuż osi Z z szacowaną kompensacją odchylenia.
TYPE_GRAVITY SensorEvent.values[0] Siła grawitacji na 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] Szybkość obrotu wokół osi X. rad/s
SensorEvent.values[1] Szybkość 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 dryf wokół osi X.
SensorEvent.values[4] Szacowany dryf wokół osi Y.
SensorEvent.values[5] Szacowany dryf wokół osi Z.
TYPE_LINEAR_ACCELERATION SensorEvent.values[0] Siła przyspieszenia wzdłuż osi X (bez grawitacji). m/s2
SensorEvent.values[1] Siła przyspieszenia wzdłuż osi Y (bez grawitacji).
SensorEvent.values[2] Siła przyspieszenia wzdłuż osi Z (bez grawitacji).
TYPE_ROTATION_VECTOR SensorEvent.values[0] Składnik wektora obrotu na osi x (x * sin(Możliwość)). Wielkości bezwymiarowe
SensorEvent.values[1] Składowa wektora obrotu wzdłuż osi y (y * sin(Możliwość kliknięcia/2)).
SensorEvent.values[2] Składowa wektora obrotu wzdłuż osi z (z * sin(Place/2)).
SensorEvent.values[3] Składnik skalarny wektora obrotu ((cos(Minimum/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ł aktywowany. 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 do wykrywania i monitorowania ruchu. Czujnik wektorów obrotowych jest szczególnie uniwersalny i można go używać do wielu zadań związanych z ruchem, takich jak wykrywanie gestów, monitorowanie zmian kątowych i monitorowanie względnych zmian orientacji. Czujnik wektorów obrotowych jest idealnym rozwiązaniem, gdy tworzysz grę, aplikację rzeczywistości rozszerzonej, 2- lub 3-wymiarowy kompas albo aplikację do stabilizacji aparatu. W większości przypadków użycie tych czujników jest lepszym rozwiązaniem niż korzystanie z akcelerometru i czujnika pola geomagnetycznego lub czujnika orientacji.

Czujniki projektu Android Open Source

Android Open Source Project (AOSP) udostępnia trzy programowe czujniki ruchu: czujnik grawitacji, 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ć za pomocą metod getVendor() i getVersion() (dostawcą jest Google LLC, a 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 trzy czujniki korzystają z żyroskopu. Jeśli urządzenie ich nie ma, czujniki się nie wyświetlają 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 określa względną orientację urządzenia w przestrzeni. Poniższy kod pokazuje, jak pobrać instancję domyślnego czujnika grawitacji:

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 czujnik przyspieszenia (m/s2), a układ współrzędnych jest taki sam jak używany przez czujnik przyspieszenia.

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

Użyj akcelerometru liniowego

Czujnik przyspieszenia liniowego przedstawia trójwymiarowy wektor reprezentujący przyspieszenie wzdłuż każdej osi urządzenia z wyłączeniem grawitacji. Możesz użyć tej wartości do wykrywania gestów. Ta wartość może też służyć jako dane wejściowe dla wewnętrznego systemu nawigacji, który używa obliczeń martwych. Poniższy kod pokazuje, 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 czujnik dostarcza dane o przyspieszeniu zgodnie z tą zależność:

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. Czujnik przyspieszenia liniowego zawsze ma przesunięcie, które należy usunąć. Najprostszy sposób to wbudowanie etapu kalibracji w aplikację. Podczas kalibracji można poprosić użytkownika o położenie 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 z 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).

Użyj czujnika wektorów obrotu

Wektor obrotu przedstawia orientację urządzenia jako kombinację kąta i osi, według której urządzenie obracało się o kąt ∆ wokół osi (x, y lub z). Poniższy kod 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);

Trzy elementy wektora obrotu są przedstawione 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), a kierunek wektora obrotu – kierunkowi osi obrotu.

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

Trzy elementy wektora obrotu są równa 3 ostatnim składnikom kwartionu jednostkowego (cos(Place/2), x*sin(obr./2), y*sin( sprawia, że (obr./2), z*sin( sprawia)/2). Elementy wektora obrotu są niejednolite. Osie x, y i z są zdefiniowane w taki sam sposób jak osie czujnika przyspieszenia. Układ współrzędnych referencyjnych jest zdefiniowany jako bezpośrednia podstawa ortonormalna (zobacz ilustrację 1). Ten układ współrzędnych ma następujące cechy:

  • X jest zdefiniowane jako iloczyn wektora Y x Z. Obszar jest stycznie do ziemi w bieżącym położeniu urządzenia i kieruje się nim w kierunku wschód.
  • Y jest stycznie względem ziemi przy bieżącej lokalizacji urządzenia i wskazuje geomagnetyczny biegun północny.
  • Punkt Z jest skierowany w stronę nieba i jest prostopadła do płaszczyzny podstawy.

Przykładowa aplikacja, która pokazuje, jak korzystać z czujnika wektorów obrotu, znajdziesz w pliku RotationVectorDemo.java.

Użyj ważnego czujnika ruchu

Znaczny czujnik ruchu wywołuje zdarzenie za każdym razem, gdy zostanie wykryty znaczny ruch, a następnie sam się wyłączy. Znaczący ruch to ruch, który może spowodować zmianę lokalizacji użytkownika. Może to być na przykład chodzenie, jazda na rowerze lub siedzenie w poruszającym się samochodzie. W poniższym kodzie pokazujemy, 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: TriggerEventListener.

Użyj czujnika licznika kroków

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 używać tego czujnika na urządzeniach z Androidem 10 (poziom interfejsu API 29) lub nowszym, musisz zadeklarować uprawnienia 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, na których działa Twoja aplikacja, używaj klasy JobScheduler do pobierania w określonych odstępach czasu bieżącej wartości z czujnika licznika kroków. Chociaż różne rodzaje aplikacji wymagają różnych interwałów odczytu z czujnika, powinien on być jak najdłuższy, chyba że aplikacja wymaga danych z czujnika w czasie rzeczywistym.

Używaj czujnika kroków

Czujnik kroków wyzwala zdarzenie za każdym razem, gdy użytkownik wykona krok. Spodziewane opóźnienie wynosi poniżej 2 sekund.

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

Poniższy kod pokazuje, jak pobrać instancję domyślnego czujnika 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 nieprzetworzonymi danymi

Te czujniki dostarczają aplikacji nieprzetworzone dane o siłach liniowych i obrotowych oddziałujących na urządzenie. Aby skutecznie korzystać z wartości z tych czujników, musisz odfiltrować czynniki ze środowiska, takie jak grawitacja. Aby zredukować szum, konieczne może być też 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 interfejsu API 31) lub nowszego, czujnik jest ograniczony do szybkości.

Czujnik przyspieszenia określa przyspieszenie stosowane do urządzenia (Ad), mierząc siły działające na sam czujnik (Fs) według tej zależności:

A_D=-(1/masa)∑F_S

Jednak siła grawitacji zawsze wpływa na mierzone przyspieszenie zgodnie z tą zależnością:

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

Dlatego, gdy siedzi ono na stole (i nie przyspiesza), akcelerometr odczytuje natężenie g = 9,81 m/s2. Podobnie, gdy urządzenie porusza się przy swobodnym upadku i tym samym szybko przyspiesza do ziemi z prędkością 9,81 m/s2, jego akcelerometr odczytuje wartość g = 0 m/s2. Dlatego, aby zmierzyć rzeczywiste przyspieszenie urządzenia, trzeba usunąć z danych akcelerometru udział siły grawitacji. Można to osiągnąć, stosując filtr górnoprzepustowy. I odwrotnie, do wyodrębnienia siły grawitacji można użyć filtra dolnoprzepustowego. 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: dane z czujnika możesz filtrować za pomocą wielu różnych metod. W przykładowym kodzie powyżej użyto prostej stałej filtra (alfa) do utworzenia filtra dolnoprzepustowego. Ta stała filtra jest określana na podstawie stałej czasowej (t), która jest przybliżoną reprezentacją czasu oczekiwania dodawanego przez filtr do zdarzeń z czujnika oraz częstotliwości dostarczania zdarzeń przez czujnik (dt). W przykładowym kodzie w celach demonstracyjnych wykorzystano wartość 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 płasko na stole w swojej naturalnej orientacji, obowiązują te warunki:

  • Jeśli popchniesz urządzenie w lewo (aby przesunęło się w prawo), wartość przyspieszenia X będzie dodatnia.
  • Jeśli popchniesz urządzenie na dół (czyli oddala się od siebie), wartość przyspieszenia 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).
  • Urządzenie stacjonarne będzie miało wartość przyspieszenia +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. Wadą może być stosowanie filtrów dolnoprzepustowych i górnoprzepustowych, które pozwalają wyeliminować siły grawitacji i ograniczyć szum.

Korzystanie z żyroskopu

Żyroskop mierzy szybkość obrotu w rad/s wokół osi x, y i z urządzenia. Poniższy kod pokazuje, jak pobrać 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 interfejsu API 31) lub nowszego, czujnik jest ograniczony do szybkości.

Układ współrzędnych czujnika jest taki sam jak używany w przypadku czujnika przyspieszenia. Obrót jest dodatni w kierunku przeciwnym do ruchu wskazówek zegara. Oznacza to, że obserwator, który patrzy z dobrego położenia na osi x, y lub z na urządzeniu umieszczonym na początku, odnotuje dodatni obrót, jeśli wydaje się, że obraca się ono w lewo. Jest to standardowa definicja matematyczna rotacji dodatniej i różni się od definicji rolki używanej przez czujnik orientacji.

Zwykle dane wyjściowe z żyroskopu są integrowane z upływem czasu w celu obliczenia obrotu opisującego zmianę kątów w danym kroku. 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 zapewniają nieprzetworzone dane obrotu bez filtrowania czy korekcji szumu i dryfu (odchylenia). W praktyce szum żyroskopu i dryf wiążą się z błędami, które trzeba rekompensować. Dryf (odchylenie) i szum możesz zwykle określić, monitorując inne czujniki, takie jak czujnik grawitacji lub akcelerometr.

Używanie nieskalibrowanego żyroskopu

Nieskalibrowany żyroskop jest podobny do żyroskopu, z tym że do szybkości obrotu nie stosuje się kompensacji żyroskopu. Kalibracja fabryczna i kompensacja temperatury są nadal stosowane do szybkości obrotu. Nieskalibrowany żyroskop jest przydatny przy przetwarzaniu i meldowaniu danych orientacji. Ogólnie gyroscope_event.values[0] będzie blisko uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]. To znaczy,

calibrated_x ~= uncalibrated_x - bias_estimate_x

Uwaga: nieskalibrowane czujniki dają więcej nieprzetworzonych wyników i mogą uwzględniać pewne odchylenia, ale ich pomiary zawierają mniej skoków po korekcie wprowadzonych przez kalibrację. 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ą fuzję czujników, wprowadzenie kalibracji może zniekształcić wyniki.

Oprócz informacji o prędkościach obrotu nieskalibrowany żyroskop pokazuje 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 dodatkowo pokazuje użycie interfejsów API opisanych na tej stronie.

Warto też przeczytać