当前位置: 首页 > news >正文

模拟心电图采样数据

定时发送数据:timeIdHeart = startTimer(150)

startTimer() 函数:

  • 所属类QObject(所有 Qt 类的基类,因此所有 Qt 对象都可以使用该方法)。
  • 函数原型int QObject::startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer)
    • interval:定时器触发的时间间隔(单位:毫秒)。
    • timerType:定时器精度类型(默认为 Qt::CoarseTimer,表示定时器触发时间允许有 5% 的误差,以优化系统资源)。
  • 返回值:返回一个唯一的整数 ID(即 timeIdHeart),用于标识这个定时器,后续可通过该 ID 停止或区分不同的定时器。

定时器处理函数:

void SerialDialog::timerEvent(QTimerEvent *event)
{if (event->timerId() == timeIdHeart) {// 每 200 毫秒执行一次的代码(心率数据发送逻辑)}else if (event->timerId() == timeIdPressure) {// 其他定时器的处理逻辑}
}

完整的代码如下:

void SerialDialog::timerEvent(QTimerEvent *event)
{if(event->timerId()==timeIdPressure){QByteArray message;unsigned char hp = (unsigned char)(120+rand()%10);unsigned char lp = (unsigned char)(80+rand()%5);unsigned char pulse = (unsigned char)(65+rand()%5);message.append(hp);message.append(lp);message.append(pulse);m_serialport->write(message);}else if(event->timerId()== timeIdHeart){QByteArray msg;QString strnums;int nums[12];for(int i=0;i<12;i++){nums[i]=arrs[index]+(unsigned)rand()%7;strnums.append(QString::number(nums[i])+",");}index++;if(index==LENGTH)index=0;// 修复:添加toUtf8()转换msg.append(strnums.toUtf8());m_serialport->write(msg);}else if(event->timerId()== timeIdBreath){QByteArray message;float num1 = 140+rand()%10*1.2;float num2 = 20 +rand()%5*1.2;QString nums = QString::number(num1,'f',1)+","+ QString::number(num2,'f',1);// 修复:添加toUtf8()转换message.append(nums.toUtf8());m_serialport->write(message);}
}

这段代码是 SerialDialog 类中处理定时器事件的核心函数,负责通过串口周期性发送模拟的生理数据(血压、心率、呼吸)。以下是详细解释:

函数整体功能

timerEvent(QTimerEvent *event) 是 Qt 框架中用于处理定时器事件的虚函数。当通过 startTimer() 启动一个定时器后,Qt 会每隔指定的毫秒数触发一次该函数,并传入对应的定时器事件(包含定时器 ID)。

函数通过判断 event->timerId() 来区分不同的定时器,执行不同的数据发送逻辑:

  • timeIdPressure:发送血压数据(每 1000ms 一次)
  • timeIdHeart:发送心率数据(每 200ms 一次)
  • timeIdBreath:发送呼吸数据(每 1000ms 一次)

血压数据发送逻辑

if(event->timerId() == timeIdPressure)
{QByteArray message;unsigned char hp = (unsigned char)(120 + rand()%10);    // 收缩压 (120-129)unsigned char lp = (unsigned char)(80 + rand()%5);     // 舒张压 (80-84)unsigned char pulse = (unsigned char)(65 + rand()%5);  // 脉搏 (65-69)message.append(hp);message.append(lp);message.append(pulse);m_serialport->write(message);
}

  • 数据格式:3 个无符号字节(8 位整数),依次为收缩压、舒张压、脉搏。
  • 范围:模拟正常血压范围,随机波动以模拟真实测量。
  • 传输方式:二进制格式(非文本),适合高效数据传输。

心率数据发送逻辑

else if(event->timerId() == timeIdHeart)
{QByteArray msg;QString strnums;int nums[12];for(int i=0; i<12; i++){nums[i] = arrs[index] + (unsigned)rand()%7;  // 从预设数组中取数据并添加随机波动strnums.append(QString::number(nums[i]) + ",");  // 转换为字符串,用逗号分隔}index++;if(index == LENGTH)index = 0;  // 数组循环使用msg.append(strnums.toUtf8());  // 转换为UTF-8字节数组m_serialport->write(msg);
}

  • 数据格式:CSV(逗号分隔值)字符串,如 16,12,9,8,6,34,20,22,20,16,12,9,
  • 数据来源arrs 数组存储预设的心率波形数据点,通过循环和随机波动模拟真实心率变化。
  • 采样频率:每 200ms 发送 12 个数据点,相当于每秒 60 个点(60Hz 采样率),符合心率监测需求。

呼吸数据发送逻辑

else if(event->timerId() == timeIdBreath)
{QByteArray message;float num1 = 140 + rand()%10 * 1.2;  // 呼吸幅度 (140-151.9)float num2 = 20 + rand()%5 * 1.2;    // 呼吸频率 (20-25.9)QString nums = QString::number(num1, 'f', 1) + "," + QString::number(num2, 'f', 1);message.append(nums.toUtf8());  // 转换为UTF-8字节数组m_serialport->write(message);
}

  • 数据格式:两个浮点数组成的 CSV 字符串,如 145.2,22.8
  • 含义
    • num1:呼吸幅度(模拟胸腔运动幅度)
    • num2:呼吸频率(次 / 分钟)
  • 精度:保留 1 位小数,适合医疗监测场景。

关键技术细节

  1. 定时器管理

    • 通过 startTimer(interval) 启动定时器,返回唯一 ID。
    • 通过 killTimer(id) 停止定时器(在串口关闭时调用)。
  2. 数据传输优化

    • 血压数据用二进制格式(节省带宽,适合嵌入式设备)。
    • 心率和呼吸数据用文本格式(可读性好,便于调试和解析)。
  3. 随机数生成

    • 使用 rand() 生成随机波动,但需注意:
      • 未调用 srand() 初始化随机种子,导致每次运行的随机序列相同。
      • 建议在程序启动时调用 srand(time(NULL)) 初始化(需包含 <cstdlib> 和 <ctime>)。
  4. 线程安全

    • timerEvent() 在主线程执行,与 UI 操作同一线程,避免多线程冲突。
    • 串口写操作是同步的,但数据量小,不会阻塞 UI。

总之:

函数通过定时器机制实现了三种生理数据的周期性模拟发送:

  • 血压:每 1 秒发送一次,二进制格式(3 字节)。
  • 心率:每 200 毫秒发送一次,CSV 格式(12 个数据点)。
  • 呼吸:每 1 秒发送一次,CSV 格式(两个浮点数)。

心电图模拟数据详解:

预设波形数组 arrs

int arrs[]={16,12,9,8,6,34,20,22,20,16,12,9,8,9,10,8,6,-1,2,-3,-7,-8 ,-7,-6,7,2,-1,1,9,9,10,8,6,-1,2,-3,-7,-8,-7,-6,-6,7,2,-1,1,9,-7,-8,-7,-6,7,2,-1,1,-1,2,-3,-7,-8 ,8,6,-7,2,-1,1,9,-101,-200,-120,-40,40,120,280,459,670,835,900,835,670,450,340,240,27,18,14,10,6};

  • 数据结构:包含 87 个整数,代表一个完整 ECG 周期的采样点。
  • 特征分析
    • QRS 波群:数组末尾部分(如 900,835,670)是最高幅值区域,对应心室去极化(心脏收缩)。
    • 基线与小波动:数组前半部分(如 16,12,9)模拟基线和 P 波、T 波等小幅波动。
  • 单位:数据单位为 微伏(μV),符合 ECG 信号的实际幅度范围。

3. 数据生成与发送逻辑

QByteArray msg;
QString strnums;
int nums[12];
for(int i=0; i<12; i++)
{nums[i] = arrs[index] + (unsigned)rand()%7;  // 从预设数组取数据并添加随机扰动strnums.append(QString::number(nums[i]) + ",");  // 转换为CSV格式
}
index++;
if(index == LENGTH)index = 0;  // 数组循环使用msg.append(strnums.toUtf8());  // 转换为字节数组
m_serialport->write(msg);      // 通过串口发送
关键步骤解析
  1. 数据采样

    • 每次从 arrs 中取出 12 个连续点,组成一组数据。
    • index 作为数组索引,每次增加 1,遍历整个 arrs 数组。
    • 当 index 达到数组长度(87)时,重置为 0,实现循环采样。
  2. 随机扰动

    • (unsigned)rand()%7 生成 0-6 的随机数,添加到原始数据上。
    • 目的:模拟测量噪声和生理波动,使数据更接近真实信号。
  3. 数据格式

    • 将 12 个整数转换为 CSV 字符串(如 16,12,9,8,6,34,20,22,20,16,12,9,)。
    • 通过 toUtf8() 转换为字节数组后通过串口发送。
  4. 发送频率

    • 定时器每 200 毫秒 触发一次(startTimer(200)),即每秒发送 5 组数据。
    • 每组 12 个点,因此采样率为 60Hz(符合 ECG 监测的基本要求)。

4. 模拟效果分析

  • 波形连续性

    • 每次发送 12 个点,相邻两组数据在 arrs 中是连续的,保证波形平滑。
    • 例如:第一组取 arrs[0]~arrs[11],第二组取 arrs[1]~arrs[12],依此类推。
  • 心率计算

    • arrs 包含一个完整周期的波形,共 87 个点。
    • 每 200ms 发送 12 个点,遍历完整个 arrs 需要 87/12 × 200ms ≈ 1.45秒
    • 因此模拟心率约为 60/1.45 ≈ 41次/分钟(略低于正常范围,可通过调整定时器间隔优化)。

效果测试图:

http://www.dtcms.com/a/274443.html

相关文章:

  • 《PyQtGraph:Python绘图领域的“超级引擎”》
  • [ARC195E] Random Tree Distance
  • 完全和零一背包
  • 游戏开发日记
  • nginx 负载均衡配置(加解决重复登录问题)
  • Reading and Writing to a State Variable
  • stm32-modbus-rs485程序移植过程
  • gRPC服务注册和故障恢复
  • AI技术重塑工业制造:从智能应用到大型模型落地
  • AMTS AHTE | 具身智能成制造升级新引擎 灵途科技助力更强感知
  • 八股训练--RabbitMQ
  • LVS-NAT模式配置
  • 《Java 虚拟机内幕:从垃圾回收到类加载的深度解析》
  • 微积分核心考点全解析
  • pnpm 的 resolution-mode 配置 ( pnpm 的版本解析)
  • 上位机知识篇---Docker
  • 静态路由综合实验报告册
  • HashMap简介
  • 五星出东方洛老师:gma绘制的洛阳市瀍河回族区的地图和兴趣点
  • 高精加法-P1601 A+B Problem(高精)
  • intellij idea的重命名shift+f6不生效(快捷键被微软输入法占用)
  • 决策树算法在医学影像诊断中的广泛应用
  • 知识科普丨详述agent含义
  • 【深度学习系列】ResNet网络原理与mnist手写数字识别实现
  • 浏览器重绘与重排
  • JAVA ---Excel高效导入(去重1000万数据对比)
  • 聊聊微服务架构中的双token
  • Junit多线程的坑
  • Python爬虫动态IP代理报错全解析:从问题定位到实战优化
  • 【牛客刷题】超级圣诞树(递归法和分形复制法)