”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 简单DIY心率监测器+心电图显示器

简单DIY心率监测器+心电图显示器

发布于2024-11-09
浏览:151

目标
这个迷你项目/教程的目标是用最少的组件制作一个超级简单的心率监视器和滚动心电图显示。

要求:

  • 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]删除
最新教程 更多>
  • 如何在 PHP 中计算日期之间的小时差?
    如何在 PHP 中计算日期之间的小时差?
    确定 PHP 中日期之间的小时差您希望计算两个日期之间的小时差,其格式为 " Y-m-d H:i:s."在 PHP 中实现此目的:将日期转换为时间戳:时间戳表示自 1970 年 1 月 1 日午夜(以您的服务器时区为准)以来的秒数。要将日期转换为时间戳,请使用 strtotime...
    编程 发布于2024-11-09
  • 反应受控/不受控组件
    反应受控/不受控组件
    在 React 中,处理表单输入主要有两种方法: 受控组件 不受控制的组件 受控组件提供更多控制和验证,而不受控组件更简单,对于间歇性值访问的基本形式有用。 受控组件 这些是表单输入,其值由 React State 控制。每当输入的值发生变化时,状态变量就会更新,并且输入的值是通过 ...
    编程 发布于2024-11-09
  • 如何处理 Selenium 中的“过时元素引用”异常?
    如何处理 Selenium 中的“过时元素引用”异常?
    陈旧元素引用:揭示原因并寻找解决方案在 Selenium 中,遇到“陈旧元素引用”异常可能会令人沮丧,因为它表明被引用的元素不再附加到页面文档。当 DOM 发生重大更改(例如动态加载或页面导航)时,通常会发生此错误。要解决此问题,确定触发异常的确切代码行至关重要。在提供的代码中,导致错误的行似乎是:...
    编程 发布于2024-11-09
  • 如何高效地在嵌套的 JavaScript 对象中查找特定对象?
    如何高效地在嵌套的 JavaScript 对象中查找特定对象?
    迭代嵌套的 JavaScript 对象迭代嵌套的 JavaScript 对象可能具有挑战性,特别是当您需要基于属性检索特定对象时价值。让我们考虑以下示例:var cars = { label: 'Autos', subs: [ { label: 'SUVs', ...
    编程 发布于2024-11-09
  • 最简单的状态教程
    最简单的状态教程
    Zustand 是一个小型、快速且可扩展的 React 状态管理库,可作为 Redux 等更复杂解决方案的替代方案。 Zustand 获得如此大关注的主要原因是与 Redux 相比,它的体积小且语法简单。 了解 Zustand 设置 首先,如果您还没有安装 Zustand 和 Typ...
    编程 发布于2024-11-09
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-11-09
  • MongoDB 服务器:概述
    MongoDB 服务器:概述
    MongoDB 是一种流行的 NoSQL 数据库,提供高性能、可扩展且灵活的数据存储解决方案。与使用表和行的传统关系数据库不同,MongoDB 使用灵活的、类似 JSON 的结构(称为 BSON(二进制 JSON))将数据存储在文档中。这使得 MongoDB 能够轻松处理复杂的数据类型和层次关系。...
    编程 发布于2024-11-09
  • 如何在 MySQL DELETE 语句中使用 LIMIT 删除一定范围的行?
    如何在 MySQL DELETE 语句中使用 LIMIT 删除一定范围的行?
    更正带有 LIMIT 的 MySQL DELETE 语句的语法尝试使用带有 LIMIT 的 DELETE 语句从 MySQL 表中删除一系列行时LIMIT 子句,如果语法不正确,您可能会遇到错误。此错误通常表明用于指定限制的语法存在问题。所提供的查询中的问题是您无法在 DELETE 语句的 LIMI...
    编程 发布于2024-11-09
  • 如何使用 os.walk() 在 Python 中创建带有深度指示器的结构化目录列表?
    如何使用 os.walk() 在 Python 中创建带有深度指示器的结构化目录列表?
    在 Python 中使用 os.walk() 递归地导航目录为了创建更结构化的目录列表,开发人员尝试修改他们的代码将目录显示为大写标题,并用虚线指示深度和目录下的文件。然而,他们最初的方法产生了不完整的结果。为了解决这个挑战,我们可以利用 Python 的 os.sep 属性来正确描述路径组件。这是...
    编程 发布于2024-11-09
  • Java 中的设计模式及其示例
    Java 中的设计模式及其示例
    Java 中的设计模式是什么? 设计模式是软件设计中常见问题的可重用解决方案。它们代表了可应用于软件开发中各种情况的最佳实践,特别是像 Java 这样的面向对象编程。 设计模式的类型 创建模式: 处理对象创建机制。 结构模式: 关注类和对象的组成方式。 行为模...
    编程 发布于2024-11-09
  • NestJS 与 Encore.ts:为您的 TypeScript 微服务选择正确的框架
    NestJS 与 Encore.ts:为您的 TypeScript 微服务选择正确的框架
    Introduction When web applications grow larger, so does the complexity in developing and maintaining the system. A common way to solve this i...
    编程 发布于2024-11-09
  • 如何在 Python 中重置生成器对象?
    如何在 Python 中重置生成器对象?
    在 Python 中重置生成器对象:探索替代方案生成器提供了一种迭代值序列的有效方法,而无需在记忆。然而,一旦生成器产生了所有值,它就会耗尽并且不能直接重用。这就提出了如何在 Python 中重置生成器对象的问题。不幸的是,生成器没有内置的重置方法。要重用生成器,您有多种选择:再次运行生成器函数: ...
    编程 发布于2024-11-09
  • 如何高效地检索MySQL中最后插入的行?
    如何高效地检索MySQL中最后插入的行?
    检索 MySQL 中最后插入的行:高效方法高效检索 MySQL 中最后插入的行是数据库编程中的常见任务。以下是实现此目的的两种有效方法:1。时间戳列:理想的解决方案是创建一个 TIMESTAMP 列,在行插入时自动捕获当前时间戳。这提供了一种可靠且准确的方法来确定最近的记录。2。 ORDER BY ...
    编程 发布于2024-11-09
  • 如何最小化 Go 中禁用跟踪日志记录语句的成本?
    如何最小化 Go 中禁用跟踪日志记录语句的成本?
    Go 中禁用语句的低成本跟踪日志记录在 Go 中,跟踪日志记录提出了一个独特的挑战:最大限度地减少关键路径中禁用日志语句的成本。与 C/C 不同,Go 没有预处理器宏,因此有必要探索替代解决方案。一种方法涉及使用 fmt.Stringer 和 fmt.GoStringer 接口。通过延迟格式化直到日...
    编程 发布于2024-11-09
  • 如何在 JavaScript 中将多个数组合并为一个?
    如何在 JavaScript 中将多个数组合并为一个?
    将数组项连接成单个数组在 JavaScript 中,将多个数组的元素组合成一个新数组可能是一种常见的需求。实现此目的的一种方法是使用循环迭代每个源数组并将项目推入目标数组。然而,这种方法可能乏味且效率低下。利用“concat”函数幸运的是,JavaScript 提供了一个更简单、更优雅的解决方案:“...
    编程 发布于2024-11-09

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3