Перегрев устройства из-за неправильной частоты опроса датчиков — типичная проблема. Разряд батареи за час вместо дня. Мы сталкивались с проектом, где 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) ) - Реализуйте логику: если magnitude < 0.5g в течение >300 мс (свободное падение), затем magnitude > 3g (удар) — отправляйте SOS.
Для носимых устройств (пожилые люди, шахтёры) этот метод отлично работает.
Как организовать фоновый мониторинг без перерасхода батареи?
Используйте батч-обновления (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.







