Knižnica Arduino na monitorovanie srdcového tepu pomocou pulzného oxymetra
Jeden z parametrov sledovaných v mojom projekte riadenia spánku
Je to pulz. zmerať to Vyvinul som prístroj založený na správaní sa hemoglobínu a oxyhemoglobínu voči rôznym vlnovým dĺžkam svetla. V podstate ide o meranie toho, koľko svetla určitého typu je schopné prejsť alebo sa odrážať v dobre zavlažovanej oblasti tela. Frekvencia, s ktorou nastáva úplný cyklus tohto javu, umožňuje meranie pulz.Vo fáze návrhu a testovania prístroj na meranie pulzu Vyvinul som niekoľko malých programov, ktoré mi pomôžu overiť, či je zostava správna. Najprv som napísal nižšie uvedený kód, ktorý z času na čas nabral namerané hodnoty (aspoň každý a maximálne každý ), keď sa menili minimálne medzi jednou a predchádzajúcou hodnotou (hodnota, ktorá zodpovedá ) a monitorované z počítača pomocou aplikácie Python aby ste ich mohli neskôr analyzovať.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#define PIN_OXIMETRO 0 // Pin analógico 0
#define MEDIDA_MINIMA 10 // Cambio menor monitorizado
#define TIEMPO_MAXIMO_MEDIDA 200 // Cambio menor entre medidas (determina la resolución vertical)
#define TIEMPO_MINIMO_MEDIDA 100 // Milisegundos entre medidas (determina la resolución horizontal)
int lectura_anterior_oximetro=0;
int lectura_oximetro;
unsigned long cronometro_minimo=0;
unsigned long cronometro_maximo=0;
void setup()
{
Serial.begin(9600);
//pinMode(PIN_OXIMETRO,INPUT); // Ya es entrada por defecto
}
void loop()
{
if(millis()>cronometro_minimo)
{
lectura_oximetro=analogRead(PIN_OXIMETRO);
if(abs(lectura_oximetro–lectura_anterior_oximetro)>MEDIDA_MINIMA||millis()>cronometro_maximo)
{
cronometro_minimo=millis()+TIEMPO_MINIMO_MEDIDA;
cronometro_maximo=millis()+TIEMPO_MAXIMO_MEDIDA;
lectura_anterior_oximetro=lectura_oximetro;
Serial.println(String(millis(),DEC)+“,”+String(lectura_oximetro,DEC));
}
}
}
|
Akonáhle boli hodnoty upravené (začínajúc veľmi hustými meraniami), dostal som zbierku hodnôt z pulzný oxymeter v priebehu času som mohol vytvoriť graf pomocou tabuľky, LibreOffice Calc de LibreOffice, konkrétne.
So zozbieranými údajmi, ako je znázornené na obrázku vyššie, bolo ďalšou operáciou určiť, či hustota hodnôt umožňuje vypočítať spoľahlivým, ale „ekonomickým“ spôsobom (bez odberu väčšieho množstva údajov, ako je potrebné), hodnotu pulz; Ako je možné vidieť v nižšie uvedenom grafe, zdalo sa, že prijaté opatrenia slúžili na získanie výsledkov, ktoré možno odôvodnene očakávať.
.
Ďalej, s informáciami zo vzorkovania dát, bolo potrebné vyvinúť algoritmus, ktorý by meral tepovú frekvenciu. Držíme sa grafu, ktorý pre jednoduchosť predpokladá, že predstavuje rozloženie podobné tomu QRS komplex, najjednoduchšie sa zdá byť meranie časov medzi najvýznamnejšími časťami, s vyššími hodnotami (čo zodpovedá qRs zóne depolarizácie komôr), vylúčením plochejšej a „hlučnejšej“ zóny, ktorá je preto náročnejšia. merať. Prijaté riešenie, ktoré zodpovedá nižšie uvedenému testovaciemu kódu, funguje podľa nasledujúceho postupu:
-
V každom prípade zistite meranú oblasť, aby ste sa venovali iba vrcholom hodnôt qR a zahodiť údolie. Na tento účel by sa mohli merať hodnoty vyššie ako určitá konštanta, ale existuje riziko, že jednotlivec a/alebo okolnosti môžu, hoci úmerne, hodnoty zvýšiť alebo znížiť. Aby sa tomu zabránilo, hodnota v oblasti sa považuje za väčšiu ako tá, ktorá presahuje priemernú hodnotu určitým koeficientom. Meranie je tak citlivo samokalibrované a dalo by sa ešte upraviť doladením koeficientu, ktorý som v mojom prípade experimentálne počas testov dosiahol.
Vyberte hodnoty zostupnej zóny pre meranie (Rs) z vrcholu qR, čo najbližšie k maximu krivky. Aby ste vedeli, že stúpajúca zóna je opustená, stačí overiť, že nová hodnota je menšia ako predchádzajúca a overiť, že hľadaná hodnota ešte nebola nájdená, pretože vo všeobecnosti existuje niekoľko hodnôt v zostupnej zóna qR v závislosti od hustoty odberu vzoriek. Na načasovanie impulzu sa uloží hodnota okamihu, v ktorom bol bod nájdený (milisekundy vrátené o millis ()) a porovná ho s nasledujúcim.
Aby sa zabezpečilo, že nameraná hodnota je najväčšia v zostupnej zóne najvyššej krivky, používa sa premenná boolovská hodnota ( v tomto príklade a v knižnici), ktorá sa aktivuje pri vstupe do vzostupnej zóny hlavnej krivky a deaktivuje sa, keď sa nájde prvá zostupná hodnota, ktorá je časovaná.
Keďže je obvyklé reprezentovať trvanie impulzu ako údery za minútu (ppm), hodnota času medzi získanými impulzmi sa koriguje výpočtom vydelením celkového času zobrazenia (jedna minúta, 60000 XNUMX milisekúnd) intervalom získaným odpočítaním aktuálnych milisekúnd (aktuálnej hodnoty) od tých, ktoré boli predtým načasované.
Aby sa predišlo falošným meraniam (ako napríklad prístroj meria vo vákuu), overuje sa, že výsledok je medzi maximálnymi a minimálnymi hodnotami, než sa to považuje za samozrejmosť. Aj keď sa za priemer považuje, že normálna hodnota pre zdravého dospelého v pokoji je medzi 60 a 100 ppm, nižšie sú prípustné hodnoty, u športovca v pokoji je ľahké nájsť 40 ppm, až do 200 ppm počas intenzívne cvičenie a viac 100 ppm u dospelých so sedavým zamestnaním v stavoch vzrušenia, presne zaujímavý faktor pre projekt manažmentu spánku čo ma vedie k tomu, aby som to rozvinul prístroj na meranie pulzu. Z tohto dôvodu je vhodné tieto hodnoty veľmi uvoľniť, aby sa nestratili extrémy, ktoré by mohli presne ukázať relevantné aspekty.
Nová priemerná hodnota sa vypočíta znížením relevantnosti aktuálneho priemeru na základe počtu vzorkovaných hodnôt a pripočíta sa posledná hodnota, tiež vážená koeficientom, ktorý ju ďalej znižuje, čím viac hodnôt bolo doteraz nameraných .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#define PIN_OXIMETRO 0 // Pin analógico 0
#define TIEMPO_MINIMO_MEDIDA 20 // Milisegundos entre medidas (determina la resolución horizontal)
#define COEFICIENTE_PULSO 1.25 // Coeficiente que determina la zona de valores en la que medir el pulso
#define PULSO_MENOR 30 // Ignorar valores menores (Es infrecuente un pulso más lento aún en reposo)
#define PULSO_MAYOR 180 // Ignorar valores mayores (Es infrecuente un pulso mayor en reposo)
#define MINUTO 60000.0 // Milisegundos en un minuto
float velocidad_pulso;
float lectura_media_oximetro=511.5;
unsigned long valores_contados=0;
int lectura_anterior_oximetro=0;
int lectura_oximetro;
boolean medir_pulso=false;
unsigned long cronometro_pulso=0;
unsigned long cronometro_oximetro=0;
void setup()
{
Serial.begin(9600);
//pinMode(PIN_OXIMETRO,INPUT); // Ya es entrada por defecto
}
void loop()
{
if(millis()>cronometro_oximetro)
{
cronometro_oximetro=millis()+TIEMPO_MINIMO_MEDIDA;
lectura_oximetro=analogRead(PIN_OXIMETRO);
valores_contados++;
lectura_media_oximetro=lectura_media_oximetro*(float(valores_contados–1)/valores_contados);
lectura_media_oximetro+=lectura_oximetro*(1.0/valores_contados);
if(lectura_oximetro>lectura_media_oximetro*COEFICIENTE_PULSO)
{
if(lectura_anterior_oximetro<lectura_oximetro)
{
medir_pulso=true;
}
else
{
if(medir_pulso)
{
velocidad_pulso=MINUTO/float(millis()–cronometro_pulso);
medir_pulso=false;
if(velocidad_pulso>PULSO_MENOR&&velocidad_pulso<PULSO_MAYOR)
{
Serial.println(“Pulso “+String(velocidad_pulso,DEC));
}
cronometro_pulso=millis();
}
}
}
lectura_anterior_oximetro=lectura_oximetro;
}
}
|
Nakoniec som pomocou vyššie opísaného algoritmu vyvinul knižnicu na výpočet pulzu detekciou prítomnosti hemoglobín o la oxyhemoglobínu (v závislosti od použitej vlnovej dĺžky svetla) z nižšie uvedeného kódu.
Knižnica očakáva, že funkcia vzorkovania sa bude volať pravidelne pulz v mojom projekte riadenia spánku
. V každom prípade sa to podľa testov, ktoré som urobil, nezdá byť potrebné; buď zariadením alebo správaním na výpočet pulzu, ktorý je možné konzultovať s funkciou alebo s funkciou priemerný pulz. Okrem toho, že ide o obmedzený zdroj, vylúčil som používanie prerušení, pretože som nepotreboval okamžité hodnoty, ale skôr trvalé hodnoty v priebehu času na monitorovanie pulz, vzorkovanie pri určitej frekvencii ponúka dostatok informácií a ich zvýšením sa nezíska oveľa viac (relevantných), ani ich nie je možné výrazne znížiť bez straty relevantných údajov pre výpočet; v skorých verziách kódu na monitorovanie čítania pulzný oxymeter Zistil som, že nie je potrebné držať sa maximálneho času merania, pretože ak sa správne zohľadnili variácie po sebe nasledujúcich hodnôt, bol veľmi blízko minima.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
//pulso.h
#if defined(ARDUINO) && ARDUINO>=100
#include “Arduino.h”
#else
#include “WProgram.h”
#endif
#define PULSO_MINIMO 5 // Pulso mínimo. Un pulso menor se considera un error.
#define PULSO_MAXIMO 200 // Pulso máximo. Un pulso mayor se considera un error.
#define COEFICIENTE_PULSO 1.25 // Coeficiente que multiplica el pulso medio para determinar si el valor medido está en la zona que se cronometra. Puede usarse para calibra el dispositivo
#define OXIMETRIA_MEDIA 511.5 // Medida media inicial del pulso (1023/2) Puede usarse para calibrar el dispositivo
#define PULSO_MEDIO 80.0 // Pulso medio de un adulto en reposo (sólo para referencia)
class Pulso
{
private:
byte pin_oximetro; // Pin analógico al que se conecta el sensor de pulso
int lectura_oximetro; // Último valor medido en el sensor de pulso
int lectura_anterior_oximetro; // Penúltimo valor medido en el sensor de pulso para compararlo con el último y establecer la zona de valores en la que se encuentra
unsigned long numero_lecturas_oximetro; // Número de medidas del oxímetro tomadas. Usado para calcular la media
unsigned long numero_lecturas_pulso; // Número de medidas de pulso tomadas. Usado para calcular la media
float lectura_media_oximetro; // Medida media del sensor de pulso. Usado para saber si un valor se encuentra en la zona de medida (que se cronometra) del pulso
boolean medicion_de_pulso_activa; // Verdadero si se ha entrado de la zona del pulso (para no confundir si aún se encuentra en una zona ya medida)
float velocidad_pulso; // Último valor de pulso calculado
float ultima_velocidad_pulso; // Último valor de pulso correcto
float velocidad_media_pulso; // Media de los pulsos calculados durante la última sesión (creación del objeto Pulso)
unsigned long cronometro_pulso; // Tiempo entre medidas de pulso consecutivas
protected:
public:
Pulso(byte pin_oximetro_solicitado);
~Pulso();
void monitorizar_pulso(); // Se llama periódicamente (entre 10 y 50 ms) para calcular el pulso
byte ultimo_pulso(); // Devuelve el último valor correcto de pulso muestreado
byte pulso_medio(); // Devuelve el pulso medio de la sesión
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
//pulso.cpp
#include “pulso.h”
Pulso::Pulso(byte pin_oximetro_solicitado)
{
pin_oximetro=pin_oximetro_solicitado;
//pinMode(pin_oximetro,INPUT); // Ya es entrada por defecto
numero_lecturas_oximetro=0;
numero_lecturas_pulso=0;
lectura_media_oximetro=OXIMETRIA_MEDIA;
velocidad_media_pulso=PULSO_MEDIO;
medicion_de_pulso_activa=false;
lectura_anterior_oximetro=0;
ultima_velocidad_pulso=0; // Para indicar que es un valor incorrecto por el momento
cronometro_pulso=0;
}
Pulso::~Pulso()
{
}
byte Pulso::ultimo_pulso()
{
return round(ultima_velocidad_pulso);
}
byte Pulso::pulso_medio()
{
return round(velocidad_media_pulso);
}
void Pulso::monitorizar_pulso()
{
lectura_oximetro=analogRead(pin_oximetro);
numero_lecturas_oximetro++;
lectura_media_oximetro=lectura_media_oximetro*(float(numero_lecturas_oximetro–1)/numero_lecturas_oximetro); // Cambiar la representatividad de la parte actual de la media de pulso
lectura_media_oximetro+=lectura_oximetro*(1.0/numero_lecturas_oximetro); // Añadir la nueva lectura a la media
if(lectura_oximetro>lectura_media_oximetro*COEFICIENTE_PULSO)
{
if(lectura_anterior_oximetro<lectura_oximetro) // Medida de valores creciente
{
medicion_de_pulso_activa=true;
}
else
{
if(medicion_de_pulso_activa)
{
velocidad_pulso=60000.0/float(millis()–cronometro_pulso); // Cálculo de las pulsaciones por minuto (representación habitual del pulso)
medicion_de_pulso_activa=false; // Ya se ha medido el pulso, no medir hasta entrar en una nueva zona ascendente
if(velocidad_pulso>PULSO_MINIMO&&velocidad_pulso<PULSO_MAXIMO)
{
numero_lecturas_pulso++;
ultima_velocidad_pulso=velocidad_pulso;
velocidad_media_pulso=velocidad_media_pulso*(float(numero_lecturas_pulso–1)/numero_lecturas_pulso); // Calcular la representatividad de la media actual en la nueva media
velocidad_media_pulso+=velocidad_pulso*(1.0/numero_lecturas_pulso); // Añadir la nueva lectura a la media
}
cronometro_pulso=millis();
}
}
}
lectura_anterior_oximetro=lectura_oximetro;
}
|
Nasledujúci príklad programu ukazuje, ako použiť predchádzajúcu knižnicu na meranie pulz s a pulzný oxymeter. Okrem inštancie triedy sledovanie úrovne oxyhemoglobínu/hemoglobín as menšou periodicitou hodnota pulz vypočítané a priemerné.
Aby sa zabezpečilo, že merania sú relevantné, pred zobrazením akejkoľvek hodnoty sa naprogramuje čakanie. Keďže hodnota môže byť nesprávna (napríklad ak používateľ odstráni zariadenie), hodnoty sa zobrazia iba vtedy, ak sú v rozsahu hodnôt, ktoré sa považujú za platné.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#define PIN_PULSOMETRO 0
#define TIEMPO_MONITORIZACION_PULSO 20
#define TIEMPO_PRESENTACION_PULSO 5000
#include “pulso.h”
Pulso pulso(PIN_PULSOMETRO);
unsigned long cronometro_monitorizacion_pulso;
unsigned long cronometro_presentacion_pulso;
byte velocidad_pulso;
byte velocidad_media_pulso;
void setup()
{
Serial.begin(9600);
cronometro_monitorizacion_pulso=0;
cronometro_presentacion_pulso=TIEMPO_PRESENTACION_PULSO*2;
}
void loop()
{
if(millis()>cronometro_monitorizacion_pulso)
{
cronometro_monitorizacion_pulso=millis()+TIEMPO_MONITORIZACION_PULSO;
pulso.monitorizar_pulso();
}
if(millis()>cronometro_presentacion_pulso)
{
cronometro_presentacion_pulso=millis()+TIEMPO_PRESENTACION_PULSO;
velocidad_pulso=pulso.ultimo_pulso();
velocidad_media_pulso=pulso.pulso_medio();
if(velocidad_pulso)
{
Serial.print(“Pulso “);
Serial.print(velocidad_pulso,DEC);
Serial.print(” | Pulso medio “);
Serial.println(velocidad_media_pulso,DEC);
}
}
}
|
Pridať komentár