Библиотека 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 милисекунди) на интервала, получен от изваждане на текущите милисекунди (от текущата стойност) от предварително измерените.
Para evitar medidas falsas (como el dispositivo midiendo en vacío, por ejemplo) se verifica que el resultado se encuentra entre unos valores máximos y mínimos antes de darlo por cierto. Aunque se considera como media que un valor normal para un adulto sano en reposo se encuentra entre 60 y 100 ppm, hay valores admisibles por debajo, es fácil encontrar 40 ppm en un atleta en reposo, hasta 200 ppm sometido a un ejercicio intenso y más de 100 ppm en adultos sedentarios en estados de excitación, precisamente un factor interesante para el proyecto de gestión del sueño que me lleva a desarrollar este устройство за измерване на пулса. Поради тази причина е препоръчително тези стойности да се отпуснат много, така че крайностите да не се губят, което би могло да покаже точно съответните аспекти.
Новата средна стойност се изчислява чрез намаляване на уместността на текущата средна стойност на базата на броя на пробните стойности и се добавя последната стойност, също претеглена с коефициент, който я намалява допълнително, колкото повече стойности са били измерени досега .
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); } } } |
Публикувай коментар