Arduino-bibliotek for pulsovervåking med pulsoksymeter
En av parameterne som overvåkes i mitt søvnhåndteringsprosjekt
Det er pulsen. å måle det Jeg utviklet en enhet basert på oppførselen til hemoglobin og oksyhemoglobin mot forskjellige bølgelengder av lys. I utgangspunktet handler det om å måle hvor mye lys av en bestemt type som kan passere gjennom eller reflekteres i et godt vannet område av kroppen. Frekvensen som en fullstendig syklus av dette fenomenet oppstår med gjør det mulig å måle puls.I design- og testfasen av pulsmåler Jeg utviklet noen små programmer for å hjelpe meg å bekrefte at sammenstillingen var riktig. Først skrev jeg koden nedenfor, som tok de målte verdiene fra tid til annen (minst hver og høyst hver ) når de varierte et minimum mellom en og den forrige (verdien som tilsvarer ) og overvåkes fra en datamaskin med en Python-applikasjon for å kunne analysere dem senere.
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));
}
}
}
|
Når verdiene ble justert (begynner med svært tette målinger) fikk jeg en samling av verdier fra pulsoksymeter over tid at jeg kunne tegne grafer ved hjelp av et regneark, LibreOffice Calc de LibreOffice, spesifikk.
Med dataene som ble samlet inn, som representert i bildet ovenfor, var neste operasjon å bestemme om tettheten av verdier var tillatt å beregne på en pålitelig, men "økonomisk" måte (ikke prøvetaking mer enn de nødvendige dataene) verdien av puls; Som det fremgår av grafen under, så virket tiltakene som ble tatt ut til å tjene til å oppnå de resultatene som er rimelig å forvente.
.
Deretter, med informasjonen fra datasamplingen, var det nødvendig å utvikle en algoritme som ville måle pulsfrekvensen. Holder oss til grafen som for enkelhets skyld antas at den representerer en layout som ligner på QRS-kompleks, det enkleste ser ut til å være å måle tiden mellom de mest fremtredende delene, med høyere verdier (som tilsvarer qRs-sonen for depolarisering av ventriklene) og forkaste den flatere og "støyere" sonen, som derfor er vanskeligere å måle. Den vedtatte løsningen, som tilsvarer testkoden nedenfor, fungerer i henhold til følgende prosedyre:
-
Oppdag området som måles i hvert enkelt tilfelle for kun å ivareta verditoppene qRs og kast bort dalen. For å gjøre dette kan verdier som er høyere enn en viss konstant måles, men det er en risiko for at et individ og/eller omstendigheter kan, selv om det er proporsjonalt, heve eller senke verdiene. For å unngå dette regnes en verdi i området som større enn den som overstiger gjennomsnittsverdien med en viss koeffisient. På denne måten er målingen sensitivt selvkalibrert og kunne justeres ytterligere ved å finjustere koeffisienten, som jeg i mitt tilfelle har oppnådd eksperimentelt under testene.
Velg verdiene for den synkende sonen for målingen (Rs) av toppen qRs, så nært som mulig til maksimum av kurven. For å vite at den stigende sonen blir forlatt, er det nok å bekrefte at en ny verdi er mindre enn den forrige og bekrefte at den søkte verdien ennå ikke er funnet siden det generelt er flere verdier i synkende sone av qRs avhengig av prøvetakingstettheten. For å tidfeste pulsen lagres verdien for øyeblikket punktet ble funnet (millisekundene returnert av millis ()) og sammenligner den med den neste.
For å sikre at den målte verdien er størst i den synkende sonen av den høyeste kurven, brukes en variabel boolsk ( i dette eksemplet og i biblioteket) som aktiveres når du går inn i den stigende sonen til hovedkurven og deaktiveres når den første synkende verdien er funnet, som er den tidsbestemte.
Ettersom det er vanlig å representere pulsens varighet som slag per minutt (ppm), blir verdien av tiden mellom oppnådde pulser korrigert ved å beregne ved å dele den totale tiden for representasjonen (ett minutt, 60000 XNUMX millisekunder) med intervallet oppnådd av subtrahere gjeldende millisekunder (av gjeldende verdi) blant de tidligere tidsbestemte.
For å unngå falske målinger (som for eksempel at enheten måler i vakuum), kontrolleres det at resultatet er mellom maksimums- og minimumsverdier før det tas for gitt. Selv om det anses som et gjennomsnitt at en normalverdi for en frisk voksen i hvile er mellom 60 og 100 ppm, er det tillatte verdier nedenfor, det er lett å finne 40 ppm hos en idrettsutøver i hvile, opp til 200 ppm i løpet av intens trening med mer på 100 ppm hos stillesittende voksne i spenningstilstander, nettopp en interessant faktor for søvnhåndteringsprosjektet som får meg til å utvikle dette pulsmåler. Av denne grunn er det tilrådelig å slappe mye av disse verdiene slik at ytterpunktene ikke går tapt, noe som nøyaktig kan vise relevante aspekter.
Den nye gjennomsnittsverdien beregnes ved å redusere relevansen til gjeldende gjennomsnitt basert på antall verdier som er samplet og den siste verdien legges til, også vektet med en koeffisient som reduserer den ytterligere jo flere verdier som er målt så langt .
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;
}
}
|
Til slutt, ved å bruke algoritmen beskrevet før, utviklet jeg biblioteket for å beregne pulsen ved å oppdage tilstedeværelsen av hemoglobin o oksyhemoglobin (avhengig av bølgelengden på lyset som brukes) fra koden nedenfor.
Biblioteket forventer at samplingsfunksjonen kalles opp med jevne mellomrom puls i mitt søvnhåndteringsprosjekt
. I alle fall, ut fra testene jeg har gjort, ser det ikke ut til å være nødvendig; enten av enheten eller av oppførselen til for å beregne pulsen, som kan konsulteres med funksjonen eller med funksjonen gjennomsnittspulsen. I tillegg til å være en begrenset ressurs, utelukket jeg å bruke avbrudd fordi jeg ikke trengte umiddelbare verdier, men heller vedvarende verdier over tid for å overvåke puls, sampling ved en viss frekvens gir nok informasjon og ikke mye mer (relevant) oppnås ved å øke den, og det er heller ikke mulig å redusere den mye uten å miste relevante data for beregningen; i tidlige versjoner av koden for å overvåke lesingen av pulsoksymeter Jeg oppdaget at det ikke var nødvendig å holde seg til en maksimal måletid siden, hvis variasjonene av suksessive verdier ble tatt riktig i betraktning, var den veldig nær minimum.
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;
}
|
Følgende eksempelprogram viser hvordan du bruker det forrige biblioteket til å måle puls med en pulsoksymeter. I tillegg til å instansiere klassen overvåking av nivået på oksyhemoglobin/hemoglobin og med en mindre periodisitet verdien av puls beregnet og gjennomsnittlig.
For å sikre at målingene er relevante, programmeres en venting før noen verdi vises. Siden verdien kan være feil (for eksempel hvis brukeren fjerner enheten), vises verdiene bare hvis de er innenfor rekkevidden til de som anses som gyldige.
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);
}
}
}
|
Legg inn kommentar