发作品签到
专业版

ESP-AIGO 户外智能相机

工程标签

2.9k
0
0
12

简介

ESP-AIGO是基于ESP32-S3芯片的户外相机,集成AI人脸识别、行人检测及物联网无线传输功能,支持拍照、录像、行车记录仪及行车辅助,打造高性价比日常出行工具,助力障碍群体拥抱智能生活。

简介:ESP-AIGO是基于ESP32-S3芯片的户外相机,集成AI人脸识别、行人检测及物联网无线传输功能,支持拍照、录像、行车记录仪及行车辅助,打造高性价比日常出行工具,助力障碍群体拥抱智能生活。
智能便携电子设备设计大赛

开源协议

GPL 3.0

创建时间:2025-02-11 05:58:23更新时间:2025-03-24 01:20:42

描述

封面.jpg

本项目参加了智能便携电子设备设计大赛

B站产品展示视频链接:https://www.bilibili.com/video/BV1KiXHYVEEk/?spm_id_from=333.1007.0.0&vd_source=c99f4cc51a936036bca99830e36262da


一、引言

ESP-AIGO项目是基于乐鑫 ESP32-S3芯片开发的户外相机,兼具拍照、录像、行车记录仪等功能。借助人工智能(AI)技术,它不仅实现了最基本的拍照和视频录制功能,还通过人脸识别算法实时捕捉面部,通过行人识别算法实时检测道路行人实现行车辅助功能。另外,借助物联网(IoT)技术,它可以建立本地无线文件中转站,实现音视频文档资料的无线传输。
本项目制作的户外相机,侧重于作为日常外出场景中记录和辅助工具,与市场上专业运动相机定位不同。由于 esp32 芯片的物联网定位以及本项目所有功能均仅采用单个芯片运算处理,该作品的拍摄效果必然无法与市面上的产品相比对;这里仅作为个人学习成果展示和部分功能测试,以此来评估这款芯片在图像处理领域的价值,故复刻需谨慎。


二、项目说明

1. 项目背景及作品理念

当前市场在售运动相机主要侧重于摄影领域、配备高性能硬件及深度软件算法以实现高质量图像拍摄,但价格高昂、不太适用于作为日常出行工具。本项目针对此需求提出了新的研究定位。项目认为,户外相机在具备基础图像记录功能的同时,还应融入更多出行辅助等实用性工具功能,以更贴近用户的日常生活需求。因此,本项目所定位的户外相机不仅要求具备图像采集功能,还可用作出行辅助工具,功能性强、性价比高,能够成为广大人群日常出行的得力助手。同时,项目还应具备物联网属性,拥有功能拓展能力,以满足未来多样化的应用需求。
ESP-AIGO项目名称得以体现其作品“智能生活,随身而行”的理念。作品基于基于乐鑫 ESP 系列芯片开发,通过结合 AI人工智能模型为出行提供辅助便利。“爱 GO”口号则更多是鼓励社会大众更多走出房门,探索和记录世界美好;飞翔行人的 LOGO 标志则更具外向性,而这种感受更是鼓励听视觉障碍群体在ESP-AIGO 的辅助下走出心门,接纳 AI 与 IOT 技术并拥抱外面的世界。

2. 作品功能

ESP-AIGO项目的软件部分结合了乐鑫视觉大模型。该模型基于深度学习技术,具备强大的人脸识别、物体检测等能力。通过集成乐鑫视觉大模型,esp-aigo实现了拍照时的人脸识别功能,能够自动捕捉人像并辅助拍照,提高了拍照的便捷性和准确性。

  • 主要功能实现:
  1. 拍照功能:用户可以通过esp-aigo的拍照功能捕捉户外图像。开启人脸识别功能后,相机能够自动捕捉人像以辅助拍摄,帮助定格美好瞬间。

  2. 延时摄影: 用户可以使用延时摄影功能捕捉风景变化或者记录外出旅途,支持记录最大 1600*1200 分辨率及固定输出 20fps 帧率。通过程序预设功能进行预设时长曝光,曝光结果自动转码储存为视频图像,省去后期图像拼合工作。

  3. 录像/行车记录仪功能:esp-aigo支持 HD 高清录像,最大分辨率 1600*1200,最高帧率为 30fps。其支持的行车记录仪功能能够静默持续循环录像,为用户提供了便捷的车辆监控解决方案。板载电池模块使其可普遍部署在汽车、电动车、摩托车、自订车,甚至是行人身上。

  4. 行车辅助功能:通过集成行人检测模型,esp-aigo能够实时检测车道上的行人并进行提示,有效提高了行车安全性。

  5. 无线文件服务器:用户可以开启esp-aigo的Wi-Fi热点功能,建立文件服务器。通过Wi-Fi连接,用户可以轻松查看和下载相机中的录制文件,实现了无线文件传输的便捷性。

3. 作品说明

本项目是个人自春节以来,在学习 esp idf 和 esp 图像处理过程中制作的项目。项目在实现使用的基础上,更侧重于对 esp32s3 芯片的图像处理能力测试和音视频性能测试。具体如下:

  • 在硬件设计方面,项目力求制作出一款音视频输入输出功能完善的作品。作品的硬件配置相对比市面上的实战派、esp-box、esp-camera 、小智等开发板都具有功能优势(后面会列表对比),意图在实现项目需求的同时还具备更多的视觉、听觉方面人工智能学习拓展性。
  • 在软件开发方面,对 esp32s3 芯片进行了较为深度的图像采集研究。目前,现有网络公开资料多是基于乐鑫官方提供的组件包,功能单一且模式化;尚且没有关于 esp32s3 在图像采集方面细致研究文章。本项目基于此进行了深度研究,包括不同摄像头、图像格式、色彩模式、分辨率下芯片采集图像帧率,以及不同 FATFS 单元大小、不同 SD 卡槽硬件设计、不同 SD 卡的图像写入效率。最后,得出一个较为高效、高质量的图片采集配置。
  • 【复刻需谨慎】项目元件数量较多、成本较高,并且焊接加工存在一定难度。由于图像采集、音频模块的模拟电路特性,虽项目进行了较为合理的隔离,但焊接加工的好与坏也会直接决定图像采集和音频噪音的大小。

三、硬件设计

1. 硬件方案总览

在硬件设计方面,项目力求制作出一款音视频输入输出功能完善的 PCB ,以适配 esp 的 ai 图像识别、ai 语音交互学习与应用。项目结合了立创 ESP32S3 实战派ESP32-S3-BOX-3ESP32-S3-LCD-EV-BoardESP32-S3-EYE等开发板的硬件电路设计,扬长避短设计出一款具备音视频输入输出功能、侧重于人工智能学习的 PCB 电路板。硬件功能对比如下:

ESP-AIGO立创实战派 S3ESP-EYEESP-BOX3LCD-EV-Board
主控芯片ESP32-S3-WROOM-1-N16R8ESP32-S3-WROOM-1-N16R8ESP32-S3-WROOM-1-N8R8ESP32-S3-WROOM-1-N16R16ESP32-S3-WROOM-1-N16R16
显示屏ST7789-SPI-320x240ST7789-SPI-320x240240x240-1.3 寸ST7789-SPI-320x240RGB 屏占用引脚多
屏幕触摸FT6336×
摄像头OV2640-1600x1200-120°广角适合户外GC0308-640x480OV2640-66.5°视角××
SD 卡4-Line SDMMC1-Line SDMMC4-Line SDMMC4-Line SDMMC×
音频输入ES8311+单 mic节省芯片、简化电路ES7210+双 mic数字麦克风ES7210+双 micES7210+双 mic
音频输出ES8311+NS4150BES8311+NS4150B×ES8311+NS4150BES8311+NS4150B
IO 扩展PCA9557×××
电池供电及监测18650 电池+板载 5V 升压及充电+电量 ADC 监测××
按键
LED

总体来看:

  1. 项目主控选用市面上能够买到的配置较高的芯片;图像输出方面配备了2.4寸主流 ST7789 触摸显示屏。
  2. 图像采集方面使用 200 万像素 OV2640 摄像头,同时 120°广角是户外相机主流角度,采集图像区域更大。
  3. SD 卡采用 4Line SDMMC 设计,能够允许高质量图像快速写入,适合较高帧率的视频录制工作场景。
  4. 音频采集和输出使用 es8311 芯片,简化了其它开发板 es7210+es8311 的设计方式,节省成本和 7210 芯片(多路输入)的资源浪费,保留单通道麦克收音功能。
  5. 上述模块的数据、时钟部分用完了芯片引脚,故采用实战派方案使用 pca9557 芯片进行 IO 扩展,对部分使能引脚、按键、LED 进行控制。
  6. 另外,选用了板载充放电电路结合类似 esp-box 3 的 18650 可替换锂电池的方案以适配设备的户外定位要求,在较高功耗条件下仍能满足较长续航需求。

2. 原理图设计

2.1 电源部分
  1. 电源输入及切换模块

采用 type-c 供电和锂电池供电的双路供电方案。要注意该 type-c 选型不带 5.1K 下拉引脚,因此不支持双头 A 口数据线。
f72ec54bee8f86d2ba8f47b80598f00d_1742194668706-de24cb1d-cf1c-4971-a5f7-fdf181085259_x-oss-process=image%2Fformat%2Cwebp.png

使用 P-mos 管进行电路隔离和切换。当外接电源输入时隔断锂电池供电;当外接电源撤出时恢复锂电池供电。

f5896198212360a5011d7e60f697020c_1742195080455-e2f16cac-4e21-426e-91e4-bbc3e6333748_x-oss-process=image%2Fformat%2Cwebp.png

  1. 锂电池充放电模块

基于以前项目中使用的 TP5400 芯片。该款芯片高度集成了充电管理和 DCDC 升压功能,外围电路简单。设计要点如下:

  • 电源输入端接 300 m 欧的热调节电阻,要达到 1W 的输入功率。
  • 充电电流由 PROG 引脚的电阻控制。根据技术手册选用 2K 电阻的充电电流约 560mA。
  • 升压部分由 L1、D2 及 Vout 相关元件构成,为异步整流升压电路,设计时需考虑 L1 的饱和电流要大于工作负载、D2 的压降以及 C17/C18/D1 的峰值电压要高于 3 倍的工作电压。

4c792a64f74f4ff7c206d61d1214a251_1742194688907-66070a93-11a6-4f5b-aa61-1b6142f1c2c3_x-oss-process=image%2Fformat%2Cwebp.png

  1. 电压调理模块

由于涉及到数字电路和模拟电路、数字电路的多个工作电压,这里采用 DCDC 和 LDO 组合的供电方案。设计要点如下:

  • 为降低模拟电路电压的纹波特性,在音频模块的 AVDD-3V3、摄像头模块的 VDD1V5、VDD2V8 选用 LDO电路,选型以 ME6211 系列芯片为主。
  • 其他数字电路的供电为保障供电电流、降低压降损耗选用了 DCDC 电路,选型为 SY8088IAAC 同步整流芯片。

8f15fabbcd7dc8dfd3bf3e602603c64e_1742195362394-b22d44fc-4d3b-44f0-8317-2fce8e241d25_x-oss-process=image%2Fformat%2Cwebp.png

2.2 图像部分
  1. 摄像头模块

选用 OV2640 摄像头,接口为 24PIN-2.54mm 的翻盖下接 FPC。设计要点如下:

  • 在 LDO 供电电压输出端和电压输入引脚增加 0 欧电阻以调节阻抗,改善电流特性;同时在引脚附近放置合适大小的电容以调节电流纹波。
  • 尤其注意,在 AVDD2V8 模拟电路的供电部分,尽可能选择较大、多个电容,并建议选择钽电容搭配陶瓷电容以调整电流纹波,否则摄像头模拟部分收到电流纹波干扰会出现彩色横纹。
  • 采用的是 8 个数据线传输,其中 sccb 接入 I2C 总线;传感器的使能 pwdm 接入 IO 扩展芯片,默认情况下处于工作状态。

e912de6c2056349df6ff0a0eeda530b4_1742195545509-7140e82f-144e-400f-9283-970beb74a648_x-oss-process=image%2Fformat%2Cwebp.png

  1. 触摸显示模块

触摸模块根据屏幕供应商选型进行拓展板绘制,并通过 2 个 8pin 排针连接到主板上。触摸电路板设计要点如下:

  • 需注意 BL 的开启是高电平,通过 Nmos 控制,接入芯片引脚以实现 PWM 调光。
  • 为节省引脚占用,CS 通过 IO 扩展控制(参考实战派),取消 MOSI、TOUCH_INT 功能,同时 RST 接入系统复位 IO、触摸 I2C 接入总线,因此实际 IO 占用仅 4 个。

bf9934990907f73966c6a697ef287774_1742196134914-153b07fa-d8a1-4be9-b52e-bfdcda14dc42_x-oss-process=image%2Fformat%2Cwebp.png

  1. SD 卡模块

在 esp-eye 的 bsp 有关技术说明中,明确指出了 SD 卡不同数据线数量对传输速率的影响,详情参考SDMMC Host Driver - 乐鑫文档。设计要点如下:

  • 除电源引脚外,其余信号线最好都接一个上拉电阻(通常为47K、51K 左右),用来保护数据免受到总线浮动影响。
  • 由于TF卡属于可插拔器件,ESD防护是必要的。ESD器件选用VBRM电压大于3.3V, 且VC电压小于电路的最大承受电压。
  • CD脚经由上拉电阻拉至高电平后,在TF卡插入后,由高电平转为低电平。esp32 在上电时有些引脚要检测特定电平,因此要避开接入这些引脚以导致芯片启动错误。

604865a3660fb773e646f3d5382f284a_1742196623327-18dda2c4-3f6c-4080-a613-bf80cc4e749d_x-oss-process=image%2Fformat%2Cwebp.png

  1. 其他外设

按键和 LED 接入 IO 扩展,电池电量监测接入 ADC 引脚。

53fddfc8cad375995a840b28b576ffd9_1742197166519-bbbf366f-f6d9-464a-b905-7cc55096a7c0_x-oss-process=image%2Fformat%2Cwebp.png

2.3 音频部分
  1. 音频编解码电路

选用 es8311 芯片(另外一提的是,立创商城的这个芯片真是好卖到爆炸,但是手动焊接难度也不小),设计要点如下:

  • 尤其要注意数字部分和模拟部分的隔离,比如供电和输出部分的 0 欧电阻、数据的滤波,以及数字地和模拟地的单点接通。
  • 使用 es8311 的输入电路(MIC1P/1N),因此 I2S 通信时要将 DSOUT 接入 IO。

1d4a2eba751843a2d0e6a97348fe9664_1742197224202-a4c4ac78-e9cc-44be-81fe-dc0be42ebf5d_x-oss-process=image%2Fformat%2Cwebp.png

  1. 音频采集

使用实战派同款模拟麦克风,0 欧电阻调节电路特性。

4c02bd02f16f42c1180cfac6a9b99b0c_1742197493400-83dbdd7a-6183-4d33-8eec-b0859066813f_x-oss-process=image%2Fformat%2Cwebp.png

  1. 音频输出

选用 esp 官方推荐的 ns4150B 芯片。设计要点如下:

  • 由于是功放芯片,工作电压为 5V,外接 4 欧 3w 喇叭。有些电路也用 3V3 供电代替,但建议 3V3 供电时接入 8 欧 1W 喇叭。不同供电电压的功放输出和功率详见手册。
  • 功放使能引脚 CTR 接入 IO 扩展芯片,并且默认关闭。

4f34df6312ad36e3b3ece206044dc28e_1742197645859-4704d5af-5563-43dd-b239-9ea05f7322d7_x-oss-process=image%2Fformat%2Cwebp.png

2.4 芯片 IO 部分
  1. esp32 模组

设计要点:

  • 由于选用的模组自带 psram,占用了 35-37 号引脚。结合外设元件设计后,芯片所有引脚均被占用。
  • 芯片供电部分按照手册设计滤波电容。
  • 为节省烧录电路成本,本项目采用了以往项目的外接烧录方案,将烧录引脚引出后使用烧录夹加持烧录,降低 ch340 使用成本。

c9b20fdd6e6964522249a6714b15729b_1742197747817-3d1be79c-7c83-41ab-856a-9bb1c6a36f57_x-oss-process=image%2Fformat%2Cwebp.png

  1. IO 扩展芯片

PCA9557 使用 I2C 协议为主控提供了 IO 拓展。需注意的是,该芯片工作条件在 400KHz 以下(多为 100kHZ),且引出 IO 通过内部寄存器存取,因此无法实现高级 IO 功能,但适用于使能、LED、按键的外设拓展。设计要点如下:

  • 通过配置 A0-A2 引脚高低以确定芯片 I2C 地址。
  • 由于芯片 IO0 用作输出模式时是开漏输出,控制 LCD 的片选引脚时高电平输出需要加上拉电阻。
  • 芯片 IO1-IO4 为输出模式, IO5-IO6 设计为按键,软件操作时应为输入模式。

29d85746f03b05068aafd8f2847d2865_1742198253078-2b254ab2-74f1-431c-8d5d-5908e9dbe0e5_x-oss-process=image%2Fformat%2Cwebp.png

3. Layout 设计

  • 将电源部分、数字部分、模拟部分分开放置,单独设计供电及接地,然后单点相接。如下图中,左上部分为数字地,下侧为功率地,右侧为模拟地,三者通过 0 欧电阻相接。

94537aa278ff230e6fba937c17f44288_1742198519308-eb94980a-7fc8-4c38-ac9a-8093973d4ddc_x-oss-process=image%2Fformat%2Cwebp.png

  • DCDC 的电路设计要注意纹波干扰,做到两点:一是大功率电路(Vin/Vout)电路要宽且通常;二是降低高频电路(高 dI/dt 区域,B 站唐老师的叫法)环路面积以降低电路干扰。本电路的 TP5400 和 SY8088 为 DCDC 升压、降压电路,前者应注意升压输出部位的回路面积,后者应注意降压输入部位的回路面积。两者的典型 Layout 布局如下(TP5400 技术手册未给出,参考普通升压芯片):

image.png

image.png

  • 本项目添加了大量的过孔以保证共地效果,设计目的有以下几点:一是在 DCDC 电路中,功率地一定要靠近电容,因此要在电容附近打大量过孔;二是在发热量较大的芯片中要增加焊盘和过孔以导热;三是高速信号线(SD 卡、摄像头数据线)为防止导线之间的干扰要增加过孔隔离;四是模拟电路中进行类似的包地处理。
  • 待改进之处:本项目的 I2C 总线电路设计的不够合理。I2C 电路由于是总线电路,因此电路设计要遵循串联规律(参考i2c总线高级篇: 典型问题/上拉电阻/原理图/PCB_哔哩哔哩_bilibili),尽量减少树形分叉处理。较大的树形分叉处理会增加电路的回路干扰,使 I2C 总线数据不稳定,尤其体现在 CLK 的时钟沿变化中。

image.png

image.png

  • 有关差分式走线:这里在进行模拟输入输出电路设计时,对麦克风信号输入和功放喇叭信号输出进行了差分式走线,保证线路的稳定性。

e78331b7a240015bfe88c2c731e32a46_1742200796906-dd6dec26-c6d3-4374-b840-b1216a410ae4_x-oss-process=image%2Fformat%2Cwebp.png

  • 最后,由于手头没有示波器和逻辑分析仪,本项目设计的电路纹波情况、I2C 的影响情况究竟如何暂未测试,以后有条件了将进行深入测试分析。

四、软件部分

1. 板级支持包(BSP)

熟悉乐鑫项目开发的伙伴会明白,乐鑫提供的很多案例工程,包括他们提供的开发板,均有板级支持包 BSP 提供。所有案例的实现均基于 BSP 提供的 API 接口进行直接调用,这样极大程度丰富了硬件适配度。由于本项目硬件除了可以实现本项目功能,还具备拓展学习功能,这里特地对其进行了 BSP 开发,并随项目同时开源,以减轻复刻者进行二次开发的难度。

1.1 BSP 及组件关系
功能依赖组件版本
显示屏esp_lcd-
触摸espressif/esp_lcd_touch_ft5x06^1.0.7
LVGLespressif/esp_lvgl_port^2.4.4
lvgl/lvgl^9.2.2
音频espressif/esp_codec_dev^1.3.4
SD 卡IDF>=5.3
IO 扩展芯片IDF>=5.3
1.2 BSP 主要宏定义及引出的 API 接口
/*************************************************************************************************
 *
 *                                          I2C 接口
 *
 **************************************************************************************************
 * 连接到I2C外围设备如下:
 * - IO扩展芯片:      PCA9557PW       - LCD屏电容触摸:   FT6336
 * - 摄像头sccb:      OV2640          - 音频编解码芯片:  ES8311
 **************************************************************************************************/

#define BSP_I2C_NUM (0)
#define BSP_I2C_SPEED (100 * 1000)

esp_err_t bsp_i2c_init(void);                     // 初始化I2C主机总线
esp_err_t bsp_i2c_deinit(void);                   // 卸载I2C并释放资源
i2c_master_bus_handle_t bsp_i2c_get_handle(void); // 获取I2C句柄
esp_err_t bsp_i2c_probe(uint16_t address);        // 检测设备是否连接至总线


/**************************************************************************************************
 *
 *                                      IO扩展芯片:PCA9557PW
 *
 **************************************************************************************************/

void bsp_pca9557_init(void);     // 初始化IO扩展芯片pca9557
void lcd_cs(uint8_t level);      // 控制 LCD_CS 引脚:     0-工作,1-睡眠
void dvp_pwdn(uint8_t level);    // 控制 DVP_PWDN 引脚:   0-工作,1-睡眠
void pa_en(uint8_t level);       // 控制 PA_EN 引脚:      0-睡眠,1-工作
void led_red(uint8_t level);     // 控制 LED_RED 引脚:    0-点亮,1-熄灭
void led_blue(uint8_t level);    // 控制 LED_BLUE 引脚:   0-点亮,1-熄灭
int get_pressed_button(void);   // 读取按键的按下状态(测试使用)


/**************************************************************************************************
 *
 *                                      SD卡:SDMMC模式
 *
 **************************************************************************************************

#define BSP_SD_MOUNT_POINT              "/sdcard"
#define BSP_SD_FORMAT_ON_MOUNT_FAIL     (false) // 挂载失败是否格式化

#define MOUNT_MAX_FILES                 (10)
#define MOUNT_ALLOCATION_UNIT_SIZE      (16 * 1024)

extern sdmmc_card_t *bsp_sdcard;

esp_err_t bsp_sdcard_mount(void);   // 通过SDMMC接口挂载MicroSD卡到虚拟文件系统
esp_err_t bsp_sdcard_unmount(void); // 从虚拟文件系统中卸载MICRO-SD卡
void test_sdmmc_speed();            // SD卡读写速度测试


/**************************************************************************************************
 *
 *                                 触摸显示屏:ST7789+FT6336
 *
 **************************************************************************************************/

/* LCD的屏幕分辨率 */
#define BSP_LCD_H_RES           (320) // 水平分辨率
#define BSP_LCD_V_RES           (240) // 垂直分辨率

/*********************************    LED 背光控制    *********************************/

#define LCD_LEDC_CH LEDC_CHANNEL_0

esp_err_t bsp_display_brightness_set(int brightness_percent);
esp_err_t bsp_display_backlight_off(void);
esp_err_t bsp_display_backlight_on(void);

/*********************************    初始化LCD驱动    *********************************/

// SPI初始化
#define BSP_LCD_SPI_NUM         (SPI3_HOST)
#define LCD_DRAW_BUFF_SIZE      (BSP_LCD_H_RES * BSP_LCD_V_RES)

// 液晶屏控制IO初始化
#define BSP_LCD_PIXEL_CLOCK_HZ  (80 * 1000 * 1000) // SPI的最大速度80MHz
#define LCD_CMD_BITS            (8)
#define LCD_PARAM_BITS          (8)

// 配置ST7789显示驱动
#define BSP_LCD_COLOR_SPACE         (ESP_LCD_COLOR_SPACE_RGB)   // 颜色空间设置,RGB顺序(或BGR)
#define BSP_LCD_BITS_PER_PIXEL      (16)                        // 像素位数
#define BSP_LCD_INVERT_COLOR        (true)                      // 色彩翻转
#define BSP_LCD_SWAP_XY             (true)                      // 显示翻转
#define BSP_LCD_MIRROR_X            (true)                      // 水平镜像
#define BSP_LCD_MIRROR_Y            (false)                     // 垂直镜像

//创建新的显示面板句柄
esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io);
// 设置液晶屏颜色
void lcd_set_color(uint16_t color); 
// 显示图片案例
void lcd_draw_pictrue(int x_start, int y_start, int x_end, int y_end, const unsigned char *gImage);
//创建新的显示面板案例
esp_err_t example_lcd_init(void);

/**************************************    LVGL      ***************************************/

//LVGL TASK
#define BSP_DISPLAY_LVGL_TASK_CORE          (0)         //lvgl任务启动在核心0
#define BSP_DISPLAY_LVGL_TASK_PRIORITY      (2)         //lvgl任务优先级
#define BSP_DISPLAY_LVGL_TASK_STACK         (1024*15)    //lvgl任务堆栈大小
#define LVGL_TICK_PERIOD_MS                 (30)         //lvgl滴答定时器的周期
#define LVGL_MAX_SLEEP_MS                   (30)       //任务睡眠的最大时间(毫秒)

//buffer
#define BSP_LCD_LV_COLOR_16_SWAP            (true)      //交换rgb656 16位颜色格式的字节
#define BSP_LCD_DRAW_BUFF_SIZE              (BSP_LCD_H_RES * 40) // BSP_LCD_V_RES
#define BSP_LCD_DRAW_BUFF_DOUBLE            (true)      //双缓存
#define BSP_DISPLAY_LVGL_BUFFER_IN_PSRAM    (false)      //lvgl缓冲区将在psram中分配,而不是在dma的内部ram中分配

// 初始化显示屏(形参含有配置swapRGB的变量)
lv_display_t *bsp_display_lcd_init_with_swap(const bsp_display_cfg_t *cfg, bool swaprgb);
//初始化LVGL显示
lv_display_t *bsp_display_start(void);
lv_display_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg);
//获取指向输入设备的指针
lv_indev_t *bsp_display_get_input_dev(void);
//屏幕旋转
void bsp_display_rotate(lv_display_t *disp, lv_disp_rotation_t rotation);

bool bsp_display_lock(uint32_t timeout_ms);//获取lvgl互斥体
void bsp_display_unlock(void);  //释放LVGL互斥体


/**************************************************************************************************
 *
 *                                       摄像头:OV2640
 *
 **************************************************************************************************

#define BSP_CAMERA_VFLIP 1                          // 垂直翻转
#define BSP_CAMERA_HMIRROR 0                        // 水平镜像
#define BSP_CAMERA_FRAME_SIZE FRAMESIZE_QVGA        // FRAMESIZE_XGA       // FRAMESIZE_QVGA //    // 图像大小
#define BSP_CAMERA_PIXEL_FORMAT PIXFORMAT_RGB565    // PIXFORMAT_RGB565  // 使用 PIXFORMAT_JPEG 时,fb_count要设置为 1 // 色彩模式
#define BSP_CAMERA_FB_LOCATION CAMERA_FB_IN_PSRAM   // PSRAM
#define BSP_CAMERA_XCLK_FREQ_HZ (20 * 1000 * 1000)

#define BSP_CAMERA_DEFAULT_CONFIG                \
    {                                            \
        .pin_pwdn = GPIO_NUM_NC,                 \
        .pin_reset = GPIO_NUM_NC,                \
        .pin_xclk = BSP_CAMERA_XCLK,             \
        .pin_sccb_sda = GPIO_NUM_NC,             \
        .pin_sccb_scl = GPIO_NUM_NC,             \
        .pin_d7 = BSP_CAMERA_D7,                 \
        .pin_d6 = BSP_CAMERA_D6,                 \
        .pin_d5 = BSP_CAMERA_D5,                 \
        .pin_d4 = BSP_CAMERA_D4,                 \
        .pin_d3 = BSP_CAMERA_D3,                 \
        .pin_d2 = BSP_CAMERA_D2,                 \
        .pin_d1 = BSP_CAMERA_D1,                 \
        .pin_d0 = BSP_CAMERA_D0,                 \
        .pin_vsync = BSP_CAMERA_VSYNC,           \
        .pin_href = BSP_CAMERA_HSYNC,            \
        .pin_pclk = BSP_CAMERA_PCLK,             \
        .xclk_freq_hz = BSP_CAMERA_XCLK_FREQ_HZ, \
        .ledc_timer = LEDC_TIMER_0,              \
        .ledc_channel = LEDC_CHANNEL_0,          \
        .pixel_format = BSP_CAMERA_PIXEL_FORMAT, \
        .frame_size = BSP_CAMERA_FRAME_SIZE,     \
        .jpeg_quality = 9,                      \
        .fb_count = 1,                           \
        .fb_location = BSP_CAMERA_FB_LOCATION,   \
        .sccb_i2c_port = BSP_I2C_NUM,            \
    }

void bsp_camera_ov2640_init(); // 摄像头硬件初始化(一般测试使用)


/**************************************************************************************************
 *
 *                                       音频编解码:ES8311
 *
 **************************************************************************************************

/*************************       I2S配置-bsp_audio_init        *******************************/

#define BSP_I2S_NUM (I2S_NUM_0) // 配置通道绑定总线端口

// 配置I2S GPIO引脚,时钟极性均不反转
#define BSP_I2S_GPIO_CFG       \
    {                          \
        .mclk = BSP_I2S_MCLK,  \
        .bclk = BSP_I2S_BCLK,  \
        .ws = BSP_I2S_WS,      \
        .dout = BSP_I2S_DOUT,  \
        .din = BSP_I2S_DSIN,   \
        .invert_flags = {      \
            .mclk_inv = false, \
            .bclk_inv = false, \
            .ws_inv = false,   \
        },                     \
    }

// I2S默认配置,不使用分时复用(TDM)模式,使用STD模式,单声道(MONO)
#define BSP_DEFALUT_SAMPLE_RATE (22050)
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate)                                                         \
    {                                                                                                 \
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate),                                          \
        .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
        .gpio_cfg = BSP_I2S_GPIO_CFG,                                                                 \
    }

/********************************       配置默认采样信息        ***************************************/

#define CODEC_DEFAULT_SAMPLE_RATE 	(BSP_DEFALUT_SAMPLE_RATE) // 采样频率Hz
#define CODEC_DEFAULT_BIT_WIDTH 	(16)
#define CODEC_DEFAULT_CHANNEL 		(I2S_SLOT_MODE_MONO)        // 单声道麦克风
#define CODEC_DEFAULT_MIC_IN_GAIN 	(50.0) // 麦克风输入增益

extern esp_codec_dev_handle_t play_dev_handle;   // 播放控制句柄
extern esp_codec_dev_handle_t record_dev_handle; // 收音控制句柄

// 初始化扬声器编解码器设备,返回指向编解码器设备句柄的指针
esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void);

// 初始化麦克风编解码器设备,返回指向编解码器设备句柄的指针
esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void);

// 音频设备初始化:配置为默认采样信息
esp_err_t bsp_codec_init();

// 为编码器配置采样参数(初始化之后执行)
esp_err_t bsp_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch);

1.3 BSP 包提供的实例
案例名称案例描述使用的功能接口
01_pca9557控制 IO 扩展 LED 灯、检测按下按键序号 IO扩展芯片PCA9557PW 相关
02_sdmmcSD 卡数据的读写测试和速度测试SDMMC模式 SD卡相关
03_camera拍摄一帧图像并保存到 SD 卡中SDMMC模式 SD卡相关
OV2640摄像头相关
04_audio播放 wav 格式的音频文件  音频编解码ES8311 相关
录制一段音频并保存到 SD 卡中SDMMC模式 SD卡相关
音频编解码ES8311 相关
05_lcd启动显示屏并加载一个图片ST7789 触摸显示相关
启动 lvgl 并展示 music demolvgl 及触控相关

2. SD 卡读写效率研究

2.1实验背景
  • 目标:评估不同配置下 SD 卡的写入/读取效率,重点关注视频录制场景(高写入需求)。
  • 变量
    • 数据宽度:sdmmc 模式, 1 线 vs. 4 线(并行传输提升带宽)。
    • SD 卡类型:SDSC(标准容量) vs. SDHC(高容量)。
    • 缓存区:PSRAM(外部) vs. IRAM(内部)。
  • 测试方法:FAT32 格式,区块大小为 32*1024; 写入/读取 1MB bin 文件,记录速度(KB/s)。
  • 硬件:实战派ESP32-S3、esp-aigo。

测试结果如下:

image.png

2.2 关键结果分析
  1. SD 卡类型对效率的影响:SDSC 卡性能瓶颈明显,即使使用 4 线模式也依然。SDHC 卡更适合高负载场景(如视频录制),因其支持更高总线频率和容量。
  2. 数据宽度对效率的影响:4 线模式对 SDHC 卡(尤其是写入 IRAM)的性能提升显著,但对 SDSC 卡提升有限。
  3. 缓存区对效率的影响:IRAM普遍优于PSRAM,尤其在 SDHC 卡和 4 线模式下。IRAM 缓存区更适合高写入场景,因其访问速度更快且延迟更低。
2.3 综合结论
  • 优先使用 SDHC 卡(如卡2),尤其在高写入负载场景(如视频录制)。
  • 启用 4 线 SDMMC 模式,显著提升带宽利用率。
  • 使用 IRAM 作为写入缓存区,以最大化写入速度。
  • 确保 SD 卡挂载配置为合理区块大小,例如 **32KB **。

3. 摄像参数优化

结合 SD卡研究结果,使用 SDHC 类型 sd 卡在 4 线 sdmmc 模式下,对 32KB 区块大小以及 16KB 的 RIFF avi 视频写入区块大小的不同摄像头参数配置的视频录制效果进行分析。 avi 视频采取 PIXFORMAT_JPEG格式录制。实验结果如下:

image.png

可以发现,在使用较低视频质量时可以达到摄像头的硬件采集帧率,而分辨率在 800*600 以上时录制程序的数据处理会导致视频采集帧率的下降。为尽可能保证视频录制的高帧率和高分辨率,综合 ov2640 的硬件性能特点,本项目选择录制参数如下:

  • 视频录像:选用FRAMESIZE_SVGA即 800*600 分辨率,fps 为 25;
  • 延时摄影:选用FRAMESIZE_UXGA即 1600*1200 分辨率,fps 由软件固定设置为 20。

4. 摄像帧率优化

在选用较高图像质量前提下,该部分对乐鑫案例进行了深度优化,将系统录制视频的帧率提高到摄像头预设帧率要求。在开发该项目的视频录制(行车记录仪)功能时,基于乐鑫官方提供 esp-iot-solution包的 video_recorder案例。通过对该案例以及相关项目(如 dudu 大神的门铃项目)研究时发现,视频录制使用了官方写的 avi_video_process组件。由于网络上有关乐鑫录制 avi 视频资料较少,并且有关 jpeg 格式封装 avi 的嵌入式程序较少,所以网络上很难找到有较高质量的视频录制项目。

基于此现状,结合前期研究成果,本部分采用将摄像头 psram 开辟的帧缓冲区按照 RIFF 区块大小循环写入到 iram 后进一步write到 sd 卡中,实现了视频录制帧率的深度优化,保证了高视频质量视频录制帧率可以跑满 OV2640 硬件所支持的最高视频帧率。

优化结果如下:

image.png

研究发现,优化后所有分辨率下均可达到硬件帧率。同时,在视频录制选用的FRAMESIZE_SVGA尺寸下,保证了 25fps 录制帧率,性能提升达到 40%,极大程度消除了原程序的视频顿挫感。

进一步的,当视频录制质量较低时,视频录制帧率可达 50fps,此时可以考虑调整 riff 分区大小,使单帧图像可以保存在单个区块内,降低程序循环耗时,提升软件帧率。由于项目不使用低分辨率配置,故具体实现过程不再赘述。

5. 摄像稳定性优化

官方提供的 avi_video_process组件在运行过程中会概率性触发错误事件。经过项目的深入调试,解决了随机错误事件的发生,保障了系统稳定性。解决方案如下:

在进行视频录制测试时,系统经常会随机触发看门狗错误。错误 log 如下:

...
rst:0x8 (TG1WDT_SYS_RST),boot:0x2b (SPI_FAST_FLASH_BOOT)
Saved PC:0x42003a1e
--- 0x42003a1e: panic_handler at F:/ESP/v5.4/esp-idf/components/esp_system/port/panic_handler.c:154 (discriminator 1)
...
W (54) boot.esp32s3: PRO CPU has been reset by WDT.
W (58) boot.esp32s3: APP CPU has been reset by WDT.
...

这表明设备在运行时发生了看门狗定时器(WDT)复位,导致设备重启。日志显示设备因TG1WDT_SYS_RST(任务看门狗定时器1系统复位)而重启。崩溃发生在panic_handler中,PC指针为0x42003a1e

经过调试发现,该组件在执行int jpeg2avi_add_frame(jpeg2avi_data_t *j2a, uint8_t *data, uint32_t len)函数时会触发看门狗。进一步的,发现在int _len = CHUNK_SIZE - last_remain - sizeof(AVI_CHUNK_HEAD)这一句会概率性将_len值赋值为负数,从而在执行memcpy(&j2a->buffer[j2a->write_len], data, _len)时使系统阻塞触发看门狗。更直观的调试 log 如下:

...
I (203633) avi recorder: _len:6820 = 16384(CHUNK_SIZE) - 9556(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (203673) avi recorder: _len:8748 = 16384(CHUNK_SIZE) - 7628(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (203713) avi recorder: _len:10592 = 16384(CHUNK_SIZE) - 5784(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (203753) avi recorder: _len:12412 = 16384(CHUNK_SIZE) - 3964(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (203793) avi recorder: _len:14424 = 16384(CHUNK_SIZE) - 1952(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (203833) avi recorder: _len:-4 = 16384(CHUNK_SIZE) - 16380(last_remain) + 8 (AVI_CHUNK_HEAD大小)
...

这里发现,当 last_remain恰好在小于CHUNK_SIZE并且last_remain + sizeof(AVI_CHUNK_HEAD)(后者固定为 8 字节),也就是last_remain +8 >CHUNK_SIZE时,_len值为负数,程序缺乏判定机制而导致了系统触发看门狗。

这里需要引入 avi 封装 jpeg 格式的 RIFF 文件的机制了。在 avi 文件构建时,程序会根据视频采集的帧图像 frame大小,按照区块大小 CHUNK_SIZE进行分块写入。首先,在构建帧头文件和数据文件时将其写入内存 j2a->buffer,然后再将其写入到 sd 卡中。当最后写入的数据小于区块大小时,会跟随下一帧的头文件和数据进行下一轮写入,因此在写入帧头和首部数据时构建的写入长度=块大小-上次遗留数据大小-帧头大小,即 _len = CHUNK_SIZE - last_remain - sizeof(AVI_CHUNK_HEAD)

了解了这一情况后,可以增加一个判断机制来消除该随机错误事件。我的处理方式如下:

...
if (remain >= CHUNK_SIZE) // 写入完整数据块(剩余数据大于等于块大小)
        {
            if (last_remain + sizeof(AVI_CHUNK_HEAD) >= CHUNK_SIZE) // last_remain +头不足以写入一个块时仅写入数据
            {
                ESP_LOGE(TAG, "last_remain + sizeof(AVI_CHUNK_HEAD) >= CHUNK_SIZE,仅写入数据,后面填充0");
                ssize_t written = write(j2a->avifile, j2a->buffer, CHUNK_SIZE);
                if (written == -1)
                {
                    ESP_LOGE(TAG, "write(j2a->avifile, j2a->buffer, j2a->write_len)写入失败,错误码: %d", errno);
                    return 1;
                }
                int _len = CHUNK_SIZE - last_remain;
                memset(&j2a->buffer[j2a->write_len], 0, _len); 	// 剩余内容填充0
                remain = remain - j2a->write_len;				//重设待写入区长度
                j2a->write_len = 0;								//复位缓冲区指针
                last_remain = 0;								//复位遗留数据大小
            }

            // 将帧头和数据写入缓存区
            memcpy(&j2a->buffer[j2a->write_len], &frame_head, sizeof(AVI_CHUNK_HEAD)); // 帧头数据复制到缓冲区
            ...

该做法的目的是,当上一次写入剩余数据大于 CHUNK_SIZE-sizeof(AVI_CHUNK_HEAD)时,增加一个条件判断。在上一循环中,程序已经通过 memcpy(&j2a->buffer[j2a->write_len], data, remain)将数据小于数据块大小的遗留数据 last_remain写入缓存中,此时缓存区指针位置为 j2a->write_len(实际上和last_remain在数值上相等,可以理解为一个是数据长度,一个是指针位置,此时已经从缓存头部 [0] 写入last_remain长度,因此指针位置与其相等)。那么在该判断中,应该将缓存区中的剩余数据长度 last_remain直接写入 sd 卡中,块空余部分通过 memset填充为 0。优化后的程序不会出现随机触发看门狗的情况了,输出 log 如下:

...
I (53178) avi recorder: _len:3692 = 16384(CHUNK_SIZE) - 12684(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (53218) avi recorder: _len:9972 = 16384(CHUNK_SIZE) - 6404(last_remain) + 8 (AVI_CHUNK_HEAD大小)
//这一行执行了优化的代码
W (53258) avi recorder: last_remain + sizeof(AVI_CHUNK_HEAD) >= CHUNK_SIZE,仅写入数据,后面填充0
//后续正常执行
I (53261) avi recorder: _len:16376 = 16384(CHUNK_SIZE) - 0(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (53274) app_video_recorder: recording 38 - 60 s
I (53298) avi recorder: _len:6376 = 16384(CHUNK_SIZE) - 10000(last_remain) + 8 (AVI_CHUNK_HEAD大小)
I (53338) avi recorder: _len:12776 = 16384(CHUNK_SIZE) - 3600(last_remain) + 8 (AVI_CHUNK_HEAD大小)
...

读者可以自行使用avi_video_process组件优化测试。


五、后续规划

除了上述主要功能外,esp-aigo项目还规划了后续的功能扩展。其中包括音视频播放功能,让用户可以通过相机播放存储在其中的音视频内容;以及语音唤醒交互功能,让用户可以通过语音指令控制相机的各项操作,进一步提高了设备的智能化程度。后续优化及功能拓展结合实际情况可能会放在其他项目中。

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
aigo_bsp.zip
43
2
作品展示视频.mp4
14
克隆工程
添加到专辑
0
0
分享
侵权投诉

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航