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

在Flutter Dart中实现Pan_Tompkins_QRS检测以计算ECG实时心率

用Dart实现Pan-Tompkins QRS检测算法计算实时心率

核心需求

  • 硬件配置:通过AD8232心电传感器、蓝牙模块、RP2040开发板获取ECG原始信号读数
  • 目标:完全基于Dart实现Pan-Tompkins QRS检测算法,实时计算心率,替代Chaquopy方案以避免应用体积过度增大

Pan-Tompkins算法Dart实现思路

Pan-Tompkins算法通过多阶段信号处理识别QRS波(心电信号中对应心跳的特征波),进而通过RR间期计算心率。核心步骤如下:

  1. 带通滤波:去除基线漂移(低频干扰)和工频干扰(如50/60Hz高频噪声)
  2. 微分运算:增强QRS波的斜率特征,区分QRS波与其他心电成分
  3. 平方运算:放大QRS波的幅值差异,抑制非QRS成分
  4. 移动窗口积分:整合QRS波的能量,生成便于阈值判断的波形
  5. 自适应阈值判定:识别QRS波峰,统计RR间期并计算实时心率

关键代码实现示例

1. 带通滤波实现(二阶巴特沃斯滤波)

class BandpassFilter {
  // 针对250Hz采样率,通带0.5-40Hz的巴特沃斯滤波系数
  final List<double> _b = [0.0003, 0.0013, 0.0019, 0.0013, 0.0003];
  final List<double> _a = [1.0, -3.7749, 5.2478, -3.3381, 0.8653];
  List<double> _x = List.filled(5, 0.0);
  List<double> _y = List.filled(5, 0.0);

  double process(double input) {
    _x[0] = _x[1];
    _x[1] = _x[2];
    _x[2] = _x[3];
    _x[3] = _x[4];
    _x[4] = input;

    _y[0] = _y[1];
    _y[1] = _y[2];
    _y[2] = _y[3];
    _y[3] = _y[4];
    _y[4] = _b[0]*_x[4] + _b[1]*_x[3] + _b[2]*_x[2] + _b[3]*_x[1] + _b[4]*_x[0]
            - _a[1]*_y[3] - _a[2]*_y[2] - _a[3]*_y[1] - _a[4]*_y[0];
    return _y[4];
  }
}

2. 微分、平方与移动窗口积分

class QRSProcessor {
  final BandpassFilter _filter = BandpassFilter();
  List<double> _diffBuffer = List.filled(4, 0.0);
  List<double> _integralBuffer = List.filled(30, 0.0); // 250Hz采样下120ms窗口

  double processSample(double ecgSample) {
    // 1. 带通滤波
    double filtered = _filter.process(ecgSample);

    // 2. 微分运算
    _diffBuffer[0] = _diffBuffer[1];
    _diffBuffer[1] = _diffBuffer[2];
    _diffBuffer[2] = _diffBuffer[3];
    _diffBuffer[3] = filtered;
    double diff = (-(1*_diffBuffer[0]) + 1*_diffBuffer[1] + 2*_diffBuffer[2] + 1*_diffBuffer[3]) / 8;

    // 3. 平方运算
    double squared = diff * diff;

    // 4. 移动窗口积分
    _integralBuffer.removeAt(0);
    _integralBuffer.add(squared);
    double integral = _integralBuffer.reduce((a, b) => a + b) / 30;

    return integral;
  }
}

3. QRS波峰检测与心率计算

class HeartRateCalculator {
  final QRSProcessor _processor = QRSProcessor();
  double _threshold = 0.0;
  double _noiseThreshold = 0.0;
  List<int> _rrIntervals = [];
  int _sampleCount = 0;
  static const int sampleRate = 250; // 与AD8232采样率匹配

  int? processEcgSample(double ecgSample) {
    _sampleCount++;
    double processed = _processor.processSample(ecgSample);

    // 自适应阈值初始化(前2秒数据)
    if (_sampleCount < sampleRate * 2) {
      _threshold = processed * 0.5;
      _noiseThreshold = processed * 0.25;
      return null;
    }

    // QRS波峰判定
    if (processed > _threshold) {
      // 记录RR间期(毫秒)
      if (_rrIntervals.isNotEmpty) {
        int interval = ((_sampleCount - _rrIntervals.last) * 1000) ~/ sampleRate;
        _rrIntervals.add(_sampleCount);
        // 保留最近10个RR间期计算平均心率
        if (_rrIntervals.length > 10) _rrIntervals.removeAt(0);
        // 计算心率:60000ms / 平均RR间期
        int avgInterval = _rrIntervals.sublist(1).map((e) => e - _rrIntervals[_rrIntervals.indexOf(e)-1]).reduce((a, b) => a + b) ~/ (_rrIntervals.length - 1);
        return 60000 ~/ avgInterval;
      } else {
        _rrIntervals.add(_sampleCount);
        return null;
      }
    } else if (processed > _noiseThreshold) {
      // 更新噪声阈值
      _noiseThreshold = 0.9 * _noiseThreshold + 0.1 * processed;
    }

    // 更新信号阈值
    _threshold = 0.9 * _threshold + 0.1 * processed;
    return null;
  }
}

注意事项

  • 采样率匹配:代码中默认250Hz采样率,需与AD8232实际配置一致,否则需调整滤波系数、积分窗口等参数
  • 实时性处理:通过Dart异步流(Stream)处理蓝牙传输的ECG数据,避免阻塞UI线程
  • 基线校正:若原始信号基线漂移严重,可在滤波前加入滑动平均基线校正步骤
  • 阈值优化:可根据实际信号调整阈值更新的权重系数,提升QRS波识别准确率

内容的提问来源于stack exchange,提问作者PremchandGat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:44:59