news 2026/9/20 5:46:55

基于 ESP-DL 的 7 类人脸情绪分类:emotion_cls 组件从模型部署到推理实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 ESP-DL 的 7 类人脸情绪分类:emotion_cls 组件从模型部署到推理实践

基于 ESP-DL 的 7 类人脸情绪分类:emotion_cls 组件从模型部署到推理实践

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

导读

本文聚焦乐鑫 esp-iot-solution 仓库中examples/ai/esp_dl/emotion_recognition示例的核心组件emotion_cls,完整讲解其在 ESP32-P4(以及可复用的 ESP32-S31)上运行 7 类人脸情绪分类模型的部署方式、推理接口与实测性能。读完本文,你将掌握如何阅读并复用该组件的模型文件、Kconfig 配置、C++ 推理 API 与测试用例,理解其量化精度与端侧工程化缓解策略,并能在自己的 ESP-IDF 工程中一键接入情绪识别能力。

组件概览:一个开箱即用的情绪分类模型

emotion_cls是 esp-iot-solution 中一个人脸情绪分类模型组件,其核心信息浓缩在 组件 README 中:

项目
支持目标芯片ESP32-P4
模型文件model/p4/emotion_cls.espdl
输入图像尺寸100 x 100
类别数7 类

7 个情绪类别在源码中按固定顺序定义,见 emotion_cls.cpp:

const char *kEmotionCategoryNames[] = { "surprise", // 惊讶 "fear", // 恐惧 "disgust", // 厌恶 "happiness", // 高兴 "sadness", // 悲伤 "anger", // 愤怒 "neutral", // 中性 };

类别索引从 0 开始,与模型输出 logits 的 top-1 索引一一对应。组件的idf_component.yml声明依赖espressif/esp-dl: "3.3.3",目标平台仅允许esp32p4esp32s31两种芯片,这与组件 README 中"Supported target: ESP32-P4"的描述一致——ESP32-S31 直接复用同一份 ESP32-P4 模型文件(见 idf_component.yml 与构建脚本中IDF_TARGET STREQUAL "esp32p4" OR IDF_TARGET STREQUAL "esp32s31"的判断)。

实测推理延迟

组件 README 给出了在 ESP32-P4 上单次推理的基准耗时:

模型名图像尺寸耗时(us)
emotion_cls100 x 100136803

即约 136.80 ms。该数据由组件的 test app 使用esp_timer_get_time()实测得到,测试代码见 app_main.cpp:

int64_t start_us = esp_timer_get_time(); emotion_cls::result_t result = cls.predict(img); int64_t elapsed_us = esp_timer_get_time() - start_us;

值得注意:136.80 ms 是纯 emotion_cls 模型推理的时间。当在完整的 emotion_recognition 示例中与 face detection 模型并发运行时,单次情绪分类的端到端平均耗时约为 870 ms,这是模型与检测流水线共享计算资源导致的现象(示例 README 中对此有明确说明)。

使用测试程序验证模型

组件内置了一个可独立运行的 test app,路径为components/emotion_cls/test_apps/。其主程序 app_main.cpp 的逻辑非常清晰:

  1. 通过dl::image::sw_decode_jpeg()将捆绑在固件中的test.jpg(100x100 测试人脸图)软解码为 RGB888 图像;
  2. 构造emotion_cls::EmotionCls cls;实例;
  3. 调用cls.predict(img)得到推理结果;
  4. 打印类别 ID、类别名、置信度分数与耗时。

组件 README 给出了打包test.jpg运行 test app 后的典型串口输出:

I (1785) emotion_cls: Inference result: class_id=6, class_name=neutral, score=3.880 I (1785) emotion_cls: Inference time: 136803 us (136.80 ms) I (1795) main_task: Returned from app_main()

测试用例还通过 partitions.csv 定义了nvsphy_initfactory(8 MB 应用分区)三个分区,以容纳模型与固件。

C++ 推理接口解析

emotion_cls对外暴露的核心类是EmotionCls,定义于 emotion_cls.hpp。它有两个核心成员:

  • result_t结构体:包含class_id(类别索引)、score(top-1 类别的 softmax 概率)、class_name(类别名字符串指针);
  • predict()重载predict(const dl::image::img_t &img)与带crop_area裁剪区域的版本,后者支持传入人脸检测框坐标,仅对 ROI 区域做分类。

构造与预处理

构造函数 emotion_cls.cpp 中完成两件关键工作:

  1. 模型加载:通过dl::Model加载模型,模型名缺省为"emotion_cls.espdl",模型位置由 Kconfig 选项CONFIG_EMOTION_CLS_MODEL_LOCATION决定;
  2. 预处理配置:创建dl::image::ImagePreprocessor,传入ImageNet 归一化参数(mean/std 乘以 255 后映射到 [0, 255] 输入域):
m_preprocessor = new dl::image::ImagePreprocessor( m_model, {123.675f, 116.28f, 103.53f}, {58.395f, 57.12f, 57.375f}, false);

这与训练侧 PyTorch 使用的 ImageNet 统计量mean=[0.485, 0.456, 0.406]std=[0.229, 0.224, 0.225]完全对应,保证端侧推理与训练时数据分布一致。

推理与结果解析

predict()的执行链路为:preprocess()m_model->run()→ 取输出张量get_output()→ 按输出 dtype 调用get_top1_result<int8_t>()get_top1_result<int16_t>()。该模板函数(emotion_cls.cpp)实现了一个数值稳定的 softmax

  1. 遍历输出找到最大 logits 值best_quant及其索引best_index
  2. ldexpf()将量化整数按张量exponent反量化为浮点 logits;
  3. expf(logit - max_logit)计算 softmax 分母,最终score = 1 / sum_exp即 top-1 类别概率。

输出同时支持 INT8 与 INT16 两种量化 dtype,这也印证了模型采用混合精度量化(基础层 INT8、敏感层 INT16)的设计。

模型存储位置:Kconfig 双模式

组件通过 Kconfig 提供模型部署位置的可配置能力,两个互斥选项:

  • EMOTION_CLS_MODEL_IN_FLASH_RODATA(默认):模型通过target_add_aligned_binary_data()以二进制方式链接进固件的 rodata 段,源码中用extern const uint8_t emotion_cls_espdl[] asm("_binary_emotion_cls_espdl_start")直接引用;
  • EMOTION_CLS_MODEL_IN_FLASH_PARTITION:模型作为独立分区数据烧录(分区名为emotion_cls),运行期从分区读取,便于独立升级模型而无需重刷固件。

此外还有一个未出现在菜单中的CONFIG_EMOTION_CLS_MODEL_IN_SDCARD分支(见 CMakeLists.txt),当启用时可跳过构建期打包逻辑,暗示模型也可从 SD 卡加载。

构建期模型打包依赖 ESP-DL 的fbs_loader工具链:CMake 会调用pack_espdl_models.pymodel/p4/emotion_cls.espdl打包为espdl_models/emotion_cls.espdl,再根据所选位置嵌入固件或烧录到分区。

模型训练与量化背景(示例级佐证)

虽然组件 README 只聚焦部署,但其所属示例的顶层 README 提供了模型的完整训练画像,可作为理解组件行为的背景依据:

  • 数据集:RAF-DB(Real-world Affective Faces Database),训练集 12,271 张预对齐人脸图,验证集 3,068 张;
  • 骨干网络:MobileNetV2(ImageNet 预训练后微调);
  • 分类头Linear(1280→256) → ReLU6 → Dropout(0.3) → Linear(256→7)
  • 输入:112×112 RGB(端侧推理时缩放到 100×100);
  • 量化:基于 ESP-PPQ 工具链的混合精度 INT8/INT16 量化。

类别分布与量化精度

RAF-DB 各类别训练样本数量差异悬殊,这在量化后体现为精度分化(FP32 验证集 vs INT8 混合精度验证集):

类别Train (FP32)Valid (FP32)Valid (INT8 混合)
happiness100.0%95.4%94.2%
neutral100.0%85.4%88.8%
surprise100.0%82.1%76.0%
sadness100.0%79.3%65.9%
anger100.0%71.0%65.4%
fear100.0%47.3%32.4%
disgust100.0%38.1%29.4%
Overall100.0%83.8%80.2%

总体精度从 FP32 的 83.8% 略降至量化后的 80.2%,但少数类(fear/disgust)由于样本严重不足(fear 仅 281 张训练图,而 happiness 约 4,800 张)且与 surprise/anger 面部表情高度混淆,量化噪声被进一步放大。这些是训练与量化层面的客观事实,并非端侧代码问题。

端侧缓解策略

示例 README 同时给出了在设备端提升实用体验的三条工程手段:

  1. 分数门控(Score gating):将score < 0.5的预测视为uncertain并跳过更新,牺牲弱类召回换取更少的误报;
  2. 时间平滑(Temporal smoothing):对最近 5 帧的 top-1 结果做多数投票,抑制单帧抖动导致的情绪跳变;
  3. 标签折叠(Label collapsing):若产品只需"正向 / 中性 / 负向"三分类,将 7 类映射到 3 类,同等权重下总体准确率可提升至 90% 以上。

完整示例:摄像头 → 检测 → 分类 → 屏显流水线

emotion_cls组件被嵌入到完整的 emotion_recognition 示例中,实现"摄像头实时取流 → 人脸检测 → 情绪分类 → LCD 叠加显示"的端到端流程。入口程序 emotion_recognition.cpp 展示了关键组装方式:

auto *panel = app_lcd_init(); auto *camera = new Camera(VIDEO_PIX_FMT_RGB565, 4, V4L2_MEMORY_MMAP, false); auto *overlay = new EmotionOverlay(panel); EmotionPipeline::Config cfg = { .camera = camera, .face_detect_period_ms = 100, // 人脸检测周期 .capture_period_ms = 20, // 取帧周期 };

运行期日志示例(来源:示例 README):

I (3657) emotion_pipeline: Face[0]: emotion=surprise score=0.357 I (4605) emotion_pipeline: Face[0]: emotion=neutral score=0.886 I (5508) emotion_pipeline: Face[0]: emotion=neutral score=0.944 I (6386) emotion_pipeline: Face[1]: emotion=neutral score=0.399

硬件要求

运行完整示例需要以下任一开发板(示例 README 明确列出):

  • ESP32-S31-Korvo:集成 DVP 摄像头、800x480 RGB LCD 与 GT1151 触摸屏;
  • ESP32-P4-Function-EV-Board:搭配 MIPI-CSI 摄像头(SC2336)与 1024x600 MIPI-DSI LCD(EK79007)。

环境与构建

  • ESP32-P4 目标要求ESP-IDF release/v5.5 及以后版本
  • ESP32-S31 属于预览目标,需使用ESP-IDF master 分支,并以idf.py --preview set-target esp32s31配置;
  • 烧录与监控命令(将PORT替换为串口号,退出串口监控按Ctrl-]):
idf.py -p PORT flash monitor

小结

emotion_cls组件为 ESP32-P4 / ESP32-S31 提供了一套完整、可复用的 7 类人脸情绪分类部署方案:100x100输入、约136.8 ms纯推理耗时、INT8/INT16 混合量化模型、rodata/分区双模式存储,以及开箱即用的 test app 验证入口。配合示例中的分数门控、时间平滑与标签折叠策略,开发者可以将其快速接入实时摄像头情绪识别的产品原型。深入阅读 emotion_cls.cpp 与 emotion_cls.hpp 即可完全掌握其加载、预处理、推理与结果解析的完整调用链。

【免费下载链接】esp-iot-solutionEspressif IoT Library. IoT Device Drivers, Documentations and Solutions.项目地址: https://gitcode.com/GitHub_Trending/es/esp-iot-solution

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 5:46:43

AI时代Git工作流重构:让commit和diff成为AI行为审计链

1. 当AI开始写代码&#xff0c;Git就不再是“提交记录仪”了我第一次在团队里用Copilot补全一个Vue组件的setup函数时&#xff0c;顺手敲下git commit -m "feat: add user profile card"&#xff0c;结果旁边同事盯着终端看了三秒&#xff0c;突然说&#xff1a;“你…

作者头像 李华
网站建设 2026/9/20 5:43:17

构建高效开放科研工作流:从可复现到全流程开放的实践指南

做科研这些年&#xff0c;我越来越发现一个扎心的事实&#xff1a;真正决定一个研究能否产生长期影响力的&#xff0c;往往不是论文里那几个漂亮的图表&#xff0c;而是背后那套能不能被别人复现、能不能被别人接着干的开放流程。我见过太多人把大量时间耗在“重新发明轮子”上…

作者头像 李华
网站建设 2026/9/20 5:37:49

用纯文本和Git构建OpenResearch:让科研过程有迹可循

一次组会上的尴尬让我彻底决定重构自己的研究工作流。当时合作者问我&#xff1a;“你的实验日志里那组对照试验&#xff0c;为什么把学习率设成0.002而不是0.001&#xff1f;”我翻了半个多小时的OneNote、Excel和微信聊天记录&#xff0c;最后只能含糊说一句“应该是试出来的…

作者头像 李华