Библиотека Arduino за наблюдение на сърдечната честота с пулсов оксиметър
Един от параметрите, наблюдавани в моя проект за управление на съня
Това е пулсът. да го измерим Разработих устройство, базирано на поведението на хемоглобина и оксихемоглобина срещу различни дължини на вълната на светлината. По принцип става въпрос за измерване колко светлина от определен тип може да премине или да се отрази в добре напоена област на тялото. Честотата, с която се случва пълен цикъл на това явление, позволява измерването на пулс.Във фазата на проектиране и тестване на устройство за измерване на пулса Разработих няколко малки програми, които да ми помогнат да проверя дали асемблирането е правилно. Първо написах кода по-долу, който вземаше измерените стойности от време на време (поне всеки и най-много всеки ), когато варираха минимум между едно и предишното (стойността, която съответства на ) и наблюдавани от компютър с приложение на Python за да можете да ги анализирате по-късно.
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));
}
}
}
|
След като стойностите бяха коригирани (започвайки с много плътни измервания), получих колекция от стойности от пулсов оксиметър с течение на времето, че мога да направя графика с помощта на електронна таблица, LibreOffice Calc de LibreOffice, специфичен.
Със събраните данни, както е представено на изображението по-горе, следващата операция беше да определим дали плътността на стойностите ни позволява да изчислим по надежден, но „икономичен“ начин (без вземане на проби от повече от необходимите данни) стойността на пулс; Както може да се види на графиката по-долу, предприетите мерки изглежда служат за постигане на разумно очакваните резултати.
.
След това, с информацията от вземането на проби от данни, беше необходимо да се разработи алгоритъм, който да измерва пулса. Придържайки се към графиката, че за простота се приема, че тя представлява оформление, подобно на QRS комплекс, най-простото нещо изглежда е да се измерят времената между най-изпъкналите части, с по-високи стойности (което съответства на qRs зоната на деполяризация на вентрикулите), като се изхвърли по-плоската и "по-шумна" зона, която следователно е по-трудна за измерване. Приетото решение, което съответства на тестовия код по-долу, работи съгласно следната процедура:
-
Открийте зоната, която се измерва във всеки случай, за да обърнете внимание само на пиковете на стойността qRs и изхвърлете долината. За да направите това, могат да бъдат измерени стойности, по-високи от определена константа, но съществува риск индивидът и/или обстоятелствата да могат, макар и пропорционално, да повишат или понижат стойностите. За да се избегне това, стойността в областта се счита за по-голяма от тази, която надвишава средната стойност с определен коефициент. По този начин измерването е чувствително самокалибрирано и може да бъде коригирано още повече чрез фина настройка на коефициента, което в моя случай постигнах експериментално по време на тестовете.
Изберете стойностите на низходящата зона за измерване (Rs) на върха qRs, възможно най-близо до максимума на кривата. За да разберете, че възходящата зона е изоставена, достатъчно е да проверите дали новата стойност е по-малка от предишната и да проверите дали търсената стойност все още не е намерена, тъй като по принцип има няколко стойности в низходящ зона на qRs в зависимост от плътността на вземане на проби. За време на импулса се съхранява стойността на момента, в който е намерена точката (милисекундите, върнати от милис ()) и го сравнява със следващия.
За да се гарантира, че измерената стойност е най-голямата в низходящата зона на най-високата крива, се използва променлива булево ( в този пример и в библиотеката), който се активира при влизане във възходящата зона на основната крива и се деактивира, след като бъде открита първата низходяща стойност, която е времевата.
Тъй като е обичайно да се представя продължителността на импулса като удари в минута (ppm), получената стойност на времето между импулсите се коригира чрез изчисляване чрез разделяне на общото време на представянето (една минута, 60000 XNUMX милисекунди) на интервала, получен от изваждане на текущите милисекунди (от текущата стойност) от предварително измерените.
За да се избегнат фалшиви измервания (като устройството, измерващо във вакуум, например), се проверява дали резултатът е между максималните и минималните стойности, преди да се приеме за даденост. Въпреки че се счита за средна, че нормалната стойност за здрав възрастен в покой е между 60 и 100 ppm, има допустими стойности по-долу, лесно е да се намерят 40 ppm при спортист в покой, до 200 ppm по време на интензивни упражнения и други от 100 ppm при заседнали възрастни в състояния на възбуда, точно интересен фактор за проекта за управление на съня което ме кара да развия това устройство за измерване на пулса. Поради тази причина е препоръчително тези стойности да се отпуснат много, така че крайностите да не се губят, което би могло да покаже точно съответните аспекти.
Новата средна стойност се изчислява чрез намаляване на уместността на текущата средна стойност на базата на броя на пробните стойности и се добавя последната стойност, също претеглена с коефициент, който я намалява допълнително, колкото повече стойности са били измерени досега .
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;
}
}
|
Накрая, използвайки алгоритъма, описан по-горе, разработих библиотеката за изчисляване на импулса чрез откриване на наличието на хемоглобин о ла оксихемоглобин (в зависимост от дължината на вълната на използваната светлина) от кода по-долу.
Библиотеката очаква функцията за вземане на проби да се извиква периодично пулс в моя проект за управление на съня
. Във всеки случай, от тестовете, които направих, не изглежда да е необходимо; или от устройството, или от поведението на за изчисляване на пулса, който може да се консултира с функцията или с функцията средният пулс. Освен че е ограничен ресурс, изключих използването на прекъсвания, защото нямах нужда от незабавни стойности, а по-скоро от устойчиви във времето, за да наблюдавам пулс, вземането на проби при определена честота предлага достатъчно информация и не се получава много повече (релевантно) чрез нейното увеличаване, нито е възможно да се намали много, без да се загубят съответните данни за изчислението; в ранните версии на кода за наблюдение на четенето на пулсов оксиметър Открих, че не е необходимо да се придържаме към максималното време за измерване, тъй като, ако вариациите на последователните стойности бяха правилно отчетени, то беше много близо до минимума.
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;
}
|
Следната примерна програма показва как да използвате предишната библиотека за измерване на пулс с пулсов оксиметър. В допълнение към инстанцирането на класа мониторинг на нивото на оксихемоглобин/хемоглобин а с по-малка периодичност стойността на пулс изчислено и средно.
За да се гарантира, че измерванията са уместни, се програмира изчакване, преди да се покаже каквато и да е стойност. Тъй като стойността може да е неправилна (например ако потребителят премахне устройството), стойностите се показват само ако са в обхвата на считаните за валидни.
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);
}
}
}
|
Публикувай коментар