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

Flutter中ECG心电图绘制:BLE设备数据解析与绘图方法问询

解析BLE ECG数据并绘制心电图的方法

一、数据解析步骤

首先要明确设备的BLE通信协议(有厂商文档优先参考),无文档时按通用逻辑推测:

  1. 确定字节序
    BLE设备常用小端(Little-Endian)或大端(Big-Endian)字节序,两种转换逻辑如下:

    • 小端:低字节在前,高字节在后,比如字节对C8 44转换为0x44C8
    • 大端:高字节在前,低字节在后,比如字节对C8 44转换为0xC844
  2. 拆分数据字段
    你收到的6字节数据C8-44-80-8E-37-C9,常见拆分方式有两种:

    • 方式1:3组16位数据(2字节/组):[C8,44], [80,8E], [37,C9]
    • 方式2:4组12位数据(3字节拆为2组12位):[C84], [480], [8E3], [7C9](从左到右取每12位)
  3. 转换为有符号十进制值
    ECG采样值通常是有符号整数,转换规则:

    • 16位有符号:若十六进制值≥0x8000,则用值 - 65536得到负数;否则直接转十进制。
    • 12位有符号:若十六进制值≥0x800,则用值 - 4096得到负数;否则直接转十进制。
  4. 校准为实际mV值
    ECG采样值是放大后的数字量,需按设备增益系数转换为毫伏值。比如增益为0.01mV/LSB时,实际ECG值 = 采样十进制值 × 0.01。

以方式1小端转换为例:

  • 第一组C8 44 → 小端转0x44C8 = 17608(正数)
  • 第二组80 8E → 小端转0x8E80 = 36480 → 36480 - 65536 = -29056(负数)
  • 第三组37 C9 → 小端转0xC937 = 51511 → 51511 - 65536 = -14025(负数)
    校准后对应176.08mV、-290.56mV、-140.25mV,符合ECG典型数值范围。

另外,BPM心率可能在单独的BLE特征中,或藏在数据的某一字段里,可通过静止/运动状态下的数值变化判断。

二、Flutter中数据解析代码示例

基于flutter_blue_plus这类BLE插件的特征回调,编写解析逻辑:

// 原始数据为List<int>类型
void parseEcgData(List<int> rawData) {
  List<int> ecgSamples = [];
  // 按2字节一组拆分(小端字节序)
  for (int i = 0; i < rawData.length; i += 2) {
    if (i + 1 >= rawData.length) break;
    // 小端转换:低字节rawData[i],高字节rawData[i+1]
    int value = (rawData[i+1] << 8) | rawData[i];
    // 转换为16位有符号整数
    if (value >= 0x8000) {
      value -= 0x10000;
    }
    ecgSamples.add(value);
  }

  // 校准为mV值(示例增益0.01mV/LSB)
  List<double> ecgMv = ecgSamples.map((v) => v * 0.01).toList();
  print('ECG采样值(mV): $ecgMv');
}

三、绘制ECG心电图的两种方式

1. 使用charts_flutter快速绘制折线图

适合静态或半实时展示:

import 'package:charts_flutter/flutter.dart' as charts;

// 数据模型
class EcgData {
  final int time;
  final double value;

  EcgData(this.time, this.value);
}

// 构建图表
Widget buildEcgChart(List<double> ecgValues) {
  List<EcgData> chartData = [];
  for (int i = 0; i < ecgValues.length; i++) {
    chartData.add(EcgData(i, ecgValues[i]));
  }

  return charts.LineChart(
    [
      charts.Series<EcgData, int>(
        id: 'ECG',
        colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
        domainFn: (EcgData data, _) => data.time,
        measureFn: (EcgData data, _) => data.value,
        data: chartData,
      )
    ],
    animate: false,
    behaviors: [
      charts.ChartTitle('ECG心电图', behaviorPosition: charts.BehaviorPosition.top),
      charts.ChartTitle('时间(采样点)', behaviorPosition: charts.BehaviorPosition.bottom),
      charts.ChartTitle('电压(mV)', behaviorPosition: charts.BehaviorPosition.start),
    ],
  );
}

2. 自定义CustomPainter实现实时滚动绘制

适合动态展示最新采样点:

class EcgPainter extends CustomPainter {
  final List<double> ecgValues;
  final double width;
  final double height;
  final double scaleX;
  final double scaleY;

  EcgPainter({
    required this.ecgValues,
    required this.width,
    required this.height,
    this.scaleX = 5.0,
    this.scaleY = 0.1,
  });

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2.0
      ..style = PaintingStyle.stroke;

    Path path = Path();
    if (ecgValues.isEmpty) return;

    // 起始点:x=0,y=画布中心 - 第一个值×缩放系数
    double startY = height / 2 - ecgValues[0] * scaleY;
    path.moveTo(0, startY);

    for (int i = 1; i < ecgValues.length; i++) {
      double x = i * scaleX;
      double y = height / 2 - ecgValues[i] * scaleY;
      path.lineTo(x, y);
    }

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant EcgPainter oldDelegate) {
    return oldDelegate.ecgValues != ecgValues;
  }
}

// 使用自定义Painter
Widget buildCustomEcgChart(List<double> ecgValues) {
  return SizedBox(
    width: 300,
    height: 200,
    child: CustomPaint(
      painter: EcgPainter(
        ecgValues: ecgValues,
        width: 300,
        height: 200,
      ),
    ),
  );
}

四、注意事项

  • 若解析数值不合理,可切换字节序或数据拆分方式,结合设备状态变化验证字段用途。
  • 实时绘制时,限制采样点数量(比如保留最近200个点),避免卡顿。
  • 做好BLE连接异常处理,应对数据丢失或断连场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:28:19