2、PPG信号采集:光电二极管选型、LED驱动电路设计、跨阻放大器设计

好,咱们进入正题。PPG信号采集,说白了就是怎么把指尖那点微弱的光信号,变成我们能用的电信号。这一步要是没做好,后面算法再牛也白搭。我见过太多项目,算法团队花三个月调模型,结果发现是前端电路噪声太大——嗯,这种坑踩过一次就记住了。

2.1 光电二极管选型:别小看这个“眼睛”

光电二极管是PPG传感器的第一道关卡。它的任务是把反射回来的光,转换成电流。你想想看,指尖的血流变化本来就微弱,反射光更是弱到可怜。所以选型时,我重点关注三个参数。

2.1.1 暗电流与灵敏度

暗电流,就是没光照时管子自己漏出来的电流。这个值越小越好。我习惯选暗电流在1nA以下的型号。为什么?因为PPG信号里的直流分量也就几十nA,暗电流要是太大,信号就被淹没了。

灵敏度嘛,单位是A/W,表示每瓦特光能产生多少电流。对于绿光(520nm左右),我推荐灵敏度在0.3 A/W以上的管子。红光和红外可以稍低,但绿光必须高——因为绿光在皮肤里穿透浅,反射信号本来就弱。

我的选型经验: 暗电流 < 1nA,绿光灵敏度 > 0.3 A/W,封装越小越好(0402或0603)。

2.1.2 响应时间与带宽

PPG信号的频率范围其实很窄,心率也就0.5-4Hz,呼吸0.1-0.5Hz。但为什么还要关注响应时间?因为LED是脉冲驱动的,不是一直亮着。脉冲宽度通常只有几十微秒,光电二极管必须能跟上这个开关速度。

我一般选响应时间在10μs以内的管子。太快了没必要,太慢了会拖尾,导致相邻采样点互相干扰。你想想看,如果LED关断后管子还在输出电流,那下一个脉冲的起点就飘了。

2.1.3 光谱匹配

这个其实很简单——LED发什么光,管子就选什么波段最灵敏的。绿光LED配绿光增强型光电二极管,红光配红光增强型。别混用。我曾经见过有人用通用型光电二极管做绿光PPG,结果灵敏度差了将近一倍,信噪比惨不忍睹。

参数 推荐值 我的备注
暗电流 < 1 nA 越小越好,但别太贵
灵敏度(绿光) > 0.3 A/W 低于这个值,信号就难搞了
响应时间 < 10 μs 够用就行,太快了反而容易引入噪声
封装 0402 / 0603 越小越好,方便贴片

2.2 LED驱动电路设计:脉冲驱动是关键

LED驱动,说白了就是怎么让LED亮起来,而且亮得稳、亮得准。PPG里用的都是脉冲驱动,不是一直亮着。为什么?因为一直亮着功耗太大,而且容易把皮肤烤热——你想想看,戴在手指上一直发热,谁受得了?

2.2.1 恒流驱动 vs 恒压驱动

我强烈建议用恒流驱动。LED的亮度取决于电流,不是电压。电压稍微波动一点,电流可能翻倍。恒流驱动就稳多了。我习惯用一颗小MOS管加一个采样电阻,做成最简单的恒流源。

具体电路是这样的:MOS管的栅极接PWM信号,源极接采样电阻到地,漏极接LED。PWM高电平时,MOS管导通,电流流过LED和采样电阻。采样电阻上的电压反馈到运放,形成一个闭环,保证电流恒定。

// 伪代码:LED脉冲时序控制
void led_pulse(uint8_t channel, uint16_t current_ma) {
    // 设置DAC输出,控制恒流源参考电压
    set_dac(channel, current_ma_to_dac_value(current_ma));
    // 开启PWM,脉冲宽度通常50-200μs
    start_pwm(channel, PULSE_WIDTH_US);
    // 等待脉冲结束
    delay_us(PULSE_WIDTH_US);
    // 关闭PWM,降低功耗
    stop_pwm(channel);
}
小技巧: 脉冲宽度别太长,50-100μs就够了。太长了不仅费电,还容易让光电二极管饱和。

2.2.2 多波长时序控制

现在的血氧仪通常有2-3个波长(红光、红外、绿光)。这些LED不能同时亮,否则光电二极管分不清谁是谁。所以要用分时复用——先亮红光,采样;再亮红外,采样;最后亮绿光,采样。每个周期大概1-2ms。

我习惯用一个状态机来控制这个时序。每个状态对应一个LED点亮,状态切换时留一个死区时间(比如10μs),防止前后两个LED的光互相串扰。

注意: 死区时间不能省!我曾经偷懒把死区去掉,结果红光和红外的信号混在一起,算法怎么都算不准血氧。后来查了三天才发现是时序问题。

2.3 跨阻放大器设计:把电流变成电压

光电二极管输出的是电流,但ADC只能读电压。所以需要一个跨阻放大器(TIA),把电流转换成电压。这一步是模拟前端最核心的部分,噪声、带宽、增益全在这里。

2.3.1 反馈电阻与增益

增益由反馈电阻Rf决定。Vout = Iin × Rf。PPG信号的电流范围大概在10nA到10μA之间,如果Rf选1MΩ,输出电压就是10mV到10V。嗯,10V太高了,ADC受不了。所以我一般选100kΩ到500kΩ,配合后级放大。

举个例子:如果光电二极管输出100nA,Rf=200kΩ,输出电压就是20mV。这个信号太小,需要再放大10-50倍才能进ADC。所以TIA后面通常还要加一级同相放大器。

2.3.2 反馈电容与稳定性

这个坑我踩过。TIA如果不加反馈电容,很容易自激振荡。为什么?因为光电二极管有结电容,和反馈电阻一起形成一个极点,相位裕度不够就振荡。

解决办法是在反馈电阻上并联一个小电容,一般几pF到几十pF。这个电容和反馈电阻形成一个零点,补偿掉光电二极管的极点。具体值怎么算?嗯,有个经验公式:Cf = 1/(2π × Rf × f_3dB)。f_3dB取信号带宽的5-10倍就行。

避坑指南: 我曾经在一个项目里忘了加反馈电容,结果TIA输出全是高频振荡,PPG信号根本看不到。后来加了10pF电容,世界清净了。

2.3.3 运放选型

TIA用的运放,要求输入偏置电流小、噪声低、带宽够。偏置电流最好在pA级别,否则会和光电二极管的信号电流混在一起。噪声嘛,电压噪声密度低于10nV/√Hz就行。

我常用的型号有:AD8605(偏置电流1pA,噪声8nV/√Hz)、OPA2376(偏置电流0.2pA,噪声7.5nV/√Hz)。这两个都是轨到轨输出,适合3.3V供电的系统。

型号 偏置电流 噪声密度 带宽 我的评价
AD8605 1 pA 8 nV/√Hz 10 MHz 性价比高,常用
OPA2376 0.2 pA 7.5 nV/√Hz 5.5 MHz 低功耗,适合电池设备
ADA4505-2 0.5 pA 10 nV/√Hz 50 kHz 超低功耗,但带宽窄

2.3.4 布局布线注意事项

最后说一句布局。TIA的反馈电阻和电容要尽量靠近运放的输入引脚,走线越短越好。为什么?因为长走线会引入寄生电容,破坏稳定性。我习惯把反馈元件放在运放背面,通过过孔直接连到输入引脚,这样寄生最小。

另外,光电二极管到TIA输入的走线也要短。如果距离超过1cm,建议加屏蔽层,防止数字信号的噪声耦合进来。嗯,这个细节很多人忽略,但往往是信号质量的瓶颈。

我的习惯: 每次画完TIA的PCB,我都会用示波器看输出噪声。如果噪声峰峰值超过5mV,就说明布局有问题,需要调整。

好了,PPG信号采集这部分就讲到这里。下一章我们聊聊模拟滤波和ADC采样——说白了,就是怎么把TIA输出的信号洗干净,变成ADC能吃的数字量。到时候我会分享一个我踩过的坑,关于滤波器截止频率选错导致心率算不准的教训。