修改一下模型相关
This commit is contained in:
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
7
core.h
7
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:节假日规则(优先级最高)
|
||||
|
||||
Reference in New Issue
Block a user