半年,1万行代码,1447个焊盘,做了一个心电仪,开源了
半年,1万行代码,1447个焊盘,做了一个心电仪,开源了
我想,优秀的心电监护仪,一定能一边进行高精度检测,一边直接打印检测数据!
抱着这样的想法,我做了这个心电仪,顺便被电视台报导了……
这个心电仪厉害在哪里?
下面,就介绍一下我做的心电监护仪,顺便分享一下——功能亮点、硬件设计、数据处理原理、软件说明、成本说明。
参考开源资料:
https://oshwhub.com/lmppbba/ecg-monitoring-defibrillator-with-12-leads
项目简介
这是一个拥有12导联的心电监护仪
作者用半年时间,写出一万行代码,放置1447个焊盘,连3310条导线,最终开源出来了这个项目!
一、功能/亮点
1.心电监护功能
一键10s快照功能
一键冻结
血压测量
实时分析计算
实时时钟显示
2.十二导联心电图功能
实时快速心律分析
一键冻结
一键10s记录
自动分析标注
一键快速打印
3.5个巧思
将心电监护仪与十二导联心电图相结合, 功能强大
减轻了整机重量,整机A4纸大小,若运用于急救系统,可减轻急救人员负重压力,可让急救员轻松穿过狭窄区域
集成8寸(A4)热敏打印机芯,可随时快速打印长度固定为30cm的记录
记录纸打印装订参考线,内置RTC时钟同步时间,便于快速整理数据,提高工作效率
打印模板数据栏简洁易懂,排列整齐,可快速查找重要数据
4.7个亮点
全站首个12导联心电图采集电路
全站首套心电图基线修正与心电图(实时)详细分析标定算法
全站首套完整基础患者监护系统
全站首套呼吸波分析算法
全站首套血压计系统(首套血压计示波法算法)
全站首次热敏打印机控制器实际专项应用项目,数组合并,实时计算算法
全站首个应用CLUT低RAM点亮大屏项目
二、硬件设计
1.电路设计
主控部分
电源部分
12导联模块
心电监护及PANDLE
除颤器部分
控制面板
参考资料页
PCB图
2.硬件说明
设计软件:嘉立创EDA
主控芯片:GD32F470ZIT6
生物电采集前端设计使用:TI ads129x
电池管理:IP5310(英集芯)
心电图前端:ADS1298(TI)
呼吸测量前端:ADS1292R (TI) ADS1294R缺货被迫选择
电压基准源:REF3433IDBVR(TI)95uA低功耗
打印头机芯:JX-8R-LXS(QJ) 目前搜到成本最低
参加开源活动:星火计划
[星火计划]提供了:PCB-550;SMT-3000;元器件-200;3D外壳-400等开发耗材;具体心电仪成本见【第5章】
三、数据处理原理
1.血氧饱和度脉搏波的获取
怎么获取更精准的数值?原理是什么?
下面图示是需要获取的PPG数据(类似)。
如果我们单纯设置一个阈值来“一刀切”,那么不在范围内的信号就会被斩于马下,呈在屏幕上呈现出满量程的假象。每一个人的脉搏波测量后所反馈所得数值都不一样,且该波形易受到意外影响,如:乱动血氧夹子,二度房室传导阻滞,窒息或其他原因的血氧跌落,肢体活动等导致血流受阻。
因此,我们必须使用特殊算法来适应。思路如下:
读取窗:
我们设置显示窗为0~1000范围,要在显示窗内显示完整图形,首先要标记出数值的最值,标记出最大值和最小值就为将图形全部放到显示窗内提供了可能。
但,传感器所反馈回来的数值非常大,并不能直接放到显示窗内,所以我们要进行下一步的必要处理。
处理窗:
脉搏波所反馈的信息很多,我们为了保留脉搏波跌落,上升,以及图形更多细节,采用分段取最值的方式,并没有采用中值滤波,现在设置一个周期,每1000周期取一次最值(红线标记相关代码,下同)
然后,我们将数值减去最小值,我们将小于最小值的数字直接略去,但分段取值的缺点因此显现,所以我们在略去的同时告诉取值部分需要重新取值(最大值同理)
这样不是又出现了满量程现象吗?
其实,这是对于显示趋势时的必要牺牲,在下一周期会重新被感应,而且一个周期持续只有2秒
处理窗:
然后,我们截取数据,将图形绘制到显示窗上,为了减少超量程现象,我们为最值增加300的宽限
PPG显示窗:
但是,在调光后几秒,脉搏波的最值取值不正确,或者患者发生心律失常时脉搏减弱,这个过程中,脉搏数据均未突破最值,但是波形异常小,难以阅读 甚至为一条直线不能阅读。
出现波形 “难以阅读 ”的现象,怎么破?
此时,我们要检测波形振幅,当振幅低于设定值,发送需要重新取最值请求
PPG显示窗(异常):
这样,当PPG信号出现振幅异常时,程序才会将波形”伸开“,至此,解决了PPG的搜索,以及意外处理
PPG显示窗(运行时):
PPG显示窗(实际运行),可以很清楚看到各个周期的处理:
*PPG与ECG不同,不能代替ECG诊断复杂情况,但PPG也有自己的用武之地,如:发现房颤,早搏或传导阻滞,过速或过缓,但只起到发现作用,还需要ECG确认和定性
2.心电信号解算
如何基于ADS1298读取心电信号解算?如何实时基线修正呢?
首先吐槽一下ADS1298的奇葩输出方式,在0到正满量程时输出值位0 - 0x7FFFFF,但是在负满量程到0时却跟在了0x7FFFFF之后,为0x800000 - 0xFFFFFF
所以,画出的数轴是这样子的,需要进一步处理将两者连到一起,否则当数据卡在正负中间时,解算的数据上上下下无法分析
怎么将两者连到一起呢?
现在红色为一组,蓝色为一组,按照uint来看,蓝色在前,红色在后,现在我们把蓝色放到后面,剩下的交给基线修正逻辑
为了把蓝色放到后面,且防止溢出,我们分为三步走。
第一步,判断数据应属于0以上还是0以下
第二步,如果是0以下,让数据减去0x800000,使得负满量程为0
第三步,如果数据是0以上(含0),让数据加上0x8000
这样,读取数据的时候就不会抽风了,可以安心丢给基线修正逻辑
旧的问题解决,新的问题油然而生。
在用”12阵法“镇住数据之后,我们发现数据上出现了很多毛刺,非常影响判读和分析……
我们需要清理这些 “数据毛刺”!
我们发现,比较突兀的毛刺电压为8mV,那么QRS电压有可能超过8mV吗?
正常肯定不会的,但是情景是多变的,我开始搜索病态心电图查找线索。在搜索的病案中,QRS电压在3mV左右, 没有超过3.5mV的,保险起见我们将超过6mV的信号定为毛刺信号去除(最终最高QRS电压为患有心力衰竭左心室扩张的亲戚,R波电压5mV)
现在,我们就和大毛刺say good bye了,小毛刺还需在电源努力。
怎么清除 “小毛刺?”
接下来,就该处理喝醉酒一样的基线了,我们可以看到基线一直在上下倾斜,这就是基线漂移现象。
基线漂移如何处理呢?
基线漂移的重中之重就是找到基线信号,紧接着将原始数据减去基线信号就可以得到修正后的波形了。
要得到基线信号,我们需要去除QRS波,P波和T波。QRS波是最好去除的,只需要沿用毛刺去除代码,将QRS判定为毛刺即可。
说明一下,在定义的时候,I导联,aVR aVL aVF不做定义,因为这些导联是算法推算的,后算即可(bsxx 即basexx为基线变量)
想象很美好,现实是残酷的,因为高采样率的缘故,QRS有上升时间,得到的基线标本(紫色)和原始数据(红色)是差不多一样的!解决这个僵局很简单,我们使用抽样调查之后,再进行接下来的处理:
就这样,我们得到了抽样调查后去掉QRS波的样本,虽然采样率被极致压缩,但是这对于解算基线已经绰绰有余了
听说你要直接拿数据减去这个?不!这里面还有未除净的p波和T波,有时候s波也混在其中!
此时,请出我们的中值滤波器。
这样我们在用“原始数据”减去“基线数据”就能得到“基线”和“最终波形”。
测试对象2:
*相关代码存在变更,测试图并不能反映最终结果
然后推算其他肢体导联。
3.NIBP无创血压数据处理
无创血压(同NIBP)?怎么测量这个数据?
怎么获取NIBP数值?一般是给袖带充气,当超过人体最高血压值一定数值,再缓慢放气,读取放气过程中袖带压(或管路压力)的变化,并处理,得出NIBP数值。具体原理如下:
当袖带充气超过收缩压之后,血流被阻断,不会对袖带产生作用力,在放气时,袖带压降低到收缩压之后血流重新流通并产生波动,对袖带产生作用力,引起袖带内压力值增高或暂时不变。继续放气低于舒张压之后,有压强差可得,袖带对血管产生压力与所受血管的支持力相等,合力为0,不产生形变,袖带内压强不再受血流冲击变化,压力值正常下降。
了解了原理,让我们将目光放回本项目!
实际操作中,如何实现精准的NIBP数值测量呢?
首先,看一下理想情况下袖带压力变化。
图表来自https://www.bilibili.com/video/BV1JV4y167AJ
然而,事实上,没有什么事情是理想的,在实际对*0.1kPa数据前处理之后,读出的波形成了这副模样:
全貌:
泄气部分:
如何处理这种 “波形泄气 ”的情况?
由于之前处理运算转换为mmHg int值时,在*0.75过程中丢失了波形细节,所以我们在这个失真的波形上意外的读出了(87/45)的奇怪NIBP值。
我们将int换为float得到以下图形:
所蕴含的信息在哪里呢?我帮你指一下:
这里可以找到四个心搏点,但是黄色标记的心搏太浅,单片机可能无法正常识别到,所以,可以认为仅存在三个有效点
接着,就要让单片机认识这几个心搏点
直接让单片机处理是不可能的,因为前后都有平直线段,而且我们也不能标定阈值,由于数据读取的特殊性,只要读错一个,所得数据会造成极大误差。
根据唐老师将电赛-电子血压计电路指导,我使用一个0.3~3.5带通滤波器协助调整。但在实际操作中,发现需要使用一个频率为<3Hz的低通滤波器,不断调整数值到最佳频率。
亿顿操作猛如虎,经过滤波,提取,再滤波之后得到这样的数据,可以轻松找到五个搏动点,但是仍然存在许多噪声,无法满足单片机的处理需要,单片机还是看不懂。
并且,左边的大竖线需要在以后汇总时屏蔽掉。
怎么削弱 “波形噪声 ”?
这里可使用阈值法。
首先,通过间隔取中值滤波,将图形稍做优化,看起来不那么杂乱无章了,就不为难单片机了。
通过平方削弱杂波成分后,再通过下降枝阈值法和干扰值排除确定出搏动。
如图,这是提取出来的脉搏点(未经过排除),可以看到,重博波与过远的无效波。
然后,再经过隔值法与直接法处理并比较有效点个数,多者胜出,运用其方案计算
隔值法原理:
现有的数据: N N N N N N N(直接法算法)
第一次处理: N N N N N N(尝试计算)
第二次处理: N N N N N N(尝试计算)
第三次处理: N N N N N N(尝试计算)
第四次处理: N N N N N N(尝试计算)
第五次处理: N N N N N N(尝试计算)
没有第六次啦!不然比较不了间隔值!
排除干扰所得数据如图,可以看到“原理数据”的值和两个有效数据之间的“干扰值”被滤去:
第二次试验:
看到先出现对应收缩压,后出现对应舒张压,就完毕啦!
4.呼吸波数据解析
首先要读取到呼吸波形
我们使用了0.1 - 4 Hz低通滤波器,以及一个中值滤波进行处理,得到初步波形,将其存入数组。
为保证分析和快照实时性,我们采取将数组左移,最后追加的方式存储数据。
我们使用memmove方法安全且快速的移动数据。
这是读取出来的原始呼吸波性,被载入到缓存数组中,但是仍然有很多毛刺,虽然在显示时无影响,但是对于数据处理是致命的
怎么处理这些毛刺?
于是我们对其进行了一次带通滤波(0.2 ~ 4 Hz)和宽窗中值滤波,让波形变平滑,最后进行下降沿检测即可。
在实验时发现总会有1的重复值,进行矫正即可:
最后我们把识别呼吸波的个数乘以三 即可。
因为采样率是10ms一次,传入数组体量为2000,含20s数据,乘以三即可获得60s数据。
5.多导联心电图联合解析算法
这是一项艰巨的任务,也是各个数字心电图机开发所面临的挑战。
R波识别
处理第一步,通过Pan-Tompkins算法滤波+平方运算削弱p、T等小波,提高R波斜率。
所得结果1:
所得结果2:
然后就可以通过“斜率识别”把R波的索引提取出来。
对于低电压的情况,识别到无任何波形,可以降低阈值继续检测。
但是如结果1所示,II导联处理的数据中间存在若干干扰,还需要引入III导联进行双重校验,得出纯净的数据。然后再将索引根据采样率计算出RR间期以及心率。
紧接着计算出QRS数据与QT数据。
QRS起点寻找
我们通过以上算法寻找的R波可能超过R波,也可能在Q与R之间,我们需要进一步向左寻找QRS波起点。
对于起点寻找,就需要考虑多种情况了。
我们总结出这几种情况:
1.无Q波,直接平直
2.有Q波,即存在转折关系
这些是可能的情况,部分情况可以合并(正向波和负向波的同类型情况可以合并,根据Q波的情况可以合并)
我们针对第一种情况设置低于4单位即为平直(别看很多,其实整个图拉的很大),遇到平直数值停止查找,定义其对应索引值为起点。
斜率符号改变视为掉头,针对第二种掉头情况。我们设置掉头次数不能超过三次(抵消干扰),并且遇到操作索引的前一个和后一个的差值不能小于3,否则立即停止查找,定义其对应索引值为起点。
在寻找起点的同时,定义起点索引前 2单位的数值为零电位(要取平均值的)。接着进入下一步,心电轴计算。
QRS电轴计算
电轴也称平均电轴,是心脏电活动的平均方向(向量),是心电图检测指标之一,指心脏除极和复极时额面最大综合向量与水平轴形成的角度。
我们已经记录了QRS波,想要让机器识别心电轴,就必须推导出计算方法。
想必学过心电图的医学生们一定对这张图非常熟悉,这张是使用I,III导联代数和进行计算的,但是,这种方法对于机器来说比较复杂,我们采取另一种方式,面积积分法。
这个方法因为人工计算麻烦而被抛弃,但是面积积分法是测量心电轴最标准的方法,也是机器计算最简单的方法。
在这幅图中,显示了振幅法和面积法的差距。
面积积分法是把I,III导联相对于等电位线正向和负向面积代数和做图在一个特殊坐标系上,其中-III与+I的夹角度数为60deg。
在计算时有四种情况:
SI <0 SIII<0 电轴不确定
SI <0 SIII>0 电轴左偏
SI >0 SIII<0 电轴右偏
SI >0 SIII>0 电轴不偏
我们选取有代表性的两种做出几何推理:
红线:SI SIII
黑线:反向延长线
绿线:相交点与电偶中心连线
定义点I为A;点III为B。
情况A:电轴不偏
延长P III 交直线I O 与点N
∵PB⊥ OB,∠NOB = ∠ = 60 deg
∴∠ONB = 30 deg
又∵∠NBO= 90 deg
∴NO = 2SIII
∴NA = 2SIII+SI
∵∠NAP = 90 deg,∠ONB = 30 deg
∴PA =
∵∠NAP = 90 deg
∴心电轴=
情况B:电轴左偏
延长BP交支线I与点N
∵∠BON = 60 deg,∠OBN = 90 deg
∴∠BNO = 30 deg
∴在△NBN中 ON = 2SIII
在△PAN中 PA =NA/√3
∵NA = ON - OA = 2SIII-SI
∴PA=
∴在Rt△PAO中,∠PAO = 90 deg
心电轴=
对于另外两种可情况,可以将两个面积和取相反数,根据对顶角相等的数学思维即可转化为以上两种情况,是不是很有趣呢。
所对应的计算是这样子的:
T波寻找与QTc计算
T波属于小波成分,斜率较小,我们使用新的带通滤波器(8-22.5Hz)提取出T波。
这样P,T的成分就明显了。
紧接着把索引内小于100与大于1800的索引编号去除,防止搜索时越界。
然后计算出最大的QT间期(QTc按550ms记,再高就不可能了,就会有尖端扭转室速)
然后跳过QRS波,并向后30s到最长QT间期寻找最大最小值,最大最小值对应T波终点(对于这个滤波器处理后的数据)
然后求平均值算出QT间期:
然后根据QTc矫正公式算出QTc,在用QT-QRS算出T波时限:
胸导联R/S波电压分析
胸导联R/S波电压RV5+SV1和RV1+SV5是心电图分析时重要的工具。可以分析左室和右室电压的大小,对心肌梗死,高血压心脏病,心室扩张,心室肥大,肺动脉高压的诊断有指导意义。
首先,根据QRS起点向左两个单位算出等电位求平均值
然后求与等电位相对值的最大最小值:
最后求电压值的平均值,然后取绝对值,将记录的值(单位)转化为电压值(mV):
结束解算代码。
四、软件说明
由于单片机片上资源极度有限,我们放弃了占用资源多的FreeRTOS与LVGL,使用裸机+LCD绘图库完成设计
软件部分主要说明什么呢?
已知,心电仪的应用设计,是两个App以及多个界面切换。
那要如何保证切换不出错?仪器按键不出错?
我使用了将App或界面返回值代入决策的方式实现切换。
这样,只需要在App函数返回一串特征代码,管理器即可切换到指定App或界面。
我们使用一个旋转开关来作为App切换,我们通过读取开关,累计标识的方式销毁当前App切换。
在读取到App不一致时开启累计标识,达到一定数值后销毁并切换,标识归零。若档位开关回到当前App,标识归零。
复杂的系统,机械按键是不允许出半点差错的,我们对于机械按键处理,使用中断调用管理器中的按键功能决策函数,根据App和页面标识决策功能。
当然在App/页面被调用时,需要设置标识:
这样,整个调度系统才能有条不紊的运作下去,长期保持稳定。而且,裸机相比于RTOS大幅提升了系统稳定性,保留更多资源。
五、成本说明
个人制作一个心电监护仪的总成本约3100元。
你觉得这个仪器做得怎么样呢?
参考开源资料:
https://oshwhub.com/lmppbba/ecg-monitoring-defibrillator-with-12-leads

加入微信
获取电子行业最新资讯
搜索微信公众号:EEPW
或用微信扫描左侧二维码