本帖最后由 dlzxlsx 于 2025-4-10 21:27 编辑
我是闽北人,闽北以茶与虫闻名。武夷岩茶不在本文讨论范围。闽北地貌复杂,气候温暖潮湿,生态环境极好,有丰富的动植物资源,其中昆虫品种繁多。
(笔者家乡发现的国家一级重点保护野生动物金斑喙凤蝶 )
本文涉及的项目是一个可用于野外观察某一地点昆虫出没情况的仪器,设计功能是:1.把仪器固定在室外某处后,打开电源,仪器会拍摄视野内新出现的物体,并把照片存储到SD卡中 ;2.若干天后,使用者取出SD卡,把照片分类标注,得到这一地点出没昆虫的图片;3.使用者把这些图片做为数据集,上传到edge impulse上训练获取物品分类模型,下载安装到该仪器的esp32s3AI开发板上;4.具有昆虫识别功能的仪器能把视野范围昆虫的名称、出现的时间以csv格式记录到SD上。在一段时间后,使用者取出SD卡,对获得的数据做进一步分析,可作为珍稀昆虫保护或害虫防治的决策使用。
下面罗列该项目的制作过程,因笔者学识与技术方面的欠缺,肯定存在不少不足之处,肯请同行指出。同时,对dfrobot提供的ESP32 S3 AI CAM模块,以及在制作过程中dfrobot工程师提供的技术支持表示感谢。
一、开发环境与器材
电脑:win11, i5-1135G7,Arduino ide;
开发板:dfrobot提供的ESP32 S3 AI CAM模块
其他:铁架台,3D打印 机,昆虫模型
捕捉野生动物违法,因此我用昆虫模型代替真实昆虫
二、对ESP32 S3 AI CAM进行测试 1.在arduino ide2.3.3安装了esp32开发板后,上打开示例文件中的cameraWebServer.
2.先删除该示例文件中的全部代码,把下面的代码粘贴到该文件中,并把下图中wifi信息修改成自己的wifi.
#include "esp_camera.h"
#include <WiFi.h>
//
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
// Ensure ESP32 Wrover Module or other board with PSRAM is selected
// Partial images will be transmitted if image exceeds buffer size
//
// You must select partition scheme from the board menu that has at least 3MB APP space.
// Face Recognition is DISABLED for ESP32 and ESP32-S2, because it takes up from 15
// seconds to process single frame. Face Detection is ENABLED if PSRAM is enabled as well
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 5
#define Y9_GPIO_NUM 4
#define Y8_GPIO_NUM 6
#define Y7_GPIO_NUM 7
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 17
#define Y4_GPIO_NUM 21
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 16
#define VSYNC_GPIO_NUM 1
#define HREF_GPIO_NUM 2
#define PCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 8
#define SIOC_GPIO_NUM 9
// ===========================
// Enter your WiFi credentials
// ===========================
const char *ssid = "***********";
const char *password = "************";
void startCameraServer();
void setupLedFlash(int pin);
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_UXGA;
config.pixel_format = PIXFORMAT_JPEG; // for streaming
//config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.jpeg_quality = 12;
config.fb_count = 1;
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if (config.pixel_format == PIXFORMAT_JPEG) {
if (psramFound()) {
config.jpeg_quality = 10;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
// Limit the frame size when PSRAM is not available
config.frame_size = FRAMESIZE_SVGA;
config.fb_location = CAMERA_FB_IN_DRAM;
}
} else {
// Best option for face detection/recognition
config.frame_size = FRAMESIZE_240X240;
#if CONFIG_IDF_TARGET_ESP32S3
config.fb_count = 2;
#endif
}
#if defined(CAMERA_MODEL_ESP_EYE)
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
#endif
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t *s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s, 1); // flip it back
s->set_brightness(s, 1); // up the brightness just a bit
s->set_saturation(s, -2); // lower the saturation
}
// drop down frame size for higher initial frame rate
if (config.pixel_format == PIXFORMAT_JPEG) {
s->set_framesize(s, FRAMESIZE_QVGA);
}
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
#endif
#if defined(CAMERA_MODEL_ESP32S3_EYE)
s->set_vflip(s, 1);
#endif
// Setup LED FLash if LED pin is defined in camera_pins.h
#if defined(LED_GPIO_NUM)
setupLedFlash(LED_GPIO_NUM);
#endif
WiFi.begin(ssid, password);
WiFi.setSleep(false);
Serial.print("WiFi connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
startCameraServer();
Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
}
void loop() {
// Do nothing. Everything is done in another task by the web server
delay(10000);
}
复制代码
3.在IDE2.3.3的工具菜单中对开发板进行设置(如下图)
在实验中发现,烧录失败,错误提示如下:
Sketch uses 3148101 bytes (100%) of program storage space. Maximum is 3145728 bytes.
Compilation error: text section exceeds available space in board.
后改在arduino IDE1.8.4中能烧录成功。不明白IDE2.3.3是什么原因不能烧录,如何解决?
三、项目制作
1.设计制作项目支架
在tinkercad上设计3D模型,打印成型。
把esp32s3AI模块固定到盒子下方,盒子上方是电池仓。整个盒子安装在铁架上,实际效果如下图。
2.为了省约时间,减少流程,笔者把设计功能的“1.把仪器固定在室外某处后,打开电源,仪器会拍摄视野内新出现的物体,并把照片存储到SD卡中;2.若干天后,使用者取出SD卡,把照片分类标注,得到这一地点出没昆虫的图片”两部分简化为手动采集昆虫图片数据。
把IDE串口监视器输出的IP地址复制到浏览器上,在下面的界面上点击save,采集昆虫图片,每种昆虫的图片不少于50张,在拍摄照片过程中,要不断变化昆虫的方向。考虑到硬件的性能,为了提高识别率,在拍摄照片时,要注意数据集照片的环境(背景、光照)要到测试时的环境一致。因此,把ESP32S3 AI模块固定到支架上,把昆虫模型放在镜头下方拍摄,这样每张照片中昆虫与镜头的距离、角度、光线者相对稳定。
3. 把五种昆虫的照片分类存入在五个文件夹内。
4. 到edge impulse上注册账号。
Edge Impulse是一个应用于嵌入式领域的在线的机器学习网站,不仅为用户提供了一些现成的神经网络模型以供训练,还能直接将训练好的模型转换成能在单片机MCU上运行的代码,使用方便,容易上手。因此本项目利用该平台训练图像分类模型,再部署到esp32s3上。
在Projects中创建工程Create new project,填写工程信息。
5.上传照片数据集
做出相应的选择后,“选择文件夹”,把五个昆虫的图片上传到网站。
之后,对每个图进行框选,标注。这一步骤十分费时,当然,也有更简单的方法,直接用文件夹的名称作为图片的Label。
标注好的数据集:
6.选择目标开发板
点击右上角的开发板选择按钮。
按下图进行选择。
7.开始impulse desgin
依次设置如下
颜色深度,考虑到部署机器的性能,我选择“grayscale".
在”Generate features”页面,点击“Generate features”按钮生成图像特征
8.训练模型。 在模型训练参数上,可设置如下
点击保存与训练
此时可以离开电脑,去拖地板、清洗冬天的棉袄。
回来之后,发现结果很满意!
千万不要手贱,别点击
否则再去洗一遍地板!
9.部署模型
点击
在跳出的页面中设置如下
点击build,右侧出现
片刻后会下载一个库文件压缩包,为了后面的叙述方面,将此文件称为模型库文件 。
10.在esp32s3 ai模块上测试模型
将下载的 模型库文件 解压到电脑中"……arduino->libraies"中
到DF的网站下载edge impulse demo文件(https://img.dfrobot.com.cn/wikic ... 974096d7a9e8650.zip ),后文称此文件为 demo文件
解压该 demo文件 ,效果如下
把上面demo文件 夹中的"depthwise_conv.cpp"和"conv.cpp" 文件复制到 模型库文件 中,具体操作:
选择demo文件夹 中的这两个文件后复制,开打arduino->libraries->的 模型库文件 insects_inferencing下的“src\edge-impulse-sdk\tensorflow\lite\micro\kernels”
粘贴到该目录下,替换原来的"depthwise_conv.cpp"和"conv.cpp"文件
再将 demo文件夹 中的整个 edge_camera文件夹复制到arduino->libraries->的模型库文件 中的的examples中。
打开arduino IDE2.3.3,选择edge_camera示例,将代码中的第一行模型库名称改为自己训练后的库名称,填入自己WiFi账号密码,然后编译烧录。
编译过程十分缓慢,十分钟左右。
程序第185行“ei_printf("%s (%f)[x: %u, y: %u, width: %u, height: %u ]\n", bb.label, bb.value, bb.x, bb.y, bb.width, bb.height);”中bb.label就是昆虫的名称。
上传成功后,测试发现,识别率令人满意。
11.在该文件的基础上进行修改,实现设计目标。先把该文件复制一份作为备份文件。
由于要把昆虫活动的时间记录在SD卡,因此要加入时间与SD卡的相关库。通过查阅esp32se的wiki,SD卡采用SPI,占用引脚如下
我电脑上SD卡的库是https://github.com/espressif/arduino-esp32/tree/master/libraries/SD
从注释中可看出,该库文件与ESP32S3AI模块上SD卡的连接完成一样,可直接引用。
另外,我用NTP功能实现时间戳,NTC库如下
在对NTP以及SD卡的代码逐个测试通过后,再把相应的功能组合在一组,写入代码中,下面是增加部分代码的截图。
引用与定义部分
初始化
模型识别出昆虫后,记录昆虫名称与时间到SD卡。
logDataToSD() 函数
经过漫长的编译后,尝试把代码上传到开发板上。
不过,在烧录时出现了 错误提示:
A fatal error occurred: Failed to connect to ESP32-S3: No serial data received
在上电情况下,长按板上的“boot"键不放,快按"RST"键,松开"boot",重新上传代码。
此时串口输出以下提示:
E (326) esp_core_dump_flash: Core dump flash config is corrupted! CRC=0x7bd5c66f instead of 0x0
E (334) esp_core_dump_elf: Elf write init failed!
E (339) esp_core_dump_common: Core dump write failed with error=-1
Rebooting...
完整信息截图:
项目又进入困境……
esp32_s3_cam.rar
全文完。