Библиотека 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 Калькулятор de LibreOffice, специфический.
После сбора данных, как показано на изображении выше, следующей операцией было определить, позволяет ли плотность значений нам вычислить надежным, но «экономным» способом (не осуществляя выборку большего количества данных, чем необходимо) значение Pulso; Как видно на графике ниже, принятые меры, похоже, послужили достижению тех результатов, которых разумно ожидать.
.
Далее, используя информацию выборки данных, необходимо было разработать алгоритм, который бы измерял частоту пульса. Придерживаясь графика, для простоты предполагается, что он представляет собой макет, аналогичный комплекс 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;
}
}
|
Наконец, используя описанный ранее алгоритм, я разработал библиотеку для расчета пульса по обнаружению присутствия гемоглобин или оксигемоглобин (в зависимости от используемой длины волны света) из приведенного ниже кода.
Библиотека ожидает, что функция выборки будет вызываться периодически. 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);
}
}
}
|
Оставить комментарий