(开源) 心电测量基础,DIY单道电生理记录仪

本文目录

1.前情回顾

想法的产生_1

2024年1月,我萌生了制作心电放大电路的想法。此前从未接触微弱信号相关项目,因此对从噪声中提取心电信号充满新奇。

想法的产生_2

又过了两个月,刚好有朋友课设要做这个,要求不使用ASIC,而是用运放搭建。于是一拍即合,决定陪他试试。

覆铜板上初步尝试

先是用INA826和TL072在覆铜板上“潦草”地试了试,信号比想象中来得容易,但波形失真严重。在排除自身健康问题后,发现罪魁祸首是高通截止频率设得太高,或者示波器开了AC耦合,这会导致低频失真。

早期AFE模块搭配BluePill

后来又搓了个更 精致 的模块,搭配BluePill顺利完成了任务💦(

弃坑的十二导联

再后来又想尝试搓一个十二导联的,仍然手动搭建AFE而不使用单片ASIC,并希望性能尽量贴近IEC标准。

但很快“弃坑”了:整机是系统工程,软硬件都包含大量功能项,背后往往是一个团队和多年的积累。作为个人兴趣项目并不值得按标准给自己上强度(

2.目标与意义

2.1.目标

简化目标后的成品

成品动图

于是重新考虑目标和需求:不再追求用运放堆出一台尽可能完备的心电图机。新的目标是电生理记录仪,面向教学演示和科研场景的生物电信号采集(

核心目标

  1. 带宽0.05–200Hz,同时提供多挡位数字滤波,适应ECG、EMG、EEG等不同场景(虽然只有一个通道💦);
  2. 关注噪声、共模抑制比和输入阻抗;
  3. 屏幕实时显示波形、心率和心率变异性;
  4. 数据可保存为CSV和BDF格式到SD卡,并支持通过Web页面下载。

明确放弃

  1. 单通道设计, 不考虑完全对标心电图机的功能和要求 :多导联电轴解算、诊断算法、除颤防护、导联脱落检测、起搏信号检测等;
  2. 不涉及电脑上位机;
  3. USB供电,不考虑低功耗。

2.2.意义

ADAS1000特性

现代心电图机往往以单片ASIC作为系统核心。市面上已经有相当多成熟选择,如TI的ADS129x系列、ADI的ADAS1000,以及一些国产厂商(如领慧立芯、类比半导体)的产品。这类芯片功能集成和指标设置上更贴近实际应用,在合理外围设计和系统配合下,通常能满足医疗标准的要求。

因此,做这个 并非为了与成熟方案竞争,也不具备严肃的医疗价值和现实意义 ,仅仅是出于兴趣与学习的驱动。 用独立元件手动搭建AFE,或许算是“重复造轮子”,但每个轮子都有自己的独特之处。在不断解决问题的过程中,能收获对板级电路的直观感受。这种折腾本身也是很大的乐趣,而成品也能作为模电或生物医工的小教具。

写文主要是为了记录和分享,也欢迎大家交流指正🫡。

3.心电测量原理

这部分着重工程内容,不深入医学细节。

3.1.信号基本特征

典型的ECG波形

一个典型的ECG波形 由P波、QRS波群、T波组成 ,50%~75%的ECG中还可能出现U波。

ECG幅度范围为100μV~3mV峰峰值, 典型值为1mV

AHA 建议诊断性带宽应至少为 0.05–150Hz 。低频响应不足会导致ST段失真;0.05Hz的低频截止频率无法消除基线漂移,而采用线性相位的FIR滤波器可将低频截止放宽至0.67Hz。高频响应不足会造成信号幅值低估,并使切迹和Q波被错误平滑。成人、青少年和儿童至少需要150Hz的高频截止,而婴儿更适合250Hz的高频截止频率。

3.2.十二导联体系

因为时间和精力有限,这个 项目只做了单导联 💦,这里还是把十二导联的内容整理一下。

导联是指电极在身体上的摆放位置以及它们怎么跟放大器连接的一套规则。

标准十二导联心电图包括三组: 标准肢体导联 (I、II、III)、 加压肢体导联 (aVR、aVL、aVF)和 胸导联 (V1~V6)。

一共要用 10个电极 :左臂(LA)、右臂(RA)、左腿(LL)、右腿(RL)各一个,胸部再放6个(V1~V6)。右腿电极一般用作参考地,由共模信号反相后驱动,其他9个电极通过多路开关选通后接入放大器。

3.2.1.标准肢体导联(I、II、III)

标准肢体导联

标准肢体导联 (I、II、III)属于双极导联,测量的是两个肢体之间的电位差。它们的定义是:

  • V_I=V_{LA}-V_{RA}
  • V_{II}=V_{LL}-V_{RA}
  • V_{III}=V_{LL}-V_{LA}

由于任意时刻都有 V_{II}= V_{I}+V_{III} ,实际设计中我们只需测出其中两个,第三个便能得到。

3.2.2.加压肢体导联(aVR、aVL、aVF)

单极导联

与标准导联不同,加压导联属于单极导联,测的是某个肢体电极相对于威尔逊中心端的电压。

威尔逊中心端由RA、LA、LL三个电极各接一个相等大小的电阻构成,电位为: V_{WT}=\frac{1}{3}(V_{RA}+V_{LA}+V_{LL}) ,相当于身体的平均电位。

演进得到加压肢体导联

但这样测出的信号幅值较小,因为参考端混入了被测肢体本身的信号。若把对应肢体的电阻拿掉,幅值能增加50%——于是便得到 “加压”肢体导联 (aVR、aVL、aVF,a源自augmented)。这三者定义是:

  • V_{aVR}=V_{RA}-\frac{1}{2}(V_{LA}+V_{LL})
  • V_{aVL}=V_{LA}-\frac{1}{2}(V_{RA}+V_{LL})
  • V_{aVF}=V_{LL}-\frac{1}{2}(V_{RA}+V_{LA})

它们也可以用标准导联换算出来:

  • V_{aVR}=-\frac{1}{2}(V_{I}+V_{II})
  • V_{aVL}=V_{I}-\frac{1}{2}V_{II}
  • V_{aVF}=V_{II}-\frac{1}{2}V_{I}

这里能发现:既然 测任意两条标准导联即可换算出其余4条肢体导联 (I、II、III、aVR、aVL、aVF),那就不必频繁切换导联完整测量——单个或有限通道的低速ADC,也能实现多导联同步显示。

加压肢体导联是冗余的

AHA 也认可这种方式,指出加压肢体导联本质是衍生出来的、是冗余的——但它们提供了观察心电活动的不同视角,临床中仍然保留。

这6个肢体导联(标准+加压)统称为额面(frontal plane)导联,反映心脏上下和左右方向的电活动。

3.2.3.胸导联(V1–V6)

胸导联

胸导联也属于单极导联,是把电极放在胸壁特定位置,参考电极仍是威尔逊中心端。每个胸导联的电压为:

V_{n}' = V_{n} - \frac{1}{3} (V_{RA} + V_{LA} + V_{LL}) \quad (n = 1,2,\cdots,6)

胸导联反映前后和左右方向的心电活动,所以也叫横面(horizontal plane)导联。最重要的是: 这六个胸导联无法通过其他导联准确换算 ,每个都需独立测量。

3.2.4.导联数与ADC通道数关系

导联数与ADC通道数关系

总结如上表。1个ADC通道只能采单导联,2个ADC通道就能覆盖6个肢体导联,8个ADC通道(如TI的ADS1298/ADS1299)能实现完整12导联(6个肢体导联+6个胸导联)。

3.3.电极与电极模型

3.3.1.半电池电位与极化电位

不同金属的半电池电位

金属电极通过导电膏或汗液等电解质与皮肤接触时,在电极-电解质界面会发生离子-电子交换反应,形成一个稳定的 半电池电位

当界面中有电流流过(如放大器的输入偏置电流)时,会产生极化现象并形成 极化电位 ;机械运动则会扰动界面电荷分布,导致极化电位波动。极化电位的不稳定性是运动伪迹的原因之一,因此不易发生极化的电极性能更优。

半电池电位与极化电位会在放大器输入端引入较高的直流共模电压,而不同电极界面之间的直流差值则表现为零点偏移。IEC标准规定ECG设备 应能耐受±300mV的直流差模输入

3.3.2.干电极与湿电极

电极一般分为两类:干电极(又称可极化电极)和湿电极(又称不可极化电极)。

干电极

干电极比较常见:肢体导联通常采用不锈钢夹子,胸导联则多用铜镀镍吸球。其优点是可重复使用,但接触阻抗和极化电压较大。

湿电极

湿电极由导电凝胶和镀Ag-AgCl金属扣组成,具有较低的半电池电位(约为223mV),几乎不产生极化,接触阻抗也更小;但导电凝胶是一次性的。

3.3.3.实际电极及等效模型

购买制作干电极_1

这里选择干电极,因为希望重复使用。只做单导联,所以只买肢体夹子,再配一根屏蔽线自己加工即可。

购买制作干电极_2

屏蔽线是单芯的。焊接时,把芯线焊到电极片上,再套上热缩管,防止和屏蔽层短路;另一端焊2P排针,同样套上热缩管,然后插入2EDGK端子压紧。这样方便插拔,线头也不容易断。建议将三个电极统一接到一个6P的端子上,分开使用2P的会容易松动。

这种肢体夹子通过不锈钢片与皮肤接触。作为干电极,其在一定程度上依赖汗液或皮肤表面湿度来降低接触阻抗,表现与皮肤状态密切相关。实测发现:

  • 夏季皮肤出汗多时,电极更容易出现极化,运动伪迹增大、直流偏移加重;
  • 皮肤比较干燥时,信号幅值略有下降,但整体稳定性更好;
  • 冬季若皮肤过于干燥,信号可能偏弱,此时可在测量前用75%酒精擦拭皮肤,适度提高表面湿度。

EC13:典型人群的单电极阻抗极限期望值

在ANSI/AAMI EC13中提到,IEC与AAMI标准在输入阻抗要求上是统一的:差分输入阻抗不小于5MΩ,等效表述为单端输入阻抗不小于2.5MΩ。5MΩ的要求形成于电子管和分立晶体管时代,对现代运放而言并不成问题。 对应这一输入阻抗水平,标准假定了电极阻抗处于几十千欧量级。

具体而言,当电极阻抗不超过50kΩ时,由5MΩ输入阻抗引入的幅值误差可控制在2%以内。当然,这一条件往往只有在一定皮肤准备(例如轻微打磨或酒精擦拭)后才能满足。如表中所示,最坏情况下个别部位电极阻抗可能达到250kΩ,此时幅值误差约10%。

IEC标准的电极阻抗模型

EC13还提到, 对绝大多数人群,在10Hz频率下,皮肤-电极阻抗通常低于50kΩ (以10µA或更小的测试电流测得)。

因此,在工程上可以将电极阻抗视为50kΩ量级。此外,由于ECG前端还需能够耐受±300mV的直流差模输入,标准模型中还加入了相应的直流电压源。

实际的等效模型

从物理角度看,实际的等效电路由两个界面组成:不仅需要考虑电极-电解质界面,还需要考虑皮肤-电解质界面。在皮肤-电解质界面,表皮会产生跨膜阻抗;同时,由于表皮角质层两侧离子浓度不一致,还会形成跨膜电位,这一部分较为复杂,这里不再展开。


因此,对于干电极而言,其等效阻抗并不一定严格等于51kΩ//47nF。出汗情况、接触压力以及角质层厚度等因素都会产生显著影响,实际阻抗范围可能从kΩ到MΩ不等。但在本设计中,为了方便分析,仍按照51kΩ//47nF进行考虑。

3.4.噪声来源

3.4.1.共模干扰

共模干扰

人体会拾取工频和RFI等共模干扰,而电极接触阻抗的不一致会将这些干扰转化为差模信号进入电路。解决方法主要有:

  • 用右腿驱动来抵消共模干扰,同时给屏蔽线添加Guard驱动;
  • 模拟前端与数字部分电气隔离,避免USB引入外部PE地的共模干扰;
  • 测试前清洁或打磨表皮角质层,降低电极接触阻抗的失配,有条件的话使用湿电极;
  • 选择高CMRR的仪表放大器,并在输入端加入RFI滤波器;
  • 添加模拟或数字工频陷波器;但要注意陷波器引入的失真,并且IEC标准要求测试CMRR时关闭陷波器。

这一部分在后面的硬件章节会进一步说明。

3.4.2.基线漂移

基线漂移

也叫“运动伪迹”,主要由呼吸或身体移动导致电极与皮肤间发生相对位移所引起。常见处理方法有:

  • 用滑动平均提取低频成分再从原信号中减去,相当于线性相位的高通滤波,会引入滞后、同时需要较高的窗口长度;
  • 截止频率0.67Hz~2Hz的IIR高通滤波,截止频率越高抑制效果越好,但ST段失真越严重,需要折中;
  • 小波或形态学滤波,在FPGA上实现比较复杂。

这里预置了0.67Hz的IIR高通滤波器,缺点是存在相位失真,波形有畸变。最现实的办法还是:让被测者安静平躺、尽量保持不动,同时优先使用Ag-AgCl湿电极。

3.4.3.肌电干扰

肌电干扰

肌电干扰是宽带随机信号,主要能量集中在20~500Hz,与心电信号频段重叠,因此无法靠简单滤波完全去除。缩小带宽到40Hz可以有所改善,但会带来信号失真。其他方法比较复杂,比如主成分分析、多导联盲源分离。

最现实的办法还是不做后期处理,测量时平躺,手臂自然放置,保持肌肉放松,别出力(

4.整体架构

架构框图

项目主要由FPGA和MCU两部分组成:

  • 模拟前端 :完成信号放大和初步滤波。

  • FPGA :采集ADC数据,并进行数字滤波及R波检测(Pan-Tompkins算法)。

    • 数字滤波包括三档可调低通滤波(40/100/200Hz)、0.67Hz高通滤波以及50Hz工频陷波器;高通滤波和陷波器可按需关闭。
    • 数据分别写入FIFO:滤波后的数据流、R波检测得到的RR间期值(毫秒)。
    • 控制DAC输出滤波后的波形,方便示波器直接观察和调试。
  • MCU :通过SPI从FIFO读取数据,并向FPGA发送命令控制滤波器;同时负责完成人机交互和数据存储。

老实说,ESP32-S3的性能和内存已经足以处理低速信号,引入FPGA在成本上不太“优雅”。不过还是想借这个项目尝试FPGA:一方面出于学习目的,另一方面也能在实现上更轻松一些。FPGA更适合做实时信号处理,也便于直接驱动AD/DA;单片机则可以腾出手来处理交互和存储等上层任务。如果完全依赖单片机实现,可能需要在程序优化上投入更多精力。

5.硬件设计

这部分主要介绍模拟信号链的设计。

5.1.INA输入滤波器

INA输入滤波器

仪表放大器(INA)作为第一级放大,其输入端加入了共模和差模的RC滤波,用于抑制导联线引入的射频干扰。若不加滤波,强RFI可能在INA输入级被整流并表现为直流偏移。

该RC滤波器同时兼作患者保护电阻。按照IEC标准,心电图机在单故障状态下的电流限值为50µA,因此常在输入端串联约100kΩ电阻,但 这也成为电路主要的热噪声来源 。这里不以完全符合该标准为目标,而是优先考虑低噪声性能,因此将输入端电阻减小为2kΩ。

5.2.INA的噪声估算和选型

INA的选型主要考虑电压噪声、电流噪声、输入偏置电流、CMRR及成本等因素。

下面以INA821为例估算它的噪声。

INA821噪声频谱密度图

输入电压噪声由1/f噪声和宽带噪声组成。由图可知,0.1Hz处的电压噪声密度为60nV/√Hz,归一化到1Hz的1/f噪声密度为

e_{N_{normal}} = 60\text{nV}/\sqrt{\text{Hz}} \times \sqrt{0.1\text{Hz}} = 19\text{nV}/\sqrt{\text{Hz}}

模拟前端的−3dB带宽限制在250Hz。由于为二阶低通滤波器,K取1.22,其等效噪声带宽为

ENBW = K \times f_H = 1.22 \times 250\text{Hz} = 305\text{Hz}

1/f噪声的有效值为

e_{N\text{1f}} = e_{N\text{normal}} \times \sqrt{\ln\left(\frac{ENBW}{f_L}\right)} 
= 19\text{nV}/\sqrt{\text{Hz}} \times \sqrt{\ln\left(\frac{305\text{Hz}}{0.1\text{Hz}}\right)} 
= 54\text{nV}

图中平坦区域的噪声密度约为15nV/√Hz,则宽带噪声的有效值为

e_{N\text{bb}} = 15\text{nV}/\sqrt{\text{Hz}} \times \sqrt{ENBW}=262\text{nV}

INA821的电流噪声密度为130fA/√Hz。由于ECG信号为高阻抗源,电流噪声会转化为电压噪声。按IEC标准规定的51kΩ电极接触电阻计算得到该转化电压噪声有效值为

e_{CN} = I_N \times R \times \sqrt{ENBW} 
= 130\text{fA}/\sqrt{\text{Hz}} \times 51\text{kΩ} \times \sqrt{305\text{Hz}} 
= 116\text{nV}

因此,总的输入电压噪声有效值为

e_{Ntotal} = \sqrt{e_{N\text{1f}}^{2} + e_{N\text{bb}}^{2} + e_{CN}^{2}} = \sqrt{54^2 + 262^2 + 116^2} =292\text{nV}。

如此计算几种INA的噪声(G=10),按照总的输入电压噪声排序,综合比较:

INA的选型比较

最终选择INA821,其性价比最高。 但当电极接触阻抗高到足以使电流噪声占主导时,INA121可能更适合。

5.3.右腿驱动和Guard驱动

5.3.1.右腿驱动

右腿驱动的实现

右腿驱动是指将INA输入端的共模电压反相后反馈至人体(通常为右腿)。其主要作用包括:

  • 通过负反馈抑制共模干扰;
  • 作为测量参考地,为INA的输入偏置电流提供返回路径。

5.3.2.Guard驱动

Guard驱动的目的

同轴线的芯线与屏蔽层之间存在寄生电容,为工频干扰提供了泄漏路径。当电极接触阻抗不平衡时,这些漏电流会转化为差模干扰进入电路。

Guard驱动的实现

Guard驱动通过运放缓冲共模电压,并将其施加到屏蔽层,使屏蔽层与芯线的电位保持基本一致,从而减小漏电流,进一步提高CMRR。

5.3.3.环路稳定性仿真

INA、右腿驱动和Guard驱动

最终的电路如上图,需要通过仿真验证右腿驱动环路的稳定性。

搭建开环仿真电路

在LTspice中搭建开环仿真,得到开环增益 A_{ol} 、反馈系数 \beta 及环路增益 A_{ol}\beta 的曲线。

未补偿时不稳定

A_{ol}1/\beta 曲线的交点对应 A_{ol}\beta=1 ,即环路增益为0dB。此时, A_{ol}\beta 曲线上的相位即为 相位裕度

稳定性的标准是相位裕度大于45°。此外还可以通过“闭合速度”判断——闭合速度是指 1/\beta 曲线与 A_{ol} 曲线在环路增益0dB处的斜率,闭合速度大于20dB/decade表示系统不稳定。

可以看到,没有补偿的右腿驱动电路是不稳定的:

  • 相位裕度: \text{phase}(A_{ol}\beta)=-26^\circ<45^\circ
  • 闭合速度: \bigl|\text{slope}(A_{ol})-\text{slope}(1/\beta)\bigr|=|-20\text{dB}-20\text{dB}|=40\text{dB}>20\text{dB}

曲线在补偿前后的变化

添加由RR1和CR1组成的滞后补偿,让 1/\beta 曲线“变平”,这时闭合速度满足要求。

补偿后满足稳定

这里的实际取值22KΩ+100pF,补偿后相位裕度提升到87°,满足稳定性要求。

之后又尝试改变仿真中的电极阻抗和同轴线寄生电容的取值,确认它在极端情况下依然能保持稳定(嗯,大抵是吧)。

5.4.高通和低通滤波

前面主要介绍了INA和右腿驱动,而INA的输出将经过高通和低通滤波,并提供后置增益。

5.4.1.高通滤波:目的和实现

高通滤波电路

高通滤波用于消除INA输出的直流偏移。电极的半电池电位和INA的Vos经过放大后会产生较大偏移,若不处理,后级放大中会饱和。

为实现低截止频率,使用了较大容值的钽电容。考虑到直流偏移的极性不确定,用C8和C9串联形成110µF的无极性电容,配合30kΩ电阻构成0.05Hz的高通滤波器,随后通过运放缓冲放大11倍。

同样的截止频率可以通过减小电容、增大电阻来实现,例如10µF和316kΩ,这样可以直接使用MLCC。然而较大的电阻会在后级运放偏置电流Ib的作用下产生直流偏移,并增加热噪声。因此选择较小的电阻,并使用低Ib低噪声的OPA2209,以减小偏移和噪声。

5.4.2.高通滤波:双截止频率

高通滤波器的截止频率可在0.05Hz和1.5Hz之间切换。0.05Hz模式下无法有效抑制呼吸和运动引起的基线漂移,此时可将HPF_SEL设为高电平,使模拟开关U2闭合接入1kΩ电阻,将截止频率提高到1.5Hz。
由于0.05Hz高通滤波需要较长的建立时间,FPGA在检测到AFE输出饱和后会切换至1.5Hz模式,并在约10s后再切回0.05Hz,以实现快速恢复。

5.4.3.高通滤波:直流伺服 vs. 直接RC

直流伺服 vs. 直接RC

高通滤波也可以通过积分器直流伺服结构间接实现。从直观理解上说,积分器可以看作低通滤波器,而当它加入到INA的负反馈中时,整体就变成了高通滤波器。直流伺服结构在这类应用中比后级加一阶RC高通更常见,但两者在滤波效果上几乎是等价的。

直流伺服结构的噪声仿真

直接RC结构的噪声仿真

在仿真中,两者有不同的噪声分布,但直流伺服结构的噪声略大一些。

两者的建立时间仿真

在建立时间上,两者也没有太大区别。所以直流伺服结构的特点是什么呢,为什么在生物电放大器中它会常见?

这也是我比较困惑的地方。 因此最终并没有采用直流伺服结构,而是选择了更直观的RC高通(


写到这里时又去搜了一下,发现ADI有提到过:如何利用间接电流模式仪表放大器放大具有大直流偏移的交流信号?

感觉解答了我的一些困惑,直流伺服可能出于这几方面考虑:

  • 在INA处就解决DC偏移,AC信号有更大输入空间,更不容易饱和,允许了从更高的首级增益中获得更好的信噪比。
  • 如果在INA输入端使用电容AC耦合,往往需要对地偏置电阻,会劣化CMRR;直流伺服则可以避免这点。
  • Holter设备往往低压单电源供电,直流伺服结构可以方便地在积分运放同相端加上中点偏置,解决钻石图限制。

    钻石图工具:ADI / TI

5.4.4.低通滤波

低通滤波电路

在高通滤波之后,信号经过一级Sallen-Key低通,将前端带宽限制在250Hz,并再次提供11倍的增益。

5.5.AFE增益设置的折中

AFE的最终电路

这是AFE的最终电路:INA 首级放大10.148倍,随后高通与低通级各提供11倍增益,
总增益为 10.148 \times 11 \times 11 \approx 1228 倍。

5.5.1.INA增益

第一级对总噪声系数的决定性影响

级联放大时,首级直接主导输入噪声,而后续各级噪声折算到输入端时会被前级增益所除。因此,首级应优先选用低噪声器件并设置较高增益,从而降低后级噪声在输入端的等效贡献。

然而,IEC标准要求ECG设备能耐受±300mV的直流差模输入。为了避免半电池电压及强环境干扰导致前级饱和、丢失微弱的ECG信号,INA增益一般设在10~15倍之间。

尤其是这里把高通放在了INA之后,INA输出要容纳直流偏移。在INA增益10倍时,±300mV的直流输入带来±3V的偏移,已经逼近±5V电源轨。

5.5.2.噪声与抗饱和

AFE总噪声仿真

仿真得到的AFE总噪声有效值为308µV,等效输入噪声251nV。

这里希望等效输入噪声足够小,所以取了较大的增益(1228倍),对于1mV量级的ECG信号可以放大到接近ADC的满量程。

ADS1259噪声性能

不过现在回过头来看,ADS1259的噪声只有3.4uV,AFE完全可以降低一些增益、换取更好的抗饱和能力。

常见ECG芯片的增益与ADC分辨率

事实上,许多24bit商用ASIC的最大增益通常不超过20倍(用于EEG等更微弱信号时,则往往再增加一级前置放大)。

低分辨率ADC的噪声较高,依赖AFE高增益,不然信号会被ADC自己的噪声淹没;而高分辨率ADC的噪声已经足够低,AFE使用小增益就足以达到同样的无噪声动态范围,对运动伪迹的容忍度更高,不易因饱和而丢失ECG信息。

这里的AFE总增益为1228倍,适合于uV到mV量级的稳定信号;但在0.05Hz高通模式下,运动伪迹可能导致饱和。或许下次可以把AFE增益降低为200倍,或者使用带PGA的ADC。

5.6.系统噪声实测

将实物的RA、LA、RLD三个电极输入端短接,测量系统的本底噪声。

信号路径为AFE→ADC→FPGA→MCU→SD卡,以200Hz带宽、600Hz采样率录制1分钟数据,导出CSV后分析。


实测系统噪声327µV,折合等效输入噪声266nV,200Hz带宽下噪声密度约18.8nV/√Hz。

5.2 估算267.5nV(去除51kΩ电极阻抗项), 5.5.2 仿真251nV,与实测三者吻合。

5.7.ADC和DAC外围

5.7.1.ADC输入驱动器

ADC外围电路

ADC与FPGA之间通过川土微IS3742实现信号隔离。输入驱动由OPA2209完成:U13.1将ADC的REF_OUT缓冲后作为共模电平Vcm,U13.2配置为加法器,将Vin与Vcm相加,使ADC以伪差分方式工作,得到AINP=Vin+Vcm、AINN=Vcm。

理想情况下,ADC可搭配FDA(如THP210、THS4551)获得最佳性能,但这里用伪差分足够满足需求,同时继续沿用AFE中用过的OPA2209,BOM更简单。

5.7.2.DAC低通滤波器

FPGA将处理后的信号通过DAC8830输出,方便直接用示波器观察调试。DAC输出需要接低通滤波器,缓冲信号并抑制带外噪声。

这里选择Sallen-Key拓扑和巴特沃兹响应。尝试了DualMono老师提到的,用FilterSolutions生成单运放的四阶滤波器,节省一个运放(

DAC四阶低通仿真

仿真二阶和四阶滤波器的频响曲线能看到:V(order_2)的滚降速度为40dB/decade,而V(order_4)为80dB/decade。

Sallen-Key拓扑的优点是输出不反相,但存在高频馈通。为解决这一问题,输出端添加了100Ω电阻以限流和隔离负载,并与100nF电容组成RC低通滤波器,改善后的频响为V(order_4_rc)。

DAC外围电路

得到DAC部分的最终电路。

与FilterSolutions生成结果不同的是,滤波器输入端的100KΩ电阻R35与1.5MΩ电阻并联,以补偿DAC8830的6.25kΩ恒定输出电阻。

DAC部分通过LDO(LP5907)供电,抑制ESP32-S3的Wi-Fi工作时电源母线的低频跌落。

5.8.低噪声隔离电源

5.3.2 中的图 所示,测量地与PE地之间的杂散电容 C_{s} 构成了共模干扰的主要泄露路径。将模拟前端和ADC部分采用浮地、使用隔离电源供电,有助于减小 C_{s} ,从而提高系统的 共模抑制比

隔离电源原理图

采用VPS8504B搭配WE760390014产生6V隔离电源,再通过TPS7A39得到低噪声的±5V。在TI的 SN6505手册 中提供了一些变压器选择,其中WE760390014比较容易买到。

SBVA042 中提到,通过适当增大LDO的参考电压滤波电容 C_{NR} 以及前馈电容 C_{FF} ,可以降低LDO的噪声。

隔离电源以模块形式制作,便于复用。

隔离电源原理图

测试时使用 EmoeNAP 对负通道空载输出放大80dB,示波器测量RMS噪声。当 C_{NR}C_{FF} 取10nF时,噪声约20µV;当两者增大至100nF后,噪声可稳定在约13µV。

其中增大前馈电容 C_{FF} 对噪声的改善效果最为明显,但过大的取值会延长启动时间,造成一些LDO的PG脚功能异常。

前馈电容会通过LDO内部的ESD管放电

在LDO通过EN脚关闭或输出被短路时, C_{FF} 会经由LDO内部的ESD管放电。若 C_{FF} 取值过大,可能导致器件击穿,此外还需关注潜在的环路稳定性问题。

6.FPGA部分实现

架构框图

FPGA是GW1NR-9,比较轻量小巧,这里直接使用了 Sipeed Tang Nano 9K开发板

6.1.数字滤波

ADC工作在3600SPS采样率,采集到的数据首先进入FIR低通滤波器,用于限制系统带宽。这里直接使用高云提供的Basic FIR IP核,之前做过的一些尝试记录在: ADS1259配合高云Basic FIR核的尝试 - 哔哩哔哩

FIR低通的抽头数为200taps,输入数据与系数位宽均为24bit,输出位宽为54bit饱和截位到24bit。FPGA内预置了3组滤波系数,由MCU控制切换,实现40/100/200Hz三档低通带宽配置。

简单试了试:向AFE输入3mVpp正弦信号,经ADC采集、FIR处理后由DAC输出,用示波器扫频得到三档带宽对应的Bode图。

可以看到低通滚降较为陡峭;通带内低频部分相位曲线基本保持平行。由于AFE与DAC包含模拟滤波环节,相位特性并非严格线性。


在继续做高通时发现:由于数据位宽大,继续用FIR IP核的话DSP资源会吃紧;而0.67Hz的高通截止频率也意味着FIR需要很高的阶数。对于陷波器,用FIR做带阻同样阶数很高,效果也不理想。

因此高通和陷波滤波器用定点IIR实现,参考了 直接型IIR滤波器Verilog实现_杜勇FPGA ,主要做法是把量化后的系数拆成移位和加减。

高Q值IIR陷波器在噪声冲激作用下,容易产生50Hz振铃甚至持续振荡。

这里的处理方式比较“粗暴”:将IIR输出的i_data再经过高通滤波得到hpf_out;对其中的振铃脉冲进行计数,当单位时间内的脉冲数peak_cnt超过阈值时,拉高alarm信号复位IIR模块,避免演变为持续振荡。

分别在开启和关闭陷波器时,给AFE输入50Hz满量程信号,各导出约180k点数据,用于测试陷波深度。

实测工频陷波深度约45dB。

示波器测量陷波器开启前后DAC的输出,−3dB带宽为2*(51.65-50)Hz=3.3Hz,对应的Q值为50/3.3=15.15。

从Q值、陷波频点的准确度及稳定性上说,在数字域实现工频陷波相较于模拟域的双T或Fliege结构更具优势。


在经过低通、高通和陷波处理后,数据被分为三路:

  • 以3600SPS送入DAC;

  • 6倍抽取至600SPS,写入FIFO供MCU读取;

  • 18倍抽取至200SPS,送入Pan–Tompkins模块。

6.2.Pan–Tompkings算法

Pan–Tompkins算法用于检测R波,相邻两个R波之间的间隔(RR间期)可以反映心率。

算法首先通过一系列预处理步骤对信号进行整形,包括5~11Hz带通滤波、微分、平方以及滑动平均,以强化R波的特征。

在此基础上进行峰值检测,并通过动态阈值将R波与噪声区分开来。

其中,检测到的R波峰值和噪声峰值分别经过中位数滤波,得到:R波峰值估计值SPK、噪声峰值估计值NPK。

SPK与NPK共同参与阈值计算,用于判断当前PEAK是否为R波;判定结果又会反过来更新SPK和NPK,使阈值随信号状态自适应调整。更详细的整理记录在: Pan–Tompkins算法的梳理 - 哔哩哔哩

尝试在FPGA上实现,用示波器录制了一段AFE输出的实际波形,作为仿真阶段的参照。

可以看到信号经过整形和峰值检测,最终输出了RR间期。

仿真可行后在实际硬件上试试。

黄色为ECG信号经ADC采样后由DAC实时输出到示波器;红色为FPGA根据检测到的RR间期翻转IO,能看到下降沿比较好地跟随ECG的R波,少量噪声和基线漂移对检测效果没有明显影响。


Pan–Tompkings算法产生于1985年,它没有涉及复杂的理论,比较偏工程主导。其中不少参数都是作者通过实验确定的:在不同参数组合下统计算法的误检和漏检次数,再从中选取效果较好的配置。

例如峰值水平(SPK、NPK)的估计方式(平均、中位数或一阶IIR滤波),以及检测阈值的系数,都是试出来的,没有明确的原因。

算法本身计算量很小,主要是加法、移位和比较,八十年代的单片机就能实时运行。后来发现作者其实公开了 对应的C实现 。不过这里觉得FPGA的闲置资源还比较多,就顺便把这部分放在FPGA了((

坦白讲,俺做的这个仅仅只是个玩具。实际发现波形饱和会让检测卡死、可能是阈值被带偏了,临时处理办法是检测到饱和就把算法模块复位。而且严谨一点,还要用MIT-BIH数据集验证算法的漏检和误检率,这一步也是没有做的。

6.3.与MCU通信

FPGA作为SPI从机,接收MCU的控制。

每帧通信以帧头 0x7E (FRAME_FLAG)起始,随后依次发送命令字节(CMD)和参数字节(PARAM),构成命令阶段。命令阶段结束后,MCU发送DUMMY字节以触发从机回传CRC8校验值。

命令阶段CRC8确认完毕后进入数据阶段,不同命令的行为如下:

  • WREG :MCU发送待写入数据,从机写入REG后回传数据阶段CRC8;
  • RREG :MCU发送DUMMY字节,从机返回REG当前值及其CRC8校验值;
  • RRSQ / ECGS :MCU通过参数字节CNT指定读取数量,从机连续返回CNT个数据单元:
    • RRSQ每单元2字节(RR间期,16位无符号整数,单位ms);
    • ECGS每单元3字节(ECG样本,24位有符号整数);
  • RSET :无数据阶段,从机立即复位所有模块至初始状态。

FPGA内置3×8 bit寄存器供MCU读写。

位域 名称 属性 默认值 描述
REG 0x00
Bit7:6 LPF[1:0] 读写 2 数字域低通滤波器截止频率选择。
0 — 40Hz,1 — 100Hz,2 — 200Hz,3 — 保留,禁止写入
Bit5 NOTCH_BP 读写 0 数字域50Hz陷波器旁路控制。
0 — 使能陷波,1 — 旁路,信号不经陷波器直接输出
Bit4 DSP_HPF_BP 读写 0 数字域0.67Hz高通滤波器旁路控制。
0 — 使能高通,1 — 旁路,信号不经高通滤波器直接输出
Bit3 AFE_HPF_SEL 读写 0 模拟域高通滤波器截止频率选择。
0 — 1.5Hz,1 — 0.05Hz
Bit2 AFE_OVLD 只读 0 模拟前端过载指示。
0 — 正常,1 — 检测到过载,自动切换至1.5Hz快速恢复模式,持续18秒
Bit1 IIR_RINGING 只读 0 IIR滤波器振铃指示。
0 — 正常,1 — 检测到振铃,FPGA自动复位数字域滤波器,此位保持约0.62秒
Bit0 RWAV_DET 只读 0 R波检测指示。
上升沿(01)对应R波到来时刻,可用于心跳节律监测
REG 0x01
Bit6:0 ECG_FIFO_COUNT 只读 0 ECG样本FIFO中可读取的样本数量
REG 0x02
Bit5:0 RR_FIFO_COUNT 只读 0 RR间期值FIFO中可读取的RR间期数量

FPGA使用有限状态机控制SPI通信,具体转换关系如图,各状态功能定义如表。

状态 编码 含义
S_IDLE 0x00 空闲,等待帧头0x7E
S_WAIT_CMD 0x01 已收到帧头,等待命令字节CMD
S_WAIT_PARAM 0x02 已收到CMD,等待参数字节PARAM,同时开始计算CRC
S_SEND_CMD_CRC 0x04 命令阶段完成,将CRC8装入发送寄存器并拉起tx_valid
S_WAIT_CMD_CRC 0x08 等待主机发送DUMMY字节以完成CRC传输,收到后进入数据阶段
S_PAYLOAD 0x10 数据阶段,根据CMD分支处理:WREG收数据、RREG发数据、RRSQ/ECGS读FIFO
S_SEND_PAYLOAD_CRC 0x20 数据阶段完成,发送数据阶段CRC8,然后回IDLE
S_WAIT_RRSQ_SEND_BYTE 0x40 逐字节发送一个RR间期值(2字节),发完后回S_PAYLOAD读下一个
S_WAIT_ECGS_SEND_BYTE 0x80 逐字节发送一个ECG采样值(3字节),发完后回S_PAYLOAD读下一个

7. 使用说明

7.1.外观与接口

7.1.1.正面

正面整体图

① 按键区 :共4个按键,从左到右依次为SW1、SW2、SW3、SW4。SW1/SW2左右移动光标,SW3切换页面,SW4确认/取消。长按SW3可直接返回主页,无需反复点按。

② FPGA开发板 (Sipeed Tang Nano 9K)

③ FPGA状态指示灯 (7个,从上到下):

LED 含义
LED1~3 点亮时分别对应数字低通滤波器截止频率:40Hz / 100Hz / 200Hz
LED4 过载恢复指示:检测到过载后自动进入快速恢复模式(AFE高通提升至1.5Hz,持续18秒),期间保持点亮
LED5 IIR振铃指示:检测到振铃时FPGA自动复位数字滤波器
LED6 心跳指示:随每次心跳闪烁
LED7 电源指示:常亮

7.1.2.内部(移去屏幕与FPGA开发板后)

移去屏幕和FPGA开发板后的内部图

④ 电源输入 :USB-C,5V 1A,支持USB转UART通信。

⑤ AFE模块

⑥ ADC部分

7.1.3.背面

背面整体图

⑦ SD卡插槽

⑧ 导联输入 :2EDGK端子,线序定义见背面丝印,导联电极的制作见 3.3.3节

⑨ DAC调试输出 (0~2.5V):ADC→FPGA→DAC实时输出,可直接接示波器观测当前信号。

⑩ 隔离电源模块

⑪ RTC电池 :断电保持走时,时间信息会用于文件名。

7.2.页面介绍

设备共有4个页面,按SW3依次切换: 主页 → 主设置页 → 网络设置页 → 录制设置页

7.2.1.主页

开机后默认停留在主页,各显示区域如下:

主页标注图

时间与日期

过载恢复指示 :与 LED4 含义相同。

实时心率 (bpm)

数字滤波器状态

走纸速度 :波形时基,仅影响屏幕显示,录制采样率固定600SPS。

1mV定标线 :波形幅度会动态缩放以适应屏幕高度,定标线随之联动。

心率变异性(HRV)指标 :SDNN和RMSSD,详见下节。

HRV指标说明

屏幕上实时显示两个时域HRV指标:

  • \text{SDNN} = \sqrt{\dfrac{1}{N}\sum_{i=1}^{N}(RR_i - \overline{RR})^2}
  • \text{RMSSD} = \sqrt{\dfrac{1}{N-1}\sum_{i=1}^{N-1}(RR_{i+1}-RR_i)^2}

计算基于一个长度为 70个RR间期 的滑动窗口。为防止噪声污染窗口,每次新RR值入队前会进行有效性检查:

if ((rr > 0) && (rr < 2000) && (abs(rr - calc->last_rr) < (calc->last_rr / 2)))

即RR间期须在0~2000ms之间,且与上一次RR间期的偏差不超过50%,否则丢弃。

使用建议 :

  • 测量前建议 按SW1清空窗口 ,以排除之前残留数据的影响;
  • 保持安静,等待窗口填满(70次心跳,静息心率下约1分钟)后数值才会趋于稳定;
  • SDNN和RMSSD通常需要较长时间窗口才有统计意义,此处仅供参考。 如需深入分析,建议录制并导出CSV或BDF文件,再用Kubios、NeuroKit2等软件处理。

录制操作

主页

  • 开始录制:长按SW4,若已配置倒计时则弹出倒计时提示,倒计时期间按任意键可跳过并立即进入录制状态。

主页录制状态

  • 暂停或继续录制:短按SW4;录制过程中,右下角持续显示已录制时长。

录制结束提示框

  • 结束录制:长按SW4,文件立即保存,并弹出持续3秒的结束提示。

7.2.2.主设置页

按SW3切换至主设置页。

主设置页

SW1/SW2在选项间移动光标, 长按可快速移动 。SW4勾选/取消勾选,设置完成后 长按SW3直接返回主页

设置项 说明
AFE HPF 模拟前端高通滤波器截止频率(0.05Hz / 1.5Hz)
DSP HPF 数字高通滤波器开关(0.67Hz)
DSP LPF 数字低通滤波器截止频率(40 / 100 / 200Hz)
DSP Notch 数字50Hz陷波器开关
System Beep 按键蜂鸣提示开关
Heartbeat Beep 心跳蜂鸣提示开关,开启时每次检测到R波响一声
Paper Speed 走纸速度(仅影响屏幕时基,不影响录制采样率)

7.2.3.网络设置页

再次按SW3切换至网络设置页。SW1/SW2移动光标,SW4确认,设置完成后 长按SW3直接返回主页

网络设置页

连接Wi-Fi后,可通过浏览器访问SD卡中的文件。支持两种模式:

STA模式(连接至现有路由器)

首次使用需配网,步骤如下:

  1. 光标选中 "Change Wi-Fi" 按钮,按SW4确认,设备进入配网状态。
    此时屏幕显示:

配网状态

  • Wi-Fi SSID:设备自身释放的临时热点名称,滚动显示
  • Wi-Fi IP:配网页面地址
  1. 用手机或电脑连接该热点,浏览器输入上述地址,打开配网页面。

Web配网页面

  1. 在列表中选择目标Wi-Fi,输入密码并点击连接。
  2. 连接成功后,设备自动保存密码,后续开机会自动连接。想连接别的Wi-Fi也可以点击"Change Wi-Fi"重新配网。

连接成功后,屏幕改为显示:

已连接状态

  • Wi-Fi SSID:已连接的目标Wi-Fi名称
  • Wi-Fi IP:Web文件管理页面地址

在浏览器输入该地址,即可进入文件管理页面,下载或上传SD卡中的文件。

AP模式(无可用路由器时)

AP模式

开启AP模式后,设备自身释放一个Wi-Fi热点。和前面所述的步骤类似,手机或电脑连接该热点后,浏览器输入指定地址同样可以访问Web文件管理页面。

Web文件管理

Web文件管理页面

文件名格式为 ECGxxx_年-月-日_时-分-秒(序号自动递增),例如 ECG026_2026-03-09_00-18-15.csv

Web文件管理页面中,文件按以下规则自动排序:

  • 文件夹始终排在文件前面;
  • ECG文件按序号从大到小排列,即最新录制的文件显示在最前;
  • 无法识别序号的文件则按文件名字母顺序排列在末尾。

当目录下文件数量超过300个时,文件大小一栏将不再显示,以保证页面加载速度。更多文件的极端情况(例如上千个)没有测试过。

7.2.4.录制设置页

再次按SW3切换至录制设置页。

录制设置页

设置项 说明
Recording Duration 录制持续时长上限(最长可设100h,但没有实际测试)
Recording Countdown 录制开始前的倒计时,可选0 / 3 / 5 / 10 / 15 / 20 / 30 / 60秒,设为0时禁用
File Split Threshold 文件分割大小,可选0 / 10 / 50 / 100 / 200 / 500 / 1024MB,设为0时保存为单文件
File Format 保存格式,CSV或BDF

文件格式

CSV

对于CSV文件:会在开头几行记录录制时的滤波器状态、开始日期时间等,而后紧随的是int24样本序列。

BDF
BDF

对于BDF文件:由512字节固定头部和数据体两部分构成。头部记录了通道标签、采样率(600SPS)、物理量纲(uV)、滤波器参数等元数据;数据体中每个样本以24位小端整型存储,每600个样本为一个数据记录块(对应1秒)。录制结束时若最后一块不足600个样本,会自动补0对齐,确保可被 第三方BDF查看软件 正常解析。

参考:BDF+ format description Biosemi file format FAQ

在存储空间利用率上,BDF以二进制方式存储样本,每个样本固定占用3字节;而CSV将数值转换为ASCII文本,一个样本往往需要7~9个字节,因此相同时长的录制数据,CSV文件体积通常是BDF的2~3倍。


以上就是各页面的完整操作说明。整体上: SW3切换页面,SW1/SW2移动光标,SW4确认,长按SW3随时返回主页 ——记住这四条就可以了。所有用户设置会保存在ESP32的NVS(非易失性存储)中,每次上电加载。

7.3.串口终端

主要用于调试和修改时区。

电源输入的USB-C口同时支持USB转UART通信,用任意串口终端工具(如串口调试助手、PuTTY等)以115200波特率连接,即可进入交互式命令行。输入 help 可列出所有可用命令:

串口终端

命令 说明
help 列出所有命令及说明
version 查看芯片型号、IDF版本等信息
restart 软件复位
free 查看当前堆内存剩余量
heap 查看程序运行以来堆内存最小剩余量
tasks 查看各FreeRTOS任务状态
stats 查看各任务运行时间占比
temperature 查看ESP32-S3芯片内部温度
log_level <tag\|*> <level> 动态调整日志级别
timenow 查看当前时间
tzset <posix_string> 设置时区
beep <freq_hz> <duty> <duration_ms> 控制蜂鸣器发声

命令历史会保存在ESP32的内部Flash中,重启后仍可用方向键上下翻出历史记录。


时区设置

设备默认时区为UTC+8 ,时间通过SNTP网络对时获取。

若需要显示本地时间,可用 tzset 命令写入POSIX时区字符串,设置会立刻生效并持久化保存。

tzset CST-8

以上命令将时区设为UTC+8(中国标准时间)。常用时区示例:

地区 命令
中国(UTC+8) tzset CST-8
日本(UTC+9) tzset JST-9
英国(UTC+0/+1,含夏令时) tzset GMT0BST,M3.5.0/1,M10.5.0
美国东部(UTC-5/-4,含夏令时) tzset EST5EDT,M3.2.0,M11.1.0
澳大利亚悉尼(UTC+10/+11,含夏令时) tzset AEST-10AEDT,M10.1.0,M4.1.0/3

8.总结与展望

本文介绍了ECG测量的基本原理,并尝试实现了一个用于教学演示的单通道电生理记录仪。

受限于时间和能力,项目仍有不少可以完善的地方:

  1. AFE加入可调增益(或改用集成PGA的ADC),高通滤波改为直流伺服;
  2. AFE扩展至多路,配合8通道ADC,以支持十二导联ECG及EEG、EMG采集;
  3. 在PC上位机或ESP32中完成进一步数据分析,探索脑机接口和肌电控制的可能性;
  4. 进一步完善软件,补充60Hz陷波、导联脱落检测等功能。

这个项目最大的收获是对电路噪声有了直观的感受。动手过程中,也愈发意识到自己在信号理论和数学基础上的不足,但也因此更清楚接下来该往哪里走。

感觉很向往那种掌控感,总觉得懂得越多,能玩的东西也就越有趣。总之,新的一年继续进步吧。


致谢

最后特别感谢Emoe工作室和交流群,在水群中收获许多,也得到很多dalao的帮助,非常感谢!

感谢你读到这里,希望本文能有所参考,也欢迎交流指正~


项目地址

📦 https://github.com/chirpyjay/BioRec-One

免责声明

本项目仅供学习与教学演示,不构成任何医疗建议,不得用于临床诊断或医疗目的。

本设备未经医疗器械认证,作者不对因复刻、使用或改造本项目而造成的任何人身伤害、财产损失或其他安全事故承担责任。复刻前请确保您具备相应的电子和安全知识。


参考资料

网络资源

标准规范

书籍

  • 李天钢、马春排,《生物医学测量与仪器原理与设计》
  • 余学飞、叶继伦,《现代医学电子仪器原理与设计》
  • 上海光电医用电子仪器有限公司,《上海光电ECG-6951D单道热线阵自动心电图机原理》
  • 约翰·G.韦伯斯特,《医学仪器:应用与设计》

文章

发表回复