真正自制的血氧仪传感器
组件和用品
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
必要的工具和机器
|
应用和在线服务
|
关于这个项目
在这段隔离期间,我制作了一个血氧计,里面已经有了零件。毕竟血氧计只是由两个 LED 和一个光电二极管制成的。
我不是医学知识专家,在项目的这个阶段,我不确定这项工作是否具有诊断价值,但它是一个很好的教育项目,可以研究它的工作原理,并且可能有一些提示可以成为自制的医疗工具。
氧饱和度和 COVID-19
在我们生命中这个令人难以置信的时期,我们学到了很多关于病毒、肺、外科口罩、肥皂和洗手的知识。每个人都会阅读有关咳嗽、发烧和呼吸困难等症状的信息。我们还知道,测量呼吸困难的一种方法是读取血液中的氧气含量。
该测量值可以通过名为 Oximeter 的医疗设备间接读取 .您可能已经看过它了,它是一种非侵入式设备,它被放置在手指上,并带有一些脉动光来完成工作。像这样:
通常,当您没事时,您的氧饱和度 (SpO2) 百分比接近或大于 95%。当饱和度下降到 90% 以下并且你咳嗽和发烧时,这是一个问题。
如果任何制造商都可以制造血氧计,那么发现感染会更容易,并且可以帮助人们在问题确实存在时决定去医院,而不是因为惊恐发作。
首先,了解心跳传感器的工作原理
我已经开始使用 KY-039 心跳传感器开始这个项目,我在我们许多人家里都有的套件传感器中找到了该传感器。正如您在下面的电路中看到的那样,它只是一个点亮光电二极管的红外 LED。还有两个电阻保护led,读取传感器的小信号。
因此,如果您没有 KY-039 传感器,您可以使用很少的组件构建自己的传感器。
手指放在传感器和光电二极管之间,如这张照片(最初取自本网站并修改):
红外线 LED 发出的光部分被指甲、皮肤和手指的所有其他部分吸收,但它不是恒定的,因为它会随着静脉中血液的变化而变化。当您的心脏跳动时,血液会被推入您的静脉,并且光吸收会发生变化。我们可以测量到达光电二极管的红外光照射光电二极管产生的电流。
KY-039 传感器具有 S (信号)引脚以读取该变化的值。
我们可以通过计算信号的峰值来测量心跳率
从传感器的可变信号中读取值并不是那么容易,因为有很多噪声,信号非常低,我们需要进行一些数学运算才能找到要绘制的好值。
我要感谢 Johan Ha 的这篇有用的帖子,它解释了如何计算信号的平均值,还解释了如何消除家用灯发出的噪音(那个灯是噪音!)。
诀窍是创建一个数组,在其中我们推送一个值并删除一个值,以求从传感器读取的最后 X 值的平均值。他还描述了一种通过计算 N 个增长值来找到信号上升的方法。我的意思是,当一个值大于前一个值 N 倍时,它是一个峰值。
使用 Arduino 串行绘图工具或其他串行工具来分析打印在 COM 端口(例如 SerialPlot)上的值,并尝试不同的值,我们可以定义正确的数字 N(rise_threshold
常数 n 代码)。如果您定义的数字太大或太小,您可能会错过一些节拍或计算重音缺口 作为一个节拍。
一旦您了解了如何调整峰值,只需计算它们,或计算一小部分节拍之间的时间即可确定您的 BPM
速率(每分钟心跳次数 ).
构建血氧计(破解 KY-039 传感器)以找到氧饱和度
随着光波长的变化,我们的血液以不同的方式吸收光。红光(~600nm
) 被含有更多氧气的血液更好地吸收,因此我们可以比较红外 LED (~950nm
) 用红色 LED 制成的,并找出我们血液中氧气的百分比。该值称为 Sp02%
(外周毛细血管血氧饱和度 ).
因为我有一个 KY-039 传感器,所以我决定修改它。它只有一个红外线 LED,所以我添加了一个 RED led,断开IR 从 Vcc 引出 并用 330 ohm
连接 电阻两个导致Arduino的两个不同的引脚。
(如果您没有要修改的 KY-039 传感器,您可以构建它,它只是几个 LED、一个光电二极管和 3 个电阻器,原理图非常简单!)
这是修改后的传感器的示意图:
这样我们就可以打开IR 引导并读取来自 KY-039 S 的值 pin,然后我们可以关闭IR led 并打开 RED led,并从 KY-039 S 读取值 针。
这是我的:
如果绘制这两个信号,您可以看到 IR 值始终低于红色值。
要找到一个好的信号,记得把指尖正确地放在光电二极管上,LED 应该接触到指甲,当你找到一个舒适的位置并且在绘图上读数良好时不要改变它。
由于信号很低且噪声很成问题,为了获得有用的测量值,我注意到始终需要良好的环境光。所以,测量时不要移动手指,也不要改变光线,传感器上的阴影可能会改变一切。
如何测量饱和度 SpO2%
氧饱和度 (SpO2) 是氧饱和血红蛋白相对于总血红蛋白的分数,是一个名为 R 的参数的函数 (我在米兰理工大学的一篇学术论文中找到了这个信息),它是使用两个信号的最小值和最大值计算出来的:
R =( (REDmax-REDmin) / REDmin ) / ((IRmax-IRmin) / IRmin)
每个乐器都有它自己的 R 需要校准才能找到连接 R 和 SpO2% 的曲线(函数)。
我们已经计算了峰值的数量,但现在我们需要找到 max 和分钟 两条曲线的值 (RED 领导和红外 领导)。
为了完成这项工作,我们评估心跳的“周期”(即一个节拍持续多少毫秒)并将其除以采样率以确定一个周期有多少样本。在我们的例子中,采样率为 40 毫秒,因为我们读取了 20 毫秒的 IR LED,然后又读取了 20 毫秒的 RED LED。
节拍周期是信号中两条上升曲线之间经过的时间。
所以我可以分析最后一个L 样本(其中 L =period / 40),我保存在一个数组中,以找到 REDmax , REDmin , IRmax 和 IRmin 价值。
使用最大值和最小值,我可以计算 R .
R、L和周期是每拍计算一次,所以R的计算也是每拍计算一次。
从 R 到 SpO2%:如何校准血氧计?
链接 R 的函数 SpO2 可以用直线简化:
SpO2 =K * R + M
所以我们需要两个点(SpO2 和 R 的两对值)来确定 K 和 M。找到这两个点的唯一方法是使用另一个血氧计并从其显示屏上读取值。
新的血氧仪将作为参考,我们在测量自制血氧仪的 R 值的同时读取 SpO2 值。
正常第一次呼吸,读取SpO2和R值,写下来。
然后尝试保持呼吸,10-20 秒后,您将看到新血氧计中的 SpO2 读数下降,您还应该看到血氧计的 R 参数增加。在晕倒之前,写下达到的 SpO2 值和你的 R 参数值。
求解二阶方程并找到血氧计的 K 和 M。
现在可以同时计算 bpm 和 SpO2 R 的每个度量值 .
我还添加了一个显示来显示所有数字,仅当我发现至少 5 个周期测量值没有太大变化时才显示值 (±10%
期间长度)。通过这种方式,我删除了变化太大的值,这些值取决于不良组件或环境光或手指移动的变化。
c value 表示显示的值是用 c 计算的 稳定措施。
项目改进:去除环境光可变性
在玩了几天我的项目后,我找到了改进它的方法。
我注意到使用这些低成本组件(我们只使用 LED 和一个光电二极管!),这些措施对环境光的依赖性太大了,如果我们想在真实环境中正确读取数据,这不是一件好事工作环境。由于我注意到在晴天比在阴天或晚上使用电灯时效果更好,因此我决定添加第三个 LED,该 LED 始终亮起并且只为手指提供光线.
使用这种 3-LED 传感器,还可以在黑布下采取措施以排除可能始终变化的环境光。
现在,效果更好,不再依赖于环境光。
我还必须重新校准血氧计,正如您在几秒钟后从视频中看到的那样,它正确地找到了 bpm ans SpO2% :
代码
- oximeter-diy-ver-0.92.ino
oximeter-diy-ver-0.92.inoArduino
这是血氧计 DIY 的源代码,它由制造商在家中可以拥有的少数组件制成。/* * 血氧计 diy。 v.0.92(小修复)* 通过破解 ky-039 心跳传感器或使用红外 LED * 红色 LED 和光电二极管。 * https://hackaday.io/project/170752-oximeter-do-it-yourself */#include#include #define maxperiod_siz 80 // 一个时期内的最大样本数#定义测量 10 // 存储的周期数#define samp_siz 4 // 平均值的样本数#definerise_threshold 3 // 确定峰值的上升测量数 // 液晶显示 BPM LiquidCrystal_I2C lcd(0x3F, 16, 2);int T =20; // 从 sensorint sensorPin =A1 读取值的槽毫秒数; int REDLed =3;int IRLed =4;byte sym[3][8] ={ B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000},{ B00000, B0000010 B000010 , B10000, B11100},{ B00000, B00100, B01010, B00010, B00100, B00100, B00000, B00100}};void setup() { Serial.begin(9600);串行.flush(); pinMode(sensorPin,INPUT); pinMode(REDLed,OUTPUT); pinMode(IRLed,输出); // 初始化 LCD lcd.init();液晶背光(); // 关闭 LED digitalWrite(REDLed,LOW);数字写入(IRLed,低); for(int i=0;i<8;i++) lcd.createChar(i, sym[i]);}void loop(){ boolfinger_status =true; float readIR[samp_siz], sumIR,lastIR, reader, start;浮点读数RED[samp_siz], sumRED,lastRED; int 期间,样本;周期=0;样本=0; int 样本计数器 =0;浮动读取IRMM[maxperiod_siz],读取REDMM[maxperiod_siz]; int ptrMM =0; for (int i =0; i =samples){ samplesCounter =0; IRmax =0; IRmin=1023; REDmax =0; REDmin=1023; for(int i=0;i IRmax) IRmax =readIRMM[i]; if(readsIRMM[i]>0&&readsIRMM[i] REDmax) REDmax =readREDMM[i]; if(readsREDMM[i]>0&&readsREDMM[i] beforeIR) {rise_count++; // 计算上升的样本数量 if (!rising &&rise_count>rise_threshold) { lcd.setCursor(3,0);液晶显示(0); // <3 // 好的,我们检测到了一条上升曲线,这意味着心跳。 // 记录自上次节拍以来的时间,跟踪前 10 个 // 峰值以获得平均值。 // 上升标志阻止我们检测到相同的上升 // 不止一次。上升 =真;措施R[m] =R; measurePeriods[m] =millis() - last_beat; last_beat =毫秒();整数周期 =0; for(int i =0; i measurePeriods[i-1] / 1.1) ) { c++; avPeriod +=measurePeriods[i]; avR +=measuresR[i];米++; m %=测量值; lcd.setCursor(12,0); lcd.print(String(c)+" "); // 显示的 bpm 和 R 计算为 // 至少 5 个良好峰值的平均值 avBPM =60000 / ( avPeriod / c); avR =avR / c; // 如果最后有 5 个度量 lcd.setCursor(12,1); if(c==0) lcd.print(" ");否则 lcd.print(String(avR) + " "); // 如果至少有 5 个好的度量... if(c> 4) { // // 饱和度是 R 的函数(校准) // Y =k*x + m // k 和 m 的计算公式为另一个血氧计 int SpO2 =-19 * R + 112; lcd.setCursor(4,0); if(avBPM> 40 &&avBPM <220) lcd.print(String(avBPM)+" "); //else lcd.print("---"); lcd.setCursor(4,1); if(SpO2> 70 &&SpO2 <150) lcd.print(" " + String(SpO2) +"% "); //else lcd.print("--%"); } else { if(c <3) { // 如果少于 2 个度量添加 ? lcd.setCursor(3,0);液晶显示(2); //每分钟? lcd.setCursor(4,1);液晶显示(2); //血氧饱和度? } } } } else { // 好的,曲线正在下降上升 =false;上升计数=0; lcd.setCursor(3,0);lcd.print(""); } // 将其与新值进行比较,并在IR =lastIR 之前找到峰值; } // 手指在里面 // 绘制所有内容 Serial.print(lastIR); Serial.print(","); Serial.print(lastRED); /* * Serial.print(","); Serial.print(R); Serial.print(","); Serial.print(IRmax); Serial.print(","); Serial.print(IRmin); Serial.print(","); Serial.print(REDmax); Serial.print(","); Serial.print(REDmin); Serial.print(",");串行打印(avR); Serial.print(","); Serial.print(avBPM); */ Serial.println(); // 处理数组 ptr++; ptr %=samp_siz; } // 循环 while 1}
示意图
在我的项目中,我修改了 KY-039,但该传感器在 Fritzing 库中不可用,所以我用少数几个组件构建它,但我没有在 Fritzing 库中找到合适的光电二极管。 oximeter-diy_oW9ZI5zQtJ.fzz 一个简单的血氧计来读取血液中的氧气,可以通过破解 KY-039 传感器,或者从头开始构建一个传感器,制造工艺