Бібліотека Arduino для моніторингу серцевого ритму за допомогою пульсоксиметра
Один із параметрів, який відстежується в моєму проекті керування сном
Це пульс. щоб виміряти його Я розробив пристрій, заснований на поведінці гемоглобіну та оксигемоглобіну проти різних довжин хвиль світла. В основному мова йде про вимірювання того, скільки світла певного типу здатне проходити або відбиватися на добре зрошуваній ділянці тіла. Частота, з якою відбувається повний цикл цього явища, дозволяє виміряти Pulso.На етапі проектування та тестування прилад для вимірювання пульсу Я розробив кілька невеликих програм, щоб допомогти мені перевірити правильність збірки. Спочатку я написав наведений нижче код, який час від часу брав виміряні значення (принаймні кожен і не більше кожного ), коли вони коливалися мінімум між одним і попереднім (значення, яке відповідає ) та відстежується з комп’ютера за допомогою програми 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, специфічний.
Після зібраних даних, як показано на зображенні вище, наступною операцією було визначити, чи дозволяє щільність значень обчислити надійним, але «економічним» способом (без вибірки більше, ніж необхідні дані) значення Pulso; Як видно на графіку нижче, вжиті заходи, здавалося, сприяли отриманню результатів, яких розумно очікувати.
.
Далі, на основі інформації із вибірки даних, необхідно було розробити алгоритм, який би вимірював частоту пульсу. Дотримуючись графіка, для простоти передбачається, що він представляє макет, подібний до Комплекс 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; } } |
Нарешті, використовуючи описаний раніше алгоритм, я розробив бібліотеку для обчислення пульсу шляхом виявлення присутності гемоглобін O оксигемоглобін (залежно від довжини хвилі світла, що використовується) з наведеного нижче коду.
Бібліотека очікує періодичного виклику функції вибірки Pulso у моєму проекті керування сном
. У будь-якому випадку, з тестів, які я зробив, це не здається необхідним; або через пристрій, або через поведінку для обчислення пульсу, який можна переглянути за допомогою функції або з функцією середній пульс. Окрім того, що це обмежений ресурс, я виключив використання переривань, оскільки мені потрібні були не миттєві значення, а скоріше стійкі з часом для моніторингу Pulso, вибірка з певною частотою пропонує достатньо інформації, і не набагато більше (релевантне) отримується шляхом її збільшення, також неможливо значно зменшити її без втрати відповідних даних для розрахунку; у ранніх версіях коду для моніторингу читання пульсоксиметр Я виявив, що немає необхідності дотримуватися максимального часу вимірювання, оскільки, якщо варіації послідовних значень були правильно враховані, він був дуже близький до мінімального.
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; } |
Наступний приклад програми показує, як використовувати попередню бібліотеку для вимірювання Pulso з одного пульсоксиметр. На додаток до створення екземпляра класу моніторинг рівня оксигемоглобін/гемоглобін і з меншою періодичністю значення в Pulso розрахунковий і середній.
Щоб гарантувати релевантність вимірювань, перед відображенням будь-якого значення запрограмовано очікування. Оскільки значення може бути неправильним (наприклад, якщо користувач видаляє пристрій), значення відображаються, лише якщо вони знаходяться в діапазоні тих, що вважаються дійсними.
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); } } } |
Дати коментар