实战:心电图信号处理与ESP32心率血氧计
实战:心电图信号处理与ESP32心率血氧计
做这个小项目的想法来源于 HPC WEEK 比赛中 CT 图像并行计算的那道题。当时虽然我看不懂题目,但是大受震撼。因为不管是各种OJ还是Leetcode上我平时都不太会看到实际数据的处理,一般都是想象出来的场景,选择合适的算法和数据结构解决(毕竟以我们这种水平也做不了什么复杂的现实场景的东西)。
我后来问了下GPT,了解到了信号处理这个方向。打算从最基础的心电图信号处理做一个小项目进行实战。
环境配置
在项目文件夹下用venv创建python3.12环境,然后安装了numpy,scipy,matplotlib,wfdb库。
1 | pip install numpy scipy matplotlib wfdb |
其中:
wfdb:读 MIT-BIH 这类心电数据库
scipy:滤波、信号处理
matplotlib:画图
选择数据
我这里选了编号为100这个病人的数据(也就是第一个病人)。
wfdb.rdrecord() 是 WFDB 库的核心函数,用于读取生理信号记录(如 ECG 心电信号)。它返回一个record对象:
1 | record.p_signal # 生理信号数据(numpy 数组) |
其中,record.p_signal是存储生理信号数据的 numpy 数组,通常为 (样本数, 通道数)
MLII (Modified Limb Lead II),是第1个通道,是最常用的监测导联。问GPT后说这个最常用,所以我就取了这个。
给原始数据画图
创建一个时间轴t,用signal采样点的序号除以频率(1/周期),就是该采样点的时间。
1 | t = [i/fs for i in range(len(signal))] |
再画前五秒的图:
1 | plt.figure() |
去噪
带通滤波
带通滤波只保留需要的频段,这里我保留0.5–40 Hz的频段。Google后就可以知道,< 0.5 Hz的低频是基线漂移噪声,> 40 Hz的是肌电漂移。
1 | b, a = butter(order, [low/nyq, high/nyq], btype='band') |
这个是巴特沃斯带通滤波器,
陷波滤波
去除50Hz的工频干扰
1 | def notch_filter(x, fs, f0=50, Q=30): |
R波检测
算心率
异常检测
下面我按**“买到 ESP32‑C3 → Arduino 连接 → 烧录 MAX30102 测血氧(SpO₂)程序”**的完整流程给你一套可直接照做的步骤,并附上一个可跑的示例代码与常见踩坑排查。
⚠️先提醒:MAX30102 这种光学法心率/血氧很容易受手指压力、环境光、手指位置影响,不适合医疗诊断用途。 [docs.sunfounder.com]
1) 硬件连接(ESP32‑C3 ↔ MAX30102)
MAX30102 模块通常是 I²C 设备(SDA/SCL),你只需要 4 根线:
- VCC → 3.3V(建议 3.3V;很多模块也能接 5V但不推荐混用)
- GND → GND
- SCL → ESP32‑C3 任意可用 GPIO(作为 I²C SCL)
- SDA → ESP32‑C3 任意可用 GPIO(作为 I²C SDA)
✅重点:ESP32 的 I²C 引脚不是固定的,你在代码里用
Wire.begin(SDA, SCL)指定就行。
✅如果你买的是那种“小黑板 MAX30102”,手指容易碰到排针焊点,人体电容会干扰 I²C,导致读数突然断掉/全 0。可以通过绝缘或把 I²C 从 400k 降到 100k 来缓解。 [blog.csdn.net], [cnblogs.com]
2) Arduino IDE 安装 ESP32‑C3 开发板支持(最关键)
2.1 添加 ESP32 Boards Manager 地址
打开 Arduino IDE → 文件(Preferences/首选项) → “附加开发板管理器网址”填入(官方):
- 稳定版:
https://espressif.github.io/arduino-esp32/package_esp32_index.json[docs.espressif.com] - 开发版:
https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json[docs.espressif.com]
如果你在国内下载慢/失败,Espressif 官方也给了 Jihulab 镜像(带 _cn 后缀):
- 稳定版(CN):
https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_index_cn.json[docs.espressif.com] - 开发版(CN):
https://jihulab.com/esp-mirror/espressif/arduino-esp32/-/raw/gh-pages/package_esp32_dev_index_cn.json[docs.espressif.com]
Espressif 文档还提到:在中国地区要选带 “-cn” 的包,并且自动更新可能不支持,需要手动更新。 [docs.espressif.com]
2.2 安装开发板包
工具 → 开发板 → 开发板管理器 → 搜索 esp32 → 安装 “esp32 by Espressif Systems”。 [docs.espressif.com]
3) 让 ESP32‑C3 在 Arduino 里“出现端口”并能烧录
ESP32‑C3 常见两种板型:
A) 你的板子是“原生 USB”(直接连到 C3 芯片 USB)
这种模式下,Espressif 提供了 USB CDC 烧录/串口功能,C3 属于 CDC only。 [docs.espressif.com]
在 Arduino IDE:
- Tools(工具) 里把 USB CDC On Boot → Enabled(ESP32‑C3 的推荐设置) [docs.espressif.com]
如果第一次刷或端口没出来,按 Espressif 文档的方式进 Download Mode:
- 按住 BOOT → 点一下 RESET → 松开,这时系统会枚举出新的 USB 设备端口,然后在 Tools → Port 里选它。 [docs.espressif.com]
B) 你的板子是“USB 转串口芯片”(CH340/CP210x 等)
这种情况下,系统会出现 COM 口/tty 口,Arduino 里 Tools → Port 选择对应端口即可;有时烧录需要按住 BOOT辅助进入下载。 [docs.espressif.com]
✅如果“板子亮灯但没有端口”,先排查:数据线是否为数据线、驱动是否装对、换 USB 口/线。
4) 安装 MAX30102 所需 Arduino 库(SparkFun MAX3010x)
很多教程/课程都使用 SparkFun MAX3010x 这个库,在 Arduino 库管理器里直接搜 “SparkFun MAX3010x” 安装即可。 [docs.sunfounder.com], [microcontr…erslab.com]
它的示例里包含心率、原始数据绘图、presence、温度等,也常被用来做 SpO₂/心率项目。 [microcontr…erslab.com]
另外不少文章也提到:在 Arduino 里用
MAX30105.h这个类名虽然写的是 30105,但 MAX30102 也能用,并且 SpO₂ 常配合spo2_algorithm.h。 [cnblogs.com], [jianshu.com]
5) 直接可烧录的 ESP32‑C3 + MAX30102(心率 + SpO₂)示例代码
说明:
- 代码思路是:采集一段红光/红外数据 → 调用
spo2_algorithm算法 → 输出 BPM 和 SpO₂。- I²C 我默认用 100kHz(
I2C_SPEED_STANDARD)来降低“手指触碰干扰”概率,这是社区常见解决方向。 [blog.csdn.net], [cnblogs.com]- 你只要改
SDA_PIN/SCL_PIN为你实际接线的 GPIO 即可。
1 |
|
如何烧录
- Arduino IDE 选择:Tools → Board → ESP32C3 Dev Module(或你板子的对应 C3 型号)。
- 选择 Tools → Port(能看到端口才行)。
- 点“上传”。如果失败:按住 BOOT 再点上传,或按 Espressif 文档进 Download Mode(BOOT+RESET)。 [docs.espressif.com], [docs.espressif.com]
6) 常见问题速查(你大概率会遇到)
Q1:Arduino 没有端口/不识别开发板
- 先换一根确认可传数据的 Type‑C/USB 线(很多线只能充电)。
- 如果是 USB 转串口方案,检查驱动(CH340/CP210x);如果是原生 USB,按 BOOT+RESET 让它枚举端口。 [docs.espressif.com], [docs.espressif.com]
Q2:手指一放上去数据就卡住、变 0、串口不输出
这是 MAX30102 黑色小板常见坑:手指碰到排针焊点干扰 I²C。
- 给排针背面做绝缘(热缩管/胶带/3D 打印壳)。
- I²C 降速到 100k(上面代码已用
I2C_SPEED_STANDARD)。 [blog.csdn.net], [cnblogs.com]
Q3:SpO₂ 总是不准/跳变大
- 这是算法与信号质量问题很常见;手指要稳定、遮光、压力适中。并且该方法不适合医疗用途。 [docs.sunfounder.com]
你如果想要我把步骤“对号入座”到你的板子
我只需要你补充 1 个信息就能把引脚和 Arduino 工具选项写到“完全匹配你的型号”的版本里:
你买的 ESP32‑C3 具体板子型号是什么?(比如 DevKitM‑1 / SuperMini / 其它)