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 przyspieszenia liniowego, wektora obrotu, grawitacji, ruchu znaczącego, 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 ma też żyroskop. 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ą pozyskiwać dane z akcelerometru i magnetometru lub z żyroskopu.
Czujniki ruchu służą 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 zwykle nie służą do monitorowania położenia urządzenia, ale można ich używać z innymi czujnikami, takimi jak czujnik pola geomagnetycznego, aby określić położenie urządzenia względem układu odniesienia świata (więcej informacji znajdziesz w sekcji Czujniki położenia).
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
) wraz z innymi parametrami SensorEvent
. Tabela 1 zawiera podsumowanie czujników ruchu dostępnych 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ą stronniczości. | ||
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] |
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] |
Prędkość 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] |
Przewidywana odchyłka wzdłuż osi Y. | ||
SensorEvent.values[5] |
Szacowany dryft 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 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. Te czujniki zostały zaktualizowane w Androidzie 4.0 i teraz korzystają z żyroskopu urządzenia (oprócz innych czujników) w celu poprawy stabilności i wydajności. 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 konieczna, ponieważ system Android traktuje te 3 czujniki jako czujniki dodatkowe. Jeśli na przykład producent urządzenia udostępnia własny czujnik przyspieszenia, czujnik przyspieszenia AOSP będzie widoczny jako dodatkowy czujnik przyspieszenia. 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ć.
Korzystanie z czujnika grawitacyjnego
Czujnik grawitacji dostarcza wektora trójwymiarowego wskazującego kierunek i wielkość przyspieszenia grawitacyjnego. 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 nieruchome, dane wyjściowe z czujnika grawitacyjnego powinny być identyczne z danymi z akcelerometru.
Korzystanie z 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. 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
Z tego czujnika zwykle korzysta się, gdy chce się uzyskać dane przyspieszenia 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.
System współrzędnych czujnika jest taki sam jak w przypadku czujnika przyspieszenia, podobnie jak 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:
gdzie moduł wektora obrotu jest równy sin(θ/2), a kierunek wektora obrotu jest równy kierunkowi osi obrotu.
3 elementy wektora obrotu są równe 3 ostatnim elementom kwaterniony jednostkowej (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 zdefiniowany 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 styczna do powierzchni 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.
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ć 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 znajdziesz w artykule TriggerEventListener
.
Korzystanie z czujnika krokomierza
Czujnik krokomierza podaje liczbę kroków wykonanych przez użytkownika od ostatniego ponownego uruchomienia, gdy czujnik był włączony. 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 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 uruchamia 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 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. Aby zmniejszyć szum, konieczne może być też zastosowanie algorytmu wygładzania do trendu wartości.
Korzystanie z akcelerometru
Czujnik przyspieszenia mierzy przyspieszenie zastosowane do urządzenia, w tym siłę grawitacji. Poniższy kod pokazuje, jak uzyskać 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ą.
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:
Siła grawitacji zawsze wpływa jednak na zmierzone przyspieszenie zgodnie z tym wzorem:
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. 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: do filtrowania danych z czujników możesz używać wielu różnych technik. Powyższy przykładowy kod używa 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). Przykład kodu używa wartości alfa 0,8 w celu demonstracji. Jeśli używasz tej metody filtrowania, może być konieczne wybranie innej wartości alfa.
Akcelerometry używają standardowego układu 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 popchniesz urządzenie w kierunku 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ły grawitacyjne (-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ół 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 na początku raportuje dodatni obrót, jeśli urządzenie obraca się w kierunku przeciwnym do ruchu wskazówek zegara. Jest to standardowa definicja matematyczna pozytywnego obrotu i nie jest tożsama z definicją przewijania używaną przez czujnik orientacji.
Zazwyczaj dane z żyroskopu są integrowane w czasie, aby obliczyć rotację opisującą zmianę kątów w interwale czasowym. 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ć. 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 działa podobnie 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 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]
.
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, ponieważ są one płynniejsze i bardziej niezawodne. Jeśli na przykład aplikacja próbuje przeprowadzić własną fuzje czujników, wprowadzenie kalibracji może spowodować zniekształcenie wyników.
Oprócz szybkości obrotu nieskalibrowany żyroskop podaje też szacowany przesunięcie 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.