Arduino knjižnica za spremljanje srčnega utripa s pulznim oksimetrom
Eden od parametrov, ki se spremlja v mojem projektu upravljanja spanja
To je utrip. da ga izmerite Razvil sem napravo, ki temelji na obnašanju hemoglobina in oksihemoglobina glede na različne valovne dolžine svetlobe.. V bistvu gre za merjenje, koliko svetlobe določene vrste lahko preide ali se odbije v dobro namakanem predelu telesa. Pogostost, s katero se zgodi celoten cikel tega pojava, omogoča merjenje pulz.V fazi načrtovanja in testiranja naprava za merjenje pulza Razvil sem nekaj majhnih programov, ki so mi pomagali preveriti, ali je sestavljanje pravilno. Najprej sem napisal spodnjo kodo, ki je občasno prevzela izmerjene vrednosti (vsaj vsak in največ vsak ), ko so spreminjali minimum med eno in prejšnjo (vrednost, ki ustreza ) in spremljati iz računalnika z aplikacijo Python da jih lahko kasneje analiziramo.
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));
}
}
}
|
Ko so bile vrednosti prilagojene (začenši z zelo gostimi meritvami), sem dobil zbirko vrednosti iz pulzni oksimeter sčasoma, da sem lahko naredil graf s preglednico, LibreOffice Calc de LibreOffice, specifično.
Z zbranimi podatki, kot je prikazano na zgornji sliki, je bila naslednja operacija ugotoviti, ali gostota vrednosti omogoča izračun na zanesljiv, a "ekonomičen" način (brez vzorčenja več kot potrebnih podatkov) vrednosti pulz; Kot je razvidno iz spodnjega grafa, se je zdelo, da so sprejeti ukrepi služili doseganju razumno pričakovanih rezultatov.
.
Nato je bilo treba z informacijami iz vzorčenja podatkov razviti algoritem, ki bi meril frekvenco pulza. Držimo se grafa, da zaradi enostavnosti predpostavljamo, da predstavlja postavitev, podobno kompleks QRS, se zdi najpreprostejša stvar meriti čase med najvidnejšimi deli, z višjimi vrednostmi (kar ustreza coni qRs depolarizacije prekatov), pri čemer zavržemo bolj ravno in "hrupno" cono, ki je zato težje meriti. Sprejeta rešitev, ki ustreza spodnji testni kodi, deluje po naslednjem postopku:
-
V vsakem primeru zaznajte območje, ki se meri, da se osredotočite samo na vrhove vrednosti qRs in zavrzite dolino. Da bi to naredili, je mogoče izmeriti vrednosti, ki so višje od določene konstante, vendar obstaja tveganje, da bi lahko posameznik in/ali okoliščine, čeprav sorazmerno, zvišale ali znižale vrednosti. Da bi se temu izognili, velja, da je vrednost v območju večja od tiste, ki za določen koeficient presega povprečno vrednost. Na ta način je meritev občutljivo samokalibrirana in jo je mogoče še dodatno prilagajati s fino nastavitvijo koeficienta, kar sem v mojem primeru med preizkusi dosegel eksperimentalno.
Izberite vrednosti padajoče cone za meritev (Rs) vrha qRs, čim bližje maksimumu krivulje. Če želite vedeti, da je naraščajoče območje opuščeno, je dovolj, da preverite, ali je nova vrednost manjša od prejšnje, in preverite, ali iskana vrednost še ni bila najdena, saj je na splošno več vrednosti v padajočem območje od qRs odvisno od gostote vzorčenja. Za merjenje impulza se shrani vrednost trenutka, ko je bila točka najdena (milisekunde, ki jih vrne milis ()) in ga primerja z naslednjim.
Za zagotovitev, da je izmerjena vrednost največja v padajočem območju najvišje krivulje, se uporabi spremenljivka logično ( v tem primeru in v knjižnici), ki se aktivira ob vstopu v naraščajoče območje glavne krivulje in deaktivira, ko je najdena prva padajoča vrednost, ki je časovno merjena.
Ker je običajno, da se trajanje utripa predstavi kot utripi na minuto (ppm), se vrednost časa med dobljenimi utripi popravi z izračunom tako, da se skupni čas utripa (ena minuta, 60000 milisekund) deli z intervalom, dobljenim z odštevanje trenutnih milisekund (od trenutne vrednosti) med predhodno merjenimi.
Da bi se izognili napačnim meritvam (kot je na primer naprava, ki meri v vakuumu), je treba preveriti, ali je rezultat med najvišjo in najmanjšo vrednostjo, preden ga vzamemo za samoumevnega. Čeprav se kot povprečje šteje, da je normalna vrednost za zdravo odraslo osebo v mirovanju med 60 in 100 ppm, so spodaj navedene dovoljene vrednosti, pri športniku v mirovanju zlahka najdemo 40 ppm, med mirovanjem pa do 200 ppm. intenzivna vadba in več 100 ppm pri sedečih odraslih v stanjih vznemirjenosti, prav zanimiv dejavnik za projekt upravljanja spanja kar me vodi k razvoju tega naprava za merjenje pulza. Zaradi tega je priporočljivo, da se te vrednosti precej sprostijo, da se ne izgubijo skrajnosti, ki bi lahko natančno prikazale pomembne vidike.
Nova povprečna vrednost se izračuna tako, da se zmanjša relevantnost trenutnega povprečja na podlagi števila vzorčenih vrednosti in se doda zadnja vrednost, prav tako ponderirana s koeficientom, ki jo zmanjša, čim več vrednosti je bilo izmerjenih do zdaj .
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;
}
}
|
Končno sem z uporabo prej opisanega algoritma razvil knjižnico za izračun utripa z zaznavanjem prisotnosti hemoglobin ali oksihemoglobin (odvisno od uporabljene valovne dolžine svetlobe) iz spodnje kode.
Knjižnica pričakuje, da bo funkcija vzorčenja občasno klicana pulz v mojem projektu upravljanja spanja
. V vsakem primeru se glede na teste, ki sem jih opravil, ne zdi potrebno; bodisi z napravo ali z vedenjem za izračun utripa, ki ga je mogoče pregledati s funkcijo ali s funkcijo povprečni utrip. Poleg tega, da gre za omejen vir, sem izključil uporabo prekinitev, ker nisem potreboval takojšnjih vrednosti, temveč dolgotrajne vrednosti za spremljanje pulz, vzorčenje pri določeni frekvenci ponuja dovolj informacij in s povečanjem le-te ne dobimo veliko več (relevantnega), niti je ni mogoče veliko zmanjšati, ne da bi izgubili relevantne podatke za izračun; v zgodnjih različicah kode za spremljanje branja pulzni oksimeter Ugotovil sem, da se ni treba držati maksimalnega časa merjenja, saj je bil, če so bile variacije zaporednih vrednosti pravilno upoštevane, zelo blizu minimuma.
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;
}
|
Naslednji primer programa prikazuje, kako uporabiti prejšnjo knjižnico za merjenje pulz s pulzni oksimeter. Poleg instanciranja razreda spremljanje ravni oksihemoglobin/hemoglobin in z manjšo periodičnostjo vrednosti pulz izračunano in povprečno.
Da bi zagotovili ustreznost meritev, je pred prikazom katere koli vrednosti programirano čakanje. Ker je vrednost lahko napačna (na primer, če uporabnik odstrani napravo), so vrednosti prikazane le, če so v območju veljavnih.
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);
}
}
}
|
po Komentar