血氧饱和度(SpO2)指的是血液中氧合血红蛋白占全部血红蛋白的比例,正常成年人一般在95%以上。随着移动健康应用的普及,越来越多的Android应用希望在端侧完成血氧数据的采集与分析。然而血氧数据来源多样,信号质量参差不齐,如果没有一套完整的分析测试流程,得出的结果很可能毫无参考价值。本文将从原理、数据采集、算法分析、测试验证四个层面,完整讲一遍Android平台上血氧分析的实现路径。

一、血氧检测的基本原理
目前主流的血氧测量技术是光电容积脉搏波描记法,简称PPG。其核心逻辑是:氧合血红蛋白和脱氧血红蛋白对红光和红外光的吸收特性不同。设备交替发射红光(约660nm)和红外光(约940nm)照射指尖或腕部,通过光电传感器接收反射或透射的光信号,计算两个波长下脉搏波的幅值比例,再结合经验标定曲线,就能估算出SpO2。
从数学上看,SpO2的计算依赖于比值R。R等于红光交流分量与直流分量之比,除以红外光交流分量与直流分量之比,即AC_red/DC_red除以AC_ir/DC_ir。得到R值后,通过经验公式SpO2 = A - B × R即可估算血氧饱和度,其中A和B是标定系数,不同设备厂家取值略有差异,常见的取值如A=110、B=25。这个线性公式只是一个近似,精确的设备会使用分段标定曲线。
理解这个原理对开发者非常重要。因为Android手机本身几乎都没有内置血氧传感器,应用要获取血氧数据通常有两条路:一是对接外部血氧仪(指夹式或腕带式),二是借助手机摄像头加闪光灯做简化版PPG测量。两种方案的精度和实现难度差别很大,后文会分别展开。
二、Android平台血氧数据采集方案
第一种方案是使用Health Connect。这是Google推出的统一健康数据存储层,很多智能手表(如部分支持血氧监测的手表)会把测得的SpO2数据同步到Health Connect中,应用申请权限后即可读取。读取血氧数据的代码如下:
// build.gradle 引入依赖
// implementation "androidx.health.connect:connect-client:1.1.0"
suspend fun readOxygenSaturation(client: HealthConnectClient) {
val request = ReadRecordsRequest(
recordType = OxygenSaturationRecord::class,
timeRangeFilter = TimeRangeFilter.between(
startTime, endTime
)
)
val response = client.readRecords(request)
for (record in response.records) {
Log.d("SpO2", "血氧值: ${record.percentage.value} %")
}
}
这种方案数据来源可靠,精度取决于上游传感器设备,但缺点是实时性差,且依赖设备生态。第二种方案是BLE直连血氧仪。大多数指夹式血氧仪通过蓝牙低功耗广播数据,常见的服务是标准健康设备规范中的PLX服务,血氧测量值存放在特征UUID 0x2A5F中。Android端通过BluetoothLeScanner扫描设备,连接后订阅特征通知即可拿到实时数据。
private final static UUID PLX_SERVICE_UUID =
UUID.fromString("00001822-0000-1000-8000-00805f9b34fb");
private final static UUID SPO2_CHAR_UUID =
UUID.fromString("00002a5f-0000-1000-8000-00805f9b34fb");
// 在onServicesDiscovered回调中开启通知
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
BluetoothGattCharacteristic chara = gatt.getService(PLX_SERVICE_UUID)
.getCharacteristic(SPO2_CHAR_UUID);
gatt.setCharacteristicNotification(chara, true);
// 部分设备还需向CCCD描述符写入0x0001
}
// 在onCharacteristicChanged中解析数据
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic chara, byte[] value) {
int flags = value[0] & 0xFF;
// 按PLX规范解析SPO2字段(一个字节,0x7F表示无效值)
int spo2 = value[1] & 0xFF;
int pulseRate = ((value[2] & 0xFF) | ((value[3] & 0xFF) << 8)) & 0x03FF;
if (spo2 != 0x7F) {
Log.d("BLE_SpO2", "血氧: " + spo2 + "%, 脉率: " + pulseRate);
}
}
第三种方案是摄像头PPG,做法是打开闪光灯,让用户指尖贴住摄像头,逐帧分析红色通道的亮度波动。这种方案成本最低,但信号噪声大,只能作为参考。需要注意Android 6.0之后摄像头权限需要动态申请,采集时要锁定曝光和白平衡,否则自动曝光会彻底淹没脉搏波信号。
三、信号处理与SpO2估算
无论哪种采集方式,拿到原始信号后都需要做处理。以摄像头方案为例,红光和红外信号分别对应不同滤镜下的亮度序列。处理流程一般是:先用带通滤波器(典型通带0.5到4Hz,对应每分钟30到240次的心跳范围)滤除呼吸基线漂移和高频噪声,再用峰谷检测或FFT求出交流幅值,最后代入比值公式计算R值。
/**
* 简化的SpO2估算
* redData / irData: 滤波后的红光、红外光信号序列
*/
public float estimateSpO2(float[] redData, float[] irData) {
float redAC = maxAbs(redData) - mean(redData); // 红光交流幅值
float irAC = maxAbs(irData) - mean(irData); // 红外光交流幅值
float redDC = mean(rawRed); // 红光直流分量
float irDC = mean(rawIr); // 红外光直流分量
if (irAC <= 0 || irDC <= 0) return -1; // 信号过弱,判定无效
float r = (redAC / redDC) / (irAC / irDC);
// 经验标定公式,需结合实测数据回归调整
float spo2 = 110f - 25f * r;
if (spo2 > 100f) spo2 = 100f;
if (spo2 < 70f) spo2 = 70f;
return spo2;
}
信号质量评估同样不可忽视。实际项目中建议引入灌注指数PI和信号噪声比SNR两个指标:PI反映脉搏波的相对强度,PI低于0.3%时数据基本不可用;SNR过低说明存在运动伪影或环境光干扰,应提示用户重新测量。此外,做3到5次连续测量取中位数,比单次读数的稳定性明显更好。
四、准确性测试与验证方法
测试环节的目标是回答两个问题:数据准不准,体验稳不稳。准确性方面,行业标准做法是与医用级参考设备做同步对比。测试时让受试者同时佩戴被测设备和金标准血氧仪,收集至少数十组配对数据,覆盖90%到100%的血氧区间(可通过深呼吸、屏气等方式适度降低血氧以扩展区间),然后计算平均误差和均方根误差。医疗级设备一般要求ArmSE不超过4%,消费级应用放宽到6%以内也基本可接受。
稳定性测试要覆盖典型干扰场景:手部晃动、环境强光、冷手指导致灌注不足、设备佩戴过松等。每种场景记录测量成功率、超时率以及无效值占比,并观察应用是否有正确的失败提示而不是给出一个看似正常的错误数值,这一点在健康类应用里尤其关键,错误的高读数比测量失败危害更大。
// 单元测试示例:验证SpO2估算逻辑的边界行为
@Test
public void testEstimateSpO2_boundaries() {
SpO2Estimator estimator = new SpO2Estimator();
// 信号过弱时应返回无效标记
assertEquals(-1f, estimator.estimateSpO2(flatRed, flatIr), 0.001f);
// 正常信号应落在70到100区间内
float result = estimator.estimateSpO2(normalRed, normalIr);
assertTrue(result >= 70f && result <= 100f);
// 极端R值应被截断,不得越界
float extreme = estimator.estimateSpO2(extremeRed, extremeIr);
assertTrue(extreme <= 100f);
}
最后提醒几个常见坑:一是Android各厂商对BLE的实现差异较大,部分国产手机需要在连接成功后延时几百毫秒再调用discoverServices,否则会搜不到服务;二是Health Connect的权限申请需要用户在系统设置中手动授权,测试时别忘了覆盖未授权分支;三是血氧相关的健康数据属于敏感信息,上传和存储务必加密并明确告知用户用途,否则上架审核可能被拒。把采集、分析、测试三条链路都打磨扎实,血氧功能才能真正可用可信。
血氧分析Android传感器健康数据采集修改时间:2026-09-15 15:26:55