Administrator
发布于 2026-09-22 / 7,662 阅读
0
0

Android 传感器框架:SensorManager 与高频数据处理

前言

计步器、体感游戏、手势识别、跌倒检测——这些功能都依赖 Android 传感器框架。但很多工程师只学到 registerListener 就开始写,结果遇到高频数据丢帧、功耗爆炸、精度不稳定等问题。

这一篇讲传感器框架的核心机制:采样率、批处理模式、传感器类型选型,以及高频数据处理的工程实践。

一、传感器体系架构

+--------+        +------------+       +-----------+
| App    |  <--  | Framework  | <--  | HAL       |
| SensorM|  Event| SensorSvc  | Data | SensorHw  |
|anager  |  回调  |            |      |           |
+--------+        +------------+       +-----------+
                                                |
                                         +--------+
                                         | 物理    |
                                         | 传感器  |
                                         +--------+
  • 硬件传感器:物理芯片(加速度计、陀螺仪、磁力计)

  • 虚拟传感器:软件合成(重力、线加速度、旋转向量),由系统从硬件数据计算

  • 合成传感器:特殊用途合成(计步器、显著运动检测)

二、传感器类型选型

Android 定义了 30+ 种传感器类型,常用的几个:

类型

常量

数据

说明

加速度计

TYPE_ACCELEROMETER

x/y/z (m/s^2)

含重力

线加速度

TYPE_LINEAR_ACCELERATION

x/y/z (m/s^2)

去重力

陀螺仪

TYPE_GYROSCOPE

x/y/z (rad/s)

角速度

重力

TYPE_GRAVITY

x/y/z (m/s^2)

纯重力分量

旋转向量

TYPE_ROTATION_VECTOR

x/y/z + scalar

四元数

游戏旋转

TYPE_GAME_ROTATION_VECTOR

x/y/z

不含磁力

计步器

TYPE_STEP_COUNTER

int (累计步数)

硬件计步

步检测

TYPE_STEP_DETECTOR

event

单步触发

显著运动

TYPE_SIGNIFICANT_MOTION

event

检测大动作

选型原则:

// 坏:用原始加速度计自己算去重力
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)

// 好:直接用系统合成的线加速度
sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)

// 坏:用 TYPE_ROTATION_VECTOR(含磁力,室内有磁场干扰)
// 好:游戏场景用 TYPE_GAME_ROTATION_VECTOR(纯陀螺仪+加速度)
sensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR)

系统合成传感器背后的算法是 Google 调优过的,比自己手写的低通滤波质量好。

三、注册监听

class MotionDetector(context: Context) {
    private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
    private var accelerometer: Sensor? = null
    private var gyroscope: Sensor? = null

    // 传感器数据缓存
    private val accelData = FloatArray(3)
    private val gyroData = FloatArray(3)

    private val sensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            when (event.sensor.type) {
                Sensor.TYPE_ACCELEROMETER -> {
                    // 注意:这里直接引用 event.values 不安全
                    // event.values 会被系统复用,下个事件会覆盖
                    System.arraycopy(event.values, 0, accelData, 0, 3)
                    processMotion()
                }
                Sensor.TYPE_GYROSCOPE -> {
                    System.arraycopy(event.values, 0, gyroData, 0, 3)
                    processMotion()
                }
            }
        }

        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
            // 精度变化:通常可以忽略
            // accuracy: SensorManager.SENSOR_STATUS_*
        }
    }

    fun start() {
        accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)

        accelerometer?.let { sensor ->
            // samplingPeriodUs: 采样周期(微秒)
            // 选项:SENSOR_DELAY_NORMAL(200ms), UI(16ms), GAME(20ms), FASTEST(0-1ms)
            // 实际采样率取决于硬件,这里只是"期望值"
            sensorManager.registerListener(
                sensorListener,
                sensor,
                SensorManager.SENSOR_DELAY_GAME  // ~50Hz
            )
        }
    }

    fun stop() {
        sensorManager.unregisterListener(sensorListener)
    }

    private fun processMotion() {
        // 在这里处理传感器数据
    }
}

四、采样率真相

registerListener 的第三个参数(采样周期)不是"精确的采样间隔",而是一个"最高频率建议"。实际采样率由硬件决定。

// 指定具体微秒值
sensorManager.registerListener(
    sensorListener,
    accelerometer,
    10000  // 10ms = 100Hz
)

// 但实际可能跑 120Hz(硬件原生频率)
// 系统会把 100Hz 的数据"下采样"到接近 100Hz 给你

不同传感器的原生频率不同:

传感器

典型原生频率

功耗

加速度计

200-800 Hz

中

陀螺仪

200-1000 Hz

高

磁力计

50-100 Hz

中

计步器

每步触发

极低

重力

取决于合成源

中

五、批处理模式(Batch Mode)

Android 4.4+ 支持批处理:硬件 FIFO 缓存多个事件,App 一次性读取。减少唤醒次数,省电。

// 普通模式:每个事件都唤醒 App
sensorManager.registerListener(
    sensorListener,
    accelerometer,
    SensorManager.SENSOR_DELAY_GAME
)

// 批处理模式:最多缓存 maxReportLatencyUs 的数据
sensorManager.registerListener(
    sensorListener,
    accelerometer,
    SensorManager.SENSOR_DELAY_GAME,
    5_000_000  // 最大报告延迟 5 秒
)

批处理模式下,如果 App 没有读取,硬件会在 FIFO 接近满时唤醒 App。CPU 唤醒次数大幅减少。

适用场景:后台计步、运动追踪。不适用于实时体感控制(延迟太大)。

六、高频数据处理实战

场景:手势识别(100Hz 加速度 + 200Hz 陀螺仪)

class GestureRecognizer(context: Context) {
    private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager

    // 环形缓冲区:存储最近 1 秒的数据
    private val accelBuffer = CircularBuffer(300)  // 100Hz * 3s
    private val gyroBuffer = CircularBuffer(600)   // 200Hz * 3s

    private val sensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            when (event.sensor.type) {
                Sensor.TYPE_LINEAR_ACCELERATION -> {
                    accelBuffer.push(event.values[0], event.values[1], event.values[2])
                    tryDetectGesture()
                }
                Sensor.TYPE_GYROSCOPE -> {
                    gyroBuffer.push(event.values[0], event.values[1], event.values[2])
                }
            }
        }

        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
    }

    private var lastDetectTime = 0L

    private fun tryDetectGesture() {
        val now = System.currentTimeMillis()
        if (now - lastDetectTime < 100) return  // 节流:100ms 检测一次
        lastDetectTime = now

        // 取最近 0.5 秒数据
        val accelWindow = accelBuffer.lastN(50)  // 100Hz * 0.5s
        val gyroWindow = gyroBuffer.lastN(100)  // 200Hz * 0.5s

        // 特征提取
        val accelMag = accelWindow.map { sqrt(it.x*it.x + it.y*it.y + it.z*it.z) }
        val gyroMag = gyroWindow.map { sqrt(it.x*it.x + it.y*it.y + it.z*it.z) }

        // 峰值检测(简单版)
        val maxAccel = accelMag.max()
        val maxGyro = gyroMag.max()

        when {
            maxAccel > 15.0f && maxGyro > 3.0f -> {
                // 摇晃手势
                onGestureDetected(Gesture.SHAKE)
            }
            maxAccel > 8.0f && maxGyro < 1.0f -> {
                // 拍击手势
                onGestureDetected(Gesture.TAP)
            }
            maxGyro > 2.0f && maxAccel < 5.0f -> {
                // 翻转手势
                onGestureDetected(Gesture.FLIP)
            }
        }
    }

    fun start() {
        val accel = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION)
        val gyro = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
        sensorManager.registerListener(sensorListener, accel, 10000)  // 100Hz
        sensorManager.registerListener(sensorListener, gyro, 5000)     // 200Hz
    }
}

data class AccelSample(val x: Float, val y: Float, val z: Float)
enum class Gesture { SHAKE, TAP, FLIP }

// 简单环形缓冲区
class CircularBuffer(private val capacity: Int) {
    private val buffer = ArrayDeque<AccelSample>()
    private val lock = Any()

    fun push(x: Float, y: Float, z: Float) {
        synchronized(lock) {
            buffer.addLast(AccelSample(x, y, z))
            if (buffer.size > capacity) buffer.removeFirst()
        }
    }

    fun lastN(n: Int): List<AccelSample> {
        synchronized(lock) {
            return buffer.takeLast(minOf(n, buffer.size))
        }
    }
}

七、功耗优化

高频传感器监听是耗电大户。优化策略:

class PowerAwareMotionDetector(context: Context) {
    // 策略:用低频触发器 + 高频采集
    private val significantMotion: Sensor? = sensorManager
        .getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)

    private val triggerListener = object : TriggerEventListener() {
        override fun onTrigger(event: TriggerEvent) {
            // 检测到大动作 -> 切换到高频采集
            startHighFreqCollection()

            // 采集完再重新注册触发器
            sensorManager.requestTriggerSensor(this, significantMotion)
        }
    }

    private fun startHighFreqCollection() {
        // 注册高频加速度计,采集 3 秒后自动停止
        sensorManager.registerListener(
            highFreqListener,
            accelerometer,
            SensorManager.SENSOR_DELAY_FASTEST
        )
        handler.postDelayed({
            sensorManager.unregisterListener(highFreqListener)
        }, 3000)
    }
}

功耗对比:

策略

日均功耗

适用场景

持续 200Hz 监听

15-25%

实时体感游戏

持续 50Hz 监听

5-10%

手势识别

显著运动触发 + 高频短采

<1%

跌倒检测

硬件计步器

<0.5%

步数统计

小结

传感器开发的核心是"选对传感器 + 设对采样率 + 处理好数据线程"。优先用系统合成传感器,高频数据用环形缓冲区 + 节流检测,后台场景用批处理或触发器。

下篇讲生物认证,BiometricPrompt 不只是指纹,还有面部和硬件密钥。


评论