Platforma Androida 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 ich typu:
- Czujniki grawitacji, przyspieszenia liniowego, wektora obrotu, znacznego ruchu, licznika kroków i wykrywania kroków są oparte na sprzęcie lub oprogramowaniu.
- Akcelerometr i żyroskop są zawsze czujnikami sprzętowymi.
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 do uzyskania danych wykorzystują one co najmniej 1 czujnik sprzętowy. W zależności od urządzenia te czujniki programowe mogą pobierać dane z akcelerometru i magnetometru lub z żyroskopu.
Czujniki ruchu są przydatne do monitorowania ruchu urządzenia, takiego jak przechylenie, potrząśnięcie, obrót czy kołysanie. Ruch jest zwykle odzwierciedleniem bezpośrednich działań użytkownika (np. kierowania samochodem lub kontrolowania piłki w grze), ale może też odzwierciedlać fizyczne otoczenie, w którym znajduje się urządzenie (np. poruszanie się wraz z użytkownikiem, gdy ten prowadzi samochód). W pierwszym przypadku monitorujesz ruch względem układu odniesienia urządzenia lub aplikacji, a w drugim – względem układu odniesienia świata. Czujniki ruchu same w sobie nie są zwykle używane do monitorowania pozycji urządzenia, ale mogą być używane z innymi czujnikami, takimi jak czujnik pola geomagnetycznego, do określania pozycji urządzenia względem układu odniesienia (więcej informacji znajdziesz w sekcji Czujniki położenia).
Wszystkie czujniki ruchu zwracają wielowymiarowe tablice wartości czujników dla każdego SensorEvent. Na przykład podczas pojedynczego zdarzenia czujnika akcelerometr zwraca dane o sile przyspieszenia dla 3 osi współrzędnych, a żyroskop – dane o szybkości obrotu dla 3 osi współrzędnych. 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 Androida.
Tabela 1. Czujniki ruchu obsługiwane na platformie Androida.
| 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 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 wzdłuż osi X. | m/s2 |
SensorEvent.values[1] |
Siła grawitacji wzdłuż osi Y. | ||
SensorEvent.values[2] |
Siła grawitacji wzdłuż osi Z. | ||
TYPE_GYROSCOPE |
SensorEvent.values[0] |
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] |
Szacowane przesunięcie wokół osi X. | ||
SensorEvent.values[4] |
Szacowane przesunięcie wokół osi Y. | ||
SensorEvent.values[5] |
Szacowane przesunięcie 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ładowa wektora obrotu wzdłuż osi X (x * sin(θ/2)). | Bez jednostek |
SensorEvent.values[1] |
Składowa wektora obrotu wzdłuż osi Y (y * sin(θ/2)). | ||
SensorEvent.values[2] |
Składowa 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ł aktywny. | 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 wektora obrotu jest szczególnie wszechstronny i może być używany do wielu zadań związanych z ruchem, takich jak wykrywanie gestów, monitorowanie zmian kątowych i monitorowanie zmian orientacji względnej. Na przykład czujnik wektora obrotu jest idealny, jeśli tworzysz grę, aplikację rzeczywistości rozszerzonej, kompas 2D lub 3D albo aplikację do stabilizacji aparatu. W większości przypadków użycie tych czujników jest lepszym rozwiązaniem niż użycie akcelerometru i czujnika pola geomagnetycznego lub czujnika orientacji.
Czujniki projektu Android Open Source
Projekt Android Open Source (AOSP) udostępnia 3 programowe czujniki ruchu: czujnik grawitacji, czujnik przyspieszenia liniowego i czujnik wektora obrotu. Te czujniki zostały zaktualizowane w Androidzie 4.0 i obecnie korzystają z żyroskopu urządzenia (oprócz innych czujników), aby poprawić stabilność i wydajność. Jeśli chcesz wypróbować te czujniki, możesz je zidentyfikować za pomocą metody getVendor() i metody getVersion() (dostawca to Google LLC, numer wersji to 3). Identyfikacja tych czujników według dostawcy i numeru wersji jest konieczna, ponieważ system Android uważa te 3 czujniki za czujniki dodatkowe. Jeśli na przykład producent urządzenia udostępnia własny czujnik grawitacji, czujnik grawitacji AOSP jest wyświetlany jako dodatkowy. Wszystkie 3 czujniki korzystają z żyroskopu. Jeśli urządzenie nie ma żyroskopu, czujniki nie są widoczne i nie można ich używać.
Używanie czujnika grawitacyjnego
Czujnik grawitacji dostarcza trójwymiarowy wektor wskazujący kierunek i wartość grawitacji. Zwykle ten czujnik służy do określania względnej orientacji urządzenia w przestrzeni. Poniższy kod pokazuje, jak uzyskać 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 te używane przez czujnik przyspieszenia (m/s2), a układ współrzędnych jest taki sam jak ten używany przez czujnik przyspieszenia.
Uwaga: gdy urządzenie jest w spoczynku, dane wyjściowe czujnika grawitacji powinny być identyczne z danymi akcelerometru.
Używanie akcelerometru liniowego
Czujnik przyspieszenia liniowego dostarcza wektor trójwymiarowy 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. Wartość ta może też służyć jako dane wejściowe dla systemu nawigacji bezwładnościowej, który korzysta z nawigacji zliczeniowej. 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);
Zgodnie z koncepcją ten czujnik dostarcza dane o przyśpieszeniu na podstawie tej zależności:
linear acceleration = acceleration - acceleration due to gravity
Zwykle używa się tego czujnika, gdy chce się 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 zawsze ma przesunięcie, które należy usunąć. Najprostszym sposobem jest dodanie do aplikacji etapu kalibracji. Podczas kalibracji możesz poprosić użytkownika o położenie urządzenia na stole, a następnie odczytać przesunięcia dla wszystkich 3 osi. Możesz następnie 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 w przypadku czujnika przyspieszenia, podobnie jak jednostki miary (m/s2).
Używanie 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ć instancję domyślnego czujnika wektora 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 następujący sposób:
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 wektora obrotu.
Trzy elementy wektora obrotu są równe ostatnim 3 składnikom kwaternionu jednostkowego (cos(θ/2), x*sin(θ/2), y*sin(θ/2), z*sin(θ/2)). Elementy wektora obrotu są bezwymiarowe. Osie X, Y i Z są zdefiniowane w taki sam sposób jak w przypadku czujnika przyspieszenia. Układ współrzędnych odniesienia jest zdefiniowany jako bezpośrednia baza ortonormalna (patrz rysunek 1). Ten układ współrzędnych ma następujące cechy:
- X jest zdefiniowany jako iloczyn wektorowy Y x Z. Jest styczna do powierzchni ziemi w bieżącej lokalizacji urządzenia i wskazuje w przybliżeniu wschód.
- Oś Y jest styczna do powierzchni ziemi w bieżącej lokalizacji urządzenia i wskazuje geomagnetyczny biegun północny.
- Oś Z jest skierowana w górę i jest prostopadła do płaszczyzny podłoża.
Przykładową aplikację, która pokazuje, jak używać czujnika wektora obrotu, znajdziesz w pliku RotationVectorDemo.java.
Używanie czujnika ruchu
Czujnik znacznego ruchu wyzwala zdarzenie za każdym razem, gdy wykryje znaczny ruch, a następnie się wyłącza. Znaczny ruch to ruch, który może prowadzić do zmiany lokalizacji użytkownika, np. chodzenie, jazda na rowerze lub siedzenie w jadącym samochodzie. Poniższy kod pokazuje, jak uzyskać instancję domyślnego czujnika ruchu istotnego 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 znajdziesz tutaj: TriggerEventListener.
Używanie czujnika licznika kroków
Czujnik licznika kroków podaje liczbę kroków wykonanych przez użytkownika od ostatniego ponownego uruchomienia, gdy czujnik był aktywny. Licznik kroków ma większe opóźnienie (do 10 sekund), ale jest dokładniejszy niż czujnik wykrywania kroków.
Uwaga: aby aplikacja mogła używać tego czujnika na urządzeniach z Androidem 10 (API na poziomie 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, na których działa Twoja aplikacja, używaj klasy JobScheduler do pobierania bieżącej wartości z czujnika licznika kroków w określonych odstępach czasu. Chociaż różne typy aplikacji wymagają różnych interwałów odczytu czujnika, należy ustawić jak najdłuższy interwał, chyba że aplikacja wymaga danych z czujnika w czasie rzeczywistym.
Używanie czujnika wykrywania kroków
Czujnik wykrywania kroków wywołuje zdarzenie za każdym razem, gdy użytkownik robi krok. Opóźnienie powinno wynosić mniej niż 2 sekundy.
Uwaga: aby aplikacja mogła używać tego czujnika na urządzeniach z Androidem 10 (API na poziomie 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 nieprzetworzonymi danymi
Te czujniki dostarczają aplikacji nieprzetworzone dane o siłach liniowych i obrotowych działających na urządzenie. Aby skutecznie wykorzystywać wartości z tych czujników, musisz odfiltrować czynniki środowiskowe, takie jak grawitacja. Może być też konieczne zastosowanie algorytmu wygładzania trendu wartości, aby zmniejszyć szum.
Używanie akcelerometru
Czujnik przyspieszenia mierzy przyspieszenie 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 aplikacja jest kierowana na Androida 12 (poziom API 31) lub nowszego, ten czujnik ma ograniczoną liczbę żądań.
Z koncepcyjnego punktu widzenia czujnik przyspieszenia określa przyspieszenie urządzenia (Ad), mierząc siły działające na sam czujnik (Fs) za pomocą tej zależności:
Jednak siła grawitacji zawsze wpływa na mierzone przyspieszenie zgodnie z tą zależnością:
Z tego powodu, gdy urządzenie leży na stole (i nie przyspiesza), akcelerometr odczytuje wartość g = 9,81 m/s2. Podobnie, gdy urządzenie spada swobodnie i w związku z tym szybko przyspiesza w kierunku ziemi z prędkością 9,81 m/s2, akcelerometr odczytuje wartość g = 0 m/s2. Dlatego, aby zmierzyć rzeczywiste przyspieszenie urządzenia, należy usunąć z danych akcelerometru wpływ siły grawitacji. Można to osiągnąć, stosując filtr górnoprzepustowy. Z kolei filtr dolnoprzepustowy może służyć do wyodrębniania siły grawitacji. Poniższy przykład pokazuje, jak możesz 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 uzyskiwana ze stałej czasowej (t), która jest przybliżonym odzwierciedleniem opóźnienia, jakie filtr dodaje do zdarzeń czujnika, oraz częstotliwości dostarczania zdarzeń przez czujnik (dt). W przykładowym kodzie do celów demonstracyjnych użyto wartości alfa 0,8. 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 płasko na stole w naturalnej orientacji, obowiązują te warunki:
- Jeśli popchniesz urządzenie po lewej stronie (tak, aby przesunęło się w prawo), wartość przyspieszenia x będzie dodatnia.
- Jeśli popchniesz urządzenie od dołu (tak, aby oddaliło się od Ciebie), wartość przyspieszenia w osi Y będzie dodatnia.
- Jeśli pchniesz urządzenie w górę z przyspieszeniem A m/s2, wartość przyspieszenia w osi Z będzie równa A + 9,81, co odpowiada przyspieszeniu urządzenia (+A m/s2) minus siła 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).
Akcelerometr jest ogólnie 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. Jedną z wad jest to, że może być konieczne wdrożenie filtrów dolno- i górnoprzepustowych, aby wyeliminować siły grawitacyjne i zmniejszyć szum.
Korzystanie z żyroskopu
Żyroskop mierzy prędkość obrotu w rad/s wokół osi X, Y i Z urządzenia. Poniższy kod pokazuje, jak uzyskać 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 API 31) lub nowszego, ten czujnik ma ograniczoną liczbę żądań.
Układ współrzędnych czujnika jest taki sam jak w przypadku czujnika przyspieszenia. Obrót jest dodatni w lewo. Oznacza to, że obserwator patrzący z dodatniego punktu na osi X, Y lub Z na urządzenie umieszczone w początku układu współrzędnych odnotuje dodatni obrót, jeśli urządzenie będzie się obracać w lewo. Jest to standardowa matematyczna definicja obrotu dodatniego i nie jest taka sama jak definicja obrotu używana przez czujnik orientacji.
Zwykle dane wyjściowe żyroskopu są integrowane w czasie, aby obliczyć obrót opisujący zmianę kątów w danym kroku 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ą nieprzetworzone dane o obrotach bez filtrowania ani korygowania szumów i dryftu (błędu). W praktyce szum i dryf żyroskopu powodują błędy, które trzeba skompensować. Dryf (błąd) i szum zwykle określa się, monitorując inne czujniki, takie jak czujnik grawitacji czy akcelerometr.
Używanie nieskalibrowanego żyroskopu
Nieskalibrowany żyroskop jest podobny do żyroskopu, ale nie stosuje się w nim kompensacji dryftu żyroskopu do prędkości obrotowej. Kalibracja fabryczna i kompensacja temperatury są nadal stosowane do szybkości obrotu. Nieskalibrowany żyroskop jest przydatny do przetwarzania i łączenia danych dotyczących orientacji. Ogólnie wartość gyroscope_event.values[0] będzie zbliżona do uncalibrated_gyroscope_event.values[0] - uncalibrated_gyroscope_event.values[3].
To znaczy,
calibrated_x ~= uncalibrated_x - bias_estimate_x
Uwaga: nieskalibrowane czujniki dostarczają bardziej nieprzetworzone wyniki i mogą zawierać pewne błędy, ale ich pomiary zawierają mniej skoków wynikających z korekt zastosowanych podczas kalibracji. Niektóre aplikacje mogą preferować te nieskalibrowane wyniki, ponieważ są one bardziej płynne i wiarygodne. Jeśli na przykład aplikacja próbuje przeprowadzić własną fuzję czujników, wprowadzenie kalibracji może zniekształcić wyniki.
Oprócz szybkoś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ładowa aplikacja BatchStepSensor pokazuje, jak korzystać z interfejsów API opisanych na tej stronie.