本帖最后由 dlzxlsx 于 2025-4-10 21:27 编辑  我是闽北人,闽北以茶与虫闻名。武夷岩茶不在本文讨论范围。闽北地貌复杂,气候温暖潮湿,生态环境极好,有丰富的动植物资源,其中昆虫品种繁多。
(笔者家乡发现的国家一级重点保护野生动物金斑喙凤蝶 
本文涉及的项目是一个可用于野外观察某一地点昆虫出没情况的仪器,设计功能是:1.把仪器固定在室外某处后,打开电源,仪器会拍摄视野内新出现的物体,并把照片存储到SD卡中 
下面罗列该项目的制作过程,因笔者学识与技术方面的欠缺,肯定存在不少不足之处,肯请同行指出。同时,对dfrobot提供的ESP32 S3 AI CAM模块,以及在制作过程中dfrobot工程师提供的技术支持表示感谢。一、开发环境与器材 Arduino  ide; 
其他:铁架台,3D打印 机,昆虫模型 二、对ESP32 S3 AI CAM进行测试 1.在arduino ide2.3.3安装了esp32开发板后,上打开示例文件中的cameraWebServer. 
#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);
 }
 
 复制代码 后改在arduino IDE1.8.4中能烧录成功。不明白IDE2.3.3是什么原因不能烧录,如何解决? 三、项目制作 1.设计制作项目支架 
在tinkercad上设计3D模型,打印成型。  把esp32s3AI模块固定到盒子下方,盒子上方是电池仓。整个盒子安装在铁架上,实际效果如下图。    2.为了省约时间,减少流程,笔者把设计功能的“1.把仪器固定在室外某处后,打开电源,仪器会拍摄视野内新出现的物体,并把照片存储到SD卡中;2.若干天后,使用者取出SD卡,把照片分类标注,得到这一地点出没昆虫的图片”两部分简化为手动采集昆虫图片数据。 3. 把五种昆虫的照片分类存入在五个文件夹内。 Edge Impulse是一个应用于嵌入式领域的在线的机器学习网站,不仅为用户提供了一些现成的神经网络模型以供训练,还能直接将训练好的模型转换成能在单片机MCU上运行的代码,使用方便,容易上手。因此本项目利用该平台训练图像分类模型,再部署到esp32s3上。 在Projects中创建工程Create new project,填写工程信息。 5.上传照片数据集 做出相应的选择后,“选择文件夹”,把五个昆虫的图片上传到网站。 6.选择目标开发板 7.开始impulse desgin 在”Generate features”页面,点击“Generate features”按钮生成图像特征 8.训练模型。 在模型训练参数上,可设置如下
千万不要手贱,别点击 
否则再去洗一遍地板!9.部署模型  
模型库文件 。
将下载的 模型库文件 解压到电脑中"……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文件夹 中的整个 模型库文件 中的的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 
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