Arduino library para sa heart rate monitoring na may pulse oximeter
Isa sa mga parameter na sinusubaybayan sa aking proyekto sa pamamahala ng pagtulog
Ito ang pulso. upang sukatin ito Gumawa ako ng device batay sa gawi ng hemoglobin at oxyhemoglobin laban sa iba't ibang wavelength ng liwanag. Karaniwang ito ay tungkol sa pagsukat kung gaano karaming liwanag ng isang tiyak na uri ang maaaring dumaan o maipakita sa isang mahusay na patubig na bahagi ng katawan. Ang dalas kung saan ang isang kumpletong cycle ng hindi pangkaraniwang bagay na ito ay nangyayari ay nagbibigay-daan sa pagsukat ng Pulso.Sa yugto ng disenyo at pagsubok ng aparato sa pagsukat ng pulso Gumawa ako ng ilang maliliit na programa para matulungan akong ma-verify na tama ang assembly. Una ay isinulat ko ang code sa ibaba, na kinuha ang mga sinusukat na halaga paminsan-minsan (hindi bababa sa bawat at higit sa bawat isa ) kapag nag-iba sila ng pinakamababa sa pagitan ng isa at ng nauna (ang halaga na tumutugma sa ) at ang sinusubaybayan mula sa isang computer na may Python application upang masuri ang mga ito sa ibang pagkakataon.
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));
}
}
}
|
Kapag ang mga halaga ay nababagay (nagsisimula sa napakasiksik na mga sukat) nakakuha ako ng isang koleksyon ng mga halaga mula sa Pulse oximeter sa paglipas ng panahon na maaari akong mag-graph gamit ang isang spreadsheet, LibreOffice Calc de LibreOffice, tiyak.
Sa data na nakolekta, tulad ng kinakatawan sa imahe sa itaas, ang susunod na operasyon ay upang matukoy kung ang density ng mga halaga ay nagpapahintulot sa amin na kalkulahin sa isang maaasahang ngunit "ekonomiko" na paraan (hindi sampling ng higit sa kinakailangang data) ang halaga ng Pulso; Tulad ng makikita sa graph sa ibaba, ang mga hakbang na ginawa ay tila nagsisilbi upang makuha ang mga resulta na makatwirang asahan.
.
Susunod, kasama ang impormasyon mula sa sampling ng data, kinakailangan na bumuo ng isang algorithm na susukat sa rate ng pulso. Nananatili sa graph na, para sa pagiging simple, ipinapalagay na ito ay kumakatawan sa isang layout na katulad ng QRS complex, ang pinakasimpleng bagay ay tila sukatin ang mga oras sa pagitan ng mga pinakatanyag na bahagi, na may mas mataas na mga halaga (na tumutugma sa qRs zone ng depolarization ng ventricles), itinatapon ang flatter at "noisier" zone, na kung saan ay mas mahirap. upang masukat. Ang pinagtibay na solusyon, na tumutugma sa test code sa ibaba, ay gumagana ayon sa sumusunod na pamamaraan:
-
I-detect ang lugar na sinusukat sa bawat kaso upang maasikaso lamang ang mga pinakamataas na halaga qRs at itapon ang lambak. Upang gawin ito, ang mga halagang mas mataas kaysa sa isang tiyak na pare-pareho ay maaaring masukat, ngunit may panganib na ang isang indibidwal at/o mga pangyayari ay maaaring, bagama't proporsyonal, taasan o babaan ang mga halaga. Upang maiwasan ito, ang isang halaga sa lugar ay itinuturing na mas malaki kaysa sa isa na lumampas sa average na halaga ng isang partikular na koepisyent. Sa ganitong paraan, ang pagsukat ay sensitibong naka-calibrate sa sarili at maaaring iakma pa sa pamamagitan ng pag-fine-tune ng koepisyent, na sa aking kaso ay nakamit ko nang eksperimental sa panahon ng mga pagsubok.
Piliin ang mga halaga ng pababang zone para sa pagsukat (Rs) ng tuktok qRs, mas malapit hangga't maaari sa maximum ng curve. Upang malaman na ang pataas na zone ay inabandona, sapat na upang i-verify na ang isang bagong halaga ay mas mababa kaysa sa nauna at i-verify na ang hinanap na halaga ay hindi pa nahahanap dahil, sa pangkalahatan, mayroong ilang mga halaga sa pababang. zone ng qRs depende sa sampling density. Sa oras ng pulso, ang halaga ng instant kung saan natagpuan ang punto ay iniimbak (ang mga millisecond na ibinalik ni millis ()) at ikumpara ito sa susunod.
Upang matiyak na ang sinusukat na halaga ay ang pinakamalaking sa pababang zone ng pinakamataas na curve, isang variable ang ginagamit boolean ( sa halimbawang ito at sa library) na naka-activate kapag pumapasok sa ascending zone ng major curve at na-deactivate kapag nakita ang unang pababang value, na siyang naka-time.
Tulad ng nakagawian na kumakatawan sa tagal ng pulso bilang mga beats bawat minuto (ppm), ang halaga ng oras sa pagitan ng mga pulse na nakuha ay itinatama sa pamamagitan ng pagkalkula sa pamamagitan ng paghati sa kabuuang oras ng representasyon (isang minuto, 60000 millisecond) sa pagitan na nakuha ng pagbabawas ng kasalukuyang millisecond ( ng kasalukuyang halaga) sa mga naunang na-time.
Upang maiwasan ang mga maling pagsukat (tulad ng pagsukat ng device sa isang vacuum, halimbawa), na-verify na ang resulta ay nasa pagitan ng maximum at minimum na mga halaga bago ito kunin nang basta-basta. Bagama't itinuturing bilang isang average na ang isang normal na halaga para sa isang malusog na nasa hustong gulang na nagpapahinga ay nasa pagitan ng 60 at 100 ppm, may mga tinatanggap na halaga sa ibaba, madaling makahanap ng 40 ppm sa isang atleta na nagpapahinga, hanggang sa 200 ppm sa panahon matinding ehersisyo at higit pa. ng 100 ppm sa mga nakaupong nasa hustong gulang na nasa estado ng kaguluhan, tiyak na isang kawili-wiling salik para sa proyekto sa pamamahala ng pagtulog na humahantong sa akin na paunlarin ito aparato sa pagsukat ng pulso. Para sa kadahilanang ito, ipinapayong i-relax ang mga halagang ito nang marami upang ang mga sukdulan ay hindi mawala, na maaaring tumpak na magpakita ng mga nauugnay na aspeto.
Ang bagong average na halaga ay kinakalkula sa pamamagitan ng pagbabawas ng kaugnayan ng kasalukuyang average batay sa bilang ng mga halaga na na-sample at ang huling halaga ay idinagdag, na binibigyang timbang din ng isang koepisyent na nagpapababa pa nito ng mas maraming mga halaga na nasusukat sa ngayon .
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;
}
}
|
Sa wakas, gamit ang algorithm na inilarawan dati, binuo ko ang library upang kalkulahin ang pulso sa pamamagitan ng pag-detect sa presensya ng hemoglobin o la oxyhemoglobin (depende sa wavelength ng liwanag na ginamit) mula sa code sa ibaba.
Inaasahan ng library na pana-panahong tatawagin ang sampling function Pulso sa aking proyekto sa pamamahala ng pagtulog
. Sa anumang kaso, mula sa mga pagsubok na ginawa ko, ito ay tila hindi kinakailangan; alinman sa pamamagitan ng aparato o sa pamamagitan ng pag-uugali ng upang kalkulahin ang pulso, na maaaring konsultahin sa pag-andar o kasama ang function ang average na pulso. Bilang karagdagan sa pagiging isang limitadong mapagkukunan, pinasiyahan ko ang paggamit ng mga pagkaantala dahil hindi ko kailangan ng mga agarang halaga ngunit sa halip ay pinananatili sa paglipas ng panahon upang masubaybayan ang Pulso, ang pagsa-sample sa isang partikular na dalas ay nag-aalok ng sapat na impormasyon at hindi gaanong higit pa (may kaugnayan) ang nakukuha sa pamamagitan ng pagtaas nito, at hindi rin posibleng bawasan ito nang malaki nang hindi nawawala ang nauugnay na data para sa pagkalkula; sa mga unang bersyon ng code upang masubaybayan ang pagbabasa ng Pulse oximeter Natuklasan ko na hindi kinakailangan na manatili sa isang maximum na oras ng pagsukat dahil, kung ang mga pagkakaiba-iba ng sunud-sunod na mga halaga ay wastong isinasaalang-alang, ito ay napakalapit sa 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;
}
|
Ang sumusunod na halimbawang programa ay nagpapakita kung paano gamitin ang nakaraang library upang sukatin ang Pulso na may Pulse oximeter. Bilang karagdagan sa instantiating ng klase pagsubaybay sa antas ng oxyhemoglobin/hemoglobin at may mas maliit na periodicity ang halaga ng Pulso kalkulado at karaniwan.
Upang matiyak na ang mga sukat ay may kaugnayan, isang paghihintay ay naka-program bago magpakita ng anumang halaga. Dahil maaaring hindi tama ang value (halimbawa kung inalis ng user ang device), ipinapakita lang ang mga value kung nasa saklaw ang mga ito ng mga itinuturing na wasto.
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 Komento