Перегрів пристрою через неправильну частоту опитування датчиків — типова проблема. Розряд батареї за годину замість дня. Ми стикалися з проектом, де sensor fusion видавав випадкові кватерніони на дешевих телефонах. Рішення — правильний вибір віртуальних датчиків та батч-оновлень. Більше 5 років досвіду та більше 20 проектів із датчиками руху — за нашими плечима.
Android Sensor Framework надає доступ до 13+ типів датчиків через єдиний SensorManager. Акселерометр та гіроскоп — апаратні. TYPE_LINEAR_ACCELERATION, TYPE_ROTATION_VECTOR, TYPE_GRAVITY — віртуальні: обчислюються з апаратних через sensor fusion у firmware. Різниця в тому, що віртуальні датчики споживають більше CPU при високій частоті та можуть бути недоступні на бюджетних пристроях. Детальніше — в Android Developer: Sensors Overview. Наш досвід показує: для точної орієнтації використовуйте TYPE_ROTATION_VECTOR, для лінійного прискорення — TYPE_LINEAR_ACCELERATION.
Реєстрація та життєвий цикл
class SensorViewModel(application: Application) : AndroidViewModel(application) { private val sensorManager = application.getSystemService(SENSOR_SERVICE) as SensorManager private val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) private val gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) private val _sensorData = MutableStateFlow<SensorData>(SensorData.Empty) val sensorData: StateFlow<SensorData> = _sensorData.asStateFlow() private val sensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { when (event.sensor.type) { Sensor.TYPE_ACCELEROMETER -> handleAccelerometer(event.values) Sensor.TYPE_GYROSCOPE -> handleGyroscope(event.values) } } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {} } fun startListening() { accelerometer?.let { sensorManager.registerListener( sensorEventListener, it, SensorManager.SENSOR_DELAY_GAME // ~20ms ) } } fun stopListening() { sensorManager.unregisterListener(sensorEventListener) } override fun onCleared() { stopListening() } } Ключове: unregisterListener строго в onCleared() ViewModel або в onPause() Activity. Забутий listener працює у фоні, жере батарею та може крашити застосунок при знищенні Activity.
Частоти опитування: константи та реальність
| Константа | Номінальна затримка | Реальна частота |
|---|---|---|
SENSOR_DELAY_NORMAL |
200 мс | ~5 Гц |
SENSOR_DELAY_UI |
60 мс | ~16 Гц |
SENSOR_DELAY_GAME |
20 мс | ~50 Гц |
SENSOR_DELAY_FASTEST |
0 мс | максимум заліза |
З Android API 9 можна задавати довільний інтервал у мікросекундах через registerListener(listener, sensor, samplingPeriodUs). Наприклад, 10000 мкс = 100 Гц.
SENSOR_DELAY_FASTEST на Snapdragon 8 Gen 2 дає до 500 Гц на акселерометрі — це потрібно тільки для спеціалізованих застосунків (аналіз вібрацій, балансування). Для більшості задач 50–100 Гц достатньо.
Чому Sensor Fusion в Android вимагає калібрування?
Датчики на дешевих пристроях мають зсуви та шум. Віртуальні датчики (TYPE_ROTATION_VECTOR) використовують вбудоване калібрування firmware, але на старих ядрах можуть дрейфувати. Ми тестуємо на апаратах різних вендорів та при необхідності додаємо свій фільтр (Mahony/Madgwick) для корекції.
TYPE_ROTATION_VECTOR — кватерніон орієнтації пристрою, обчислений з акселерометра + гіроскопа + магнітометра. Точніше ніж робити fusion вручну:
val rotationVector = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) // В onSensorChanged: val rotationMatrix = FloatArray(9) SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) val orientationAngles = FloatArray(3) SensorManager.getOrientation(rotationMatrix, orientationAngles) val azimuth = Math.toDegrees(orientationAngles[0].toDouble()) // 0-360° від півночі val pitch = Math.toDegrees(orientationAngles[1].toDouble()) // -90 до +90° val roll = Math.toDegrees(orientationAngles[2].toDouble()) // -180 до +180° TYPE_LINEAR_ACCELERATION — акселерометр без гравітації (аналог userAcceleration в iOS CoreMotion). Використовуємо замість TYPE_ACCELEROMETER коли потрібно вимірювати тільки динамічне прискорення.
Як реалізувати детекцію падіння?
- Використовуйте акселерометр із частотою 50 Гц.
- Застосуйте високочастотний фільтр для відділення гравітації:
val gravity = FloatArray(3) val linearAccel = FloatArray(3) val alpha = 0.8f // В onSensorChanged для TYPE_ACCELEROMETER: 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] linearAccel[0] = event.values[0] - gravity[0] linearAccel[1] = event.values[1] - gravity[1] linearAccel[2] = event.values[2] - gravity[2] val magnitude = sqrt( linearAccel[0].pow(2) + linearAccel[1].pow(2) + linearAccel[2].pow(2) )Для носимих пристроїв (літні люди, шахтарі) цей метод чудово працює.
Як організувати фоновий моніторинг без перевитрати батареї?
Використовуйте батч-оновлення (
maxReportLatencyUs) таJobSchedulerдля періодичної обробки. Віртуальні датчики у фоні краще не використовувати — вони споживають CPU постійно. Для простої детекції активності (кроки, спокій) достатньо акселерометра з частотою 5-10 Гц та батчем в 1 секунду (1000 мс).Класифікація активностей
Паттерни: ходьба — регулярні піки 1.5–2.5 м/с² з частотою 1.5–2.5 Гц. Їзда — низькочастотні вібрації < 0.5 Гц. Спокій — magnitude < 0.1 м/с². Для класифікації використовуйте простий пороговий детектор або модель машинного навчання (наприклад, випадковий ліс) — це покращує точність до 95%.
Батч-оновлення (Android 4.4+)
SensorManager.flush()та параметрmaxReportLatencyUsвregisterListenerдозволяють накопичувати дані в hardware FIFO та отримувати пачками. Корисно для фонових застосунків — сенсор копить дані, поки CPU спить, прокидається раз на N секунд, віддає все одразу:sensorManager.registerListener( listener, accelerometer, 10_000, // samplingPeriodUs = 100 Гц 500_000 // maxReportLatencyUs = 500 мс batch )Об'єм FIFO у різних чипсетів різний (512–4096 семплів). При переповненні старі дані витісняються — враховуйте при тривалих сесіях.
Що входить у роботу з інтеграції датчиків
- Аналіз — вибір датчиків під сценарій (навігація, трекінг активності, носимі пристрої)
- Архітектура — ViewModel/Service, обробка lifecycle, сенсорна оптимізація батареї
- Реалізація — реєстрація, фільтрація, класифікація подій (падіння, кроки, повороти)
- Тестування — на реальних пристроях (5+ моделей з різними чипсетами)
- Підтримка — документація API, навчання команди, гарантія на код до 6 місяців
Порівняння підходів до sensor fusion
Характеристика TYPE_ROTATION_VECTOR (вбудований) Ручний Madgwick/Mahony Точність на дорогих пристроях ±1° ±2-3° після калібрування Споживання CPU Низьке Високе (оновлення 200+ раз/с) Підтримка на бюджетних телефонах Часто немає Працює всюди Складність інтеграції Мінімальна Потрібне налаштування параметрів Наш досвід: для 90% проектів вистачає вбудованих віртуальних датчиків. Ручний fusion потрібний тільки для AR/VR або роботи з застарілими SoC.
Терміни
Базова інтеграція 1–2 датчиків з обробкою даних — від 3 до 5 робочих днів. Багатодатчиковий алгоритм із класифікацією активностей, фоновим записом та аналітикою — від 2 до 4 тижнів. Вартість розраховується індивідуально після аналізу вимог. Часові рамки уточнюємо після аудиту вашого проекту.
Наша команда — більше 5 років досвіду та більше 20 успішних проектів із датчиками руху. Замовте консультацію — оцінимо feasibility та запропонуємо архітектуру під ваш сценарій. Отримайте якісну інтеграцію з гарантією на код до 6 місяців.
Детальніше про Sensor API читайте в офіційній документації Android.







