2026年“寒假练” - 基于人工智能硬件实验套件平台实现的环境预测与场景联动桌搭
该项目使用了人工智能硬件实验套件平台,实现了环境风险的预测的设计,它的主要功能为:基于多种传感器的输出、输入到模型中预测风险的概率。
标签
嵌入式系统
ESP32
机器学习
edge impuse
Wang Chong
更新2026-03-24
43

任务介绍

环境预测与场景联动:通过使用人工智能硬件实验平台中自带的各项传感器来进行数据采集,并且将传感器数据输入到训练好的模型中进行风险的预测。

69b43e67f1789362196f6cef3ad52607.jpg

项目介绍

1. 采集温度、湿度、环境光强、震动或倾斜等多源数据,并间隔一段时间持续记录。  
2. 使用TensorFlow等平台训练轻量模型,输出风险预测。 
3. OLED 上设置多级菜单,显示各传感器实时值、预测评分与趋势、过往采集数据等,使用四按键控制菜单切换、数据显示、数据情况、重置设备等;三色 LED 用红黄绿显示风险等级。  
4. 预测风险从正常转为可能存在风险时,蜂鸣器短鸣3次进行提示,三色LED由绿色转为黄色;预测风险达到阈值时蜂鸣器长鸣警报,三色LED转为红色。  

硬件介绍

本次项目的人工智能硬件实验套件是一个功能非常完善的套件,其中套件的开发板是ESP32S3-XIAO-Sense。其主控是采用的是ESP32-S3N8R8模组,具备8MB的Flash和8MB的PSRAM。开发板上自带了一个麦克风和摄像头以及SD卡的插槽。整个开发板仅仅有拇指的的指甲大小确具备非常强大的计算性能。

de739dcf91d015f4ad2938bf61d474bb.jpg

其他任务中所使用到的器件如下所示:

eb28ae5e0a11702dace084eb94ea35dc.jpg

  1. DHT11 湿温度传感器
  2. 灰度传感器
  3. OLED屏幕
  4. 电位器
  5. 蜂鸣器
  6. LED灯
  7. MPU6050 (未在上图标注)

方案框图和项目设计思路介绍

项目的主体的设计思路分为四步:

一、驱动上述的所有模块

二、使用第一步中采集到的数据在Edge impulse中建立机器学习模型,并且训练、下载和部署。

三、使用模拟数据调试好第二步中训练的模型使其程序可以正常的输出预测结果。

四、将第一步中的驱动代码和第三步中调试的程序相结合、使其输入真正的数据而非是模拟的数据。


未命名绘图.drawio.png

功能框图如上所示


首先ESP32初始化各个使用到的外设,并且采集对应的传感器的数据,然后将数据输入到风险预测的模型中进行数据的推理。然后根据推理的结果来更新屏幕、LED、和触发蜂鸣器报警。

上述中需要注意的一点是:其MPU6050的I2C地址是0x69所以不能直接使用MPU6050的驱动库进行驱动。建议这里手动使用I2C来读取寄存器的值从而来获取到加速度传感器和陀螺仪的数据等。


模型训练和部署


模型的训练采用的是Edge impuse 平台。首先我们需要对传感器的数据进行采集并且划分好风险的等级。例如0:正常

1、中等 和 2:高危风险。然后准备好需要的数据集如下所示。(下述数据集,我一共采集了1200条数据)

image.png

然后打开Edge Impuse 平台、在数据获取的页面上首先来设置CSV文件的格式化。

image.png

选择后确认好CSV的划分规则。

image.png

然后确认并不包含时序的信号

image.png

之后便可以在数据集处上传我们的数据集,对应的CSV文件中的数据会按照我们设置的分割方式进行分割。如下所示

image.png

然后按照下图来创建一个Impuse任务。这里采用的是分类的任务对上述输入的数据特征进行学习和分类处对应的风险

image.png

然后在Raw data中生成特称image.png

然后便是便是对模型进行训练,由于我这里的数据量较多所以为了能让模型更好的学习到其中的特征我这里设置了1000轮的训练。到最后的模型验证数据集的正确率可以达到92%,表现十分不错。

image.png

之后便是可以将模型部署到ESP32上。在部署页面上选择量化过的int8版本,实际上无论是int8 或者是float32 对性能基本上没有什么影响,float32 也仅仅花费3MS时间进行推理,所以可以忽略不计,但是如果你使用的是图像的分类任务的话,建议使用INT8.

image.png

在把对应的库安装到Arduino中之后,打开对应的static_buffer exmaple 进行测试。 注意:这个demo并不是针对现在训练的模型的,所以需要做一些修改才好(建议这一步借助AI来完成)。

image.png

效果如下:成功调用了我们训练的模型。

image.png


软件流程图及关键代码介绍

使用软件汇总:

  1. Arduino IDE
  2. EDGE impulse (模型训练)
  3. Draw.io (流程图绘制)

项目的采用了Arduino开发,其主要原因有三。一、Seed 官方具有丰富的Demo可以借鉴参考,同时Eetree以往也有很多优秀的使用ESP32S3-Xiao开发的经典案例。比如说图像的训练EEtree就有现成的文档可以参考。(我这里使用的是分类的模型因此、这里需要对Edge impulse的库稍稍修改一下) 二、无缝衔接了Edge impulse和Arduino的库,当我们使用Edge impulse训练好模型之后可以直接的导入对应的Arduino的库到本地。三、Arduino提供了丰富的库函数支持。



image.png

接下来便是需要将两者的代码融合在一起,移除模拟的数据,使其可以读取传感器的数据来进行真实的推理。核心推理代码如下所示

// ===== Edge Impulse Inference =====
void runEdgeImpulseInference() {
Serial.println("\n=== Starting EI Inference ===");


// Read DHT11 data
int temperature = 0;
int humidity = 0;
int dhtResult = dht11.readTemperatureHumidity(temperature, humidity);


// Read grayscale sensor
int rawAvg = readAverageAnalog(GRAYSCALE_SENSOR_PIN, NUM_SAMPLES);
float grayVoltage = (rawAvg * VREF) / ADC_MAX;


// Read MPU6050 latest data
readMPU6050();


// Fill feature array
features[0] = grayVoltage;
features[1] = (float)temperature;
features[2] = (float)humidity;
features[3] = roll_angle;
features[4] = ax;
features[5] = ay;
features[6] = az;


Serial.printf("Gray:%.2fV", grayVoltage);
if (dhtResult == 0) {
Serial.printf(", Temp:%dC, Hum:%d%%", temperature, humidity);
} else {
Serial.printf(", DHT error:%d", dhtResult);
}
Serial.printf(", Roll:%.1f°, A(%.2f,%.2f,%.2f)g\n",
roll_angle, ax, ay, az);


// Run inference
signal_t features_signal;
features_signal.total_length = EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE;
features_signal.get_data = &raw_feature_get_data;


ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR res = run_classifier(&features_signal, &result, false);


if (res == EI_IMPULSE_OK) {
print_inference_result(result);
} else {
Serial.print("Inference failed: ");
Serial.println(res);
}


last_inference_time = millis();
}

完整的程序Loop循环

// ===== Main Loop =====
void loop() {
unsigned long currentMillis = millis();
static unsigned long lastSensorRead = 0;


// Check page switch
checkMenuSwitch();


// Read MPU6050 at 50Hz
if (currentMillis - lastMPURead >= MPU_READ_INTERVAL) {
lastMPURead = currentMillis;
readMPU6050();
}


// Read other sensors every 500ms
if (currentMillis - lastSensorRead >= 500) {
lastSensorRead = currentMillis;


// Read DHT11
int temperature = 0;
int humidity = 0;
int dhtResult = dht11.readTemperatureHumidity(temperature, humidity);


// Read grayscale sensor
int rawAvg = readAverageAnalog(GRAYSCALE_SENSOR_PIN, NUM_SAMPLES);
float voltageAvg = (rawAvg * VREF) / ADC_MAX;


// Read button voltage
int buttonADC = analogRead(ADC_BUTTON_PIN);
float buttonVoltage = (buttonADC * VREF) / ADC_MAX;


// Calculate risk level
int riskLevel = 0;
if (dhtResult == 0) {
riskLevel = calculateRiskLevel(temperature, humidity, voltageAvg);
addToHistory(temperature, humidity, riskLevel);
}


// Run inference on EI page
if (currentPage == PAGE_EI_INFERENCE) {
if (currentMillis - last_inference_time > INFERENCE_INTERVAL) {
runEdgeImpulseInference();
}
} else {
// 不在EI页面时,用简单规则更新LED
if (dhtResult == 0) {
if (riskLevel != previous_risk_level) {
controlBuzzer(riskLevel, previous_risk_level);
previous_risk_level = riskLevel;
}
setRiskLED(riskLevel);
}
}


// Update display
switch (currentPage) {
case PAGE_SENSOR:
displaySensorPage(temperature, humidity, dhtResult, rawAvg, voltageAvg, buttonVoltage);
break;
case PAGE_RISK:
displayRiskPage(temperature, humidity, voltageAvg, riskLevel, buttonVoltage);
break;
case PAGE_HISTORY:
displayHistoryPage(buttonVoltage);
break;
case PAGE_EI_INFERENCE:
displayEIInferencePage(buttonVoltage);
break;
}


// Serial debug output
Serial.printf("Voltage:%.2fV ", buttonVoltage);
Serial.printf("Page:%d ", currentPage);
if (dhtResult == 0) {
Serial.printf("Temp:%dC Hum:%d%% Risk:%d ", temperature, humidity, riskLevel);
}
Serial.printf("Roll:%.1f° A(%.2f,%.2f,%.2f)\n",
roll_angle, ax, ay, az);
}
}

其中主要的推理的时间设置了5秒,在数据收集完毕进行推理获取到对应的风险的程度之后则触发屏幕的更新和对应的LED及其蜂鸣器的报警。需要注意一点的是、蜂鸣器是一个无源蜂鸣器,因此这里的蜂鸣器的控制函数使用了Arduino自带的tone方法。

 for (int i = 0; i < 2; i++) {
digitalWrite(GREEN_LED_PIN, HIGH);
tone(BUZZER_PIN, 1500); // 启动提示音,1.5kHz
delay(100);
digitalWrite(GREEN_LED_PIN, LOW);
noTone(BUZZER_PIN);
delay(100);
}


菜单切换功能代码展示

// ===== Menu Switching =====
void checkMenuSwitch() {
unsigned long currentMillis = millis();
if (currentMillis - lastPageChange < PAGE_CHANGE_DELAY) return;


int buttonADC = analogRead(ADC_BUTTON_PIN);
float buttonVoltage = (buttonADC * VREF) / ADC_MAX;


if (buttonVoltage >= VOLTAGE_RESET) {
resetDevice();
lastPageChange = currentMillis;
return;
}


MenuPage newPage = PAGE_SENSOR;
if (buttonVoltage < 0.8) newPage = PAGE_SENSOR;
else if (buttonVoltage < 1.6) newPage = PAGE_RISK;
else if (buttonVoltage < 2.4) newPage = PAGE_HISTORY;
else newPage = PAGE_EI_INFERENCE;


if (newPage != currentPage) {
currentPage = newPage;
lastPageChange = currentMillis;
Serial.print("Switch to page: ");
switch (currentPage) {
case PAGE_SENSOR: Serial.println("Sensor"); break;
case PAGE_RISK: Serial.println("Risk"); break;
case PAGE_HISTORY: Serial.println("History"); break;
case PAGE_EI_INFERENCE: Serial.println("EI Inference"); break;
default: break;
}
}
}

功能展示图及说明

菜单一:所有传感器数据页面

01552ecd1dade282d011276f1dc9e988.jpg

当电位器电压在0.8V以下,则进入菜单1显示所有的传感器数据


菜单二:显示当前的风险等级

4bb4312ce814384d2e8d61768e7854dd.jpg

当电位器电压在0.8-1.6v时则进入菜单二


菜单三:显示历史的湿度和温度

7cd436a94b4058f97164217bd5759be5.jpg

当电压在1.6-2.4之间则进入菜单3


菜单四:实时推理和置信度页面

5eb4be322602492633665ec4f6b1236b.jpg

当电压在3.3V以下的时候则会进入到实时推理的页面,程序会五秒推理一次和更新状态。


低风险绿色灯展示

9fd2031d668a5b13d61e27a3818e3c8e.jpg


中风险黄色灯展示(伴随蜂鸣器警报和切换)

0058e5245b2c2aa5ecc82fd4d98b95cd.jpg

高风险展示(高温、倾斜、震动、关照低)

image.png


项目中遇到的难题及解决方法

其实在做这一个题目的时候并没有遇见什么很大的问题,我原本目标寒假练的选题并不是这个环境联动的,而是那个语音和摄像头的控制灯条。在做那个任务的时候图像识别的基本上尝试了7-8次的训练和测试部署及其调试后、虽然ESP32的性能不是很强,但是在400MS的延迟下识别拳头、手掌和1的手势正确率非常高在90%(模型的验证数据集)左右。但是在处理语音的识别的时候遇见了大麻烦。原本我是使用手机的麦克风采集的音频数据在Edge impuse中进行训练,其正确率可以达到95%在手机上进行实时分类的时候。但是一旦将模型部署到ESP32上并且通过I2S进行输入的时候(采样率相同)基本上处于完全不可用的情况。在最初我还以为是模型的精度不好泛化性不好。我还找来了我弟弟我们一共采集了大概10分钟的五个指令的音频的数据。在20-30次尝试中总共采集的音频的时长超过了一个小时。但是都是效果非常非常的差。基本上用不了。后来跟着Seeed的教程使用ESP32-S3来采集音频并且将音频保存到内存卡中在Edge impuse中进行分割。效果还是十分的感人,基本上不可用、甚至直接在Edge impuse中的Live classification中使用手机测试时基本上也都是错误的分类。但是实际上模型训练出来的精度还是非常高的基本上在85%左右。但是在测试的时候基本上是100%错误,如果训练的时候采集了白噪音的话,基本上所有的分类都会被识别为白噪音。因此在这个音频分类上我从大年初一一直折腾到了大年初三都没有搞定、因为edge impuse 比较方便我也没有学习过怎么处理音频的数据比如处理特征等,所以我也没有尝试使用tensorflow进行音频的处理(基于ESP-IDF)最终更换了题目花费了两天的时间完成了这次的课题。

心得体会

我这次最大的心得和最大的收获就是在Edge impuse的平台上成功的在esp32的平台上部署了分类(classification)模型和图像的分类的模型。我成功的打通了整个流程。正值新年之际,人家都说过年的时候最幸福的时候是年前的几天在和家人置办年货的时候。就是like最接近幸福的时候最幸福。所以我想说的是,这次活动中对我而言,不断探索和学习的过程才是最令我觉得印象深刻。最后感谢主办方的大力支持、祝Eetree的全体员工新年快乐!越办越好!

附件下载
ei-classification-arduino-1.0.1.zip
该文件为训练的风险分类的Arduino库,包括模型等,需要导入使用然后再烧录我提供的Arduino代码。
final.ino.zip
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号