eefocus_3940056 发表于 3 天前

《2024DigiKey汽车应用创意挑战赛》基于MCXN的端侧推理可穿戴式体征检测系统

简介
本项目旨在设计与实现一套基于端侧模型推理的可穿戴式体征监测系统,面向连续、低功耗、实时的生命体征采集与分析需求。MCXN947,其内置神经网络处理单元(NPU),可在本地高效运行深度学习推理模型,实现对采集数据的实时处理与智能分析,减少对云端计算的依赖,有效保障数据隐私与响应速度,AD8232心率/心电信号采集模块,用于高精度获取用户心电波形与心率信息;端侧模型推理,系统能够实时检测生命体征变化,识别异常状态

项目图片展示
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/222928ioi2vu9wknu9q6y1.jpg)
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/230043ja2ud9av88p8pdm2.jpg)
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/230051kppc2fw25kcpk24z.jpg)

项目使用了集成电池充电管理升压供电 与信号处理一体式主板
原理图如下

!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/223330meej5eod8doyeoxc.png)
TP4057电路负责给电池充电 确保续航可靠性
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/223439dyhyx3n2bymj2n3z.png)

SGM6603电路负责将电池升压到5V给系统供电

!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/223901ps6bhi66yz6zpssz.png)

LDO电路负责将电池电压转换成3V3给OLED屏幕和运放供电

!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/224133aimd6ee6ejrdbnbb.png)

信号处理电路如上
ECG模块输出的交流信号通过电容耦合到运放输入端 同时运放被提供了一个偏执的电源轨心电信号被放大了10倍
ECG模块将微弱信号放大了1000倍 总计倍放大了10k倍

!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/224343crw1ytt16tc81c0p.png)
信号板电路图如下

!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/224444yrrf9bbq5mr80mm0.png)

代码部分
心电处理的核心部分是从ADC的电压信号中准确的处理找到心电的P波T波等各种特征信号

首先建立心电信号结构体

``````
// ==================== 数据结构定义 ====================
typedef struct {
    float P_amplitude;      // P波幅度 (mV)
    float P_duration;       // P波持续时间 (ms)
    float PR_interval;      // PR间期 (ms)
    float QRS_amplitude;    // QRS波幅度 (mV)
    float QRS_duration;   // QRS波持续时间 (ms)
    float ST_level;         // ST段电平 (mV)
    float T_amplitude;      // T波幅度 (mV)
    float QT_interval;      // QT间期 (ms)
    float RR_interval;      // RR间期 (ms)
    int heart_rate;         // 心率 (bpm)
    float hrv;            // 心率变异性 (ms)
} ECG_Parameters;
```

```
``````

由于ADC采集来的数据包含大量干扰 需要使用滤波器滤除干扰 提取出有用的数据 心电信号频率不高 一般为40Hz-120Hz之间 我们使用matlab辅助设计滤波器如下

```
// ==================== 滤波器实现 ====================
typedef struct {
    float b0, b1, b2;
    float a1, a2;
    float x1, x2;
    float y1, y2;
} IIRFilter;

// 巴特沃斯带通滤波器 (0.5-40Hz)
static const IIRFilter bandpass_filter = {
    .b0 = 0.0014, .b1 = 0, .b2 = -0.0014,
    .a1 = -1.9778, .a2 = 0.9780
};

// 50Hz陷波滤波器
static const IIRFilter notch_filter = {
    .b0 = 0.9890, .b1 = -1.9778, .b2 = 0.9890,
    .a1 = -1.9778, .a2 = 0.9780
};

// IIR滤波器处理函数
float iir_filter_process(float input, IIRFilter *filter) {
    float output = filter->b0 * input
                   + filter->b1 * filter->x1
                   + filter->b2 * filter->x2
                   - filter->a1 * filter->y1
                   - filter->a2 * filter->y2;
   
    // 更新历史值
    filter->x2 = filter->x1;
    filter->x1 = input;
    filter->y2 = filter->y1;
    filter->y1 = output;
   
    return output;
}
```

滤波器使用如下

```
// ==================== 信号预处理 ====================
void ecg_preprocess(float sample) {
    static IIRFilter bp_filt = bandpass_filter;
    static IIRFilter nt_filt = notch_filter;
   
    // 带通滤波去除基线漂移和高频噪声
    float filtered = iir_filter_process(sample, &bp_filt);
   
    // 陷波滤波去除工频干扰
    filtered = iir_filter_process(filtered, &nt_filt);
   
    //存储到缓冲区
    filtered_ecg.buffer = filtered;
    filtered_ecg.index = (filtered_ecg.index + 1) % filtered_ecg.size;
}
```

接着将预处理好的电压信号进行特征峰提取

```
// ==================== R波检测算法 ====================
int detect_r_peaks(float *signal, int length, int *r_locations) {
    int count = 0;
    float derivative;
    float squared;
   
    // 计算导数 (强调QRS波的陡峭部分)
    for (int i = 0; i < length - 1; i++) {
      derivative = signal - signal;
    }
   
    // 平方增强
    for (int i = 0; i < length - 1; i++) {
      squared = derivative * derivative;
    }
   
    // 移动平均滤波
    float ma_window;
    int ma_size = 5;
    for (int i = ma_size; i < length - 1 - ma_size; i++) {
      float sum = 0;
      for (int j = 0; j < ma_size; j++) {
            sum += squared;
      }
      float moving_avg = sum / ma_size;
      
      // 自适应阈值检测
      float threshold = 0.5 * moving_avg;
      if (squared > threshold &&
            signal > r_detector.threshold &&
            (count == 0 || i - r_locations > ECG_SAMPLE_RATE * 0.2)) { // 最小RR间期200ms
            
            r_locations = i;
            count++;
            
            // 更新阈值
            r_detector.threshold = 0.875 * r_detector.threshold + 0.125 * signal;
      }
    }
   
    return count;
}

// ==================== ECG特征提取 ====================
void extract_ecg_features(float *signal, int r_locations[], int r_count) {
    if (r_count < 2) return;
   
    // 提取RR间期和心率
    float rr_sum = 0;
    float rr_intervals;
   
    for (int i = 1; i < r_count; i++) {
      rr_intervals = (r_locations - r_locations) * 1000.0 / ECG_SAMPLE_RATE;
      rr_sum += rr_intervals;
    }
   
    float avg_rr = rr_sum / (r_count - 1);
    current_params.heart_rate = (int)(60000.0 / avg_rr);
   
    // 提取QRS特征
    if (r_count > 0) {
      int r_idx = r_locations;
      
      // Q波检测 (R波前0.08s内寻找负向波)
      int q_search_start = r_idx - (int)(0.08 * ECG_SAMPLE_RATE);
      if (q_search_start < 0) q_search_start = 0;
      
      float q_min = signal;
      int q_idx = r_idx;
      for (int i = q_search_start; i < r_idx; i++) {
            if (signal < q_min) {
                q_min = signal;
                q_idx = i;
            }
      }
      
      // S波检测 (R波后0.08s内寻找负向波)
      int s_search_end = r_idx + (int)(0.08 * ECG_SAMPLE_RATE);
      if (s_search_end >= filtered_ecg.size) s_search_end = filtered_ecg.size - 1;
      
      float s_min = signal;
      int s_idx = r_idx;
      for (int i = r_idx; i < s_search_end; i++) {
            if (signal < s_min) {
                s_min = signal;
                s_idx = i;
            }
      }
      
      // QRS持续时间
      current_params.QRS_duration = (s_idx - q_idx) * 1000.0 / ECG_SAMPLE_RATE;
      
      // QRS幅度
      current_params.QRS_amplitude = signal - q_min;
      
      // ST段分析 (S波后80ms处)
      int st_idx = s_idx + (int)(0.08 * ECG_SAMPLE_RATE);
      if (st_idx < filtered_ecg.size) {
            current_params.ST_level = signal;
      }
      
      // P波检测 (Q波前寻找正向波)
      int p_search_start = q_idx - (int)(0.2 * ECG_SAMPLE_RATE);
      if (p_search_start < 0) p_search_start = 0;
      
      float p_max = signal;
      int p_idx = q_idx;
      for (int i = p_search_start; i < q_idx; i++) {
            if (signal > p_max) {
                p_max = signal;
                p_idx = i;
            }
      }
      
      current_params.P_amplitude = p_max - signal;
      current_params.P_duration = (q_idx - p_idx) * 1000.0 / ECG_SAMPLE_RATE;
      current_params.PR_interval = (q_idx - p_idx) * 1000.0 / ECG_SAMPLE_RATE;
      
      // T波检测 (S波后寻找正向波)
      int t_search_start = s_idx + (int)(0.1 * ECG_SAMPLE_RATE);
      int t_search_end = t_search_start + (int)(0.3 * ECG_SAMPLE_RATE);
      
      if (t_search_end < filtered_ecg.size) {
            float t_max = signal;
            int t_idx = t_search_start;
            
            for (int i = t_search_start; i < t_search_end; i++) {
                if (signal > t_max) {
                  t_max = signal;
                  t_idx = i;
                }
            }
            
            current_params.T_amplitude = t_max - signal;
            current_params.QT_interval = (t_idx - q_idx) * 1000.0 / ECG_SAMPLE_RATE;
      }
    }
}
```

通过以上处理 完成心电特征提取

心电图数据集使用MIT-BIH心率不齐数据库以及欧盟ST-T心电数据库
(需要网络环境访问)
我们采用三导联方式数据

训练好模型加载到npu中即可进行数据实时监控

数据采集与提取框图如下
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/225710pcyycbycwcub1wyw.png)

NPU推理部分代码运行框图
!(https://www.eefocus.com/forum/data/attachment/forum/202601/26/225742a33qpurrk3z399ua.png)

当电池电量过低时 系统会关闭屏幕进入超低功耗模式运行
(https://)

通过网盘分享的文件:digikey
链接: https://pan.baidu.com/s/1ecrgbQRSSxj7rG84Cv-sdA?pwd=n5n5 提取码: n5n5


页: [1]
查看完整版本: 《2024DigiKey汽车应用创意挑战赛》基于MCXN的端侧推理可穿戴式体征检测系统