Compare commits

..

3 Commits
main ... dev

Author SHA1 Message Date
35a4f5c4ff [Add Code]: Add hanning windows 2025-05-09 16:25:05 +08:00
ad03636629 [Add Code]: fix hr down && up bug 2025-05-09 09:17:41 +08:00
d1a7b23ea8 [Add:Branch Check] 2025-04-22 11:17:13 +08:00
15 changed files with 12091 additions and 194 deletions

View File

@ -3,5 +3,10 @@
"board/esp32-wrover-kit-3.3v.cfg"
],
"idf.portWin": "COM12",
"idf.flashType": "UART"
"idf.flashType": "UART",
"files.associations": {
"main.h": "c",
"bsp_algcom.h": "c",
"bsp_mqtt.h": "c"
}
}

View File

@ -13,4 +13,10 @@
```
Date 2025年4月22日
Log : 旧版本体征监测垫程序 ,兼容最新算法
新建分支
```
---
```
Date 2025年5月9日
Log : 增加相关修正数据 增加窗函数
```

File diff suppressed because one or more lines are too long

View File

@ -494,6 +494,10 @@ int MyToolSelectHp(float *hpList, float *ratioList, float *formerHps, uint16_t t
else
{
res = (int)round(loMax);
if (noZeroCount > 10 && fabs(noZeroAvg - res) > 20)
res = noZeroAvg;
if (res < 48)
res = noZeroAvg;
}
if (res == 0)

View File

@ -37,6 +37,10 @@ int count_up_hr = 0, count_down_hr = 0, count_up_hr_m = 0, count_down_hr_m = 0,
// 数据后处理时计数数据上升与下降次数
int count_up_br = 0, count_down_br = 0, count_up_br_m = 0, count_down_br_m = 0, count_out_range_br = 0;
//////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
void breathDataPostProcessing(void);
/**
* @function:
@ -742,6 +746,17 @@ void cwt_getBreath_Test(void)
}
#include "MyBandFitter.h"
void apply_hanning_window(float32_t *input, float32_t *output, uint32_t data_len, uint32_t FFT_SIZE)
{
// 生成汉宁窗系数并加窗
for (uint32_t i = 0; i < data_len; i++)
{
float32_t window = 0.5f * (1 - cosf(2 * PI * i / (data_len - 1)));
output[i] = input[i] * window;
}
// 补零至FFT_SIZE
memset(&output[data_len], 0, (FFT_SIZE - data_len) * sizeof(float32_t));
}
/**
* @function: fft_Output
@ -753,13 +768,14 @@ void cwt_getBreath_Test(void)
#define FFT_POINT 2048
uint32_t FFT_Size = FFT_POINT;
float32_t FFT_InPut[FFT_POINT] = {0};
float32_t FFT_OutPut[FFT_POINT] = {0};
float32_t FFT_OutPut[(FFT_POINT / 2) + 1] = {0};
float32_t FFT_OutPut_Real[FFT_POINT] = {0};
float stash_Ratio[ARRAY_NUMBER] = {0};
uint16_t fft_HeartBeatCurrentFilter = 0;
float32_t HeartValueRes2Peaks[4] = {0.0f};
float32_t FFT_HeartRateRes[2] = {0.0f};
float32_t Energy_Ratio[4] = {0.0f};
float32_t fft_Output(float32_t *Input, int Length, char type)
{
float32_t FFT_HeartRateVal[2] = {0};
@ -779,22 +795,18 @@ float32_t fft_Output(float32_t *Input, int Length, char type)
// printf("\r\n-----------------Start---------------------\n");
/* 初始化结构体S*/
arm_rfft_fast_init_f32(&FFT_Struct, FFT_Size);
for (int i = 0; i < FFT_POINT; i++)
{
if (i < Length)
FFT_InPut[i] = Input[i];
else
FFT_InPut[i] = 0;
}
apply_hanning_window(Input, FFT_InPut, Length, FFT_Size); // 加hanning窗且补零到2048
arm_rfft_fast_f32(&FFT_Struct, FFT_InPut, FFT_OutPut_Real, FFT_Dir);
/* 实数FFT运算*/
arm_cmplx_mag_f32(FFT_OutPut_Real, FFT_OutPut, FFT_Size);
// for (int i = 0; i < FFT_POINT / 2; i++)
// {
// printf("%f\r\n", FFT_OutPut[i]);
// }
// printf("\r\n-----------------End---------------------\n");
// 偏置的计算 用于加权
arm_cmplx_mag_f32(FFT_OutPut_Real, FFT_OutPut, (FFT_POINT / 2) + 1);
// 能量修正乘以1.63
float32_t total_energy = 0.0f;
for (int i = 0; i < (FFT_Size / 2) + 1; i++)
{
FFT_OutPut[i] *= 1.63f; // 能量校准
total_energy += (FFT_OutPut[i]) * (FFT_OutPut[i]);
}
#if 0
if (type == TYPE_HEART)
{
@ -812,10 +824,17 @@ float32_t fft_Output(float32_t *Input, int Length, char type)
valuePeakNum = peakProcess(FFT_OutPut, 200, maxPeakIndex, maxPeakValue);
// 对不一样滤波器 进行计算
if (fft_HeartBeatCurrentFilter == FILTER_HI_TYPE)
{
stash_Ratio[0] = maxPeakValue[1] / maxPeakValue[0];
Energy_Ratio[0] = (maxPeakValue[0] * maxPeakValue[0]) / total_energy;
Energy_Ratio[1] = (maxPeakValue[1] * maxPeakValue[1]) / total_energy;
}
else if (fft_HeartBeatCurrentFilter == FILTER_LO_TYPE)
{
stash_Ratio[1] = maxPeakValue[1] / maxPeakValue[0];
Energy_Ratio[2] = (maxPeakValue[0] * maxPeakValue[0]) / total_energy;
Energy_Ratio[3] = (maxPeakValue[1] * maxPeakValue[1]) / total_energy;
}
FFT_HeartRateVal[0] = (((float32_t)maxPeakIndex[0] * 50.0f) / FFT_POINT) * 60;
FFT_HeartRateVal[1] = (((float32_t)maxPeakIndex[1] * 50.0f) / FFT_POINT) * 60;
printf("FFT_HeartRateVal [0] : %f", FFT_HeartRateVal[0]);
@ -844,11 +863,8 @@ float32_t fft_Output(float32_t *Input, int Length, char type)
*/
void fft_heartDataPostProcessing(float32_t data)
{
move_update(f_BPM_hr, 5, round(data));
if (f_BPM_hr[4] <= 90)
f_BPM_hr[4] = f_BPM_hr[4] - 3;
else
f_BPM_hr[4] = f_BPM_hr[4] - 4;
move_update(f_BPM_hr, 5, (data));
f_BPM_hr[4] -= (f_BPM_hr[4] <= 90) ? 3 : 4; // 固定offset
// note: 增加对应的偏置
#if LINE_COEFF_EN
if (HeartBeatRate >= 83 && HeartBeatRate <= 110)
@ -857,40 +873,37 @@ void fft_heartDataPostProcessing(float32_t data)
;
#endif
//========================================================================数据后处理
for (int i = 0; i < 4; i++) // 把前4个数据复制出来供后处理分析
{
f_BPM_hr2[i] = f_BPM_hr[i];
}
memcpy(f_BPM_hr2, f_BPM_hr, sizeof(float) * 4);
if (f_BPM_hr[0] > 0) // 当数据满5个之后开始后处理
{
if ((f_BPM_hr[4] - MyToolMidValue(f_BPM_hr2, 4) >= 12) || (f_BPM_hr[4] - f_BPM_hr[3] >= 8)) // 比前4个中位数大7
if ((f_BPM_hr[4] - MyToolMidValue(f_BPM_hr2, 4) >= 10) || (f_BPM_hr[4] - f_BPM_hr[3] >= 5)) // 比前4个中位数大12或比前面一个大8????????????????????????????????????????????
{
count_up_hr += 1;
count_up_hr += 1; // 判断上升的条件+1,苛刻
#if FFT_VERSION
if (count_up_hr >= 10)
if (count_up_hr >= 12)
#else
if (count_up_hr >= 8)
#endif
{
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3]));
f_BPM_hr[4] = f_BPM_hr[3] + (3 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 3 : (f_BPM_hr[4] - f_BPM_hr[3])); // 最多+3
}
#if FFT_VERSION
else if (count_up_hr >= 5)
else if (count_up_hr >= 6)
#else
else if (count_up_hr >= 4)
#endif
{
f_BPM_hr[4] = f_BPM_hr[3] + (1 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 1 : (f_BPM_hr[4] - f_BPM_hr[3]));
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3])); // 最多+2
}
else
{
f_BPM_hr[4] = f_BPM_hr[3];
f_BPM_hr[4] = f_BPM_hr[3] + 1;
}
count_down_hr = 0;
count_down_hr_m = 0;
count_out_range_hr = 0;
}
else if ((f_BPM_hr[4] - MyToolMidValue(f_BPM_hr2, 4) <= (-8)) || (f_BPM_hr[4] - f_BPM_hr[3] <= (-6))) // 比前4个中位数小8
else if ((f_BPM_hr[4] - MyToolMidValue(f_BPM_hr2, 4) <= (-8)) || (f_BPM_hr[4] - f_BPM_hr[3] <= (-6))) // 比前4个中位数小8或比前面一个值小6
{
count_down_hr += 1;
#if FFT_VERSION
@ -923,20 +936,20 @@ void fft_heartDataPostProcessing(float32_t data)
{
count_up_hr_m += 1;
#if FFT_VERSION
if (count_down_hr + count_down_hr_m >= 8)
if (count_up_hr + count_up_hr_m >= 10)
#else
if (count_down_hr + count_down_hr_m >= 8)
if (count_up_hr + count_up_hr_m >= 8)
#endif
{
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3]));
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3])); // 最多+2
}
#if FFT_VERSION
else if (count_up_hr_m >= 10)
else if (count_up_hr_m >= 8)
#else
else if (count_up_hr_m >= 8)
#endif
{
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3]));
f_BPM_hr[4] = f_BPM_hr[3] + (2 < (f_BPM_hr[4] - f_BPM_hr[3]) ? 2 : (f_BPM_hr[4] - f_BPM_hr[3])); // 最多+2
}
#if FFT_VERSION
else if (count_up_hr_m >= 2)
@ -954,17 +967,18 @@ void fft_heartDataPostProcessing(float32_t data)
count_down_hr = 0;
count_down_hr_m = 0;
count_out_range_hr = 0;
count_up_hr = 0;
// count_up_hr = 0;
}
else if (f_BPM_hr[4] - f_BPM_hr[3] < 0)
{
if (f_BPM_hr[4] - f_BPM_hr[3] <= (-3))
{
count_down_hr_m += 1;
#if FFT_VERSION
if (count_up_hr + count_up_hr_m >= 8)
if (count_down_hr + count_down_hr_m >= 8)
#else
if (count_up_hr + count_up_hr_m >= 8)
if (count_down_hr + count_down_hr_m >= 8)
#endif
{
f_BPM_hr[4] = f_BPM_hr[3] + ((-2) > (f_BPM_hr[4] - f_BPM_hr[3]) ? (-2) : (f_BPM_hr[4] - f_BPM_hr[3]));
@ -1003,13 +1017,13 @@ void fft_heartDataPostProcessing(float32_t data)
f_BPM_hr[4] = 170;
}
/**/
HeartBeatRate = f_BPM_hr[4];
HeartBeatRate = (uint8_t)round(f_BPM_hr[4]);
// rt_kprintf("------------------- Freq %.6f\r\n", freq_calculated(nf, 0));
}
else
else // 数据没满五个,取平均值, 且输出值固定在65-75之间
{
int Count = 0;
float sum, avg = 0.0f;
float sum = 0.0f, avg = 0.0f;
for (char i = 0; i < 5; i++)
{
// rt_kprintf("f_BPM_hr[%d] = %f\r\n", i, f_BPM_hr[i]);

View File

@ -1,5 +1,6 @@
#include "find_peaks.h"
#include "MyTool.h"
void compute_dynamic_threshold_optimized(const float *data, float *thresholds)
{
@ -67,7 +68,7 @@ PeakResult find_peaks(const float *input)
PeakResult result_struct = {
.peaks = {0}, // 初始化peaks数组全为0
.thresholds = {0.0f}, // 初始化thresholds数组全为0.0f
.count = 0}; // 初始化count为0
.count = 0}; // 初始化count为0
// memset(&result_struct,0,sizeof(result_struct));
float data[DATA_LENGTH] = {0.0f};
@ -157,3 +158,71 @@ int time_domain_heart_rate(float *hp_data)
}
return hr;
}
//
float compute_target_acf(const float *data, int lag, float mean, float total_energy)
{
const int N = DATA_LENGTH - lag;
if (N <= 0 || total_energy < 1e-6f)
return 0.0f;
float sum_product = 0.0f;
for (int i = 0; i < N; i++)
{
float x = data[i] - mean;
float y = data[i + lag] - mean;
sum_product += x * y;
}
return sum_product / total_energy;
}
// 快速选择心率
int fast_select_hr(const float candidates[4], const float *data, float *fft_energy_ratios)
{
float32_t std = 0.0f;
float32_t avg = 0.0f;
avg = MyToolAvgValue((float32_t *)data, DATA_LENGTH);
std = MyToolStdValue((float32_t *)data, DATA_LENGTH);
// 预处理计算均值和总能量
float parsed_data[DATA_LENGTH];
// memset(parsed_data, data, sizeof(float) * DATA_LENGTH);
float mean = 0.0f, total_energy = 0.0f;
for (int i = 0; i < DATA_LENGTH; i++)
{
parsed_data[i] = (data[i] - avg) / std;
mean += parsed_data[i];
total_energy += parsed_data[i] * parsed_data[i];
}
mean /= DATA_LENGTH;
total_energy -= DATA_LENGTH * mean * mean; // 修正总能量
// 遍历候选心率
float max_score = -1.0f;
int selected_idx = -1;
float temp = 0.0f;
for (int i = 0; i < 4; i++)
{
const float hr = (i < 2) ? candidates[i] / 2 : candidates[i];
// 转换候选心率为lag值
int target_lag = (int)(50 * 60.0f / hr + 0.5f);
if (target_lag <= 0 || target_lag >= DATA_LENGTH / 2)
continue;
// 局部窗口ACF峰值检测
float acf_peak = -1.0f;
int search_radius = 1;
for (int offset = 0; offset <= search_radius; offset++)
{
int current_lag = target_lag + offset;
if (current_lag < 1 || current_lag >= DATA_LENGTH - 1)
continue;
float acf_val = compute_target_acf(parsed_data, current_lag, mean, total_energy);
acf_peak = fmax(acf_peak, acf_val);
}
// 综合评分网页6的加权策略
float score = 0.7f * acf_peak + 0.3f * fft_energy_ratios[i];
if (score > max_score)
{
max_score = score;
selected_idx = i;
}
}
return selected_idx;
}

View File

@ -24,5 +24,6 @@ typedef struct {
int time_domain_heart_rate(float* hp_data);
int fast_select_hr(const float candidates[4], const float *data, float *fft_energy_ratios);
#endif // FIND_PEAKS_H

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1048,6 +1048,7 @@ extern char i_count_off_bed;
extern char i_count_no_human;
extern char i_count_No_Breath;
extern float Diff_detail_Record;
extern uint16_t EnergyData;
extern uint16_t FiveMinutesCount;
extern uint16_t fft_HeartBeatRate_Result;
extern uint16_t time_HeartBeatRate_Result;
@ -1088,17 +1089,14 @@ void wifi_RealTime_log_send(char *MyStr)
\"bootResult\": %d,\"log\":\"%s\",\"Diff_detail_Record\": %f,\"ADC_Value\": %.4f,\"Channel_1310\": %d,\"i_count_no\": %d,\"i_count_off_bed\": %d,\
\"i_count_No_Breath\": %d,\"i_count_max\": %f,\"FFT_br\": %f,\"Diff\": %f,\"Diff_Detail\": %f,\"OPM\": %f,\"OPM_Record\": %f,\"OPM_Record_2\": %f,\
\"OPM_Origin\": %f,\"Ratio[0]\": %f,\"Ratio[1]\": %f,\"Peak[0]\": %f,\"Peak[1]\": %f,\"Peak[2]\": %f,\"Peak[3]\": %f,\"HR_result\": %d,\"time_result\": %d,\
\"Optical\": [",
\"EnergyData\": %d}",
UploadVersion, BuildTimeString,
(timeClockCount_Send + (((signed char)warning_config.timeZone) * 60 * 60 * 1000)), timeClockCount_Send, bootResult,
MyStr, (float)Diff_detail_Record, ADC_1310, channel_1310, i_count_no_human, i_count_off_bed, i_count_No_Breath, (float)i_count_Max,
(float)FFT_br, (float)Diff, (float)Diff_Detail, (float)OPM_Current, (float)OPM_Record, (float)OPM_Record_2, (float)OPM_Original,
(float)(stash_Ratio[0]), (float)(stash_Ratio[1]), (float)(HeartValueRes2Peaks[0]), (float)(HeartValueRes2Peaks[1]),
(float)(HeartValueRes2Peaks[2]), (float)(HeartValueRes2Peaks[3]), fft_HeartBeatRate_Result, time_HeartBeatRate_Result);
length = strlen(JsonOutputBuffer) + strlen(array) + 2;
// strcat(JsonOutputBuffer, array);
// printf("Length 2 = %d\n", length);
strcat(JsonOutputBuffer, "]}");
(float)(HeartValueRes2Peaks[2]), (float)(HeartValueRes2Peaks[3]), fft_HeartBeatRate_Result, time_HeartBeatRate_Result, EnergyData);
length = strlen(JsonOutputBuffer) + strlen(array);
}
// printf("%d JSON OUTPUT: %s \n", length, JsonOutputBuffer);
str[0] = 0XA5;

View File

@ -123,6 +123,7 @@ extern char FitterCollectDoneFlag_Breath;
extern char FitterCollectDoneFlag_CheckBed;
extern char WarnConfigDoneFlag;
extern float32_t Energy_Ratio[4];
extern warning_config_t warning_config;
WarningMsg_t WarningMsg; // 告警消息
@ -872,6 +873,7 @@ float KalmarFilter(float inData)
* @return {*}
* @author: lzc
*/
uint16_t EnergyData = 0;
uint16_t Chip_ID_Data = 0;
uint16_t fft_HeartBeatRate_Hi = 0;
uint16_t fft_HeartBeatRate_Lo = 0;
@ -896,6 +898,7 @@ int main(void)
struct tm *time;
time_t timestamp = 0; /* 时间戳 */
char Send_Once = 1;
int EnergyIndex = 0;
int ReSendStartCommand_Count = 0;
MattressParameterInit(); // 床垫 初始化
// 初始化IIC
@ -1009,16 +1012,6 @@ int main(void)
if (start_calculate_flg == 1)
{
start_calculate_flg = 0;
//------------------------------ 算法验证 ---------------------------------------//
/*
printf("\n--------------------- Start --------------------------------\n");
fft_heartDataPostProcessing(round(fft_Output(outPutFitterOPMData_Heart,
HEART_FITTER_LENGTH - HEART_IGNORE_LENGTH, TYPE_HEART)));
fft_breathDataPostProcessing(round(fft_Output(outPutFitterOPMData_Breath,
BREATH_FITTER_LENGTH - BREATH_IGNORE_LENGTH, TYPE_BREATH)));
printf("Heart Rate: %d Breath Rate: %d\n", HeartBeatRate, BreathRate);
printf("\n--------------------- End --------------------------------\n");
*/
//------------------------------ FFT算法 ---------------------------------------//
if (equipment_status_value == InBed_Status)
{

View File

@ -32,7 +32,7 @@
#include "system_stm32f4xx.h"
// 1 表示是 养老 0 表示 是BCS
#define THE_VERSION 1
#define THE_VERSION 0
// 1 表示是 提速 0 表示 正常
#define SPEED_UP_MODE_EN 0

File diff suppressed because one or more lines are too long

View File

@ -390,6 +390,11 @@
<WinNumber>1</WinNumber>
<ItemText>BreathRate</ItemText>
</Ww>
<Ww>
<count>46</count>
<WinNumber>1</WinNumber>
<ItemText>FFT_HeartRateVal</ItemText>
</Ww>
</WatchWindow1>
<WatchWindow2>
<Ww>
@ -603,7 +608,7 @@
<Group>
<GroupName>USER</GroupName>
<tvExp>1</tvExp>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
@ -891,7 +896,7 @@
<Group>
<GroupName>Application</GroupName>
<tvExp>0</tvExp>
<tvExp>1</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>

File diff suppressed because one or more lines are too long