Objectif
Le but de ce miniprojet/tutoriel est de créer un moniteur HR super simple et un affichage ECG défilant avec un minimum de composants.
Exigences :
Contexte rapide
Les muscles du cœur créent des signaux électriques. Certains de ces signaux sont détectables à la surface de la peau.
Nous pouvons capter ces signaux à l’aide d’électrodes de surface. Le problème est que ce ne sont pas les seuls signaux électriques sur la peau. Heureusement, la plupart des signaux que nous souhaitons voir se limitent à environ 1-40 Hz.
Processus
Nous allons prendre notre câble 1/4", qui fera office d'électrode et l'enfoncer dans notre peau à proximité du cœur. Ensuite, nous utilisons l'interface audio USB pour amplifier et convertir le signal analogique en numérique. . Enfin nous filtrons et affichons en python
.Étapes
Étape 1 : Un câble de 1/4" comporte deux parties, le manchon et la pointe. Les deux parties doivent entrer en contact avec votre peau - tenez simplement le manchon avec votre main et écrasez-le contre le côté gauche de votre poitrine/cage thoracique supérieure (certains câbles peuvent avoir plus de canaux, assurez-vous simplement qu'ils sont tous en contact pour démarrer). Ajustez le gain sur l'interface audio (j'augmente le mien au maximum).
Étape 2 : Exécutez le code ci-dessous. Assurez-vous de vérifier que la ligne input_device_index pointe vers votre interface audio. Ce que nous faisons, c'est prendre des morceaux de l'audio entrant, les convertir dans le domaine fréquentiel à l'aide de fft, définir toutes les fréquences inutiles sur 0, puis les reconvertir dans le domaine temporel. Ensuite on retrouve les pics pour calculer la FC puis on trace un graphique de manière à défiler.
import numpy as np import pyaudio as pa import struct import matplotlib.pyplot as plt from scipy.signal import decimate, find_peaks CHUNK = 4410 #.1 second FORMAT = pa.paInt16 CHANNELS = 1 RATE = 44100 # in Hz fstep = RATE/CHUNK p = pa.PyAudio() values = [] dsf=44 #down sample factor rds=RATE/dsf #down sampled rate stream = p.open( format = FORMAT, channels = CHANNELS, rate = RATE, input_device_index=3, #adjust based on input input=True, frames_per_buffer=CHUNK ) #set up graph fig,ax = plt.subplots(1) x = np.arange(0,2*CHUNK,2) line, = ax.plot(x, np.random.rand(CHUNK)) ax.set_ylim(-100,100) ax.set_xlim(0,2500) text = ax.text(0.05, 0.95, str(0), transform=ax.transAxes, fontsize=14, verticalalignment='top') fig.show() def getFiltered(x,hp=1,lp=41): #this sets the unneeded freqs to 0 fft=np.fft.fft(x) hptrim=len(fft)/RATE*hp lptrim=len(fft)/RATE*lp fft[int(lptrim):-int(lptrim)]=0 fft[0:int(hptrim)]=0 return np.real(np.fft.ifft(fft)) def getHR(x): pdis = int(0.6 * rds) #minimum distance between peaks. stops rapid triggering. also caps max hr, so adjust peaks, _ = find_peaks(x, distance=pdis, height=0.1) intervals = np.diff(peaks)/rds # in seconds hr = 60 / intervals # in BPM return peaks,round(np.mean(hr),0) #peaks,avg hr while 1: data = stream.read(CHUNK) dataInt = struct.unpack(str(CHUNK) + 'h', data) filtered=getFiltered(dataInt) #filter (working with full chunk) dsed=decimate(filtered, 44) #down sample (turns chunk into ds chunk) values=np.concatenate((values,dsed)) #puts the chunks into an array peaks,hr = getHR(values*-1) # gets the peaks and determins avg HR. text.set_text(str(hr)) line.set_xdata(np.arange(len(values))) line.set_ydata(values*-10) #the negative is bc it comes in upside down with my set up. the *10 is just for fun ax.set_xlim(max(0,len(values)-2500),len(values)) #keep the graph scrolling vlines = ax.vlines(peaks,ymin=-100,ymax=100,colors='red', linestyles='dashed') # pop some lines at the peaks fig.canvas.draw() fig.canvas.flush_events() vlines.remove() if len(values)>10000: #keeps the array managably sized, and graph scrolling pretty values=values[5000:] #5 seconds @ ~1000 sr.
Remarques
Maintenez le câble immobile : vous devrez peut-être attendre quelques secondes après le mouvement pour obtenir une fréquence cardiaque précise. Je l'ai vérifié avec ma montre Garmin et elle a systématiquement renvoyé des valeurs similaires.
Sortie
Avis de non-responsabilité
Gardez à l’esprit que vous faites techniquement votre corps une partie du circuit. Le câble est connecté à l'interface qui est connectée à l'ordinateur qui est connecté à la prise de courant murale... Essayez ceci à vos risques et périls. Je ne suis pas un expert, j'aime juste jouer avec des trucs et je voulais partager.
Prochaines étapes
Cette méthode ne fonctionne pas vraiment très bien pour voir clairement toutes les différentes parties d'un signal ECG. L'électrode est super éraflée et j'ai fait un strict minimum de filtrage.
Il ne fonctionne pas non plus bien dans la détection de signaux plus petits comme pour l'EMG.
À partir de là, vous pouvez approfondir le côté logiciel et jouer avec des filtres supplémentaires, ou créer un circuit réel et utiliser de vraies électrodes. Un sac d'électrodes pour ce genre de chose est assez bon marché sur Amazon (attention, l'adhésif est ennuyeux). Pour un circuit, j'ai essayé quelques configurations différentes - ce que j'ai trouvé le plus simple/qui fonctionnait le mieux pour moi était un simple circuit amplificateur d'instrumentation utilisant un ampli-op JFET (assemblé sur une planche à pain). 3 électrodes, il suffit de chercher un schéma pour savoir où les mettre. Si vous utilisez l'interface audio pour l'ADC, le code ici devrait fonctionner avec la configuration de la planche à pain à 3 électrodes (il faudra peut-être ajuster le gain)
Pourquoi
L'inspiration pour ce mini-projet est née en jouant avec un plug-in d'égalisation dans une DAW tout en tenant un câble de guitare.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!