From 670b86d2001dc685574cb8d4a1b9c4b7c37e282b Mon Sep 17 00:00:00 2001 From: spdis Date: Wed, 24 Sep 2025 12:42:13 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=B8=8B=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dorm-Air-Conditioner-Smart-Controller.ino | 6 + audio_model_esp32.h | 228 ++++++++-------------- core.h | 7 +- 3 files changed, 92 insertions(+), 149 deletions(-) diff --git a/Dorm-Air-Conditioner-Smart-Controller.ino b/Dorm-Air-Conditioner-Smart-Controller.ino index 22ee1fd..d04a2de 100644 --- a/Dorm-Air-Conditioner-Smart-Controller.ino +++ b/Dorm-Air-Conditioner-Smart-Controller.ino @@ -872,6 +872,12 @@ void handleSetSettings() { prefs.putFloat("min_temp", minTemp); prefs.putFloat("max_temp", maxTemp); + Serial.printf("温度设置已更新 - 最低: %.1f°C, 最高: %.1f°C\n", minTemp, maxTemp); + + // 温度设置更改后立即执行一次判断 + Serial.println("温度设置已更改,立即执行核心判断..."); + executeJudgeLogic(); + server.send(200, "application/json", "{\"success\":true}"); } diff --git a/audio_model_esp32.h b/audio_model_esp32.h index aad2a3e..f99e6da 100644 --- a/audio_model_esp32.h +++ b/audio_model_esp32.h @@ -37,7 +37,7 @@ extern "C" { // 预处理参数 #define MEL_FMIN 0.0f // Mel滤波器最低频率 #define MEL_FMAX 8000.0f // Mel滤波器最高频率 -#define WINDOW_TYPE_HANN 1 // 汉宁窗 +#define WINDOW_TYPE_HANN 1 // 海宁窗 #define ENERGY_THRESHOLD 0.01f // 音频活动检测阈值 #define CONFIDENCE_THRESHOLD 0.6f // 预测置信度阈值 @@ -100,6 +100,8 @@ float calculate_rms_energy(const int16_t* audio_data, int length); void audio_model_cleanup(void); const unsigned char* get_audio_model_data(void); size_t get_audio_model_size(void); +const char* get_class_name_en(AudioClassType class_id); +const char* get_class_name_cn(AudioClassType class_id); // ==================== 核心API函数 ==================== @@ -124,7 +126,7 @@ int audio_model_init(void) { return -1; } - // 预计算汉宁窗 + // 预计算海宁窗 for (int i = 0; i < N_FFT; i++) { g_preprocessor.window_buffer[i] = 0.5f * (1.0f - cosf(2.0f * M_PI * i / (N_FFT - 1))); } @@ -185,7 +187,7 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic strcpy(g_last_error, "无效参数"); return -1; } - + uint32_t start_time = micros(); // 添加看门狗喂狗 @@ -193,128 +195,73 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic yield(); #endif - // 使用栈上的小缓冲区替代大数组,减少内存使用 - const int REDUCED_FEATURES = 32; // 减少特征数量 - float mel_features[REDUCED_FEATURES]; + // TODO: 集成TensorFlow Lite模型进行真实的音频识别 + // 当前使用audio_model_data.h中的TensorFlow Lite模型数据 + // 需要实现以下步骤: + // 1. 初始化TensorFlow Lite解释器 + // 2. 加载模型数据 (audio_model_data) + // 3. 预处理音频数据为模型输入格式 + // 4. 执行推理 + // 5. 解析输出结果 - // 简化的音频特征提取,避免复杂的Mel频谱图计算 - if (preprocess_audio_to_mel_simple(audio_data, audio_length, mel_features, REDUCED_FEATURES) != 0) { - strcpy(g_last_error, "音频预处理失败"); - return -1; - } - - // 添加看门狗喂狗 + // 临时实现:基于音频能量的简单分类,提供更合理的结果 #ifdef ARDUINO - yield(); + Serial.println("警告:当前使用临时实现,等待TensorFlow Lite模型集成"); #endif - // 使用简化的特征分析替代复杂的TensorFlow Lite推理 - // 计算基本统计特征 - float mean_energy = 0.0f; - float energy_variance = 0.0f; - float max_energy = -1000.0f; - float min_energy = 1000.0f; + // 计算音频能量来做简单的分类判断 + float rms_energy = calculate_rms_energy(audio_data, audio_length); - // 计算平均能量和能量范围 - for (int i = 0; i < REDUCED_FEATURES; i++) { - mean_energy += mel_features[i]; - if (mel_features[i] > max_energy) max_energy = mel_features[i]; - if (mel_features[i] < min_energy) min_energy = mel_features[i]; - - // 定期喂狗 - if (i % 10 == 0) { - #ifdef ARDUINO - yield(); - #endif + // 添加调试信息:检查音频数据的实际值 + #ifdef ARDUINO + int non_zero_count = 0; + int16_t min_val = 32767, max_val = -32768; + long long sum_abs = 0; + + for (int i = 0; i < min(100, audio_length); i++) { // 检查前100个样本 + if (audio_data[i] != 0) non_zero_count++; + if (audio_data[i] < min_val) min_val = audio_data[i]; + if (audio_data[i] > max_val) max_val = audio_data[i]; + sum_abs += abs(audio_data[i]); + } + + Serial.printf("音频数据调试: 非零样本=%d/100, 最小值=%d, 最大值=%d, 平均绝对值=%lld\n", + non_zero_count, min_val, max_val, sum_abs/100); + #endif + + // 基于能量水平进行简单分类 + AudioClassType predicted_class; + float confidence; + + if (rms_energy > 0.1f) { + // 高能量:可能是关门声或钥匙声 + if (rms_energy > 0.3f) { + predicted_class = AUDIO_CLASS_DOOR_CLOSING; + confidence = 0.75f; + } else { + predicted_class = AUDIO_CLASS_KEY_JINGLING; + confidence = 0.65f; } - } - mean_energy /= REDUCED_FEATURES; - - // 计算能量方差 - for (int i = 0; i < REDUCED_FEATURES; i++) { - float diff = mel_features[i] - mean_energy; - energy_variance += diff * diff; - } - energy_variance /= REDUCED_FEATURES; - - // 添加看门狗喂狗 - #ifdef ARDUINO - yield(); - #endif - - // 基于简化特征的分类逻辑 - memset(result->class_probabilities, 0, sizeof(result->class_probabilities)); - - // 使用能量和方差进行简单分类 - float energy_range = max_energy - min_energy; - - // 钥匙声特征:中等能量,高方差 - float key_score = 0.0f; - if (mean_energy > -5.0f && mean_energy < -2.0f && energy_variance > 2.0f) { - key_score = 0.4f; + } else if (rms_energy > 0.02f) { + // 中等能量:室内有人 + predicted_class = AUDIO_CLASS_PERSON_PRESENT; + confidence = 0.70f; + } else { + // 低能量:室内无人 + predicted_class = AUDIO_CLASS_PERSON_ABSENT; + confidence = 0.80f; } - // 关门声特征:高能量,低方差 - float door_score = 0.0f; - if (mean_energy > -2.0f && energy_variance < 1.0f) { - door_score = 0.5f; - } + // 设置结果 + result->predicted_class = predicted_class; + result->confidence = confidence; - // 人员活动声特征:中等能量,中等方差 - float person_score = 0.0f; - if (mean_energy > -6.0f && mean_energy < -1.0f && energy_variance > 0.5f && energy_variance < 3.0f) { - person_score = 0.3f; - } - - // 无人声特征:低能量,低方差 - float absent_score = 0.0f; - if (mean_energy < -8.0f && energy_variance < 0.5f) { - absent_score = 0.6f; - } - - // 添加看门狗喂狗 - #ifdef ARDUINO - yield(); - #endif - - // 归一化概率 - float total_score = key_score + door_score + person_score + absent_score; - if (total_score < 0.1f) { - // 默认为有人状态 - person_score = 0.4f; - total_score = 0.4f; - } - - // 添加少量随机性模拟AI不确定性 - uint32_t audio_hash = 0; - for (int i = 0; i < audio_length; i += 1000) { - audio_hash = audio_hash * 31 + (uint32_t)abs(audio_data[i]); - } - float noise_factor = (float)(audio_hash % 50) / 1000.0f; // 0-0.05的随机因子 - - result->class_probabilities[AUDIO_CLASS_KEY_JINGLING] = (key_score / total_score) + noise_factor; - result->class_probabilities[AUDIO_CLASS_DOOR_CLOSING] = (door_score / total_score) + noise_factor * 0.8f; - result->class_probabilities[AUDIO_CLASS_PERSON_PRESENT] = (person_score / total_score) + noise_factor * 0.6f; - result->class_probabilities[AUDIO_CLASS_PERSON_ABSENT] = (absent_score / total_score) + noise_factor * 0.4f; - - // 重新归一化 - float prob_sum = 0.0f; + // 设置概率分布 for (int i = 0; i < NUM_CLASSES; i++) { - prob_sum += result->class_probabilities[i]; - } - if (prob_sum > 0) { - for (int i = 0; i < NUM_CLASSES; i++) { - result->class_probabilities[i] /= prob_sum; - } - } - - // 找到最高概率的类别 - result->confidence = 0.0f; - result->predicted_class = AUDIO_CLASS_PERSON_PRESENT; - for (int i = 0; i < NUM_CLASSES; i++) { - if (result->class_probabilities[i] > result->confidence) { - result->confidence = result->class_probabilities[i]; - result->predicted_class = (AudioClassType)i; + if (i == (int)predicted_class) { + result->class_probabilities[i] = confidence; + } else { + result->class_probabilities[i] = (1.0f - confidence) / (NUM_CLASSES - 1); } } @@ -329,6 +276,11 @@ int audio_model_predict(const int16_t* audio_data, int audio_length, AudioPredic g_total_confidence += result->confidence; } + #ifdef ARDUINO + Serial.printf("音频能量: %.4f, 预测类别: %s, 置信度: %.2f\n", + rms_energy, get_class_name_cn(predicted_class), confidence); + #endif + return 0; } @@ -406,22 +358,16 @@ int preprocess_audio_to_mel_simple(const int16_t* audio_data, int audio_length, int16_t prev_sample = 0; for (int i = start_idx; i < end_idx; i++) { - // 音频增益放大20倍,然后进行能量计算 - int32_t amplified_sample = (int32_t)audio_data[i] * 20; - // 防止溢出,限制在int16_t范围内 - if (amplified_sample > 32767) amplified_sample = 32767; - if (amplified_sample < -32768) amplified_sample = -32768; - - float sample = (float)amplified_sample / 32768.0f; + float sample = (float)audio_data[i] / 32768.0f; energy += sample * sample; - // 零交叉率计算 - 使用放大后的音频数据 + // 零交叉率计算 if (i > start_idx && - ((amplified_sample >= 0 && prev_sample < 0) || - (amplified_sample < 0 && prev_sample >= 0))) { + ((audio_data[i] >= 0 && prev_sample < 0) || + (audio_data[i] < 0 && prev_sample >= 0))) { zero_crossings += 1.0f; } - prev_sample = (int16_t)amplified_sample; + prev_sample = audio_data[i]; // 添加看门狗喂狗,防止长时间计算 #ifdef ARDUINO @@ -479,22 +425,16 @@ int preprocess_audio_to_mel(const int16_t* audio_data, int audio_length, float* int16_t prev_sample = 0; for (int i = start_idx; i < end_idx; i++) { - // 音频增益放大20倍,然后进行能量计算 - int32_t amplified_sample = (int32_t)audio_data[i] * 20; - // 防止溢出,限制在int16_t范围内 - if (amplified_sample > 32767) amplified_sample = 32767; - if (amplified_sample < -32768) amplified_sample = -32768; - - float sample = (float)amplified_sample / 32768.0f; + float sample = (float)audio_data[i] / 32768.0f; energy += sample * sample; - // 零交叉率计算 - 使用放大后的音频数据 - if (i > start_idx && - ((amplified_sample >= 0 && prev_sample < 0) || - (amplified_sample < 0 && prev_sample >= 0))) { - zero_crossings += 1.0f; - } - prev_sample = (int16_t)amplified_sample; + // 零交叉率计算 + if (i > start_idx && + ((audio_data[i] >= 0 && prev_sample < 0) || + (audio_data[i] < 0 && prev_sample >= 0))) { + zero_crossings += 1.0f; + } + prev_sample = audio_data[i]; // 添加看门狗喂狗,防止长时间计算 #ifdef ARDUINO @@ -670,13 +610,7 @@ float calculate_rms_energy(const int16_t* audio_data, int length) { float sum = 0.0f; for (int i = 0; i < length; i++) { - // 音频增益放大20倍,然后计算RMS能量 - int32_t amplified_sample = (int32_t)audio_data[i] * 20; - // 防止溢出,限制在int16_t范围内 - if (amplified_sample > 32767) amplified_sample = 32767; - if (amplified_sample < -32768) amplified_sample = -32768; - - float sample = (float)amplified_sample / 32768.0f; // 归一化到[-1,1] + float sample = (float)audio_data[i] / 32768.0f; // 归一化到[-1,1] sum += sample * sample; } return sqrtf(sum / length); diff --git a/core.h b/core.h index 84b4b44..40dff5c 100644 --- a/core.h +++ b/core.h @@ -188,10 +188,13 @@ int judge() { // 获取用户设置的温度范围 (从Preferences读取) Preferences prefs; prefs.begin("DACSC", true); // 只读模式 - float min_temp = prefs.getFloat("min_temp", 5.0); // 默认最低温度22°C - float max_temp = prefs.getFloat("max_temp", 28.0); // 默认最高温度26°C + float min_temp = prefs.getFloat("min_temp", 10.0); // 默认最低温度10°C + float max_temp = prefs.getFloat("max_temp", 28.0); // 默认最高温度28°C prefs.end(); + // 打印当前使用的温度设置 + Serial.printf("当前温度设置 - 最低: %.1f°C, 最高: %.1f°C\n", min_temp, max_temp); + // 判断逻辑:基于节假日、音频识别、时间、温度和湿度的智能控制 // 规则1:节假日规则(优先级最高)