Czujniki ruchu

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

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

  • Czujniki grawitacji, przyspieszenia liniowego, wektoru obrotu, ruchu znaczącego ruchu, licznika kroków oraz detektorów kroków są oparte na sprzęcie lub oprogramowaniu.
  • Czujniki akcelerometru i żyroskopu są zawsze sprzętowe.

Większość urządzeń z Androidem ma akcelerometr, a wiele z nich ma teraz żyroskop. Dostępność czujników programowych jest bardziej zmienna, ponieważ uzyskanie danych często wymaga użycia jednego lub kilku czujników sprzętowych. W zależności od urządzenia czujniki te mogą pobierać dane z akcelerometru i magnetometru lub z żyroskopu.

Czujniki ruchu przydają się do monitorowania ruchu urządzenia, takiego jak pochylenie, wstrząsanie, obrót i huśtawka. Ruch zazwyczaj odzwierciedla bezpośrednie działania użytkownika (np. kieruje samochodem w grze lub piłkę w grze), ale może też odzwierciedlać środowisko fizyczne, w którym siedzi urządzenie (np. porusza się razem z Tobą podczas jazdy samochodem). W pierwszym przypadku monitorujesz ruch względem układu odniesienia urządzenia lub aplikacji, a w drugim – względem naszego układu odniesienia. Same czujniki ruchu nie są zwykle używane do monitorowania pozycji urządzenia, ale można ich używać z innymi czujnikami, np. z czujnikiem pola geomagnetycznego, aby określić położenie urządzenia względem światowego układu odniesień (więcej informacji znajdziesz w sekcji Czujniki położenia).

Wszystkie czujniki ruchu zwracają wielowymiarowe macierze wartości z czujników dla każdego parametru SensorEvent. Na przykład w przypadku pojedynczego zdarzenia z czujnika akcelerometr zwraca dane siły przyspieszenia dla trzech osi współrzędnych, a żyroskop zwraca dane dotyczące szybkości obrotu dla trzech osi współrzędnych. Te wartości danych są zwracane w tablicy float (values) razem z innymi parametrami SensorEvent. Tabela 1 zawiera podsumowanie czujników ruchu dostępnych na platformie Android.

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

Czujnik Dane zdarzeń z czujnika Opis Jednostki miary
TYPE_ACCELEROMETER SensorEvent.values[0] Siła przyspieszenia wzdłuż osi X (z uwzględnieniem grawitacji). m/s2
SensorEvent.values[1] Siła przyspieszenia wzdłuż osi Y (z uwzględnieniem grawitacji).
SensorEvent.values[2] Siła przyspieszenia wzdłuż osi Z (z uwzględnieniem grawitacji).
TYPE_ACCELEROMETER_UNCALIBRATED SensorEvent.values[0] Zmierzone przyspieszenie na osi X bez kompensacji odchylenia. m/s2
SensorEvent.values[1] Zmierzone przyspieszenie na osi Y bez kompensacji odchylenia.
SensorEvent.values[2] Zmierzone przyspieszenie wzdłuż osi Z bez kompensacji odchylenia.
SensorEvent.values[3] Zmierzone przyspieszenie na osi X z szacowaną kompensacją odchylenia.
SensorEvent.values[4] Zmierzone przyspieszenie na 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 ciężkości wzdłuż osi X. m/s2
SensorEvent.values[1] Siła grawitacji na 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] Prędkość 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] Szacunkowe dryf wokół osi X.
SensorEvent.values[4] Szacunkowe dryf wokół osi Y.
SensorEvent.values[5] Szacowane 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 wzdłuż osi x (x * sin(≠s/2)). Bezsznurkowe
SensorEvent.values[1] Składnik wektora obrotu wzdłuż osi y (y * sin(kolejnej)).
SensorEvent.values[2] Składnik wektora obrotu wzdłuż osi z (z * sin(≠s/2)).
SensorEvent.values[3] Składnik skalarny wektora obracania ((cos(szczegółowo/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 po aktywowaniu czujnika. Kroki
TYPE_STEP_DETECTOR Nie dotyczy Nie dotyczy Nie dotyczy

1 Komponent skalarny jest wartością opcjonalną.

Czujniki wektorów obrotowych i grawitacji to najczęściej używane czujniki do wykrywania i monitorowania ruchu. Obrotowy czujnik wektorów jest wyjątkowo wszechstronny i można go używać do wielu różnych zadań związanych z ruchem, takich jak wykrywanie gestów, monitorowanie zmian kątowych i monitorowanie względnych zmian orientacji. Na przykład czujnik wektorów obrotowych jest idealny, jeśli tworzysz grę, aplikację obsługującą rzeczywistość rozszerzoną, kompas dwuwymiarowy lub trójwymiarowy albo aplikację do stabilizacji kamery. W większości przypadków użycie tych czujników jest lepszym wyborem niż korzystanie z akcelerometru i czujnika pola geomagnetycznego lub czujnika orientacji.

Czujniki projektu Android Open Source

Android Open Source Project (AOSP) obejmuje trzy oparte na oprogramowaniu czujniki ruchu: czujnik grawitacji, czujnik przyspieszenia liniowego oraz czujnik wektorów obrotowych. Czujniki te zostały zaktualizowane w Androidzie 4.0 i teraz wykorzystują żyroskop urządzenia (oprócz innych czujników), aby poprawić stabilność i wydajność. Jeśli chcesz wypróbować te czujniki, możesz je zidentyfikować, korzystając z metod getVendor() i getVersion() (dostawcą jest Google LLC, a numer wersji to 3). Identyfikacja tych czujników jest niezbędna według dostawcy i numeru wersji, ponieważ system Android traktuje te czujniki jako czujniki dodatkowe. Jeśli na przykład producent urządzenia ma własny czujnik grawitacji, taki czujnik wyświetla się jako dodatkowy czujnik grawitacji AOSP. Wszystkie 3 czujniki wykorzystują żyroskop: jeśli urządzenie nie ma żyroskopu, czujniki te nie będą widoczne i nie będą dostępne do użycia.

Użyj czujnika grawitacji

Czujnik grawitacji dostarcza trójwymiarowy wektor wskazujący kierunek i siłę grawitacji. Zwykle używa się go do określania względnej orientacji urządzenia w przestrzeni. Ten kod pokazuje, jak pobrać wystąpienie 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 akceleracji.

Uwaga: gdy urządzenie znajduje się w stanie spoczynku, dane wyjściowe czujnika grawitacji powinny być takie same jak dane akcelerometru.

Użycie akcelerometru liniowego

Czujnik przyspieszenia liniowego dostarcza trójwymiarowy wektor reprezentujący przyspieszenie wzdłuż każdej osi urządzenia, z wyłączeniem grawitacji. Możesz używać tej wartości do wykrywania gestów. Ta wartość może też służyć jako dane wejściowe dla bezwładnego systemu nawigacji, który korzysta z liczenia zwłok. Poniższy kod pokazuje, jak pobrać wystąpienie 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);

Ogólnie czujnik dostarcza dane przyspieszenia zgodnie z następującą zależnością:

linear acceleration = acceleration - acceleration due to gravity

Zwykle używa się go, gdy chcesz uzyskać dane dotyczące przyspieszenia bez wpływu grawitacji. Możesz na przykład użyć tego czujnika, aby sprawdzić, jak szybko porusza się samochód. Czujnik przyspieszenia liniowego zawsze ma przesunięcie, które należy usunąć. Najprostszym sposobem jest wbudowanie kalibracji w aplikację. Podczas kalibracji możesz poprosić użytkownika o ustawienie urządzenia na stole, a następnie odczytać odsunięcia wszystkich 3 osi. Następnie możesz odjąć to przesunięcie od bezpośrednich odczytów czujnika akceleracji, aby uzyskać rzeczywiste przyspieszenie liniowe.

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

Użyj czujnika wektora obrotowego

Wektor obrotu przedstawia orientację urządzenia jako kombinację kąta i osi, w której urządzenie obróciło się o kąt ≠ oś wokół osi (x, y lub z). Poniższy kod pokazuje, jak pobrać instancję 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ą wyrażone w ten sposób:

x*sin(≠/2), y*sin( kolejnej)

Gdzie wielkość wektora obrotu jest równa sin(≠/2), a kierunek wektora obrotu jest równy kierunkowi osi obrotu.

Rysunek 1. Układ współrzędnych używany przez czujnik wektorów obrotowych.

Trzy elementy wektora obrotu są równe ostatnim trzem składom kwternionu jednostkowego (cos(≠/2), x*sin(szczegół/2), y*sin(szczegółowo/2), z*sin(kolejnej)). Elementy wektora rotacji nie są zjednoczone. Osie x, y i z są zdefiniowane w taki sam sposób jak czujnik przyspieszenia. Referencyjny układ współrzędnych jest zdefiniowany jako bezpośrednia podstawa ortonormalna (zobacz ilustrację 1). Ten układ współrzędnych ma następujące cechy:

  • X jest zdefiniowany jako iloczyn wektorowy Y x Z. Jest styki względem ziemi w bieżącej lokalizacji urządzenia i w przybliżeniu wschodnim.
  • Y jest styczne do ziemi w bieżącym położeniu urządzenia i kieruje w kierunku geomagnetycznego bieguna północnego.
  • Oś Z wskazuje niebo i jest prostopadła do płaszczyzny podłoża.

Przykładową aplikację, która pokazuje, jak korzystać z czujnika wektorów obrotowych, znajdziesz tutaj: RotationVectorDemo.java.

Używaj czujnika znaczącego ruchu

Czujnik ruchu wywołuje zdarzenie za każdym razem, gdy zostanie wykryty ruch, a potem się wyłącza. Znaczący ruch to ruch, który może spowodować zmianę lokalizacji użytkownika, np. spacer, jazda na rowerze lub siedzenie w jadącym samochodzie. Poniższy kod pokazuje, jak uzyskać wystąpienie domyślnego czujnika znacznego ruchu i jak 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 wskazuje liczbę kroków wykonanych przez użytkownika od ostatniego ponownego uruchomienia, gdy czujnik został aktywowany. Licznik kroków ma większe opóźnienie (do 10 sekund), ale ma 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ć uprawnienie ACTIVITY_RECOGNITION.

Ten kod pokazuje, jak pobrać wystąpienie domyślnego czujnika 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 typy aplikacji wymagają różnych interwałów odczytu czujnika, zalecamy, aby ten interwał był jak najdłuższy, chyba że aplikacja wymaga danych w czasie rzeczywistym z czujnika.

Użyj czujnika kroków

Czujnik kroków wyzwala zdarzenie za każdym razem, gdy użytkownik robi krok. Oczekiwane opóźnienie nie przekracza 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ć uprawnienie ACTIVITY_RECOGNITION.

Ten kod pokazuje, jak pobrać wystąpienie 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 stosowanych do urządzenia. Aby skutecznie korzystać z danych z czujników, musisz odfiltrować czynniki ze środowiska, takie jak grawitacja. Może być też konieczne zastosowanie algorytmu wygładzania do trendu wartości, aby zmniejszyć szum.

Korzystanie z akcelerometru

Czujnik przyspieszenia mierzy przyspieszenie stosowane do urządzenia, w tym siłę grawitacji. Ten kod pokazuje, jak pobrać wystąpienie 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 wyższe, czujnik ma ograniczoną częstotliwość.

Czujnik przyspieszenia określa przyspieszenie stosowane do urządzenia (Ad), mierząc siły działające na sam czujnik (Fs), zgodnie z tą zależność:

A_D=-(1/masa)∑F_S

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

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

Dlatego, gdy urządzenie leży na stole (i nie przyspiesza), akcelerometr odczytuje wartość g = 9,81 m/s2. Podobnie gdy urządzenie ma swobodny upadek i szybko przyspiesza w kierunku ziemi z prędkością 9, 81 m/s2, jego akcelerometr odczytuje wartość g = 0 m/s2. Dlatego, aby zmierzyć rzeczywiste przyspieszenie urządzenia, należy usunąć z danych akcelerometru działanie siły grawitacji. Można to osiągnąć, stosując filtr górnoprzepustowy. I na odwrót: filtr dolnoprzepustowy może wyizolować siłę 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: dane z czujnika można filtrować na wiele różnych sposobów. W przykładowym kodzie powyżej użyto prostej stałej filtra (alfa) do utworzenia filtra dolnoprzepustowego. Ta stała filtra jest generowana ze stałej czasowej (t), która stanowi przybliżone odzwierciedlenie czasu oczekiwania, który filtr dodaje do zdarzeń z czujnika, oraz częstotliwości dostarczania zdarzeń z czujnika (dt). W przykładowym kodzie użyto dla celów demonstracyjnych wartości alfa wynoszącej 0,8. Jeśli używasz tej metody filtrowania, może być konieczne wybranie innej wartości alfa.

Akcelerometry korzystają ze standardowego układu współrzędnych czujnika. W praktyce oznacza to, że gdy urządzenie leży płasko na stole w naturalnej orientacji, mają zastosowanie te warunki:

  • Jeśli popchniesz urządzenie po lewej stronie (aby przesunęło się w prawo), wartość przyspieszenia x będzie dodatnia.
  • Jeśli popchniesz urządzenie na dół (aby odsunęło się od Ciebie), wartość przyspieszenia y będzie dodatnia.
  • Jeśli przesuwasz urządzenie w stronę nieba z przyspieszeniem A m/s2, wartość przyspieszenia Z jest równa A + 9,81, czyli przyspieszenia urządzenia (+Am/s2) pomniejszonego o siłę grawitacji (-9,81 m/s2).
  • Urządzenie nieruchome ma wartość przyspieszenia +9,81, co odpowiada przyspieszeniu (0 m/s2 minus siła grawitacji, która wynosi -9,81 m/s2).

Ogólnie akcelerometr doskonale nadaje się do monitorowania ruchu urządzenia. Niemal każdy telefon i tablet z Androidem ma akcelerometr, który zużywa około 10 razy mniej energii niż pozostałe czujniki ruchu. Wadą jest konieczność zastosowania filtrów dolnoprzepustowych i górnoprzepustowych, aby eliminować siły grawitacji i ograniczyć szum.

Użyj żyroskopu

Żyroskop mierzy szybkość obrotów w rad/s wokół osi x, y i z urządzenia. Ten kod pokazuje, jak pobrać instancję 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 wyższe, czujnik ma ograniczoną częstotliwość.

Układ współrzędnych czujnika jest taki sam jak używany przez czujnik przyspieszenia. Obrót jest dodatni w kierunku przeciwnym do ruchu wskazówek zegara. Oznacza to, że obserwator, który patrzy od pewnego dodatniego miejsca na osi X, Y lub Z w urządzeniu umieszczonym w punkcie początkowym, zadeklarowałby obrót w kierunku przeciwnym do ruchu wskazówek zegara. Jest to standardowa matematyczna definicja rotacji dodatniej, która różni się od definicji rolki używanej przez czujnik orientacji.

Dane wyjściowe żyroskopu są zwykle zintegrowane w czasie, aby obliczyć obrót opisujący 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 dostarczają nieprzetworzone dane obrotowe bez filtrowania i korekty pod kątem szumu i dryfu (odchylenia). W praktyce szum i dryf żyroskopu spowodują błędy, które należy zrekompensować. Dryf (odchylenie) i szum określa się zwykle, monitorując inne czujniki, takie jak czujnik grawitacji lub akcelerometr.

Używanie nieskalibrowanego żyroskopu

Nieskalibrowany żyroskop jest podobny do żyroskopu z tą różnicą, że do szybkości obrotu nie jest stosowana kompensacja dryfu żyroskopu. Do szybkości obrotu nadal stosowane są ustawienia fabryczne i kompensację temperatury. Nieskalibrowany żyroskop jest przydatny podczas przetwarzania i scalania danych orientacji. Ogólnie gyroscope_event.values[0] będzie zbliżony do uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3]. Oznacza to, że

calibrated_x ~= uncalibrated_x - bias_estimate_x

Uwaga: nieskalibrowane czujniki dają więcej surowych wyników i mogą zawierać pewne odchylenia, ale ich pomiary zawierają mniej przeskoków spowodowanych korekcją za pomocą kalibracji. Niektóre aplikacje mogą preferować takie nieskalibrowane wyniki, ponieważ są one płynniejsze i bardziej wiarygodne. Jeśli na przykład aplikacja próbuje przeprowadzić własną fuzję czujników, wprowadzenie kalibracji może zniekształcać wyniki.

Oprócz szybkości obrotu, nieskalibrowany żyroskop dostarcza również szacunkowe dryf wokół każdej osi. Ten kod pokazuje, jak pobrać 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 użycia BatchStepSensor dodatkowo pokazuje wykorzystanie interfejsów API omówionych na tej stronie.

Przeczytaj też