跌倒检测是 Android 健康与安全类应用中的常见功能,它通过手机内置的加速度计和陀螺仪感知用户运动状态,在检测到疑似跌倒时触发报警或通知。测试这类功能不能只靠人工拿着手机反复做动作,需要把传感器数据、算法逻辑和业务响应拆开验证。本文围绕真机测试、数据采集和指标评估展开,重点讨论如何设计能够发现漏报和误报的测试方案。

一、先理解传感器数据:合加速度与角速度
跌倒检测的基础是惯性传感器数据。Android 设备通常提供三轴加速度计和三轴陀螺仪,加速度计反映线性加速度,陀螺仪反映旋转角速度。一个人从站立到摔倒,加速度计会先出现一个明显的失重或冲击过程,随后身体与地面碰撞产生较大的合加速度峰值。仅用单轴数据容易受手机朝向影响,因此多数实现会计算合加速度幅值,公式为 sqrt(x*x + y*y + z*z)。当合加速度低于 0.6g 左右时可能处于自由落体或快速下坠阶段,高于 2.5g 至 3g 时通常意味着发生了碰撞。实际测试中需要把这些阈值当作可配置项,而不是写死在代码里。
陀螺仪在跌倒检测中用于区分突然加速是跌倒还是其他剧烈动作。跌倒时身体姿态会从直立快速变为水平,角速度在几百毫秒内出现较大变化。比如弯腰捡东西虽然有角度变化,但变化过程相对缓慢,角速度峰值明显低于跌倒。传感器采样频率也直接影响判断效果,一般建议加速度计使用 50Hz 到 100Hz,陀螺仪使用 50Hz 左右。采样过低会漏掉碰撞峰值,过高则增加功耗和噪声。测试前可以先通过 Android 的 SensorManager 注册传感器,记录一段时间的数据,观察不同动作下的波形特征。
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
val gyroscope = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
// 注册加速度计,采样间隔约 20ms,即 50Hz
sensorManager.registerListener(listener, accelerometer, SensorManager.SENSOR_DELAY_GAME)
sensorManager.registerListener(listener, gyroscope, SensorManager.SENSOR_DELAY_GAME)
private val listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) {
val x = event.values[0]
val y = event.values[1]
val z = event.values[2]
val magnitude = sqrt(x * x + y * y + z * z)
// 记录 magnitude 到日志或内存队列
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
上面代码只展示了加速度计的数据读取。真实产品通常会用一个固定长度的时间窗口保存最近若干秒的数据,例如 1 秒到 3 秒,然后提取均值、方差、峰值、过零率等特征。测试时如果发现某些设备始终无法触发跌倒判断,可以先检查传感器是否存在、采样率是否被系统限制,以及权限是否正常申请。部分国产 ROM 对后台传感器采样有额外限制,这一点在兼容性测试中很容易被忽略。
二、怎么设计跌倒用例和混淆动作用例
测试跌倒检测最容易出现的问题是只测几个标准摔倒动作,忽略日常动作带来的误报。一个完整的用例集至少需要覆盖两类:正向跌倒用例和负向混淆用例。正向用例包括向前扑倒、向后仰倒、左侧摔倒、右侧摔倒、从椅子滑落、突然晕厥倒地等。每种跌倒的传感器波形不完全相同,向后仰倒时碰撞峰值可能来自背部而非手腕,从椅子滑落则可能缺少明显的自由落体阶段。因此不能用一种跌倒结果代表所有跌倒类型。
负向用例主要用于验证误报率,包括快速坐下、弯腰系鞋带、跳跃、上下楼梯、跑步急停、挥手、把手机从口袋取出放到桌面、乘坐电梯和车辆颠簸。很多误报来自手机在口袋或包里的晃动,而不是用户身体姿态的真实变化。如果算法只依赖合加速度峰值,那么把手机快速拍到桌上也可能被判定为跌倒。测试人员可以把每个动作重复 20 到 50 次,记录触发报警的次数,计算误报率。对于正向动作则记录漏报次数,最终得到灵敏度与特异度。
自动化测试方面,直接让机械臂拿着手机模拟跌倒是最理想的方式,但成本较高。折中方案是开发一个传感器数据录制工具,把真人执行动作时产生的加速度和陀螺仪数据保存为 CSV 或 JSON 文件,然后在测试环境中回放给检测算法。这样同一组数据可以对不同阈值、不同模型进行回归验证,也能方便地定位是某个动作未被识别还是某个动作被误判。录制时应当同时记录动作标签,例如跌倒类型或日常动作名称,便于后续统计。
fun saveSensorData(timestamp: Long, label: String, x: Float, y: Float, z: Float) {
val line = "$timestamp,$label,$x,$y,$z\n"
File(requireContext().filesDir, "sensor_log.csv").appendText(line)
}
// 测试时先设置当前动作标签
fun setCurrentLabel(label: String) {
currentLabel = label
}
// onSensorChanged 中调用
fun handleAccel(timestamp: Long, x: Float, y: Float, z: Float) {
saveSensorData(timestamp, currentLabel, x, y, z)
}
对于核心指标,灵敏度等于正确识别的跌倒次数除以总跌倒次数,特异度等于正确识别的日常动作次数除以总日常动作次数。报警延迟一般指从跌倒发生到应用触发通知的时间差,通常要求在 3 秒以内。测试报告中不能只写总体准确率,因为跌倒样本远少于日常动作样本时,高准确率可能掩盖严重漏报。建议对每一类跌倒动作分别统计灵敏度,对每一类日常动作分别统计误报次数,这样更容易定位问题。
三、从阈值判断到状态机:如何降低误报与漏报
简单的阈值算法在真机测试中往往表现不稳定,原因是不同设备的传感器量程和噪声水平不同。例如同一台手机上,合加速度阈值设置为 2.8g 可以识别大部分摔倒,但换到另一台设备后可能因为传感器灵敏度差异出现漏报。工程上通常会把阈值判断与时间窗口、状态机结合起来。先检测到自由落体或失重,再检测到碰撞峰值,最后检测到静止或水平姿态,才判定为跌倒。这种时序约束可以过滤掉大部分短时冲击,比如手机掉落桌面。
状态机方案中,可以把检测流程划分为正常、疑似失重、碰撞确认、跌倒确认四个状态。进入疑似失重后启动一个计时器,如果在一定时间内没有出现碰撞峰值,就回到正常状态;如果出现碰撞峰值,再检查后续 2 秒内设备的姿态角或静止程度。这样做的好处是逻辑可解释,测试时也容易针对每个状态设计边界数据。例如可以把碰撞峰值阈值调低,观察是否会有更多日常动作被错误迁移到碰撞确认状态。
sealed class FallState {
object Normal : FallState()
object FreeFall : FallState()
object Impact : FallState()
object FallConfirmed : FallState()
}
fun evaluate(currentState: FallState, magnitude: Float, lowThreshold: Float, highThreshold: Float): FallState {
return when (currentState) {
is FallState.Normal -> if (magnitude < lowThreshold) FallState.FreeFall else FallState.Normal
is FallState.FreeFall -> if (magnitude > highThreshold) FallState.Impact else FallState.Normal
is FallState.Impact -> if (isLyingStill()) FallState.FallConfirmed else FallState.Normal
is FallState.FallConfirmed -> currentState
}
}
机器学习模型可以提取更丰富的特征,降低对单一阈值的依赖。但模型在测试中面临数据采集成本高、标注困难等问题。如果产品处于早期阶段,建议先用手工特征加规则完成可用的检测,再用录制数据逐步训练模型。测试人员需要关注模型在不同 Android 版本和不同传感器芯片上的泛化能力,最好在至少三到五款主流设备上执行同一套用例,对比灵敏度与误报率差异。
省电和后台运行策略也会影响测试结果。部分应用为了降低功耗,会在屏幕关闭后降低传感器采样频率,此时跌倒检测的灵敏度可能下降。测试时要分别验证屏幕亮起、屏幕关闭、应用在后台、设备锁屏等状态下的检测效果。传感器数据采集和判断逻辑如果在主线程执行,还会引起界面卡顿。建议把传感器事件处理放到单独线程,并通过采样间隔控制 CPU 占用。经过这些工程优化后,再回到正向和负向用例上做回归,才能确认改动没有引入新的漏报或误报。
Android跌倒检测加速度传感器测试用例修改时间:2026-09-18 11:50:19