Análisis de Señales Biomédicas y Filtrado
Análisis de Señales Biomédicas y Filtrado
1
ÍNDICE
DIAGRAMA DE GANTT 3
HISTORIAL DE REUNIONES 4
INTRODUCCIÓN 5
DESCRIPCIÓN DE LAS SEÑALES 6
Señal ECG 6
Señal de presión sanguínea 6
ANÁLISIS DE LAS SEÑALES PROPORCIONADAS EN LAS PRÁCTICAS 4 (COMPLETA) Y 6
(SEGUNDA PARTE) 8
SEÑAL ECG Y SEÑAL DE PRESIÓN SANGUÍNEA 8
SEÑAL DE PRESIÓN SANGUÍNEA Y SONIDOS DE KOROTKOFF 21
SEÑAL DE VOLUMEN SANGUÍNEO 32
ANÁLISIS FRECUENCIAL DE LOS SONIDOS DE KOROTKOFF 37
ANÁLISIS DE SEÑALES PROPORCIONADAS EN ATENEA 42
SEÑALES REGISTRADAS 42
RUIDO I/O INTERFERENCIAS 44
Dominio temporal 44
Dominio frecuencial 46
FILTRADO I ACONDICIONAMENTO DE LAS SEÑALES 49
Presión Sanguínea 49
Sonidos de Korotkoff 51
ECG 53
ANÁLISIS DE LAS SENYALS ECG, PRESIÓN SANGUÍNEA Y SONIDOS DE KOROTKOFF
53
ECG 53
Presión Sanguínea 55
Sonidos de Korotkoff 57
CONCLUSIONS 59
BIBLIOGRAFIA 60
ANNEX I. CODI DELS ALGORITMES AMB COMENTARIS 61
2
DIAGRAMA DE GANTT
A continuación se muestra el diagrama de Gantt que recoge la planificación llevada a cabo
para abordar el trabajo. Ha sido una herramienta de gran ayuda para organizarnos y seguir
los plazos del proyecto con tranquilidad. No se ha realizado ninguna modificación respecto
al documento entregado al inicio del proyecto, ya que la planificación planteada fue muy
realista y, aunque en algunas partes se ha producido algún pequeño retraso, estamos muy
satisfechos con la organización seguida.
Donde:
3
HISTORIAL DE REUNIONES
Para la realización y coordinación del presente proyecto, el equipo de trabajo mantuvo una
comunicación constante y realizó el seguimiento de las tareas mediante diversas sesiones
virtuales. Todas las comunicaciones de coordinación se llevaron a cabo a través de la
plataforma Whatsapp y Google Meet.
4
INTRODUCCIÓN
Las señales biomédicas son señales que permiten obtener información sobre el
funcionamiento del cuerpo humano, como por ejemplo la actividad cardíaca, la presión
sanguínea, la actividad muscular o la respiración. Este tipo de señales suelen tener una
amplitud muy pequeña, del orden de los milivoltios, y además están afectadas por diferentes
tipos de ruido e interferencias que dificultan su análisis, como el ruido de la red eléctrica, el
movimiento del paciente o imperfecciones en el sistema de adquisición.
En la segunda parte del proyecto se trabaja con señales registradas durante la asignatura de
Sensores y Acondicionadores de Señales Biomédicas, concretamente un ECG, un
electromiograma (EMG) y una señal respiratoria. En este caso, el análisis se centra
especialmente en la detección de ruido e interferencias y en la aplicación de técnicas de
filtrado y acondicionamiento adaptadas a señales reales.
Para llevar a cabo todo el procesado y análisis de las señales se ha utilizado el programa
Matlab, que permite trabajar de forma cómoda con señales biomédicas y aplicar
herramientas como la Transformada Rápida de Fourier (FFT), el uso de ventanas y el diseño
de filtros digitales.
5
DESCRIPCIÓN DE LAS SEÑALES
Señal ECG
La señal electrocardiográfica (ECG) representa la actividad eléctrica del corazón a lo largo
del tiempo y permite analizar el funcionamiento del sistema cardíaco. Se trata de una señal
de baja amplitud, del orden de los milivoltios, y de naturaleza periódica, asociada al ciclo
cardíaco.
Un latido cardíaco típico en la señal ECG consta de diferentes ondas características: la onda
P, asociada a la despolarización auricular; el complejo QRS, que corresponde a la
despolarización ventricular; y la onda T, relacionada con la repolarización ventricular. De
estos componentes, el complejo QRS presenta la mayor amplitud y pendiente, lo que facilita
su detección automática.
Debido a su baja amplitud, la señal ECG suele verse afectada por ruido e interferencias,
como el ruido de la red eléctrica, el movimiento del paciente o el ruido muscular. Por este
motivo, es necesario aplicar técnicas de filtrado antes de su análisis. A partir de la detección
de los complejos QRS, es posible obtener parámetros como la frecuencia cardíaca y
establecer relaciones temporales con otras señales biomédicas, como la presión arterial.
El rango normal de la presión sistólica en un adulto en reposo se sitúa entre 100 y 139 mmHg,
mientras que el rango normal de la presión diastólica en un adulto en reposo se encuentra
entre 60 y 89 mmHg. No obstante, para evitar la pulsatilidad de la señal, la presión arterial
media (PAM) se calcula como (presioˊ n sistoˊ lica + 2 × presioˊ n diastoˊ lica)/3 , ya que esta
expresión tiene en cuenta una aproximación geométrica al calcular el valor medio.
6
En la práctica de laboratorio (4), esta medida no se realiza de forma directa, sino que se
obtiene a partir de la señal de ECG. El ritmo cardíaco se detecta a partir de la derivada del
ECG: cuando dicha derivada alcanza valores máximos, representa la pendiente máxima del
complejo QRS y, al superar un determinado umbral, se detecta cada latido cardíaco.
Sonidos de Korotkoff
Los sonidos de Korotkoff fueron descritos por el cirujano
ruso Nikolái Serguéievich Korotkoff (1874–1920) a
comienzos del siglo XX. Este descubrimiento representó un
avance fundamental en la medicina clínica, ya que permitió
el desarrollo de un método no invasivo, fiable y reproducible
para la medición de la presión arterial. Gracias a su sencillez
y eficacia, este procedimiento continúa siendo el método
estándar utilizado en la práctica médica actual.
El análisis de estos sonidos permite identificar distintas fases, conocidas como sonidos de
Korotkoff, que se interpretan de la siguiente manera:
Este método auscultatorio permite obtener valores precisos de la presión arterial y sigue
siendo una herramienta esencial para el diagnóstico y seguimiento de numerosas patologías
cardiovasculares.
7
ANÁLISIS DE LAS SEÑALES
PROPORCIONADAS EN LAS PRÁCTICAS 4
(COMPLETA) Y 6 (SEGUNDA PARTE)
SEÑAL ECG Y SEÑAL DE PRESIÓN SANGUÍNEA
En este apartado analizaremos una señal ECG y una de presión sanguínea. La función
utilizada es la siguiente:
% DEFINICIONES
derSenyal = diff(senyal);
numMostres = length(derSenyal);
offset = 50;
llindar = .5*max(derSenyal(1:2500));
comptador = 0;
mostra = 400;
8
plot([marques marques]'/250, repmat(get(gca, 'Ylim')', size(marques')),
'k--');
title('ECG')
xlabel('temps (s)')
hold off;
xlim([2 4]);
pan xon;
9
a) Cálculo del ritmo cardíaco en latidos/minuto a partir de la señal ECG
A partir de dichas marcas, se calcula la diferencia entre marcas consecutivas, lo que permite
obtener el intervalo RR expresado en número de muestras. Estos intervalos se convierten a
tiempo dividiendo por la frecuencia de muestreo. El ritmo cardíaco se obtiene como el
inverso del intervalo RR multiplicado por 60, obteniendo así el ritmo cardíaco latido a latido
en pulsaciones por minuto. Finalmente, se calcula el valor medio del ritmo cardíaco durante
todo el registro.
marques = detecrc(ecg1);
fs = 250;
RR_mostres = diff(marques);
RR_segons = RR_mostres / fs;
ritmocardiaco = 60 ./ RR_segons;
>> mean(ritmocardiaco)
ans =
69.2330
Para ello, se utilizan las marcas obtenidas previamente a partir de la detección de los
complejos QRS del ECG. Estas marcas delimitan cada latido y permiten analizar la señal de
presión sanguínea de forma latido a latido.
10
Presión sistólica
Para cada intervalo comprendido entre dos marcas consecutivas, la presión sistólica se
obtiene como el valor máximo de la señal de presión sanguínea en dicho intervalo. Este
procedimiento se repite para todos los latidos del registro, obteniéndose así un vector con
los valores de presión sistólica latido a latido.
for i = 1:length(marques)-1
psis(i) = max(psan(marques(i):marques(i+1)));
end
figure;
plot(psis)
xlabel('Latido')
ylabel('Presión sistólica (mmHg)')
title('Presión sistólica durante el registro')
>> mean(psis)
ans =
124.9763
11
Presión diastólica
for i = 1:length(marques)-1
pdias(i) = min(psan(marques(i):marques(i+1)));
end
figure;
plot(pdias)
xlabel('Latido')
ylabel('Presión diastólica (mmHg)')
title('Presión diastólica durante el registro')
mean(pdias)
ans =
67.3853
12
Presión de pulso
figure;
plot(pp)
xlabel('Latido')
ylabel('Presión de pulso (mmHg)')
title('Presión de pulso durante el registro')
>> mean(pp)
ans =
57.5910
13
Presión arterial media (MAP)
Finalmente, se calcula la presión arterial media (MAP), la cual se puede expresar como:
𝑃𝑃 𝑃sistoˊ lica + 2 ⋅ 𝑃diastoˊ lica
𝑀𝐴𝑃 = + 𝑃diastoˊ lica =
3 3
Utilizando la expresión, se obtiene la MAP latido a latido:
figure;
plot(MAP)
xlabel('Latido')
ylabel('Presión arterial media (mmHg)')
title('Presión arterial media durante el registro')
mean(MAP)
ans =
86.5823
Por tanto, a partir del análisis latido a latido de la señal de presión sanguínea, se han obtenido
los valores de presión sistólica, presión diastólica, presión de pulso y presión arterial media,
cuyos valores medios se encuentran dentro de rangos compatibles con una presión arterial
normal en reposo. Cabe destacar que los valores normales de estos parámetros pueden
variar en función de la edad, el sexo y las condiciones fisiológicas del paciente.
14
c) El pulso cardíaco en pulsos por minuto obtenido de dos maneras
diferentes: a partir de los instantes en los que se alcanza la presión
sistólica y a partir de los instantes en los que se alcanza la presión
diastólica.
A partir de estas marcas, se utiliza la función diff de Matlab para obtener la diferencia entre
marcas consecutivas, lo que proporciona el número de índices que hay entre un pulso y el
siguiente. Dado que estos índices no se han convertido a segundos, se puede calcular el
número total de pulsos producidos durante el registro dividiendo la longitud total de la señal
entre dicha diferencia. Finalmente, dividiendo este resultado entre la duración total del
registro (20 minutos), se obtiene el pulso cardíaco en pulsos por minuto.
for i = 1:length(marques)-1
[psis(i), idx_max(i)] = max(psan(marques(i):marques(i+1)));
marquessistole(i) = marques(i) + idx_max(i) - 1;
pulsossistole = mean(pminsist);
pulsossistole
pulsosdiastole = mean(pmindias);
pulsosdiastole
figure;
plot(pminsist)
hold on
plot(pmindias)
xlabel('Pulso')
ylabel('Pulsos por minuto')
title('Pulso cardíaco calculado a partir de presiones sistólicas y
diastólicas')
legend('Pulso por presión sistólica', 'Pulso por presión diastólica')
15
Figura 10: Pulso cardíaco calculado a partir de presiones sistólicas y diastólicas
>> apartadoc
pulsossistole =
69.5023
pulsosdiastole =
71.6919
En este apartado se calcula el retardo temporal existente entre la onda R del ECG y el
instante en el que se alcanza la presión sistólica en la señal de presión sanguínea. Este
retardo está asociado al tiempo que tarda la onda de presión generada por la contracción
ventricular en propagarse desde el corazón hasta el punto de medida de la presión.
Para su cálculo, basta con obtener la diferencia entre los índices correspondientes a las
marcas de las ondas R del ECG y las marcas de los máximos de la señal de presión
sanguínea (presiones sistólicas). Dicha diferencia se expresa inicialmente en número de
muestras y posteriormente se convierte a tiempo dividiendo por la frecuencia de muestreo
de la señal.
retardorsist = abs(mean(retardos(:)));
retardorsist
16
retardorsist =
0.1466
e) Aislar una onda T, realizar la correlación cruzada con el ECG para obtener
los valores máximos de la onda T y calcular el retardo entre la presión
sistólica y la onda T.
Para el cálculo del retardo entre la onda T del ECG y la presión sistólica, se emplea un
procedimiento basado en la correlación cruzada. En primer lugar, a partir de la
representación del ECG filtrado y de las marcas obtenidas mediante la rutina detecrc, se
selecciona de forma visual una onda T representativa del registro. En este caso, se ha
escogido la onda T comprendida entre los índices 𝑋 = 660 y 𝑋 = 708.
Una vez aislada la onda T en un vector, se utiliza la función xcorr de Matlab para realizar la
correlación cruzada entre la señal ECG completa y el vector correspondiente a la onda T.
Este proceso permite detectar automáticamente los instantes temporales en los que aparece
la onda T a lo largo de todo el registro, ya que la correlación alcanza valores máximos cuando
ambas señales presentan una alta similitud.
ondat = ecg1(660:708);
marcast = detecrc(correlacion);
retardot_medio = abs(mean(retardot(:)));
retardot_medio
>> apartadoe
retardot_medio =
2.2840
17
Resultados
Si se superponen los gráficos de los tres métodos para obtener los latidos por minuto, se
observa la siguiente gráfica:
En esta figura se muestran los latidos por minuto obtenidos a partir de tres métodos distintos:
ECG, presión sistólica y presión diastólica. Se observa que, durante la mayor parte del
registro, los tres métodos proporcionan valores muy similares, con un ritmo cardíaco medio
estable alrededor de 65–75 BPM, lo que indica una buena coherencia entre los distintos
procedimientos de cálculo.
18
Figura 12: Evolución de las diferentes presiones durante el registro
La presión sistólica presenta los valores más elevados, seguida de la presión arterial media
y la presión diastólica, mientras que la presión de pulso refleja correctamente la diferencia
entre las presiones sistólica y diastólica. Se aprecia algún descenso puntual brusco,
atribuible a errores de detección o artefactos de la señal, que no alteran la tendencia global
del registro.
19
Figura 13: Retardo entre onda T y R vs. Presión sistólica
En esta figura se muestran los retardos temporales entre la onda R del ECG y la presión
sistólica, así como entre la onda T y dicha presión. Se observa que el retardo correspondiente
a la onda R presenta valores reducidos y relativamente constantes a lo largo del registro, lo
que concuerda con el corto intervalo temporal existente entre la despolarización ventricular
y la aparición del máximo de presión.
20
SEÑAL DE PRESIÓN SANGUÍNEA Y SONIDOS DE
KOROTKOFF
En esta sección vamos a analizar los sonidos de Korotkoff. En primer lugar, subimos el
archivo al Matlab [Link] proporcionado con señales de presión, sonidos Korotkoff y
ECG de un sujeto. La frecuencia de muestreo es de 500 Hz.
Inicialmente obtuvimos las señales proporcionadas, con quien trabajaremos más adelante:
Para comparar mejor visualmente los sonidos de Korotkoff con el electrocardiograma, hemos
superpuesto ambas señales de la siguiente manera:
21
Figura 16: Sonidos de Korotkoff superpuestos al ECG ampliado
Resulta interesante notar el retraso constante con el que el pico sonoro sigue al impulso
eléctrico: este intervalo representa el tiempo de tránsito de la onda de presión desde el
corazón hasta el punto de detección braquial. La ampliación permite apreciar la naturaleza
oscilatoria de cada latido individual capturado por el sensor.
Esta imagen ofrece una perspectiva más amplia, documentando todo el procedimiento de
medición de la presión arterial: inicialmente, no se oyen sonidos; sin embargo, a medida que
la presión externa varía durante la prueba, las señales acústicas se vuelven progresivamente
más intensas y turbulentas, alcanzando su amplitud máxima alrededor del segundo 26. A
partir de este punto, la señal tiende a atenuarse hasta casi desaparecer por completo
alrededor del segundo 31, lo que indica el momento en que el flujo sanguíneo vuelve a ser
laminar y deja de ser audible para el sistema.
22
Por lo tanto, se requiere determinar la presión sistólica y la diastólica a partir del momento
en que los sonidos aparecen y desaparecen. Asimismo, se solicita calcular el desfase entre
la onda R y el sonido, así como entre la onda T y el sonido. En la gráfica siguiente puede
observarse que existe una relación muy estrecha entre la presión arterial y los ruidos de
Korotkoff:
23
Presión sistólica y diastólica de los sonidos de Korotkoff
Tras aplicar este filtro, se confirma que solo permanecen en el registro las señales vinculadas
a los ruidos de Korotkoff. El siguiente paso fundamental es localizar los sonidos que marcan
la presión sistólica y la presión diastólica. La lógica aplicada es directa: el primer ruido
detectado se asocia a la presión sistólica, mientras que el último corresponde a la diastólica.
Obtienes:
24
Una vez que los picos han sido localizados y marcados, el sistema permite determinar los
valores de presión sistólica y diastólica del sujeto. Para obtener estos resultados, se ha
desarrollado el siguiente código en MATLAB:
indices_sonidos = marcas_sonidos(:);
presion_diastolica = [];
presion_sistolica = [];
tiempos_diastolica = [];
tiempos_sistolica = [];
if numel(indices_sonidos) >= 1
% La primera sistólica se asocia al primer sonido detectado
presion_sistolica = [presion_sistolica; presion(indices_sonidos(1))];
tiempos_sistolica = [tiempos_sistolica; indices_sonidos(1)];
for k = 1:numel(indices_sonidos)-1
% Si hay un silencio largo (> 2 s), detectamos el fin de una fase
y el inicio de otra
if (indices_sonidos(k+1) - indices_sonidos(k)) > 1000
presion_diastolica = [presion_diastolica;
presion(indices_sonidos(k))];
presion_sistolica = [presion_sistolica;
presion(indices_sonidos(k+1))];
Para mayor claridad, hemos organizado los resultados obtenidos en la siguiente tabla:
Ruido 1 2 3 4 5 6 7
Presión 128.62 113.48 111.82 115.47 118.07 118.81 108.03
Sistólica
(mmHg)
Presión 68.289 68.099 25.849 68.037 68.838 69.829 68.682
Diastólica
(mmHg)
Tabla 1. Resumen de presión sistólica y diastólica
25
Los valores obtenidos concuerdan con los rangos estándar para la población adulta media.
Por consiguiente, se puede concluir que este algoritmo es capaz de calcular de manera
automática las presiones sistólica y diastólica. Cabe destacar que, para el correcto
funcionamiento del proceso, es imprescindible disponer de las señales completas desde el
primer momento, ya que la identificación de la presión sistólica se realiza mediante un
análisis en sentido inverso del registro. A continuación, se presenta la gráfica comparativa
que relaciona ambos niveles de presión:
Como era de esperar, se observa claramente que la presión sistólica es mayor que la
diastólica.
26
Retraso entre la onda R y los sonidos
Ahora se calcula el retardo entre la onda R y los ruidos de Korotkoff; para calcular este
retraso, usamos el siguiente código con la función detec_ona_R:
if ~isempty(indices_sonidos_on)
marcas_inicio_sonido = indices_sonidos_on( diff([0;
indices_sonidos_on]) > 50 );
else
marcas_inicio_sonido = [];
end
retardo_R_sonido = [];
i_ptr = 1;
j_ptr = 1;
% Conversión a segundos
diff_tiempo = (marcas_inicio_sonido(i_ptr) - localizaciones_R(j_ptr))
/ 500;
if diff_tiempo < 0
i_ptr = i_ptr + 1;
else
retardo_R_sonido(end+1) = diff_tiempo;
i_ptr = i_ptr + 1;
j_ptr = j_ptr + 1;
end
end
27
Figura 22: Evolución del retardo entre la onda R y los sonidos de Korotkoff
El siguiente paso del análisis consiste en determinar el retraso entre el vértice de la onda T y
la aparición de los ruidos de Korotkoff. Para aislar correctamente estas referencias, se
manipuló la señal original del ECG para eliminar los complejos de ondas R. El algoritmo
funciona basándose en que, al no encontrar los picos dominantes de la onda R, identifica y
extrae las ondas T como puntos de referencia principales. A continuación, se muestra el
segmento de código implementado para filtrar y eliminar las ondas R y el gráfico resultante
adjunto:
retardo_T_sonido = [];
i_ptr = 1;
j_ptr = 1;
if diff_T < 0
i_ptr = i_ptr + 1;
else
retardo_T_sonido(end+1) = diff_T;
i_ptr = i_ptr + 1;
j_ptr = j_ptr + 1;
end
end
28
Figura 23: Señal cardíaca sin onda R
Una vez aisladas las ondas T mediante la manipulación del ECG, se generó el gráfico a
continuación, que ilustra el retraso de cada latido analizado:
Figura 24: Análisis estadístico del retardo temporal entre la onda T y los ruidos de Korotkoff
29
%% 3. Cálculo del retardo entre la onda T y los ruidos de Korotkoff
retardo_T_final = [];
i_ptr = 1;
j_ptr = 1;
if diff_T_calc < 0
% Si el ruido aparece antes que la onda T, pasamos al siguiente
ruido
i_ptr = i_ptr + 1;
else
% Retardo válido encontrado dentro del rango fisiológico
retardo_T_final(end+1) = diff_T_calc;
i_ptr = i_ptr + 1;
j_ptr = j_ptr + 1;
end
end
30
Finalmente, este código puede identificar los ruidos más cercanos a las ondas T presentes
en la señal del ECG, y se presenta el siguiente gráfico para visualizar el retraso entre los
ruidos y las ondas T:
En este último gráfico, presentamos los resultados del cálculo del retraso entre la señal
eléctrica del corazón (onda T) y el momento en que se escucha el sonido en el brazo. La
variación mostrada depende de cómo se modifica la presión arterial en las arterias durante
la prueba.
El hecho de que el gráfico sea tan detallado demuestra que filtrar las ondas R de la señal
funcionó bien, ya que permitió al programa encontrar siempre la onda T y calcular el tiempo
preciso de cada latido individual sin confusión.
31
SEÑAL DE VOLUMEN SANGUÍNEO
En este apartado se presentan los resultados obtenidos a partir del análisis de la señal de
pulso sanguíneo y su comparación con la señal electrocardiográfica (ECG). El estudio se ha
realizado utilizando una frecuencia de muestreo de 200 Hz y una duración total del registro
de 45 segundos, lo que garantiza una resolución temporal suficiente para la detección
precisa de los eventos cardíacos.
El objetivo principal es analizar la relación temporal entre la actividad eléctrica del corazón
(ECG) y la respuesta mecánica asociada al flujo sanguíneo periférico, así como validar la
coherencia entre ambas medidas mediante el cálculo del ritmo cardíaco.
Una vez detectadas las marcas R, se ha calculado la diferencia temporal entre marcas
consecutivas, que corresponde a la duración de cada ciclo cardíaco. Posteriormente, se ha
obtenido la frecuencia cardíaca instantánea dividiendo la longitud total de la señal entre
dichos intervalos. Finalmente, se ha aplicado un factor de corrección teniendo en cuenta que
la duración del registro es de 45 segundos, equivalente a tres cuartos de minuto, con el fin
de expresar el resultado en latidos por minuto (BPM).
% Resultat promig
mean(latmin)
Resultado: El ritmo cardíaco medio obtenido a partir de la señal ECG es de 76.3825 latidos
por minuto, un valor coherente con una situación fisiológica de reposo.
32
Figura 26: Detección de complejos QRS (onda R
Este procedimiento permite caracterizar la amplitud del pulso sanguíneo asociada a cada
latido cardíaco.
33
Resultados (valores medios en mV):
Estos valores reflejan una variabilidad adecuada de la señal de pulso sanguíneo y una
correcta detección de las oscilaciones asociadas a cada latido cardíaco.
Se ha modificado el algoritmo anterior para obtener los índices temporales en los que se
producen los máximos del pulso sanguíneo (utilizando la función find). A partir de estos
índices, se ha calculado la diferencia entre ellos y se ha derivado la frecuencia cardíaca de
forma análoga a la descrita en el apartado a).
34
Resultado: El ritmo cardíaco medio calculado a partir del pulso sanguíneo es de 76.5417
pulsos por minuto.
(Los resultados son muy similares a los obtenidos a partir del ECG, lo que valida el método
empleado).
Se calcula la diferencia absoluta entre los índices correspondientes a las ondas R y los
índices de los máximos del pulso, dividiendo dicho valor por la frecuencia de muestreo
(200 Hz) para expresarlo en segundos.
Código:
Código:
35
Resultado: El retardo medio entre la onda T y el pico de presión es de 0.6657 s.
36
ANÁLISIS FRECUENCIAL DE LOS SONIDOS DE
KOROTKOFF
Para este apartado se analiza el contenido frecuencial de los sonidos de Korotkoff
registrados durante la medida de presión arterial. Se utiliza el archivo
korotkoff_retallats_2024.mat, que contiene siete registros (so1…so7) muestreados a
Fs=500Hz. El objetivo es obtener la Distribución Espectral de Energía (DEE) de sonidos
individuales y cuantificar su evolución mediante la frecuencia media (MNF) y la frecuencia
mediana (MDF).
Metodología
Preprocesado
Cada registro se centra eliminando el valor medio:
𝑥" [𝑛] = 𝑥[𝑛] − 𝑥[𝑛]
para reducir el componente DC y facilitar el análisis espectral.
El criterio para la selección de los sonidos, se implementó un procedimiento semiautomático
reproducible para localizar los instantes de los sonidos y mantener el mismo criterio en los
siete registros. En clase se planteó la posibilidad de seleccionar los sonidos manualmente.
Sin embargo, por limitaciones prácticas (disponibilidad de equipo/tiempo de trabajo), se
siguió el criterio semiautomático.
Cálculo de la DEE
Se calcula la FFT con NFFT = 2048 (zero-padding) y se obtiene el semiespectro (0 a Fs/2).
La DEE se define como:
𝐷𝐸𝐸[𝑘] = |𝑋[𝑘]|#
Para comparar entre sonidos, se representa la DEE normalizada:
𝐷𝐸𝐸_𝑁[𝑘] = 𝐷𝐸𝐸[𝑘]/𝑚𝑎𝑥(𝐷𝐸𝐸[𝑘])
En las figuras se limita el rango a 0–200 Hz para visualizar la región donde se concentra la
energía en estos registros.
37
Cálculo de MNF y MDF
La frecuencia media (MNF) se calcula como promedio ponderado por energía:
𝑀𝑁𝐹 = 𝛴$ 𝑓[𝑘] · 𝐷𝐸𝐸[𝑘]/𝛴$ 𝐷𝐸𝐸[𝑘]
La frecuencia mediana (MDF) se define como la frecuencia donde la energía acumulada
alcanza el 50%:
1
$ 𝐷𝐸𝐸 = $ 𝐷𝐸𝐸[𝑘]
2
" $ %&' 𝒌
Resultados
Registro MNF media ± std (Hz) MDF media ± std (Hz)
so1 68.30 ± 6.85 63.15 ± 8.36
so2 66.16 ± 4.62 61.98 ± 7.58
so3 63.16 ± 3.05 60.85 ± 5.11
so4 60.70 ± 7.82 56.67 ± 7.39
so5 66.57 ± 5.27 66.27 ± 4.07
so6 67.13 ± 5.94 65.54 ± 6.55
so7 65.08 ± 8.33 64.53 ± 10.04
Tabla 2. Resumen de MNF y MDF (media ± desviación típica) por registro (so1…so7)
Se aprecia una disminución de los valores medios desde so1 hasta so4, seguida de un
aumento en so5–so6 y una ligera reducción en so7. Esto indica que el contenido espectral
varía entre registros.
38
Distrubución espectral de energía (DEE)
Figuras 29: DEE normalizadas de los sonidos de Korotkoff detectados en el registro so1…so7 (Fs=500 Hz,
NFFT=2048). Rango mostrado: 0–200 Hz.
39
Evolución de MNF y MDF
Figuras 30: Evolución de la frecuencia media (MNF) y frecuencia mediana (MDF) para los sonidos consecutivos
del registro so1…so7
40
En las Figuras correspondientes a las DEE normalizadas de los sonidos de Korotkoff (so1–
so7) se observa que, en todos los registros, la mayor parte de la energía espectral se
concentra en un rango de bajas frecuencias, aproximadamente entre 40 y 100 Hz. Este
comportamiento es coherente con la naturaleza acústica de los sonidos de Korotkoff, que
presentan un contenido predominantemente grave asociado a la turbulencia del flujo
sanguíneo durante la descompresión del manguito.
Para cada medida de presión se han representado de forma superpuesta las DEE de los
sonidos consecutivos detectados. En general, las curvas muestran una forma espectral
similar, aunque con ligeros desplazamientos del máximo y variaciones en la anchura del
espectro. Estas diferencias reflejan la variabilidad natural entre latidos y el carácter no
estacionario de la señal, además de la sensibilidad del análisis a la segmentación temporal
de cada sonido.
41
ANÁLISIS DE SEÑALES PROPORCIONADAS
EN ATENEA
SEÑALES REGISTRADAS
En esta segunda parte del proyecto se analizan las señales biomédicas proporcionadas
como ejemplo en ATENEA. El archivo contiene tres canales diferentes que corresponden a
la presión sanguínea, a los sonidos de Korotkoff registrados mediante un estetoscopio y a
una señal de electrocardiograma.
Las señales presentan una frecuencia de muestreo de 5000 Hz, que se ha calculado a partir
del intervalo de muestreo proporcionado en el archivo, de 0,2 ms. Esta frecuencia es
adecuada para la señal de presión sanguínea, ya que presenta una evolución lenta en el
tiempo, pero limita la resolución temporal de las señales de sonidos de Korotkoff y del ECG.
Además, las señales de presión sanguínea se miden en mmHg, mientras que los sonidos de
Korotkoff y el ECG se expresan en milivoltios (mV).
En la siguiente figura se representan las tres señales registradas sin ningún tipo de
preprocesado; no obstante, en el caso del ECG, la señal se ha obtenido aplicando un filtro
paso banda de 0,5 Hz a 35 Hz, tal y como se indica en la descripción de los datos, con el
objetivo de eliminar la deriva de la línea base y el ruido de alta frecuencia antes de su análisis.
load [Link]
Fs = 1/(isi*1e-3)
t = (0:size(data,1)-1)/Fs;
presion = data(:,1);
korotkoff = data(:,2);
ecg = data(:,3);
42
Figura 31. Registro de las señales en el dominio temporal.
Podemos observar las tres señales registradas en el dominio temporal. La señal de presión
sanguínea muestra una fase inicial de incremento seguida de un descenso progresivo, lo cual
está relacionado con el proceso de inflado y desinflado del manguito durante la medición.
Los sonidos de Korotkoff aparecen de forma intermitente durante intervalos concretos del
registro, mientras que la señal ECG muestra complejos QRS claramente identificables a lo
largo de todo el registro.
43
RUIDO I/O INTERFERENCIAS
En este apartado se analizan las tres señales con el objetivo de detectar los posibles ruidos
o interferencias que afectan y limitan las señales reales, tanto en el dominio temporal como
en el frecuencial. Estas señales se encuentran todavía sin filtrar, a excepción del filtro paso
banda de 0,5–35 Hz aplicado al ECG, previamente comentado, con el fin de poder identificar
fácilmente las fuentes de ruido presentes en cada señal proporcionada.
Dominio temporal
En la siguiente figura se muestra un intervalo común de las tres señales en el dominio
temporal, concretamente un zoom de las primeras 2000 muestras, que corresponden
aproximadamente a los primeros 0,4 segundos. De este modo, se puede observar con mayor
detalle la presencia de ruido y las irregularidades propias de las señales reales antes de
aplicar filtrados adicionales.
Ns = 2000;
figure
subplot(3,1,1)
plot(t(1:Ns), presion(1:Ns))
title('Presión sanguínia (dominio temporal)')
ylabel('mmHg')
grid on
subplot(3,1,2)
plot(t(1:Ns), korotkoff(1:Ns))
title('Sonidos de Korotkoff (dominio temporal)')
ylabel('mV')
grid on
subplot(3,1,3)
plot(t(1:Ns), ecg(1:Ns))
title('ECG (dominio temporal)')
ylabel('mV')
xlabel('Tiempo (s)')
grid on
44
Figura 32. Intervalo común, primeros 0,4 segundos.
En la señal de presión sanguínea se observa una evolución lenta con pequeñas variaciones
escalonadas superpuestas, que pueden atribuirse a la resolución limitada del sensor o del
sistema de adquisición, más que a oscilaciones fisiológicas rápidas.
Por otro lado, la señal correspondiente a los sonidos de Korotkoff presenta picos cortos de
elevada amplitud únicamente durante un intervalo concreto; fuera de este intervalo se
observa un cierto ruido de fondo asociado al entorno y al proceso de adquisición.
El ECG es la señal que muestra un mayor ruido de fondo continuo, superpuesto a los
complejos QRS. Se aprecia además una ligera inestabilidad de la línea base y pequeñas
fluctuaciones de baja frecuencia, probablemente producidas por la interferencia de la
respiración o por movimientos del paciente.
45
Dominio frecuencial
A continuación, se analiza cómo se traducen las interferencias observadas en el dominio
temporal al dominio frecuencial mediante el uso de la Transformada Discreta de Fourier
(DFT), con el objetivo de estudiar el contenido espectral de las señales reales. La primera
figura representada corresponde al espectro de la señal de presión sanguínea.
N = length(presion_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
P_FFT = abs(fft(presion_dc)).^2;
figure
plot(f(1:N/2), 10*log10(P_FFT(1:N/2)))
xlabel('Frecuencia (Hz)')
ylabel('Energía (dB)')
title('Espectro de la presión sanguínia')
grid on
46
Para el análisis en el dominio frecuencial de la señal de los sonidos de Korotkoff también se
ha eliminado la componente continua y se ha representado el espectro en escala logarítmica,
con el objetivo de facilitar la visualización del contenido espectral. Esto se ha conseguido
con el siguiente código:
N = length(kor_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
K_FFT = abs(fft(kor_dc)).^2;
figure
plot(f(1:N2), 10*log10(K_FFT(1:N2)))
xlabel('Frecuencia (Hz)')
ylabel('Energía (dB)')
title('Espectro de los sonidos de Korotkoff')
grid on
47
Para finalizar el análisis del dominio frecuencial, se estudia también el contenido de la señal
ECG mediante la Transformada Discreta de Fourier. Previamente, se ha eliminado la
componente continua de la señal, lo que facilita la visualización de la distribución de la
energía, y se ha representado el espectro en escala logarítmica con el siguiente código:
N = length(ecg_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
ECG_FFT = abs(fft(ecg_dc)).^2;
figure
plot(f(1:N2), 10*log10(ECG_FFT(1:N2)))
xlabel('Frecuencia (Hz)')
ylabel('Energía (dB)')
title('Espectro del señal ECG')
grid on
En el espectro del ECG se observa que la mayor parte de la energía se concentra en bajas
frecuencias, principalmente por debajo de los 35 Hz, lo cual concuerda con el prefiltrado
aplicado a la señal antes de su análisis. La ausencia de componentes significativas a
frecuencias más elevadas indica una correcta atenuación del ruido de alta frecuencia. No
obstante, se aprecia la presencia de energía residual a frecuencias muy bajas, que podría
asociarse a pequeñas derivas de la línea base.
48
FILTRADO I ACONDICIONAMENTO DE LAS SEÑALES
En este apartado se realiza el filtrado y acondicionamiento de las señales de ECG, presión
sanguínea y sonidos de Korotkoff, con el objetivo de reducir el ruido y las interferencias
presentes y permitir la aplicación de los algoritmos de análisis desarrollados previamente.
Presión Sanguínea
Comenzando por la señal de presión sanguínea, a partir del análisis realizado en el dominio
temporal y frecuencial se observa que la señal presenta una evolución lenta con la presencia
de pequeñas oscilaciones de alta frecuencia asociadas principalmente al ruido de medida.
Para reducir este ruido y preservar la información fisiológica relevante, se ha aplicado un
filtrado paso bajo:
Ns = 10000;
figure
plot(t(1:Ns), presion(1:Ns), '--b','LineWidth', 1.5)
hold on
plot(t(1:Ns), presion_filt(1:Ns), 'r')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Presión sanguínea antes y despues del filtraje')
legend('Original','Filtrada')
grid on
49
El procedimiento seguido ha consistido en el diseño de un filtro digital Butterworth de cuarto
orden con una frecuencia de corte de 15 Hz. Esta elección se debe a que este tipo de filtro
proporciona una respuesta suave en la banda de paso y permite atenuar el ruido de alta
frecuencia detectado sin introducir ondulaciones significativas en la señal. Además,
mediante el uso de la función filtfilt se ha evitado la aparición de desfases temporales.
Para visualizar mejor el efecto del filtrado, en la Figura 37 se muestra una ampliación de la
señal de presión sanguínea, lo que permite apreciar con mayor claridad la diferencia entre la
señal original y la señal filtrada, observándose un filtrado muy suave.
Como se puede observar, las diferencias entre la señal original y la señal filtrada son sutiles,
ya que la señal de presión sanguínea presenta principalmente componentes de baja
frecuencia. El filtrado permite suavizar pequeñas oscilaciones de alta frecuencia sin alterar
la forma global de la señal, que es, en definitiva, la información de interés.
50
Sonidos de Korotkoff
De acuerdo con el análisis previo en los dominios temporal y frecuencial, la señal de los
sonidos de Korotkoff presenta componentes espectrales distribuidas en un amplio rango de
frecuencias, además de ruido de fondo. Para aislar las componentes con información
fisiológica relevante y mejorar la relación señal-ruido, se ha aplicado un filtro paso banda.
f_low = 25;
f_high = 150;
Wn = [f_low f_high] / (Fs/2);
[b_k, a_k] = butter(4, Wn, 'bandpass');
kor_filt = filtfilt(b_k, a_k, korotkoff);
Ns = 2000; % 0.4 s
figure
plot(t(1:Ns), korotkoff(1:Ns), 'b')
hold on
plot(t(1:Ns), kor_filt(1:Ns), 'r')
xlabel('Tiempo (s)')
ylabel('mV')
title('Sonidos de Korotkoff antes y despues del filtrado')
legend('Original','Filtrado')
grid on
51
La comparación entre la señal original y la señal filtrada muestra una reducción significativa
del ruido de fondo y una mejor definición de los transitorios acústicos, lo que facilita la
detección posterior de los sonidos de Korotkoff y la estimación de la presión sistólica y
diastólica.
figure
plot(t(1:Ns), envolvente(1:Ns))
xlabel('Tiempo (s)')
ylabel('Amplitud')
title('Envolvente dels sonidos de Korotkoff')
grid on
52
ECG
En el caso del ECG no es necesario aplicar ningún filtrado adicional. Como se ha comentado
anteriormente, se trata de una señal que ya ha sido prefiltrada antes de su distribución. Dicho
prefiltrado corresponde a un filtro paso banda con frecuencias de corte de 0,5–35 Hz, cuyo
objetivo es eliminar la deriva de la línea base asociada a componentes de muy baja
frecuencia y atenuar el ruido de alta frecuencia que no contiene información fisiológica
relevante.
Tal y como se ha observado en el análisis del dominio frecuencial del apartado anterior, la
mayor parte de la energía del ECG se concentra por debajo de los 35 Hz. Esto confirma que
el filtrado aplicado es adecuado para preservar las componentes principales de la señal,
especialmente los complejos QRS. La ausencia de componentes significativas a frecuencias
más elevadas indica una correcta reducción del ruido de alta frecuencia. Este
acondicionamiento de la señal permite una detección robusta de los picos R y el cálculo
posterior de los intervalos RR y de la frecuencia cardíaca, que serán necesarios para el
análisis de las señales en el siguiente apartado. Por tanto, no es necesario aplicar ningún
filtrado adicional.
ECG
El análisis de la señal ECG tiene como finalidad caracterizar la actividad cardíaca a partir del
registro obtenido. En este subapartado se realiza la detección de los picos R,
correspondientes a los complejos QRS, y se calculan los intervalos RR y la frecuencia
cardíaca. Los algoritmos desarrollados en la práctica 4 se han adaptado para funcionar
correctamente con una señal real, teniendo en cuenta la presencia de ruido residual y
posibles variaciones en la amplitud de la señal.
53
La detección de los picos R se ha llevado a cabo mediante un algoritmo basado en la
identificación de máximos locales, utilizando criterios de umbral de amplitud y de distancia
mínima entre picos. Estos parámetros se han ajustado para evitar la detección de ruido y
garantizar una separación temporal con sentido fisiológico entre latidos consecutivos.
Obteniendo la siguiente figura a partir del código:
ecg_sig = ecg;
figure
plot(t, ecg_sig)
hold on
plot(loc_R, pk_R, 'ro')
xlabel('Temps (s)')
ylabel('mV')
title('Detecció de pics R en el senyal ECG')
grid on
A partir de la detección de los picos R, como se observa en la Figura 40, se han calculado
los intervalos RR y la frecuencia cardíaca instantánea, así es como, finalmente, se ha
obtenido la frecuencia cardíaca media del registro.
Resultando un valor de 86,4605 bpm, que proporciona una estimación global del ritmo
cardíaco durante el periodo de adquisición. Este valor, como se observa en la Figura 41, se
encuentra dentro del rango fisiológico normal para un adulto en reposo.
54
Todo se ha obtenido a partir del código:
RR = diff(loc_R) % Intervalos RR
FC_inst = 60 ./ RR % Frecuencia cardíaca instantanea
FC_mitjana = mean(FC_inst) % Frecuencia cardíaca media
figure
plot(FC_inst)
xlabel('Latido')
ylabel('Frecuencia cardíaca (bpm)')
title('Frecuencia cardíaca instantanea')
grid o
Presión Sanguínea
Para ello, se han utilizado los algoritmos desarrollados en la práctica 4, adaptándolos para
que funcionen correctamente en una señal de este tipo, cuya evolución es lenta y puede
asociarse al proceso de inflado y desinflado del manguito. Mediante un algoritmo de
detección de máximos y mínimos similar al utilizado en el ECG, se han identificado los valores
de presión sistólica y diastólica del registro. Posteriormente, se ha representado la evolución
de ambas presiones durante la medición, a partir del código siguiente:
55
[pks_sys, loc_sys] = findpeaks(presion_filt, Fs, 'MinPeakDistance', 0.5);
P_sistolica = max(pks_sys)
figure
plot(t, presion_filt)
hold on
plot(loc_sys, pks_sys, 'ro')
plot(loc_dia, pks_dia, 'go')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Detección de presión sistolica i diastolica')
legend('Presión filtrada','Sistolica','Diastolica')
grid on
56
Sonidos de Korotkoff
El código utilizado para obtener esta gráfica y los resultados posteriores es:
envolvente_des = envolvente(idx_max:end);
presion_des = presion_filt(idx_max:end);
t_des = t(idx_max:end);
idx_sons = find(sons_des);
idx_inici = idx_sons(1);
idx_final = idx_sons(end);
t_inici = t_des(idx_inici);
t_final = t_des(idx_final);
P_sistolica_real = presion_des(idx_inici)
P_diastolica_real = presion_des(idx_final)
figure
plot(t, presion_filt)
hold on
xline(t_inici, '--r', 'Inicio Korotkoff')
xline(t_final, '--g', 'Final Korotkoff')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Determinación de la presión sistolica y diastolica mediante
Korotkoff')
legend('Presión filtrada','Sistolica','Diastolica')
grid on
57
Figura 45. Detección de la presión sistólica y diastólica mediante los sonidos de Korotkoff.
Este procedimiento permite combinar la información de los sonidos de Korotkoff con la señal
de presión del manguito, mostrando cómo los algoritmos desarrollados en prácticas previas
pueden adaptarse a señales reales para extraer parámetros fisiológicos relevantes. Para
evitar la detección de falsos positivos debidos al ruido presente en los instantes iniciales del
registro, la detección de los sonidos de Korotkoff se ha restringido al tramo de desinflado
del manguito, una vez alcanzada la presión máxima.
58
CONCLUSIONES
En este proyecto se ha llevado a cabo un análisis completo de distintas señales biomédicas,
concretamente la señal electrocardiográfica (ECG), la presión sanguínea y los sonidos de
Korotkoff, con el objetivo de aplicar los conocimientos adquiridos a lo largo de la asignatura
de Procesamiento de Señales Biomédicas. El trabajo ha permitido integrar conceptos
teóricos y prácticos relacionados con el análisis temporal y frecuencial, el filtrado digital y la
extracción de parámetros fisiológicos de interés.
En la segunda parte del proyecto se ha trabajado con señales reales, lo que ha supuesto una
mayor dificultad debido a la presencia de ruido, interferencias y variabilidad propia de los
sistemas de adquisición y del sujeto. En este contexto, ha sido necesario realizar un análisis
más detallado en los dominios temporal y frecuencial, así como diseñar y ajustar filtros
digitales específicos para cada tipo de señal. El filtrado aplicado al ECG, a la presión
sanguínea y a los sonidos de Korotkoff ha permitido mejorar la relación señal-ruido y preparar
adecuadamente los registros para su posterior análisis.
El análisis del ECG ha permitido detectar de forma robusta los complejos QRS y calcular los
intervalos RR y la frecuencia cardíaca, obteniendo valores coherentes con una situación
fisiológica normal. En el caso de la señal de presión sanguínea, se ha analizado su evolución
temporal y se han identificado los valores máximos y mínimos asociados a la presión sistólica
y diastólica, comprobando que los algoritmos desarrollados pueden adaptarse a señales
reales manteniendo una interpretación fisiológica adecuada.
59
BIBLIOGRAFÍA
[1] Diseño de un sistema de adquisición y procesamiento de la señal de ECG basado en
instrumentación virtual, 2015. [Online]. Disponible en:
[Link]
[6] S. M. Kay, Modern Spectral Estimation: Theory and Application, Prentice Hall, 1988.
[10] MathWorks, Digital Signal Processing Toolbox – Filtering and Spectral Analysis.
[Online]. Disponible en:
[Link]
60
ANNEX I. CODI DELS ALGORITMES AMB
COMENTARIS
A. SEÑAL ELECTROCARDIOGRÁFICA Y SEÑAL DE PRESIÓN SANGUÍNEA
buscar = false;
%Se establece un nuevo umbral, que corresponde a la media entre el
%umbral anterior y el 50% del máximo de la derivada en el último
%índice analizado.
end
else
mostra = mostra + 1;
if mostra >= numMostres
continuar = false;
end
end
end
else
mostra = mostra + 1;
61
if mostra >= numMostres
continuar = false;
end
end
end
% Con el siguiente comando se grafican los resultados
t = (1/250:1/250:numMostres/250)';
figure; box on; hold on;
plot(t, senyal(1:numMostres));
plot(t, derSenyal, 'r');
axis tight;
plot([marques marques]'/250, repmat(get(gca,'Ylim')', size(marques')), 'k-
-');
title('ECG')
xlabel('temps (s)')
hold off;
xlim([2 4]);
pan xon;
%ritmocardiaco%
marques = detecrc(ecg1);
fs = 250;
RR_mostres = diff(marques);
RR_segons = RR_mostres / fs;
ritmocardiaco = 60 ./ RR_segons;
%presion sistólica%
for i = 1:length(marques)-1
psis(i) = max(psan(marques(i):marques(i+1)));
end
figure;
plot(psis)
xlabel('Latido')
ylabel('Presión sistólica (mmHg)')
title('Presión sistólica durante el registro')
if sum(P) ~= 0,
num = sum(f.*P);
den = sum(P);
fmean = num/den;
den = sum(P);
k = 1;
while sum(P(1:k)) <= den/2
62
k = k+1;
end
i = k;
if i < length(P),
alfa1 = sum(P(1:i-1))/den;
if P(i) <= P(i+1)
radi = roots([abs(P(i+1)-P(i)) 2*P(i) -2*(0.5-alfa1)*den]);
else
radi = roots([abs(P(i+1)-P(i)) P(i)+P(i+1) -2*(0.5-alfa1)*den]);
end
if radi(1) > 0 && radi(1) < 1,
x = radi(1);
else
x = radi(2);
end;
df = f(2) - f(1);
fmed = f(k) + df*x;
else
fmean = NaN;
fmed = NaN;
end
else
fmean = NaN;
fmed = NaN;
end
%% INICIALIZACIÓN
clearvars; close all; clc;
fm = 250;
numMostres = length(normECG);
duracio = numMostres/fm;
tMin = 1/fm;
tDelta = 1/fm;
tMax = numMostres/fm;
t = (tMin:tDelta:tMax)';
tDisplay = 2;
numSteps = 10*duracio/tDisplay;
%% DETECCIÓN DE LATIDOS
marques = detecrc(ecg1);
63
ritmes = 60./duracions;
%% DIBUJAR SEÑALES
fig(1) = dibuixar_senyals([normECG normPsan], fm, {'ECG', 'PSan'},
marques, [0 0 0]);
subplot(2,1,2);
plot(marques(1:end-1)/fm, ritmes, 'Color', [.8 .2 .2], 'LineWidth', 2);
axis tight;
ylabel('ritme (batecs/min)');
xlabel('temps (s)');
64
narginchk(0,6); % Comprovem que s'ha cridat la funcio usant entre 0 i
6 arguments d'entrada
nargoutchk(0,1); % Comprovem que s'ha cridat la funcio usant cap o 1
argument de sortida
if verLessThan('matlab', '8.4')
help dibuixar_senyals
hFig = NaN;
return;
end
if nargin < 2
help dibuixarSenyals
hFig = NaN;
return;
end
if nargin < 3
noms = [];
end
if nargin < 4
marques = [];
end
if nargin < 5
colorMarques = [0 0 0]; % Color negre per defecte
end
if nargin < 6
% Figura
hFig = figure(...
'Name',' Revisio de senyals', ...
'NumberTitle', 'off', ...
'DockControls','off', ...
'Position', centra_figura(midaX, midaY), ...
'MenuBar', 'none', ...
'ToolBar', 'none', ...
'Units', 'pixels');
[Link] = @ajusta_mida; % No ho fem a la definicio
anterior
% per evitar errors (ajusta_mida
fa servir hFig)
[Link] = @scroll;
65
% Eixos
hAx = axes(...
'Units', 'pixels', ...
'FontSize', 9, ...
'Ytick', [], ...
'Position', [10 70 midaX-15 midaY-100], ...
'Box', 'On', ...
'Visible', 'On', ...
'SortMethod', 'ChildOrder', ...
'NextPlot', 'Add', ...
'Layer', 'Top', ...
'XMinorTick', 'On', ...
'Parent', hFig);
title(hAx, 'P3 Lab PSB', 'FontWeight', 'Bold', 'FontSize', 11);
xlabel('temps (s)');
if numSenyals > 0
[Link] = [min(min(senyals)) max(max(senyals))] + [-0.1
0.1]*(max(max(range(senyals))));
% Slider horitzontal
hSlider = uicontrol('Style', 'slider', ...
'Min', temps(1), ...
'Max', temps(end)-tempsVisible, ...
'Value', temps(1), ...
'SliderStep', [0.5*tempsVisible tempsVisible]/(numMostres/fm), ...
'Units', 'pixels', ...
'Position', [10 5 midaX-15 15]);
[Link] = temps(1)+[0 tempsVisible]; % Ajustem l'eix horitzontal
visible
66
if not(isempty(marques))
plot([marques(:) marques(:)]'/fm, ...
repmat([Link]', 1, length(marques(:))), ...
'Color', colorMarques, ...
'LineStyle', '-');
text((marques/fm)+(0.01*mean(diff(marques)))/fm, ...
repmat([Link](2)-([Link](2)-[Link](1))*.05, size(marques)),
...
cellstr(num2str((1:length(marques))')), ...
'Color', colorMarques);
end
drawnow;
%% CALLBACKS
function ajusta_eix_horitzontal(~, evData)
[Link] = [Link]+[0 tempsVisible];
end
function ajusta_mida(~,~)
[Link] = [10 5 [Link](3)-15 15];
[Link] = [10 70 [Link](3)-15 [Link](4)-100];
end
%% FUNCIONS AUXILIARS
function pos = centra_figura(midaX, midaY)
screenSize = get(0, 'ScreenSize');
pos = [ceil((screenSize(3)-midaX)/2) ceil((screenSize(4)-midaY)/2),
midaX, midaY];
end
end
%presiondiástole%
for i = 1:length(marques)-1
pdias(i) = min(psan(marques(i):marques(i+1)));
end
figure;
plot(pdias)
xlabel('Latido')
ylabel('Presión diastólica (mmHg)')
title('Presión diastólica durante el registro')
%presiónpulso%
pp = psis - pdias;
figure;
plot(pp)
xlabel('Latido')
ylabel('Presión de pulso (mmHg)')
67
title('Presión de pulso durante el registro')
%MAP%
MAP = pp/3 + pdias;
figure;
plot(MAP)
xlabel('Latido')
ylabel('Presión arterial media (mmHg)')
title('Presión arterial media durante el registro')
%apartado c%
for i = 1:length(marques)-1
[psis(i), idx_max(i)] = max(psan(marques(i):marques(i+1)));
marquessistole(i) = marques(i) + idx_max(i) - 1;
figure;
plot(pminsist)
hold on
plot(pmindias)
xlabel('Pulso')
ylabel('Pulsos por minuto')
title('Pulso cardíaco calculado a partir de presiones sistólicas y
diastólicas')
legend('Pulso por presión sistólica', 'Pulso por presión diastólica')
%apartado e%
68
correlacion = xcorr(ondat, ecg1);
retardot_medio = abs(mean(retardot(:)));
retardot_medio
%retardo%
retardoindices = marquessistole - marques;
retardos = retardoindices ./ 250;
retardorsist = abs(mean(retardos(:)));
retardorsist
69
B. SEÑAL DE PRESIÓN SANGUÍNEA Y SONIDOS DE KOROTKOFF
% Gráficos de sonidos de Korotkoff, ECG y Presión
clear all
clc
load [Link] % Se cargan los datos del archivo [Link]
fm = 500 ; %Frecuencia de muestreo
70
xlabel('tiempo(s)');
hold off
71
P_sistolica = [128.6210, 113.4770, 111.8190, 115.4730, 118.0670, 118.8100,
108.0320];
P_diastolica = [68.2890, 68.0990, 25.8490, 68.0370, 68.8380, 69.8290, 68.6820];
% Creación de tabla de resultados para visualización en consola
TablaResultados = table(ruido_id', P_sistolica', P_diastolica', ...
'VariableNames', {'Ruido', 'P_sistolica_mmHg', 'P_diastolica_mmHg'});
fprintf('RESULTADOS ANÁLISIS KOROTKOFF');
disp(TablaResultados);
72
end
% 4. GRÁFICO FINAL (FIGURA 9)
figure(9);
plot(retardo_R_sonido, 'LineWidth', 1.5);
title('Evolución del retardo entre la onda R y los ruidos de Korotkoff');
xlabel('Número del ruido');
ylabel('Tiempo de retardo [s]');
grid on;
if ~isempty(retardo_R_sonido)
xlim([0 40]);
ylim([0.05 0.4]);
end
% 5. ANÁLISIS FINAL: RETRASO DE LA ONDA T Y ELIMINACIÓN DE LA ONDA R
ecg_sin_R = ecg2;
for k = 1:length(latidos_R)
idx_R = latidos_R(k);
ventana_R = max(1, idx_R-15):min(length(ecg2), idx_R+15);
ecg_sin_R(ventana_R) = 0;
end
% 6. Detección manual de ondas T
posiciones_T = [];
umbral_T = 0.05;
dist_min_T = 200;
for k = 2:length(ecg_sin_R)-1
if ecg_sin_R(k) > umbral_T && ecg_sin_R(k) > ecg_sin_R(k-1) && ecg_sin_R(k) >
ecg_sin_R(k+1)
if isempty(posiciones_T) || (k - posiciones_T(end)) > dist_min_T
posiciones_T(end+1) = k;
end
end
end
% 7. Cálculo del retardo Onda T -> Sonido
retardo_T_sonido = [];
i_ptr = 1; j_ptr = 1;
while i_ptr <= length(marcas_inicio_sonido) && j_ptr <= length(posiciones_T)
diff_T = (marcas_inicio_sonido(i_ptr) - posiciones_T(j_ptr)) / fm;
if diff_T < 0
i_ptr = i_ptr + 1;
elseif diff_T > 0.40
j_ptr = j_ptr + 1;
else
retardo_T_sonido(end+1) = diff_T;
i_ptr = i_ptr + 1;
j_ptr = j_ptr + 1;
end
end
% 8. GRÁFICOS FINALES DE ONDA T
% Figura 10: ECG sin onda R
figure(10);
plot((1:length(ecg_sin_R))/fm, ecg_sin_R);
title('Señal cardíaca sin onda R');
xlabel('Tiempo (s)'); ylabel('mV');
xlim([10 14]); grid on;
% Figura 11: Evolución del retraso de la onda T
figure(11);
if ~isempty(retardo_T_sonido)
% Se grafica el retardo con línea roja y marcadores circulares blancos
plot(retardo_T_sonido, 'r-o', 'LineWidth', 1.5, 'MarkerFaceColor', 'w');
title('Evolución del retardo entre la onda T y ruidos de Korotkoff');
xlabel('Número del ruido'); ylabel('Tiempo de retardo [s]');
grid on;
% Se ajustan los límites del eje Y para visualizar mejor las variaciones
térmicas/fisiológicas
ylim([min(retardo_T_sonido)*0.8, max(retardo_T_sonido)*1.2]);
end
73
% PARTE FINAL: ANÁLISIS DE LA ONDA T Y RETARDOS (FIG 12)
% 1. Creación de la señal sin onda R (Fig 12)
% Eliminamos los picos R de la señal ECG original para permitir que las
% ondas T (de menor amplitud) sean detectables más fácilmente.
ecg_sin_R_final = ecg2;
for k = 1:length(latidos_R)
idx_R = latidos_R(k);
% % Se pone a cero una pequeña ventana (40 muestras aprox.) alrededor de cada pico
R
ventana_limpieza = max(1, idx_R-20):min(length(ecg2), idx_R+20);
ecg_sin_R_final(ventana_limpieza) = 0;
end
% 2. Detección manual de las ondas T (sin usar la función findpeaks)
posiciones_T_final = [];
umbral_T_final = 0.04; % Umbral mínimo para identificar la deflexión de la onda T
dist_min_T_final = 200; % Distancia mínima entre ondas T consecutivas (basada en
fm=500)
for k = 2:length(ecg_sin_R_final)-1
% Se busca un máximo local que supere el umbral establecido
if ecg_sin_R_final(k) > umbral_T_final && ecg_sin_R_final(k) > ecg_sin_R_final(k-1)
&& ecg_sin_R_final(k) > ecg_sin_R_final(k+1)
% Verificación de distancia para evitar falsos positivos en una misma onda
if isempty(posiciones_T_final) || (k - posiciones_T_final(end) > dist_min_T_final)
posiciones_T_final(end+1) = k;
end
end
end
% 3. Cálculo del retardo entre la onda T y los ruidos de Korotkoff
retardo_T_final = [];
i_ptr = 1; j_ptr = 1;
while i_ptr <= length(marcas_inicio_sonido) && j_ptr <= length(posiciones_T_final)
% Cálculo de la diferencia de tiempo convirtiendo muestras a segundos
diff_T_calc = (marcas_inicio_sonido(i_ptr) - posiciones_T_final(j_ptr)) / fm;
if diff_T_calc < 0
% Si el ruido aparece antes que la onda T, pasamos al siguiente ruido
i_ptr = i_ptr + 1;
elseif diff_T_calc > 0.45
%Si el retardo es excesivo (>0.45s), se considera que la onda T
% corresponde a un latido anterior; pasamos a la siguiente onda T.
j_ptr = j_ptr + 1;
else
% Retardo válido encontrado dentro del rango fisiológico
retardo_T_final(end+1) = diff_T_calc;
i_ptr = i_ptr + 1;
j_ptr = j_ptr + 1;
end
end
% FIGURA 12: Visualización final del retardo de la onda T
figure(12);
if ~isempty(retardo_T_final)
plot(retardo_T_final, 'LineWidth', 1.5);
title('Retardo entre la onda T y los ruidos de Korotkoff');
xlabel('Número del ruido'); ylabel('Retardo [s]');
grid on;
xlim([0 45]);
ylim([0 0.45]);
end
74
C. SEÑAL VOLUMEN SANGUÍNEO
% Resultat promig
mean(latmin)
75
% Cálculo del retardo
retardot = abs([marcast - indices]);
retras = retardot./200;
mean(retras)
76
77
78
ANALISIS DE SEÑALES PROPORCIONADAS POR ATENEA
%% Apartado A)
load [Link]
Fs = 1/(isi*1e-3)
t = (0:size(data,1)-1)/Fs;
presion = data(:,1);
korotkoff = data(:,2);
ecg = data(:,3);
79
xlabel('Tiempo (s)')
grid on
%% Apartado B)
%% Dominio temporal
Ns = 2000;
figure
subplot(3,1,1)
plot(t(1:Ns), presion(1:Ns))
title('Presión sanguínia (dominio temporal)')
ylabel('mmHg')
grid on
subplot(3,1,2)
plot(t(1:Ns), korotkoff(1:Ns))
title('Sonidos de Korotkoff (dominio temporal)')
ylabel('mV')
grid on
subplot(3,1,3)
plot(t(1:Ns), ecg(1:Ns))
title('ECG (dominio temporal)')
ylabel('mV')
xlabel('Tiempo (s)')
grid on
%% Domini freqüencial
% Pressió sanguínia
presion_dc = presion - mean(presion);
N = length(presion_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
P_FFT = abs(fft(presion_dc)).^2;
figure
plot(f(1:N/2), 10*log10(P_FFT(1:N/2)))
xlabel('Frecuencia (Hz)')
ylabel('Energía (dB)')
title('Espectro de la presión sanguínia')
grid on
% Sons Korotkoff
kor_dc = korotkoff - mean(korotkoff);
N = length(kor_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
K_FFT = abs(fft(kor_dc)).^2;
figure
plot(f(1:N2), 10*log10(K_FFT(1:N2)))
xlabel('Frecuencia (Hz)')
80
ylabel('Energía (dB)')
title('Espectro de los sonidos de Korotkoff')
grid on
% ECG
ecg_dc = ecg - mean(ecg);
N = length(ecg_dc);
N2 = floor(N/2);
f = (0:N2-1)*(Fs/N);
ECG_FFT = abs(fft(ecg_dc)).^2;
figure
plot(f(1:N2), 10*log10(ECG_FFT(1:N2)))
xlabel('Frecuencia (Hz)')
ylabel('Energía (dB)')
title('Espectro del señal ECG')
grid on
Ns = 10000;
figure
plot(t(1:Ns), presion(1:Ns), '--b','LineWidth', 1.5)
hold on
plot(t(1:Ns), presion_filt(1:Ns), 'r')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Presión sanguínea antes y despues del filtrado')
legend('Original','Filtrada')
grid on
Ns = 2000; % 0.4 s
figure
plot(t(1:Ns), korotkoff(1:Ns), 'b')
hold on
plot(t(1:Ns), kor_filt(1:Ns), 'r')
xlabel('Tiempo (s)')
ylabel('mV')
title('Sonidos de Korotkoff antes y despues del filtrado')
legend('Original','Filtrat')
grid on
81
envolvente = abs(hilbert(kor_filt)); % Añadimos envolvente
figure
plot(t(1:Ns), envolvente(1:Ns))
xlabel('Tiempo (s)')
ylabel('Amplitud')
title('Envolvente dels sonidos de Korotkoff')
grid on
figure
plot(t, ecg_sig)
hold on
plot(loc_R, pk_R, 'ro')
xlabel('Tiempo (s)')
ylabel('mV')
title('Detección de picos R en el señal ECG')
grid on
RR = diff(loc_R) % Intervalos RR
FC_inst = 60 ./ RR % Frecuencia cardíaca instantanea
FC_mitjana = mean(FC_inst) % Frecuencia cardíaca media
figure
plot(FC_inst)
xlabel('Latido')
ylabel('Frecuencia cardíaca (bpm)')
title('Frecuencia cardíaca instantanea')
grid on
P_sistolica = max(pks_sys)
figure
plot(t, presion_filt)
hold on
plot(loc_sys, pks_sys, 'ro')
plot(loc_dia, pks_dia, 'go')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Detección de presión sistolica i diastolica')
legend('Presión filtrada','Sistolica','Diastolica')
grid on
82
envolvente_des = envolvente(idx_max:end);
presion_des = presion_filt(idx_max:end);
t_des = t(idx_max:end);
idx_sons = find(sons_des);
idx_inici = idx_sons(1);
idx_final = idx_sons(end);
t_inici = t_des(idx_inici);
t_final = t_des(idx_final);
P_sistolica_real = presion_des(idx_inici)
P_diastolica_real = presion_des(idx_final)
figure
plot(t, presion_filt)
hold on
xline(t_inici, '--r', 'Inicio Korotkoff')
xline(t_final, '--g', 'Final Korotkoff')
xlabel('Tiempo (s)')
ylabel('mmHg')
title('Determinación de la presión sistolica y diastolica mediante
Korotkoff')
legend('Presión filtrada','Sistolica','Diastolica')
grid on
83