Librería Arduino para monitorización de la frecuencia cardíaca con oxímetro de pulso

publicado en: Portada | 8

Uno de los parámetros que se monitorizan en mi proyecto de gestión del sueño es el pulso. Para medirlo desarrollé un dispositivo basado en el comportamiento de la hemoglobina y la oxihemoglobina frente a las diferentes longitudes de onda de la luz. Básicamente se trata de medir cuánta luz de determinado tipo es capaz de atravesar o es reflejada en una zona del cuerpo bien irrigada. La frecuencia con la que ocurre un ciclo completo de este fenómeno permite medir el pulso.

En la fase de diseño y prueba del dispositivo para medir el pulso desarrollé algunos pequeños programas para ayudarme a verificar que el montaje era correcto. En primer lugar escribí el código de abajo, que iba tomando los valores medidos cada cierto tiempo (como mínimo cada TIEMPO_MAXIMO_MEDIDA y como máximo cada TIEMPO_MINIMO_MEDIDA) cuando variaban un mínimo entre uno y el anterior (el valor que corresponde con MEDIDA_MINIMA) y los monitorizaba desde un ordenador con una aplicación en Python para poder analizarlos posteriormente.

Una vez ajustados los valores (empezando en medidas muy densas) conseguí una colección de valores del oxímetro de pulso a lo largo del tiempo que podía representar gráficamente utilizando una hoja de cálculo, LibreOffice Calc de LibreOffice, en concreto.

gráfica de las medidas del oxímetro de pulso con  de

Con los datos recabados, como representa la imagen de arriba, la siguiente operación era determinar si la densidad de valores permitía calcular de manera fiable pero «económica» (no muestrear más de los datos necesarios) el valor del pulso; como puede verse en el gráfico de abajo, las medidas tomadas parecían servir para obtener los resultados que es razonable esperar.

.

medida de la presencia de oxihemoglobina a lo largo del tiempo con un oxímetro de pulso

A continuación, con la información del muestreo de datos, quedaba elaborar un algoritmo que midiera la frecuencia del pulso. Ateniéndose a la gráfica que, por simplificar, se asume que representa un trazado similar al complejo QRS, lo más sencillo parece medir los tiempos entre las partes más destacadas, con valores mayores (que corresponde con la zona qRs de despolarización de los ventrículos) descartando la zona más plana y «ruidosa», más difícil de medir, por tanto. La solución adoptada, que corresponde con el código de prueba de abajo, funciona conforme al siguiente procedimiento:

  • Detectar la zona que se está midiendo en cada caso para atender sólo a los picos de valor qRs y desechar el valle. Para hacerlo se podrían medir valores superiores a cierta constante pero existe el riesgo de que un individuo y/o unas circunstancias que, aunque proporcionalmente, subieran o bajaran los valores. Para evitarlo, se considera un valor en la zona mayor al que supera en cierto coeficiente al valor medio. De esta forma, la medida se auto-calibra sensiblemente y se podría ajustar aún más afinando el coeficiente, que en mi caso he conseguido experimentalmente durante las pruebas.

    Elegir para la medida los valores de la zona descendente (Rs) del pico qRs, lo más cercanos posible al máximo de la curva. Para saber que se abandona la zona ascendente basta con comprobar que un nuevo valor es menor que el anterior y verificar que no se ha encontrado todavía el valor buscado puesto que, en general, hay varios valores en la zona descendente de qRs dependiendo de la densidad de muestreo. Para cronometrar el pulso se almacena el valor del instante en el que se encontró el punto (los milisegundos devueltos por millis()) y se compara con el siguiente.

    Para asegurarse de que el valor que se mide es el mayor de la zona descendente de la curva más alta se usa una variable booleana (medir_pulso en este ejemplo y medicion_de_pulso_activa en la librería) que se activa al entrar en la zona ascendente de la curva mayor y se desactiva una vez encontrado el primer valor descendente, que es el cronometrado.

    Como lo habitual es representar la duración del pulso como pulsaciones por minuto (ppm) se corrige el valor de tiempo entre pulsaciones obtenido calculando dividiendo el tiempo total de la representación (un minuto, 60000 milisegundos) entre el intervalo obtenido al restar los milisegundos actuales (del valor actual) entre los anteriormente cronometrados.

    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 dispositivo de medición del pulso. Por esto, es recomendable relajar mucho estos valores de manera que no se pierdan los extremos, que precisamente podrían mostrar aspectos relevantes.

    El nuevo valor medio se calcula disminuyendo la relevancia de la media actual en función del número de valores muestreados y se añade el último valor, ponderado también con un coeficiente que lo reduce más cuanto más valores se hayan medido hasta el momento.

Por último, utilizando el algoritmo descrito antes, desarrollé la librería para calcular el pulso detectando la presencia de la hemoglobina o la oxihemoglobina (según la longitud de onda de la luz usada) del código de abajo.

La librería espera que se llame periódicamente a la función de muestreo monitorizar_pulso() para ir calculando el pulso, que puede consultarse con la función ultimo_pulso() o con la función pulso_medio() el pulso medio. Además de por ser un recurso limitado, descarté utilizar interrupciones por no necesitar valores inmediatos sino sostenidos en el tiempo para monitorizar el pulso en mi proyecto de gestión del sueño . En cualquier caso, por las pruebas que he hecho, no parece ser necesario; ya sea por el dispositivo o por el comportamiento del pulso, un muestreo a determinada frecuencia ofrece suficiente información y no se obtiene mucha más (relevante) por aumentarlo ni es posible disminuirla mucho sin perder datos relevantes para el cálculo; en las primeras versiones del código para monitorizar la lectura del oxímetro de pulso descubrí que no era necesario atenerse a un tiempo de medida máximo ya que, si se consideraban correctamente las variaciones de valores sucesivos, era muy cercano al mínimo.

En el siguiente programa de ejemplo se muestra cómo utilizar la librería anterior para medir el pulso con un oxímetro de pulso. Además de instanciar la clase Pulso se llama periódicamente a la monitorización del nivel de oxihemoglobina/hemoglobina y con una periodicidad menor se muestra el valor del pulso calculado y de la media.

Para asegurar que las medidas son relevantes se programa una espera antes de mostrar ningún valor. Como el valor puede ser incorrecto (por ejemplo si el usuario se retira el dispositivo) sólo se muestran valores si están dentro del rango de los considerados válidos.

Víctor Ventura

Desarrollando aplicaciones para la web conocí el potencial de internet de las cosas, encontré la excusa perfecta para satisfacer la inquietud de aprender electrónica que he tenido desde siempre y ahora puedo darme el gusto de programar las cosas que yo mismo diseño y fabrico.

Más entradas - Página web

Sígueme:
TwitterLinkedIn

8 Respuestas

  1. hola que tal me intereso la informacion me gustaria tener el esquematico del pulso oximetro para la utilisacion de esa libreria

    gracias

  2. donde encuantro esa libreria

  3. Jeisson Ochoa

    hola me gustaría saber cual es la periodicidad con la que se debe encender el led rojo y el infrarrojo con el arduino para la medición de oxihemoglobina/hemoglobina gracias

    • Hola, Jeisson.

      Los LED de cada color se encienden cuando va a leerse cada uno de ellos, no es buena idea mantener ambos encendidos a la vez, ya que se puede falsear la lectura porque, salvo parejas emisores-receptores muy bien limitadas en longitud de onda, los receptores de infrarrojos detectan algo de la luz roja y al contrario.

      El uso de la librería prevé que se tomen muestras cada vez que sea posible, es decir, a lo largo del programa en el que la uses, debes incluir monitorizar_pulso() cada vez que sea posible.

      Si el teorema de muestreo de Nyquist-Shannon es cierto 🙂 debes muestrear a una frecuencia superior al doble de la que esperas medir, pero la forma (muy simple) de calcular el pulso que hace la librería necesita encontrar puntos en los que ascienda y descienda el nivel, digamos cuatro. Dependiendo del pulso máximo que esperes, digamos 150 ppm (2.5 Hz) sería necesario tomar, para tener un mínimo de seguridad, 2×4×2.5=20 muestras cada segundo, es decir, una muestra cada 50 ms. Si te fijas en el código de ejemplo, tomo muestras cada 20 ms y funciona bien. La verdad, no he probado a 50 ms, solamente teorizo para ayudarte a entender cómo lo he planteado.

      Sería fenomenal que nos contaras el resultado de tus pruebas. Y por supuesto, se admiten sugerencias.

      Gracias por visitar polaridad.es y un saludo.

  4. Buenas tardes.

    Gracias por el artículo, me ha servido de gran utilidad. Lo único, es una tontería pero hay una pequeña errata en el segundo gráfico del pulso (el de color verde), donde pone “IrrigacióN (Y) a lo largo del tiempo (X en ms) |—-| pulso ¬ 91 ms ¬ 66 ppm”, haciendo referencia a la detección entre pulso y pulso, donde el gráfico indica que hay 91 ms cuando debería ser 910 ms

    Gracias por el artículo, un saludo.

    • Hola, Javier.

      Tienes razón. Que fuera verde (y no «color polaridad.es») tendría que haberme dado una pista de que el gráfico no era el bueno.

      Ya está corregido para no confundir a los lectores.

      Gracias por avisar 🙂

Dejar una opinión