Arduino-kirjasto sykkeen seurantaan pulssioksimetrillä
Yksi unenhallintaprojektissani seuranneista parametreista
Se on pulssi. mittaamaan sitä Kehitin laitteen, joka perustuu hemoglobiinin ja oksihemoglobiinin käyttäytymiseen valon eri aallonpituuksia vastaan. Pohjimmiltaan kyse on sen mittaamisesta, kuinka paljon tietyntyyppistä valoa pystyy kulkemaan läpi tai heijastumaan hyvin kastetulle kehon alueelle. Taajuus, jolla tämän ilmiön täydellinen sykli tapahtuu, mahdollistaa sen mittaamisen pulssi.Suunnittelu- ja testausvaiheessa pulssin mittauslaite Kehitin joitain pieniä ohjelmia auttamaan minua varmistamaan, että kokoonpano oli oikein. Ensin kirjoitin alle koodin, joka otti mitatut arvot aika ajoin (ainakin joka ja korkeintaan jokainen ), kun ne vaihtelivat vähintään yhden ja edellisen välillä (arvo, joka vastaa ) ja valvotaan tietokoneelta Python-sovelluksella voidakseen analysoida niitä myöhemmin.
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));
}
}
}
|
Kun arvot oli säädetty (alkaen erittäin tiheistä mittauksista), sain kokoelman arvoja pulssioksimetri ajan myötä pystyin piirtämään kaavion laskentataulukon avulla, LibreOffice Lask de LibreOffice, erityistä.
Kerätyillä tiedoilla, kuten yllä olevassa kuvassa esitetään, seuraava toimenpide oli määrittää, pystyikö arvojen tiheys laskemaan luotettavalla, mutta "taloudellisella" tavalla (ei ota enempää kuin tarpeellista dataa) pulssi; Kuten alla olevasta kaaviosta näkyy, toteutetut toimenpiteet näyttivät auttavan saavuttamaan kohtuudella odotettavissa olevia tuloksia.
.
Seuraavaksi näytteenotosta saatujen tietojen perusteella oli tarpeen kehittää algoritmi, joka mittaisi pulssin. Pidä kiinni kaaviosta, että yksinkertaisuuden vuoksi oletetaan, että se edustaa asettelua, joka on samanlainen kuin QRS-kompleksi, yksinkertaisin asia näyttää olevan mitata näkyvimpien osien väliset ajat korkeammilla arvoilla (joka vastaa kammioiden depolarisaation qRs-vyöhykettä), hylätä litteämpi ja "meluisampi" vyöhyke, mikä on siksi vaikeampaa mitata. Valittu ratkaisu, joka vastaa alla olevaa testikoodia, toimii seuraavan menettelyn mukaisesti:
-
Tunnista mitattava alue kussakin tapauksessa huomioidaksesi vain arvohuiput qRs ja heittää pois laakson. Tätä varten voidaan mitata tiettyä vakiota korkeampia arvoja, mutta on olemassa riski, että yksilö ja/tai olosuhteet voivat, vaikkakin suhteellisesti, nostaa tai laskea arvoja. Tämän välttämiseksi alueen arvoa pidetään suurempana kuin se, joka ylittää keskiarvon tietyllä kertoimella. Tällä tavalla mittaus on herkästi itsekalibroitunut ja sitä voisi vieläkin säätää hienosäätämällä kerrointa, minkä olen omassa tapauksessani saavuttanut kokeellisesti testien aikana.
Valitse mittausta varten laskevan alueen arvot (Rs) huipusta qRs, mahdollisimman lähellä käyrän maksimiarvoa. Tietääksesi, että nouseva vyöhyke hylätään, riittää, kun tarkistat, että uusi arvo on pienempi kuin edellinen ja varmistat, että etsittyä arvoa ei ole vielä löydetty, koska yleensä laskevassa vyöhykkeessä on useita arvoja vyöhyke qRs näytteenottotiheydestä riippuen. Pulssin ajoittamiseksi tallennetaan sen hetken arvo, jolloin piste löydettiin (millisekunnit, jotka palauttavat millis ()) ja vertaa sitä seuraavaan.
Sen varmistamiseksi, että mitattu arvo on suurin korkeimman käyrän laskevalla vyöhykkeellä, käytetään muuttujaa boolean ( tässä esimerkissä ja kirjastossa), joka aktivoituu pääkäyrän nousevalle vyöhykkeelle siirtyessä ja deaktivoituu, kun ensimmäinen laskeva arvo, joka on ajastettu, löydetään.
Kuten on tavallista esittää pulssin kesto lyönteinä minuutissa (ppm), saatu pulssien välinen aika korjataan laskemalla jakamalla esityksen kokonaisaika (yksi minuutti, 60000 XNUMX millisekuntia) välillä, joka saadaan vähentämällä nykyiset millisekunnit ( nykyisestä arvosta) aiemmin ajoitetuista.
Väärien mittausten (kuten tyhjiössä mittaavan laitteen) välttämiseksi varmistetaan, että tulos on maksimi- ja minimiarvojen välillä ennen kuin se otetaan itsestäänselvyytenä. Vaikka terveen aikuisen normaaliarvon katsotaan olevan levossa keskimäärin 60-100 ppm, sallitut arvot ovat alla, levossa olevasta urheilijasta on helppo löytää 40 ppm, 200 ppm asti. intensiivinen harjoittelu ja enemmän. 100 ppm istumista liikkuvilla aikuisilla jännittyneissä tiloissa, juuri mielenkiintoinen tekijä unenhallintaprojektissa mikä saa minut kehittämään tätä pulssin mittauslaite. Tästä syystä näitä arvoja on suositeltavaa rentoutua paljon, jotta äärimmäisyydet eivät katoa, mikä voisi näyttää täsmällisesti oleelliset näkökohdat.
Uusi keskiarvo lasketaan vähentämällä nykyisen keskiarvon relevanssia otettujen arvojen perusteella ja viimeinen arvo lisätään, myös painotettuna kertoimella, joka pienentää sitä edelleen mitä enemmän arvoja on tähän mennessä mitattu. .
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;
}
}
|
Lopuksi, käyttämällä edellä kuvattua algoritmia, kehitin kirjaston laskemaan pulssi havaitsemalla läsnäolon hemoglobiini tai oksihemoglobiini (riippuen käytetystä valon aallonpituudesta) alla olevasta koodista.
Kirjasto odottaa, että näytteenottofunktiota kutsutaan säännöllisesti pulssi unenhallintaprojektissani
. Joka tapauksessa tekemieni testien perusteella se ei näytä tarpeelliselta; joko laitteesta tai sen käytöksestä pulssin laskemiseen, jota voidaan tarkastella funktiolla tai toiminnon kanssa keskimääräinen pulssi. Sen lisäksi, että resurssi oli rajallinen, suljin pois keskeytyksiä, koska en tarvinnut välittömiä arvoja vaan pikemminkin pitkiä arvoja ajan mittaan seuratakseni pulssi, näytteistys tietyllä taajuudella tarjoaa riittävästi tietoa, eikä sitä suurentamalla saada paljoa enempää (relevanssia), eikä sitä ole mahdollista pienentää paljon menettämättä laskentaa varten oleellista dataa; koodin varhaisissa versioissa seurataksesi koodin lukemista pulssioksimetri Huomasin, että maksimimittausajoissa ei ollut tarpeen pitää kiinni, koska jos peräkkäisten arvojen vaihtelut huomioitiin oikein, se oli hyvin lähellä minimiä.
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;
}
|
Seuraava esimerkkiohjelma näyttää, kuinka edellistä kirjastoa käytetään mittaamaan pulssi yksi pulssioksimetri. Luokan instantoimisen lisäksi tason seurantaa oksihemoglobiini/hemoglobiini ja pienemmällä jaksollisuudella arvo pulssi laskettu ja keskiarvo.
Mittausten asianmukaisuuden varmistamiseksi ohjelmoidaan odotus ennen minkään arvon näyttämistä. Koska arvo voi olla virheellinen (esimerkiksi jos käyttäjä poistaa laitteen), arvot näytetään vain, jos ne ovat kelvollisiksi katsottujen rajojen sisällä.
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);
}
}
}
|
Post Comment