目標
這個迷你專案/教學的目標是用最少的組件製作一個超級簡單的心率監視器和滾動心電圖顯示。
要求:
快速背景
心臟的肌肉產生電訊號。其中一些訊號可以在皮膚表面檢測到。
我們可以使用表面電極來擷取這些訊號。問題是,這些並不是皮膚上唯一的電訊號。值得慶幸的是,我們想要看到的大多數訊號都限制在 1-40Hz 左右。
過程
我們將使用 1/4" 電纜作為電極,將其插入心臟附近的皮膚。然後我們使用 USB 音訊介面放大類比訊號並將其轉換為數位訊號最後我們用python進行過濾和顯示。
步驟
第1 步: 1/4" 電纜有兩個部分:套管和尖端。這兩個部分都需要與您的皮膚接觸- 只需用手握住套管並將其壓在胸部/上肋骨的左側(某些電纜可能有更多通道,只需確保它們都可以接觸即可啟動)。
第 2 步:
執行下列程式碼。 確保檢查 input_device_index 行是否指向您的音訊介面。我們正在做的是獲取傳入音訊的區塊,使用 fft 轉換到頻域,將所有不必要的頻率設為 0,然後轉換回時域。接下來,我們找到峰值來計算 HR,然後以滾動的方式繪製圖表。
將 numpy 導入為 np
將 pyaudio 導入為 pa
導入結構體
將 matplotlib.pyplot 導入為 plt
從 scipy.signal 導入抽取,find_peaks
區塊 = 4410 #.1 秒
格式 = pa.paInt16
頻道 = 1
速率 = 44100 # 以赫茲為單位
fstep = 速率/區塊
p = pa.PyAudio()
值 = []
dsf=44 #下取樣因子
rds=RATE/dsf #下取樣率
流 = p.open(
格式=格式,
頻道=頻道,
比率 = 比率,
input_device_index=3, #根據輸入調整
輸入=真,
frames_per_buffer=區塊
)
#設定圖表
圖,ax = plt.子圖(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)
文字 = ax.text(0.05, 0.95, str(0), 變換=ax.transAxes, 字體大小=14,
垂直對齊='頂部')
圖.show()
def getFiltered(x,hp=1,lp=41): #這將不需要的頻率設為0
fft=np.fft.fft(x)
hptrim=len(fft)/速率*hp
lptrim=len(fft)/速率*lp
fft[int(lptrim):-int(lptrim)]=0
fft[0:int(hptrim)]=0
返回 np.real(np.fft.ifft(fft))
def getHR(x):
pdis = int(0.6 * rds) #峰之間的最小距離。停止快速觸發。也限制了最大小時,所以調整
峰值, _ = find_peaks(x, 距離=pdis, 高度=0.1)
間隔 = np.diff(peaks)/rds # 以秒為單位
小時 = 60 / BPM 中的間隔#
返回峰值,round(np.mean(hr),0) #peaks,avg hr
而1:
資料 = 流.read(CHUNK)
dataInt = struct.unpack(str(CHUNK) 'h', data)
Filtered=getFiltered(dataInt) #filter(使用完整區塊)
dsed=decimate(filtered, 44) #down 樣本(將 chunk 變成 ds chunk)
value=np.concatenate((values,dsed)) #將區塊放入陣列中
Peaks,hr = getHR(values*-1) # 取得峰值並確定平均 HR。
文字.set_text(str(小時))
line.set_xdata(np.arange(len(值)))
line.set_ydata(values*-10) #缺點是 bc 它與我的設定顛倒了。 *10 只是為了好玩
ax.set_xlim(max(0,len(values)-2500),len(values)) #保持圖表滾動
vlines = ax.vlines(peaks,ymin=-100,ymax=100,colors='red', linestyles='dashed') # 在峰值處彈出一些線條
圖.canvas.draw()
Fig.canvas.flush_events()
vlines.remove()
if len(values)>10000: #保持數組大小可控,並且圖形滾動漂亮
值=值[5000:] #5 秒 @ ~1000 sr。
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.筆記
保持電纜不動 - 您可能需要在運動後等待幾秒鐘才能獲得準確的心率。我對照我的 Garmin 手錶檢查了它,它始終返回相似的值。
請記住,從技術上講,您正在使您的身體
成為電路的一部分。電纜連接到連接到電腦的接口,該接口連接到電腦連接到牆壁電源插座...嘗試此操作需要您自擔風險。我不是專家 - 我只是喜歡玩弄東西,並想分享。
這種方法對於清楚地看到心電圖訊號的所有不同部分來說並不能很好地發揮作用。電極非常磨損,我只做了最低限度的過濾。
它在檢測肌電圖等較小訊號方面也表現不佳。
從這裡,您可以在軟體方面進行更深入的挖掘並使用其他濾波器,或建立實際電路並使用真實電極。用於此類物品的一袋電極在亞馬遜上非常便宜(注意,黏合劑很煩人)。對於電路,我嘗試了幾種不同的配置 - 我發現最簡單/最適合我的是使用 JFET 運算放大器的簡單儀表放大器電路(放在麵包板上)。 3個電極,只需查一下圖表即可知道放置位置。如果您使用 ADC 的音訊接口,此處的代碼應與設定的 3 電極麵包板配合使用(可能需要調整增益)
為什麼
這個迷你項目的靈感來自於拿著吉他線在 DAW 中使用 EQ 插件時的感受。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3