You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现ESP32以2kHz频率发送模拟信号并稳定传输至Python?

解决ESP32 2kHz ECG采样频率波动及数据格式异常问题

数据格式异常的原因与修复

  1. 串口波特率不匹配:ESP32代码设置的74880与Python脚本的115200不一致,直接导致数据乱码(如128r/110这类异常格式),必须统一波特率。
  2. ESP32代码缺失引脚定义:原代码未声明analogPin变量,编译后会导致采样错误,需根据实际硬件连接添加引脚定义(如const int analogPin = 34;)。
  3. Python脚本初始化问题:未导入time模块、未初始化data列表,且首次读取时串口缓冲区可能残留旧数据,需清空缓冲区后再开始采样。

采样频率不稳定的原因与修复

  1. 字符串传输效率过低:原代码用Serial.println()发送字符串,每个样本需传输多个字符(含换行符),2kHz采样率下会造成串口阻塞,打乱采样周期。改用二进制传输可大幅提升效率。
  2. 采样间隔控制逻辑缺陷:原代码的忙等延迟未考虑串口发送耗时,若发送时间超过periodUs,实际采样间隔会被拉长。改用"绝对时间触发"方式,固定下一次采样的时间点,避免延迟累积。
  3. Python端读取逻辑缺陷:原代码依赖readline()读取字符串,换行符解析开销大且易丢包,改为读取二进制数据可减少解析耗时,同时设置合理超时避免阻塞。

修改后的ESP32代码

const int sampleRate = 2000;
const int analogPin = 34; // 根据实际连接的ADC引脚修改
unsigned long nextSampleTime = 0;

void setup() {
  Serial.begin(115200); // 与Python端统一波特率
  analogReadResolution(12); // ESP32默认12位ADC,可根据需求调整
}

void loop() {
  // 采用绝对时间触发采样,保证间隔稳定
  if (micros() >= nextSampleTime) {
    uint16_t adcValue = analogRead(analogPin);
    // 以二进制发送ADC值(2字节,比字符串快数倍)
    Serial.write((uint8_t*)&adcValue, sizeof(adcValue));
    // 更新下一次采样时间
    nextSampleTime += 1000000 / sampleRate;
    // 处理时间溢出(避免系统时间绕回后无法触发)
    if (nextSampleTime < micros()) {
      nextSampleTime = micros() + 1000000 / sampleRate;
    }
  }
}

修改后的Python脚本

import serial
import time

# 初始化串口,设置短超时避免阻塞
ser = serial.Serial(port='COM7', baudrate=115200, timeout=0.001)
ser.flushInput()  # 清空缓冲区残留数据,避免首次读取乱码

data = []
sample_duration = 10  # 采样时长10秒
t0 = time.time()
t_end = t0 + sample_duration

while time.time() < t_end:
    # 读取2字节二进制数据(对应ESP32发送的uint16_t类型)
    byte_data = ser.read(2)
    if len(byte_data) == 2:
        # 解析二进制数据,匹配ESP32的小端字节序
        adc_value = int.from_bytes(byte_data, byteorder='little')
        data.append(adc_value)

t1 = time.time()
actual_sample_count = len(data)
actual_sample_rate = round(actual_sample_count / (t1 - t0))

print(f"实际采样频率: {actual_sample_rate} Hz")
print(f"累计采样样本数: {actual_sample_count}")

进阶优化建议

  • 使用定时器中断采样:若需要更高精度的采样间隔,可利用ESP32的定时器中断触发ADC采样和数据发送,彻底脱离loop()函数的执行干扰。
  • 启用硬件流控:若串口传输仍存在瓶颈,可在ESP32和Python端同时启用RTS/CTS硬件流控,避免数据丢失。
  • Python端线程读取:将串口读取逻辑放在独立线程中,避免主线程阻塞导致漏读样本。

内容的提问来源于stack exchange,提问作者Raúl Nieto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 23:10:42