Перегрев устройства из-за неправильной частоты опроса датчиков — типичная проблема. Разряд батареи за час вместо дня. Мы сталкивались с проектом, где 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.







