Arduino-Bibliothek zur Herzfrequenzüberwachung mit Pulsoximeter
Einer der Parameter, die in meinem Schlafmanagementprojekt überwacht werden
Es ist der Puls. um es zu messen Ich habe ein Gerät entwickelt, das auf dem Verhalten von Hämoglobin und Oxyhämoglobin gegenüber verschiedenen Lichtwellenlängen basiert. Im Grunde geht es darum, zu messen, wie viel Licht einer bestimmten Art einen gut durchströmten Bereich des Körpers durchdringen bzw. dort reflektiert werden kann. Die Häufigkeit, mit der ein vollständiger Zyklus dieses Phänomens auftritt, ermöglicht die Messung Pulso.In der Entwurfs- und Testphase des Pulsmessgerät Ich habe einige kleine Programme entwickelt, die mir dabei helfen, zu überprüfen, ob der Zusammenbau korrekt war. Zuerst habe ich den folgenden Code geschrieben, der von Zeit zu Zeit (zumindest alle) die Messwerte erfasst und höchstens jeder ), wenn sie ein Minimum zwischen einem und dem vorherigen variierten (der Wert, der entspricht ) und das von einem Computer aus mit einer Python-Anwendung überwacht werden um sie später analysieren zu können.
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));
}
}
}
|
Nachdem die Werte angepasst wurden (beginnend mit sehr dichten Messungen), erhielt ich eine Sammlung von Werten aus dem Pulsoximeter Mit der Zeit konnte ich mithilfe einer Tabellenkalkulation eine grafische Darstellung erstellen. LibreOffice-Rechner de LibreOffice, Spezifisch.
Mit den gesammelten Daten, wie im Bild oben dargestellt, bestand der nächste Schritt darin, zu bestimmen, ob die Wertedichte es uns ermöglichte, den Wert auf zuverlässige, aber „wirtschaftliche“ Weise (ohne mehr als die erforderlichen Daten abzufragen) zu berechnen Pulso; Wie in der folgenden Grafik zu sehen ist, schienen die ergriffenen Maßnahmen dazu zu dienen, die vernünftigerweise zu erwartenden Ergebnisse zu erzielen.
.
Als nächstes musste mit den Informationen aus der Datenerfassung ein Algorithmus entwickelt werden, der die Pulsfrequenz messen würde. Der Einfachheit halber wird davon ausgegangen, dass das Diagramm ein ähnliches Layout wie das Diagramm darstellt QRS-KomplexAm einfachsten scheint es zu sein, die Zeiten zwischen den markantesten Teilen mit höheren Werten zu messen (was der qRs-Depolarisationszone der Ventrikel entspricht) und die flachere und „verrauschtere“ Zone zu verwerfen, was daher schwieriger ist messen. Die gewählte Lösung, die dem untenstehenden Testcode entspricht, funktioniert nach folgendem Verfahren:
-
Erfassen Sie jeweils den zu messenden Bereich, um nur die Wertespitzen zu berücksichtigen qRs und wirf das Tal weg. Zu diesem Zweck könnten Werte gemessen werden, die über einer bestimmten Konstante liegen, es besteht jedoch das Risiko, dass eine Person und/oder Umstände die Werte, wenn auch proportional, erhöhen oder senken könnten. Um dies zu vermeiden, wird ein Wert in der Fläche als größer angesehen als derjenige, der den Durchschnittswert um einen bestimmten Koeffizienten überschreitet. Auf diese Weise ist die Messung empfindlich selbstkalibriert und könnte durch eine Feinabstimmung des Koeffizienten noch weiter angepasst werden, was ich in meinem Fall experimentell während der Tests erreicht habe.
Wählen Sie die Werte der absteigenden Zone für die Messung (Rs) des Gipfels qRs, so nah wie möglich am Maximum der Kurve. Um zu wissen, dass die aufsteigende Zone verlassen wird, reicht es aus, zu überprüfen, ob ein neuer Wert kleiner als der vorherige ist, und zu überprüfen, ob der gesuchte Wert noch nicht gefunden wurde, da es im Allgemeinen mehrere Werte in der absteigenden Zone gibt Zone von qRs abhängig von der Probendichte. Zur Zeitmessung des Impulses wird der Wert des Zeitpunkts gespeichert, zu dem der Punkt gefunden wurde (die von zurückgegebenen Millisekunden). millis ()) und vergleicht es mit dem nächsten.
Um sicherzustellen, dass der gemessene Wert im abfallenden Bereich der höchsten Kurve am größten ist, wird eine Variable verwendet boolesch ( in diesem Beispiel und in der Bibliothek), der beim Betreten der aufsteigenden Zone der Hauptkurve aktiviert und deaktiviert wird, sobald der erste absteigende Wert gefunden wird, der zeitgesteuert ist.
Da es üblich ist, die Dauer des Impulses in Schlägen pro Minute (ppm) darzustellen, wird der erhaltene Wert der Zeit zwischen den Impulsen korrigiert, indem die Gesamtzeit der Darstellung (eine Minute, 60000 Millisekunden) durch das erhaltene Intervall dividiert wird Subtrahieren der aktuellen Millisekunden (des aktuellen Werts) von den zuvor gemessenen.
Um Fehlmessungen zu vermeiden (z. B. wenn das Gerät im Vakuum misst), wird zunächst überprüft, ob das Ergebnis zwischen Maximal- und Minimalwert liegt, bevor es als gegeben angenommen wird. Obwohl man davon ausgeht, dass ein normaler Wert für einen gesunden Erwachsenen im Ruhezustand zwischen 60 und 100 ppm liegt, gibt es zulässige Werte darunter, so kann man bei einem Sportler im Ruhezustand leicht 40 ppm finden, im Ruhezustand bis zu 200 ppm intensiver körperlicher Betätigung und mehr. von 100 ppm bei sitzenden Erwachsenen in Erregungszuständen, genau ein interessanter Faktor für das Schlafmanagementprojekt was mich dazu bringt, dies zu entwickeln Pulsmessgerät. Aus diesem Grund empfiehlt es sich, diese Werte stark zu lockern, damit die Extreme nicht verloren gehen, die relevante Aspekte präzise aufzeigen könnten.
Der neue Durchschnittswert wird berechnet, indem die Relevanz des aktuellen Durchschnitts basierend auf der Anzahl der erfassten Werte verringert und der letzte Wert hinzugefügt wird, ebenfalls gewichtet mit einem Koeffizienten, der ihn umso weiter reduziert, je mehr Werte bisher gemessen wurden .
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;
}
}
|
Schließlich habe ich unter Verwendung des zuvor beschriebenen Algorithmus die Bibliothek entwickelt, um den Puls durch Erkennen des Vorhandenseins von zu berechnen Hämoglobin oder Oxyhämoglobin (abhängig von der Wellenlänge des verwendeten Lichts) aus dem folgenden Code.
Die Bibliothek erwartet, dass die Sampling-Funktion regelmäßig aufgerufen wird Pulso in meinem Schlafmanagementprojekt
. Den von mir durchgeführten Tests zufolge scheint dies jedenfalls nicht notwendig zu sein; entweder durch das Gerät oder durch das Verhalten des um den Puls zu berechnen, der mit der Funktion konsultiert werden kann oder mit der Funktion der durchschnittliche Puls. Abgesehen davon, dass es sich um eine begrenzte Ressource handelt, schloss ich die Verwendung von Unterbrechungen aus, da ich zur Überwachung keine unmittelbaren Werte, sondern über einen längeren Zeitraum hinweg nachhaltige Werte benötigte PulsoDie Abtastung mit einer bestimmten Häufigkeit liefert genügend Informationen und durch Erhöhen erhält man nicht viel mehr (relevantes), und es ist auch nicht möglich, sie stark zu verringern, ohne relevante Daten für die Berechnung zu verlieren. in frühen Versionen des Codes, um das Lesen des zu überwachen Pulsoximeter Ich habe festgestellt, dass es nicht notwendig ist, sich an eine maximale Messzeit zu halten, da diese bei korrekter Berücksichtigung der Schwankungen aufeinanderfolgender Werte sehr nahe am Minimum liegt.
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;
}
|
Das folgende Beispielprogramm zeigt, wie die vorherige Bibliothek zum Messen verwendet wird Pulso mit einem Pulsoximeter. Zusätzlich zur Instanziierung der Klasse Überwachung des Niveaus von Oxyhämoglobin/Hämoglobin und mit einer kleineren Periodizität der Wert des Pulso berechnet und gemittelt.
Um sicherzustellen, dass die Messungen relevant sind, ist eine Wartezeit programmiert, bevor ein Wert angezeigt wird. Da der Wert falsch sein kann (z. B. wenn der Benutzer das Gerät entfernt), werden Werte nur angezeigt, wenn sie im Bereich der als gültig angesehenen Werte liegen.
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);
}
}
}
|
Geben Sie Anmerkung