」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 簡單DIY心率監測器+心電圖顯示器

簡單DIY心率監測器+心電圖顯示器

發佈於2024-11-09
瀏覽:189

目標
這個迷你專案/教學的目標是用最少的組件製作一個超級簡單的心率監視器和滾動心電圖顯示。

要求:

  • Python
  • 音訊介面
  • 1/4吋線/吉他線/樂器線(只需透過音訊介面連接電腦即可)

快速背景

心臟的肌肉產生電訊號。其中一些訊號可以在皮膚表面檢測到。

我們可以使用表面電極來擷取這些訊號。問題是,這些並不是皮膚上唯一的電訊號。值得慶幸的是,我們想要看到的大多數訊號都限制在 1-40Hz 左右。

過程

我們將使用 1/4" 電纜作為電極,將其插入心臟附近的皮膚。然後我們使用 USB 音訊介面放大類比訊號並將其轉換為數位訊號最後我們用python進行過濾和顯示。

步驟

第1 步: 1/4" 電纜有兩個部分:套管和尖端。這兩個部分都需要與您的皮膚接觸- 只需用手握住套管並將其壓在胸部/上肋骨的左側(某些電纜可能有更多通道,只需確保它們都可以接觸即可啟動)。

Simple DIY HR Monitor ECG Display

第 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 手錶檢查了它,它始終返回相似的值。

輸出


Simple DIY HR Monitor ECG Display

免責聲明

請記住,從技術上講,您正在使您的身體
成為電路的一部分。電纜連接到連接到電腦的接口,該接口連接到電腦連接到牆壁電源插座...嘗試此操作需要您自擔風險。我不是專家 - 我只是喜歡玩弄東西,並想分享。

後續步驟

這種方法對於清楚地看到心電圖訊號的所有不同部分來說並不能很好地發揮作用。電極非常磨損,我只做了最低限度的過濾。
它在檢測肌電圖等較小訊號方面也表現不佳。

從這裡,您可以在軟體方面進行更深入的挖掘並使用其他濾波器,或建立實際電路並使用真實電極。用於此類物品的一袋電極在亞馬遜上非常便宜(注意,黏合劑很煩人)。對於電路,我嘗試了幾種不同的配置 - 我發現最簡單/最適合我的是使用 JFET 運算放大器的簡單儀表放大器電路(放在麵包板上)。 3個電極,只需查一下圖表即可知道放置位置。如果您使用 ADC 的音訊接口,此處的代碼應與設定的 3 電極麵包板配合使用(可能需要調整增益)

為什麼

這個迷你項目的靈感來自於拿著吉他線在 DAW 中使用 EQ 插件時的感受。

版本聲明 本文轉載於:https://dev.to/benwwq/simple-diy-hr-monitorecg-display-4ol5?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • 在Oracle SQL中如何提取下劃線前的子字符串?
    在Oracle SQL中如何提取下劃線前的子字符串?
    [ 在oracle sql 解決方案: Explanation:SUBSTR function extracts a substring starting from the specified position (0) and continuing for a specified length.IN...
    程式設計 發佈於2025-05-20
  • 如何使用Java.net.urlConnection和Multipart/form-data編碼使用其他參數上傳文件?
    如何使用Java.net.urlConnection和Multipart/form-data編碼使用其他參數上傳文件?
    使用http request 上傳文件上傳到http server,同時也提交其他參數,java.net.net.urlconnection and Multipart/form-data Encoding是普遍的。 Here's a breakdown of the process:Mu...
    程式設計 發佈於2025-05-20
  • Java為何無法創建泛型數組?
    Java為何無法創建泛型數組?
    通用陣列創建錯誤 arrayList [2]; JAVA報告了“通用數組創建”錯誤。為什麼不允許這樣做? 答案:Create an Auxiliary Class:public static ArrayList<myObject>[] a = new ArrayList<my...
    程式設計 發佈於2025-05-20
  • Java中假喚醒真的會發生嗎?
    Java中假喚醒真的會發生嗎?
    在Java中的浪費喚醒:真實性或神話? 在Java同步中偽裝喚醒的概念已經是討論的主題。儘管存在這種行為的潛力,但問題仍然存在:它們實際上是在實踐中發生的嗎? Linux的喚醒機制根據Wikipedia關於偽造喚醒的文章,linux實現了pthread_cond_wait()功能的Linux實現,...
    程式設計 發佈於2025-05-20
  • Async Void vs. Async Task在ASP.NET中:為什麼Async Void方法有時會拋出異常?
    Async Void vs. Async Task在ASP.NET中:為什麼Async Void方法有時會拋出異常?
    在ASP.NET async void void async void void void void void的設計無需返回asynchroncon而無需返回任務對象。他們在執行過程中增加未償還操作的計數,並在完成後減少。在某些情況下,這種行為可能是有益的,例如未期望或明確預期操作結果的火災和...
    程式設計 發佈於2025-05-20
  • 如何使用不同數量列的聯合數據庫表?
    如何使用不同數量列的聯合數據庫表?
    合併列數不同的表 當嘗試合併列數不同的數據庫表時,可能會遇到挑戰。一種直接的方法是在列數較少的表中,為缺失的列追加空值。 例如,考慮兩個表,表 A 和表 B,其中表 A 的列數多於表 B。為了合併這些表,同時處理表 B 中缺失的列,請按照以下步驟操作: 確定表 B 中缺失的列,並將它們添加到表的...
    程式設計 發佈於2025-05-20
  • 如何有效地轉換PHP中的時區?
    如何有效地轉換PHP中的時區?
    在PHP 利用dateTime對象和functions DateTime對象及其相應的功能別名為時區轉換提供方便的方法。例如: //定義用戶的時區 date_default_timezone_set('歐洲/倫敦'); //創建DateTime對象 $ dateTime = ne...
    程式設計 發佈於2025-05-20
  • C++20 Consteval函數中模板參數能否依賴於函數參數?
    C++20 Consteval函數中模板參數能否依賴於函數參數?
    [ consteval函數和模板參數依賴於函數參數在C 17中,模板參數不能依賴一個函數參數,因為編譯器仍然需要對非contexexpr futcoriations contim at contexpr function進行評估。 compile time。 C 20引入恆定函數,必須在編譯時進...
    程式設計 發佈於2025-05-20
  • 如何在其容器中為DIV創建平滑的左右CSS動畫?
    如何在其容器中為DIV創建平滑的左右CSS動畫?
    通用CSS動畫,用於左右運動 ,我們將探索創建一個通用的CSS動畫,以向左和右移動DIV,從而到達其容器的邊緣。該動畫可以應用於具有絕對定位的任何div,無論其未知長度如何。 問題:使用左直接導致瞬時消失 更加流暢的解決方案:混合轉換和左 [並實現平穩的,線性的運動,我們介紹了線性的轉換。...
    程式設計 發佈於2025-05-20
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-05-20
  • PHP陣列鍵值異常:了解07和08的好奇情況
    PHP陣列鍵值異常:了解07和08的好奇情況
    PHP數組鍵值問題,使用07&08 在給定數月的數組中,鍵值07和08呈現令人困惑的行為時,就會出現一個不尋常的問題。運行print_r($月份)返回意外結果:鍵“ 07”丟失,而鍵“ 08”分配給了9月的值。 此問題源於PHP對領先零的解釋。當一個數字帶有0(例如07或08)的前綴時,PHP...
    程式設計 發佈於2025-05-20
  • 如何將PANDAS DataFrame列轉換為DateTime格式並按日期過濾?
    如何將PANDAS DataFrame列轉換為DateTime格式並按日期過濾?
    Transform Pandas DataFrame Column to DateTime FormatScenario:Data within a Pandas DataFrame often exists in various formats, including strings.使用時間數據時...
    程式設計 發佈於2025-05-20
  • 對象擬合:IE和Edge中的封面失敗,如何修復?
    對象擬合:IE和Edge中的封面失敗,如何修復?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    程式設計 發佈於2025-05-20
  • Python元類工作原理及類創建與定制
    Python元類工作原理及類創建與定制
    python中的metaclasses是什麼? Metaclasses負責在Python中創建類對象。就像類創建實例一樣,元類也創建類。他們提供了對類創建過程的控制層,允許自定義類行為和屬性。 在Python中理解類作為對象的概念,類是描述用於創建新實例或對象的藍圖的對象。這意味著類本身是使用...
    程式設計 發佈於2025-05-20
  • 如何使用Depimal.parse()中的指數表示法中的數字?
    如何使用Depimal.parse()中的指數表示法中的數字?
    在嘗試使用Decimal.parse(“ 1.2345e-02”中的指數符號表示法表示的字符串時,您可能會遇到錯誤。這是因為默認解析方法無法識別指數符號。 成功解析這樣的字符串,您需要明確指定它代表浮點數。您可以使用numbersTyles.Float樣式進行此操作,如下所示:[&& && && ...
    程式設計 發佈於2025-05-20

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3