2025-3-6 08:48:14 [显示全部楼层]
3634浏览
查看: 3634|回复: 1

[ESP8266/ESP32] 【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!

[复制链接]
本帖最后由 RRoy 于 2025-3-6 08:56 编辑

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图1

本项目将教你创建一个 ESP32 边缘 AI 摄像头系统,该系统旨在简化 AI 任务的数据收集和模型部署。它结合了 ESP32-S3 微控制器的强大功能、摄像头模块以及 3D 打印的磁吸外壳,紧凑、便携,而且易于安装在各种地方。

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图2

这个项目的独特之处在于简化的数据采集和上传到 Edge Impulse 的流程。通过使用为 ESP32 定制的 Arduino 代码以及运行在 Flask 上的 Python Web 界面,我们可以快速收集、预览带标签的图像数据,并将其上传到 Edge Impulse。

工作原理如下:
  • 运行 Python Web 界面,并通过 USB 连接到 ESP32
  • 输入你的 Edge Impulse API 密钥,选择“训练”或“测试”模式,并可选择为图像指定标签
  • 点击“捕获并上传”按钮
  • ESP32 通过 USB 接收命令,捕获图像,并将其发送回 PC
  • 图像被保存,在 Web 界面上预览,并使用所需的设置上传到 Edge Impulse

该系统无需手动连接 SD 卡、传输图像和标记数据,非常适合快速原型设计和数据集生成。

完成本项目后,你会学到:
  • 使用 ESP32 摄像头捕获数据。
  • 使用收集的数据训练 Edge Impulse 模型。
  • 将训练好的模型部署回 ESP32,以进行实时 AI 推理




材料清单


步骤 1:CAD 设计与 3D 打印

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图3

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图4

首先,使用 Fusion 360 为 ESP32-S3 摄像头设计一个定制外壳。 设计包括:
  • 用于轻松安装的磁吸系统
  • 侧面按钮的一个复位按钮和一个可编程按钮

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图5

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图6

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图7

你可以使用提供的 Fusion 360 文件修改设计,也可以直接下载并 3D 打印以下部件:
  • 1x Housing.stl (外壳)
  • 1x Cover.stl (盖子)
  • 2x Button.stl (按钮)

此外,设计中还包含了一个简单的安装板。 你可以自定义它或创建自己的版本以满足不同的需求。

这边使用 Bambu Lab P1S 上的灰色和橙色 PLA 长丝打印了外壳。 盖子使用了长丝更换技术来实现双色效果。 给设计增添了独特的触感,同时保持了外壳的功能完整性。
附件
【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图8

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图9

STL 文件可以在文末打包下载。

步骤 2:天线组装
只需将 ESP32-S3 随附的天线连接到 ESP32-S3 板上的指定端口即可。

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图10

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图11


步骤 3:ESP32 外壳组装
  • 取出 3D 打印的外壳和两个按钮延伸件
  • 将按钮插入外壳上指定的插槽中
  • 将 ESP32-S3 板放入外壳内,确保 Type-C 端口与外壳上的开口对齐

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图12

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图13

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图14


步骤 4:摄像头盖组装
  • 取出 ESP32-S3 随附的摄像头模块、3D 打印的盖子和快干胶
  • 小心地将少量快干胶涂到摄像头模块上
  • 将摄像头模块连接到盖子上的孔上,并确保其方向正确并牢固就位。 小心使用胶水,避免与镜头或摄像头的精细部件接触

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图15

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图16

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图17


步骤 5:最终组装
  • 取出外壳组件和盖子组件
  • 将摄像头模块电缆连接到 ESP32 板
  • 将天线电线和摄像头电缆整齐地塞入外壳内,确保没有任何东西被挤压或阻塞
  • 将盖子卡到外壳上以将其牢固关闭
  • 你的 ESP32 边缘 AI 摄像头现已完全组装完毕

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图18

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图19

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图20

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图21

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图22

步骤 6:Edge Impulse 项目设置创建一个 Edge Impulse 帐户
【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图23
  • 单击“创建新项目”

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图24
  • 输入项目的名称
  • 选择将项目设为“私有”或“公开”
  • 点击“创建”

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图25

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图26

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图27

什么是 Edge Impulse
Edge Impulse 是一个旨在在微控制器和嵌入式系统等边缘设备上构建、训练和部署机器学习模型的平台。 它简化了处理数据、创建 ML 模型以及将它们直接部署到你的硬件的过程。

Edge Impulse 的主要功能包括:
  • 数据收集和管理: 高效地收集和标记你的数据
  • 模型训练: 训练针对你的用例量身定制的自定义机器学习模型
  • 部署: 将训练好的模型部署到具有优化性能的边缘设备

数据采集面板

Edge Impulse 中的数据采集面板是你上传和管理数据的地方。 工作方式如下:
  • 你可以直接从你的设备或通过 API 等集成上传图像、音频或传感器数据
  • 数据可以组织成训练集和测试集,以进行有效的模型评估
  • 可以将标签添加到你的数据中,帮助 Edge Impulse 理解和分类输入
  • 还支持通过连接的设备进行实时数据采集,这简化了收集实时输入的过程

数据类型以及如何收集
  • 你可以将预先收集的数据(例如图像、音频或传感器读数)直接上传到平台
  • Edge Impulse Data Forwarder 允许你将来自 ESP32 等连接设备的实时数据直接发送到平台
  • 使用 Edge Impulse API 以编程方式从外部来源上传数据



步骤 7:上传 Arduino 代码

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图28

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图29
  • 下载代码
    • GitHub 仓库: GitHub仓库链接:https://github.com/MukeshSankhla/ESP32-Edge-Capture
    • 解压文件并在项目文件夹中找到 ESP32_Code.ino 文件。
    • 在计算机上打开 Arduino IDE。
    • 导航到“文件”>“打开”,然后从解压的文件夹中选择 ESP32_Code.ino 文件。
  • 安装 ESP32 板管理器

    如果你还没有在 Arduino IDE 中配置 ESP32 环境,按照以下指南操作:
  • 安装所需的库

    你需要为此项目安装DFRobot_AXP313A 的库:https://github.com/cdjq/DFRobot_AXP313A/archive/refs/heads/master.zip
    • 下载 DFRobot_AXP313A 的库
    • 在 Arduino IDE 中,转到“项目”>“包含库”>“添加 .ZIP 库”,然后选择下载的 DFRobot_AXP313A.zip 文件。
  • 上传代码

    使用 Type-C USB线 把 ESP32 边缘 AI 摄像头连接到 PC。

    【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图30

    转到“工具”并设置以下内容:

    单击 Arduino IDE 中的“上传”按钮(右箭头)。
    • 开发板: 选择 DFRobot FireBeetle 2 ESP32-S3。
    • 端口: 选择与你的 ESP32 板关联的 COM 端口。

步骤 8:运行 Python 代码并捕获图像

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图31

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图32

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图33

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图34

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图35
设置
安装摄像头:
  • 使用双面胶将磁性安装板连接到你的笔记本电脑或所需的表面。
  • 将 AI 摄像头安装到板上,然后使用 USB 线将它连接到你的 PC。

准备代码:
  • 在 VS Code 中打开下载的项目文件夹。
  • 确保你的系统上安装了 Python 3。
  • 通过运行以下命令安装所需的 Python 模块:
  1. pip3 install -r requirements.txt
复制代码


运行 Web 界面:
  • 通过运行以下命令启动 Flask Web 应用程序:
  1. python app.py
复制代码

  • 打开你的 Web 浏览器并导航到默认地址:http://localhost:5000。
使用 Web 界面
  • 选择 COM 端口:
    • 选择你的 ESP32 摄像头连接到的 COM 端口。
  • 输入 API 密钥:
    • 输入你的 Edge Impulse API 密钥启用数据上传。
  • 选择模式:
    • 选择“训练”模式以收集训练数据。
    • 选择“测试”模式以收集测试数据。
  • 添加标签(可选):
    • 为你的数据集输入标签。
  • 捕获并上传:
    • 摄像头捕获。
    • 在 Web 界面中预览。
    • 使用指定的标签和模式上传到 Edge Impulse。
    • 单击“捕获并上传”按钮以使用 ESP32 捕获图像。
    • 该图像将被:

当提供标签时,将在图像周围绘制一个带有给定标签名称的边界框:

  1. files = [
  2. ('data', (filename, open(filepath, 'rb'), 'image/jpeg'))
  3. ]
  4. if label:
  5. # Prepare metadata (with bounding boxes if needed)
  6. metadata = {
  7. "version": 1,
  8. "type": "bounding-box-labels",
  9. "boundingBoxes": {
  10. filename: [
  11. {
  12. "label": label,
  13. "x": 0, # Full image bounding box coordinates
  14. "y": 0,
  15. "width": 640,
  16. "height": 480
  17. }
  18. ]
  19. }
  20. }
  21. bbox_label = json.dumps(metadata, separators=(',', ':'))
  22. # Add label data to files
  23. files.append(('data', ('bounding_boxes.labels', bbox_label)))
复制代码


当感兴趣的对象占据整个图像时,这非常有用,因为边界框默认覆盖整个图像分辨率。

但是,如果你将标签字段留空,则不会绘制任何边界框,并且图像不会分配任何标签。


步骤 9:使用 ESP32 AI 摄像头进行手指计数


为了演示 ESP32 边缘 AI 摄像头的功能,我们将创建一个简单的项目——使用 ESP32 AI 摄像头进行手指计数。 项目将检测在摄像头前显示的手指数量,并输出相应的计数(例如,显示一个手指将会“计数 1”)。

数据收集
  • 使用 ESP32 Edge Capture 系统收集用于训练和测试数据集的图像。
  • 捕获尽可能多的图像以确保准确性。 一个好的经验法则是保持训练和测试数据集之间 5:1 的比例(例如,每 50 个训练图像,收集 10 个测试图像)。
  • 包括各种光照条件、角度和背景,使模型更强大。

数据集示例



【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图36
  • 一个手指: 捕获举起一个手指的图像。
  • 两个手指: 捕获举起两个手指的图像。
  • 继续捕获三个、四个和五个手指的图像。
  • 包括一些空图像(未显示任何手指)表示“零计数”。

此步骤确保模型有足够的数据来学习并在实时推理期间表现良好。

标记数据集

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图37

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图38

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图39

收集所有图像后,就可以在 Edge Impulse 中标记它们了。 可以按照以下步骤操作:
  • 转到“标记队列”部分。
  • 根据显示的手指数量标记每个图像
  • 仔细检查每个标记的图像确保准确性,因为不正确的标签会降低模型的性能。


步骤 10:创建 Impulse

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图40

导航到 Edge Impulse 中的 “Impulse Design” 部分。

使用以下内容设置 Image Data 块:
  • Input axes: Image
  • Image width: 48 pixels
  • Image height: 48 pixels
  • Resize mode: Fit shortest axis

即:
  • 输入轴: 图像
  • 图像宽度: 48 像素
  • 图像高度: 48 像素
  • 调整大小模式: 适应最短轴

在 Learning Blocks 下添加一个 Object Detection 块。
  • Input Feature: Select the Image block.
  • Output Features: Define the labels as 1, 2, 3, 4, 5.

即:
  • 输入特征: 选择图像块。
  • 输出特征: 将标签定义为 1、2、3、4、5。

配置完成后,单击 保存 Impulse。


步骤 11:生成数据特征
【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图41

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图42

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图43

设置处理参数:
  • 在 Parameters 部分下:
    • 确保“图像颜色深度”设置为 Grayscale(灰度) 或适合你用例的其他值。
    • 单击 Save Parameters(保存参数) 应用设置。

Generate Features(生成特征):
  • 切换到 Generate Features 选项卡。
  • 查看训练数据集的摘要:
    • Classes(类别): 检查与你的数据集关联的标签/类别(例如,1、2、3、4、5)。
    • Data in training set(训练集中的数据): 项目数(例如,如屏幕截图所示的 64 个项目)。
    • 单击 Generate Features 按钮处理数据。

Analyze Feature Output(分析特征输出):
  • 处理后,在页面右侧查看 Feature Explorer(特征资源管理器)。
  • 特征资源管理器中的散点图可视化了如何根据生成的特征分离不同的类。
  • 每种颜色对应于一个特定的类。
  • 确保每个类的特征都很好地分离; 这表明数据集适合训练。

步骤 12:训练模型

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图44

导航到对象检测:
  • 在 Edge Impulse 中,转到“Impulse Design”>“Object Detection(对象检测)”。

Set Training Parameters(设置训练参数):
  • Number of Training Cycles(训练周期数): 30
  • Learning Rate(学习率): 0.005
  • Training Processor(训练处理器): 选择 CPU
  • Enable Data Augmentation(启用数据增强): 选中数据增强的框(建议用于提高模型的泛化能力)。

选择模型:

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图45
  • 单击“选择其他模型”,然后从列表中选择 FOMO(更快的对象,更多对象)MobileNetV2 0.35。
  • 确认选择。

训练模型:
  • 单击“保存并训练”以开始训练过程。

理解结果:

训练后,你将看到以下输出:

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图46

F1 Score: 82.8%
  • 表示模型在识别所有类别的对象时的准确性。

Confusion Matrix(混淆矩阵):
  • 此矩阵显示模型对每个类别的性能。
  • 绿色单元格表示正确的预测,红色单元格表示错误分类。

Metrics (Validation Set) 指标(验证集):
  • Precision(精确度): 0.75 – 正面预测的准确性。
  • Recall(召回率): 0.92 – 模型查找所有相关实例的能力。
  • F1 Score: 0.83 – 精确度和召回率之间的平衡。


步骤 13:部署模型

部署过程涉及从你训练好的 Edge Impulse 模型创建一个与 Arduino 兼容的库。

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图47

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图48

1. 生成部署文件
  • 导航到你的 Edge Impulse 项目中的“部署”选项卡。
  • 在部署选项下,选择“Arduino 库”。
  • 确保已选择“量化 (int8)”选项(这可确保模型针对 ESP32 进行了优化)。
  • 单击“构建”以生成库。
  • 构建完成后,下载生成的 Arduino 库的 .zip 文件。
  • 在 Arduino IDE 中,转到“项目”>“包含库”>“添加 .ZIP 库”,然后选择下载的 zip 文件。

2. 修改 ei_classifier_config.h 文件

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图49
  • 导航到提取的库文件夹内的  ei_classifier_config.h  文件:
    • 路径:Documents\Arduino\libraries\ESP32_Edeg_AI_Camera_inferencing\src\edge-impulse-sdk\classifier\ei_classifier_config.h
  • 在文本编辑器(如记事本或 VS Code)中打开此文件。
  • 找到以下行:
    1. #define EI_CLASSIFIER_TFLITE_ENABLE_ESP_NN 1
    复制代码

    将其从 1 更改为 0:
  1. #define EI_CLASSIFIER_TFLITE_ENABLE_ESP_NN 0
复制代码

为什么要进行更改?
  • 这个设置禁用 TensorFlow Lite 的 ESP-NN(神经网络加速),有时会导致 ESP32 上某些模型出现问题。 禁用它可以帮助确保模型正常运行。
  • 进行此更改后千万记得保存文件。

步骤 14:上传代码

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图50

【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图51
  • 复制提供的代码并将其粘贴到 Arduino IDE 中
  • 在 Arduino IDE 的“工具”菜单中选择正确的开发板 (DFRobot FireBeetle 2 ESP32-S3)。
  • 为你的设备选择正确的端口。
  • 单击“上传”将代码刷到 ESP32上。

  1. /*
  2. Project: ESP32 Edge Capture
  3. Author: Mukesh Sankhla
  4. Website: https://www.makerbrains.com
  5. GitHub: https://github.com/MukeshSankhla
  6. */
  7. #include <ESP32_Edeg_AI_Camera_inferencing.h>
  8. #include "edge-impulse-sdk/dsp/image/image.hpp"
  9. #include "esp_camera.h"
  10. #include "DFRobot_AXP313A.h"
  11. // Camera model definition
  12. #define CAMERA_MODEL_DFRobot_FireBeetle2_ESP32S3
  13. DFRobot_AXP313A axp;
  14. // FireBeetle ESP32S3 Camera GPIO Pins
  15. #define PWDN_GPIO_NUM -1
  16. #define RESET_GPIO_NUM -1
  17. #define XCLK_GPIO_NUM 45
  18. #define SIOD_GPIO_NUM 1
  19. #define SIOC_GPIO_NUM 2
  20. #define Y9_GPIO_NUM 48
  21. #define Y8_GPIO_NUM 46
  22. #define Y7_GPIO_NUM 8
  23. #define Y6_GPIO_NUM 7
  24. #define Y5_GPIO_NUM 4
  25. #define Y4_GPIO_NUM 41
  26. #define Y3_GPIO_NUM 40
  27. #define Y2_GPIO_NUM 39
  28. #define VSYNC_GPIO_NUM 6
  29. #define HREF_GPIO_NUM 42
  30. #define PCLK_GPIO_NUM 5
  31. /* Constant defines -------------------------------------------------------- */
  32. #define EI_CAMERA_RAW_FRAME_BUFFER_COLS 320
  33. #define EI_CAMERA_RAW_FRAME_BUFFER_ROWS 240
  34. #define EI_CAMERA_FRAME_BYTE_SIZE 3
  35. /* Private variables ------------------------------------------------------- */
  36. static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
  37. static bool is_initialised = false;
  38. uint8_t *snapshot_buf; // Points to the output of the capture
  39. static camera_config_t camera_config = {
  40. .pin_pwdn = PWDN_GPIO_NUM,
  41. .pin_reset = RESET_GPIO_NUM,
  42. .pin_xclk = XCLK_GPIO_NUM,
  43. .pin_sscb_sda = SIOD_GPIO_NUM,
  44. .pin_sscb_scl = SIOC_GPIO_NUM,
  45. .pin_d7 = Y9_GPIO_NUM,
  46. .pin_d6 = Y8_GPIO_NUM,
  47. .pin_d5 = Y7_GPIO_NUM,
  48. .pin_d4 = Y6_GPIO_NUM,
  49. .pin_d3 = Y5_GPIO_NUM,
  50. .pin_d2 = Y4_GPIO_NUM,
  51. .pin_d1 = Y3_GPIO_NUM,
  52. .pin_d0 = Y2_GPIO_NUM,
  53. .pin_vsync = VSYNC_GPIO_NUM,
  54. .pin_href = HREF_GPIO_NUM,
  55. .pin_pclk = PCLK_GPIO_NUM,
  56. .xclk_freq_hz = 20000000,
  57. .ledc_timer = LEDC_TIMER_0,
  58. .ledc_channel = LEDC_CHANNEL_0,
  59. .pixel_format = PIXFORMAT_GRAYSCALE, // YUV422, GRAYSCALE, RGB565, JPEG
  60. .frame_size = FRAMESIZE_QVGA, // QQVGA-UXGA Do not use sizes above QVGA when not JPEG
  61. .jpeg_quality = 12, // 0-63 lower number means higher quality
  62. .fb_count = 1, // If more than one, i2s runs in continuous mode. Use only with JPEG
  63. .fb_location = CAMERA_FB_IN_PSRAM,
  64. .grab_mode = CAMERA_GRAB_WHEN_EMPTY,
  65. };
  66. /* Function definitions ------------------------------------------------------- */
  67. bool ei_camera_init(void);
  68. void ei_camera_deinit(void);
  69. bool ei_camera_capture(uint32_t img_width, uint32_t img_height, uint8_t *out_buf);
  70. /**
  71. * @brief Arduino setup function
  72. */
  73. void setup() {
  74. Serial.begin(115200);
  75. while (!Serial);
  76. Serial.println("Edge Impulse Inferencing Demo");
  77. while (axp.begin() != 0) {
  78. Serial.println("AXP313A initialization failed");
  79. delay(1000);
  80. }
  81. // Enable camera power
  82. axp.enableCameraPower(axp.eOV2640);
  83. if (ei_camera_init() == false) {
  84. ei_printf("Failed to initialize Camera!\r\n");
  85. } else {
  86. ei_printf("Camera initialized\r\n");
  87. }
  88. ei_printf("\nStarting continuous inference in 2 seconds...\n");
  89. ei_sleep(2000);
  90. }
  91. /**
  92. * @brief Main loop for inferencing
  93. */
  94. void loop() {
  95. if (ei_sleep(5) != EI_IMPULSE_OK) {
  96. return;
  97. }
  98. snapshot_buf = (uint8_t *)malloc(EI_CAMERA_RAW_FRAME_BUFFER_COLS * EI_CAMERA_RAW_FRAME_BUFFER_ROWS * EI_CAMERA_FRAME_BYTE_SIZE);
  99. if (snapshot_buf == nullptr) {
  100. ei_printf("ERR: Failed to allocate snapshot buffer!\n");
  101. return;
  102. }
  103. ei::signal_t signal;
  104. signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
  105. signal.get_data = &ei_camera_get_data;
  106. if (ei_camera_capture((size_t)EI_CLASSIFIER_INPUT_WIDTH, (size_t)EI_CLASSIFIER_INPUT_HEIGHT, snapshot_buf) == false) {
  107. ei_printf("Failed to capture image\r\n");
  108. free(snapshot_buf);
  109. return;
  110. }
  111. ei_impulse_result_t result = {0};
  112. EI_IMPULSE_ERROR err = run_classifier(&signal, &result, debug_nn);
  113. if (err != EI_IMPULSE_OK) {
  114. ei_printf("ERR: Failed to run classifier (%d)\n", err);
  115. return;
  116. }
  117. ei_printf("Predictions (DSP: %d ms, Classification: %d ms, Anomaly: %d ms): \n",
  118. result.timing.dsp, result.timing.classification, result.timing.anomaly);
  119. if (err != EI_IMPULSE_OK) {
  120. ei_printf("ERR: Failed to run classifier (%d)\n", err);
  121. free(snapshot_buf);
  122. return;
  123. }
  124. // Check if classification results are valid
  125. if (EI_CLASSIFIER_LABEL_COUNT > 0 && result.classification != nullptr) {
  126. for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
  127. if (result.classification[ix].label != nullptr) { // Check label pointer
  128. ei_printf(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
  129. } else {
  130. ei_printf(" ERR: Null label at index %d\n", ix);
  131. }
  132. }
  133. }
  134. // Check and print anomaly score if applicable
  135. if (EI_CLASSIFIER_HAS_ANOMALY == 1) {
  136. ei_printf(" Anomaly score: %.3f\n", result.anomaly);
  137. }
  138. free(snapshot_buf);
  139. }
  140. bool ei_camera_init(void) {
  141. if (is_initialised) return true;
  142. esp_err_t err = esp_camera_init(&camera_config);
  143. if (err != ESP_OK) {
  144. Serial.printf("Camera init failed with error 0x%x\n", err);
  145. return false;
  146. }
  147. sensor_t *s = esp_camera_sensor_get();
  148. if (s->id.PID == OV3660_PID) {
  149. s->set_vflip(s, 1);
  150. s->set_brightness(s, 1);
  151. s->set_saturation(s, 0);
  152. }
  153. is_initialised = true;
  154. return true;
  155. }
  156. void ei_camera_deinit(void) {
  157. esp_camera_deinit();
  158. is_initialised = false;
  159. }
  160. bool ei_camera_capture(uint32_t img_width, uint32_t img_height, uint8_t *out_buf) {
  161. if (!is_initialised) {
  162. ei_printf("ERR: Camera is not initialized\r\n");
  163. return false;
  164. }
  165. camera_fb_t *fb = esp_camera_fb_get();
  166. if (!fb) {
  167. ei_printf("Camera capture failed\n");
  168. return false;
  169. }
  170. // Ensure the framebuffer size matches the expected dimensions
  171. if (fb->width != img_width || fb->height != img_height) {
  172. ei_printf("ERR: Framebuffer size mismatch. Expected: %lux%lu, Got: %lux%lu\n",
  173. (unsigned long)img_width, (unsigned long)img_height,
  174. (unsigned long)fb->width, (unsigned long)fb->height);
  175. esp_camera_fb_return(fb);
  176. return false;
  177. }
  178. // Directly copy grayscale data
  179. memcpy(out_buf, fb->buf, fb->len);
  180. esp_camera_fb_return(fb);
  181. return true;
  182. }
  183. static int ei_camera_get_data(size_t offset, size_t length, float *out_ptr) {
  184. size_t pixel_ix = offset;
  185. size_t pixels_left = length;
  186. size_t out_ptr_ix = 0;
  187. while (pixels_left != 0) {
  188. // Convert grayscale byte to a float between 0 and 1
  189. out_ptr[out_ptr_ix] = snapshot_buf[pixel_ix] / 255.0f;
  190. out_ptr_ix++;
  191. pixel_ix++;
  192. pixels_left--;
  193. }
  194. return 0;
  195. }
  196. #if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_CAMERA
  197. #error "Invalid model for current sensor"
  198. #endif
复制代码

如果你要部署自己的 Edge Impulse 模型,则需要确保在代码中链接了正确的模型。

在代码中找到这一行:
  1. #include <ESP32_Edeg_AI_Camera_inferencing.h>
复制代码

并将其更改为
  1. #include <Your_Custom_Model.h> // Update this line for your model
复制代码

输出应如下所示:

  1. Edge Impulse Inferencing Demo
  2. Camera initialized
  3. Starting continuous inference in 2 seconds...
  4. Predictions (DSP: 45 ms, Classification: 25 ms, Anomaly: 3 ms):
  5. BACKGROUND: 0.95000
  6. 1: 0.02000
  7. 2: 0.01500
  8. 3: 0.00500
  9. 4: 0.00500
  10. 5: 0.00500
  11. Anomaly score: 0.010
  12. Predictions (DSP: 46 ms, Classification: 26 ms, Anomaly: 3 ms):
  13. BACKGROUND: 0.10000
  14. 1: 0.75000
  15. 2: 0.13000
  16. 3: 0.01000
  17. 4: 0.00500
  18. 5: 0.00500
  19. Anomaly score: 0.050
  20. Predictions (DSP: 44 ms, Classification: 24 ms, Anomaly: 3 ms):
  21. BACKGROUND: 0.01000
  22. 1: 0.01000
  23. 2: 0.01000
  24. 3: 0.80000
  25. 4: 0.08000
  26. 5: 0.09000
  27. Anomaly score: 0.020
复制代码

步骤 15:结论
【实战指南】低成本+低门槛玩转ESP32边缘AI视觉相机!图52

在这个项目中,我们逐步介绍了使用 Edge Impulse 构建对象检测模型的完整过程,从设置设备和收集数据到创建 Impulse、生成特征以及准备用于训练的数据集。 每个步骤都突出了 Edge Impulse 在使开发人员能够构建针对其特定应用量身定制的边缘 AI 解决方案方面的简单性和效率。

通过生成特征并通过特征资源管理器可视化它们,我们确保数据已得到充分准备和区分,这是实现准确和可靠的模型性能的关键步骤。 此外,该平台用于评估设备上性能的指标(例如处理时间和内存使用情况)使优化模型以部署在 ESP32 边缘 AI 摄像头等资源受限的设备上变得更加容易。

该项目的一个关键亮点是集成了你开发的自定义工具,该工具简化了 Edge Impulse 工作流程中的特定任务。 此工具通过自动化重复流程、改进数据预处理以及提供对收集数据的更深入的见解,从而显着提高了效率。 它的包含演示了自定义解决方案如何补充现有平台以获得更好的结果。

Edge Impulse 提供了一个功能强大、用户友好的界面,允许任何人在没有先前 AI 经验的情况下为实际应用创建机器学习模型。 借助你的自定义工具的额外支持,工作流程变得更加强大,从而为高级且可扩展的 AI 驱动项目铺平了道路。

原文地址:https://www.instructables.com/ESP32-Edge-AI-Camera/

项目作者:Mukesh_Sankhla

译文首发于:DF创客社区

转载请注明来源信息


ESP32 边缘AI摄像头.zip

14.99 MB, 下载次数: 67

刘睿鹏  中级技师

发表于 2025-3-7 16:04:39

WHAT A WONDERFUL IDEA IT IS
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail