
ESP-AIGO 户外智能相机
简介
ESP-AIGO是基于ESP32-S3芯片的户外相机,集成AI人脸识别、行人检测及物联网无线传输功能,支持拍照、录像、行车记录仪及行车辅助,打造高性价比日常出行工具,助力障碍群体拥抱智能生活。
简介:ESP-AIGO是基于ESP32-S3芯片的户外相机,集成AI人脸识别、行人检测及物联网无线传输功能,支持拍照、录像、行车记录仪及行车辅助,打造高性价比日常出行工具,助力障碍群体拥抱智能生活。开源协议
:GPL 3.0
描述
本项目参加了智能便携电子设备设计大赛
一、引言
ESP-AIGO项目是基于乐鑫 ESP32-S3芯片开发的户外相机,兼具拍照、录像、行车记录仪等功能。借助人工智能(AI)技术,它不仅实现了最基本的拍照和视频录制功能,还通过人脸识别算法实时捕捉面部,通过行人识别算法实时检测道路行人实现行车辅助功能。另外,借助物联网(IoT)技术,它可以建立本地无线文件中转站,实现音视频文档资料的无线传输。
本项目制作的户外相机,侧重于作为日常外出场景中记录和辅助工具,与市场上专业运动相机定位不同。由于 esp32 芯片的物联网定位以及本项目所有功能均仅采用单个芯片运算处理,该作品的拍摄效果必然无法与市面上的产品相比对;这里仅作为个人学习成果展示和部分功能测试,以此来评估这款芯片在图像处理领域的价值,故复刻需谨慎。
二、项目说明
1. 项目背景及作品理念
当前市场在售运动相机主要侧重于摄影领域、配备高性能硬件及深度软件算法以实现高质量图像拍摄,但价格高昂、不太适用于作为日常出行工具。本项目针对此需求提出了新的研究定位。项目认为,户外相机在具备基础图像记录功能的同时,还应融入更多出行辅助等实用性工具功能,以更贴近用户的日常生活需求。因此,本项目所定位的户外相机不仅要求具备图像采集功能,还可用作出行辅助工具,功能性强、性价比高,能够成为广大人群日常出行的得力助手。同时,项目还应具备物联网属性,拥有功能拓展能力,以满足未来多样化的应用需求。
ESP-AIGO项目名称得以体现其作品“智能生活,随身而行”的理念。作品基于基于乐鑫 ESP 系列芯片开发,通过结合 AI人工智能模型为出行提供辅助便利。“爱 GO”口号则更多是鼓励社会大众更多走出房门,探索和记录世界美好;飞翔行人的 LOGO 标志则更具外向性,而这种感受更是鼓励听视觉障碍群体在ESP-AIGO 的辅助下走出心门,接纳 AI 与 IOT 技术并拥抱外面的世界。
2. 作品功能
ESP-AIGO项目的软件部分结合了乐鑫视觉大模型。该模型基于深度学习技术,具备强大的人脸识别、物体检测等能力。通过集成乐鑫视觉大模型,esp-aigo实现了拍照时的人脸识别功能,能够自动捕捉人像并辅助拍照,提高了拍照的便捷性和准确性。
- 主要功能实现:
-
拍照功能:用户可以通过esp-aigo的拍照功能捕捉户外图像。开启人脸识别功能后,相机能够自动捕捉人像以辅助拍摄,帮助定格美好瞬间。
-
延时摄影: 用户可以使用延时摄影功能捕捉风景变化或者记录外出旅途,支持记录最大 1600*1200 分辨率及固定输出 20fps 帧率。通过程序预设功能进行预设时长曝光,曝光结果自动转码储存为视频图像,省去后期图像拼合工作。
-
录像/行车记录仪功能:esp-aigo支持 HD 高清录像,最大分辨率 1600*1200,最高帧率为 30fps。其支持的行车记录仪功能能够静默持续循环录像,为用户提供了便捷的车辆监控解决方案。板载电池模块使其可普遍部署在汽车、电动车、摩托车、自订车,甚至是行人身上。
-
行车辅助功能:通过集成行人检测模型,esp-aigo能够实时检测车道上的行人并进行提示,有效提高了行车安全性。
-
无线文件服务器:用户可以开启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-3、ESP32-S3-LCD-EV-Board、ESP32-S3-EYE等开发板的硬件电路设计,扬长避短设计出一款具备音视频输入输出功能、侧重于人工智能学习的 PCB 电路板。硬件功能对比如下:
ESP-AIGO | 立创实战派 S3 | ESP-EYE | ESP-BOX3 | LCD-EV-Board | |
---|---|---|---|---|---|
主控芯片 | ESP32-S3-WROOM-1-N16R8 | ESP32-S3-WROOM-1-N16R8 | ESP32-S3-WROOM-1-N8R8 | ESP32-S3-WROOM-1-N16R16 | ESP32-S3-WROOM-1-N16R16 |
显示屏 | ST7789-SPI-320x240 | ST7789-SPI-320x240 | 240x240-1.3 寸 | ST7789-SPI-320x240 | RGB 屏占用引脚多 |
屏幕触摸 | FT6336 | √ | × | √ | √ |
摄像头 | OV2640-1600x1200-120°广角适合户外 | GC0308-640x480 | OV2640-66.5°视角 | × | × |
SD 卡 | 4-Line SDMMC | 1-Line SDMMC | 4-Line SDMMC | 4-Line SDMMC | × |
音频输入 | ES8311+单 mic节省芯片、简化电路 | ES7210+双 mic | 数字麦克风 | ES7210+双 mic | ES7210+双 mic |
音频输出 | ES8311+NS4150B | ES8311+NS4150B | × | ES8311+NS4150B | ES8311+NS4150B |
IO 扩展 | PCA9557 | √ | × | × | × |
电池供电及监测 | 18650 电池+板载 5V 升压及充电+电量 ADC 监测 | × | √ | √ | × |
按键 | √ | √ | √ | √ | √ |
LED | √ | √ | √ | √ | √ |
总体来看:
- 项目主控选用市面上能够买到的配置较高的芯片;图像输出方面配备了2.4寸主流 ST7789 触摸显示屏。
- 图像采集方面使用 200 万像素 OV2640 摄像头,同时 120°广角是户外相机主流角度,采集图像区域更大。
- SD 卡采用 4Line SDMMC 设计,能够允许高质量图像快速写入,适合较高帧率的视频录制工作场景。
- 音频采集和输出使用 es8311 芯片,简化了其它开发板 es7210+es8311 的设计方式,节省成本和 7210 芯片(多路输入)的资源浪费,保留单通道麦克收音功能。
- 上述模块的数据、时钟部分用完了芯片引脚,故采用实战派方案使用 pca9557 芯片进行 IO 扩展,对部分使能引脚、按键、LED 进行控制。
- 另外,选用了板载充放电电路结合类似 esp-box 3 的 18650 可替换锂电池的方案以适配设备的户外定位要求,在较高功耗条件下仍能满足较长续航需求。
2. 原理图设计
2.1 电源部分
- 电源输入及切换模块
采用 type-c 供电和锂电池供电的双路供电方案。要注意该 type-c 选型不带 5.1K 下拉引脚,因此不支持双头 A 口数据线。
使用 P-mos 管进行电路隔离和切换。当外接电源输入时隔断锂电池供电;当外接电源撤出时恢复锂电池供电。
- 锂电池充放电模块
基于以前项目中使用的 TP5400 芯片。该款芯片高度集成了充电管理和 DCDC 升压功能,外围电路简单。设计要点如下:
- 电源输入端接 300 m 欧的热调节电阻,要达到 1W 的输入功率。
- 充电电流由 PROG 引脚的电阻控制。根据技术手册选用 2K 电阻的充电电流约 560mA。
- 升压部分由 L1、D2 及 Vout 相关元件构成,为异步整流升压电路,设计时需考虑 L1 的饱和电流要大于工作负载、D2 的压降以及 C17/C18/D1 的峰值电压要高于 3 倍的工作电压。
- 电压调理模块
由于涉及到数字电路和模拟电路、数字电路的多个工作电压,这里采用 DCDC 和 LDO 组合的供电方案。设计要点如下:
- 为降低模拟电路电压的纹波特性,在音频模块的 AVDD-3V3、摄像头模块的 VDD1V5、VDD2V8 选用 LDO电路,选型以 ME6211 系列芯片为主。
- 其他数字电路的供电为保障供电电流、降低压降损耗选用了 DCDC 电路,选型为 SY8088IAAC 同步整流芯片。
2.2 图像部分
- 摄像头模块
选用 OV2640 摄像头,接口为 24PIN-2.54mm 的翻盖下接 FPC。设计要点如下:
- 在 LDO 供电电压输出端和电压输入引脚增加 0 欧电阻以调节阻抗,改善电流特性;同时在引脚附近放置合适大小的电容以调节电流纹波。
- 尤其注意,在 AVDD2V8 模拟电路的供电部分,尽可能选择较大、多个电容,并建议选择钽电容搭配陶瓷电容以调整电流纹波,否则摄像头模拟部分收到电流纹波干扰会出现彩色横纹。
- 采用的是 8 个数据线传输,其中 sccb 接入 I2C 总线;传感器的使能 pwdm 接入 IO 扩展芯片,默认情况下处于工作状态。
- 触摸显示模块
触摸模块根据屏幕供应商选型进行拓展板绘制,并通过 2 个 8pin 排针连接到主板上。触摸电路板设计要点如下:
- 需注意 BL 的开启是高电平,通过 Nmos 控制,接入芯片引脚以实现 PWM 调光。
- 为节省引脚占用,CS 通过 IO 扩展控制(参考实战派),取消 MOSI、TOUCH_INT 功能,同时 RST 接入系统复位 IO、触摸 I2C 接入总线,因此实际 IO 占用仅 4 个。
- SD 卡模块
在 esp-eye 的 bsp 有关技术说明中,明确指出了 SD 卡不同数据线数量对传输速率的影响,详情参考SDMMC Host Driver - 乐鑫文档。设计要点如下:
- 除电源引脚外,其余信号线最好都接一个上拉电阻(通常为47K、51K 左右),用来保护数据免受到总线浮动影响。
- 由于TF卡属于可插拔器件,ESD防护是必要的。ESD器件选用VBRM电压大于3.3V, 且VC电压小于电路的最大承受电压。
- CD脚经由上拉电阻拉至高电平后,在TF卡插入后,由高电平转为低电平。esp32 在上电时有些引脚要检测特定电平,因此要避开接入这些引脚以导致芯片启动错误。
- 其他外设
按键和 LED 接入 IO 扩展,电池电量监测接入 ADC 引脚。
2.3 音频部分
- 音频编解码电路
选用 es8311 芯片(另外一提的是,立创商城的这个芯片真是好卖到爆炸,但是手动焊接难度也不小),设计要点如下:
- 尤其要注意数字部分和模拟部分的隔离,比如供电和输出部分的 0 欧电阻、数据的滤波,以及数字地和模拟地的单点接通。
- 使用 es8311 的输入电路(MIC1P/1N),因此 I2S 通信时要将 DSOUT 接入 IO。
- 音频采集
使用实战派同款模拟麦克风,0 欧电阻调节电路特性。
- 音频输出
选用 esp 官方推荐的 ns4150B 芯片。设计要点如下:
- 由于是功放芯片,工作电压为 5V,外接 4 欧 3w 喇叭。有些电路也用 3V3 供电代替,但建议 3V3 供电时接入 8 欧 1W 喇叭。不同供电电压的功放输出和功率详见手册。
- 功放使能引脚 CTR 接入 IO 扩展芯片,并且默认关闭。
2.4 芯片 IO 部分
- esp32 模组
设计要点:
- 由于选用的模组自带 psram,占用了 35-37 号引脚。结合外设元件设计后,芯片所有引脚均被占用。
- 芯片供电部分按照手册设计滤波电容。
- 为节省烧录电路成本,本项目采用了以往项目的外接烧录方案,将烧录引脚引出后使用烧录夹加持烧录,降低 ch340 使用成本。
- IO 扩展芯片
PCA9557 使用 I2C 协议为主控提供了 IO 拓展。需注意的是,该芯片工作条件在 400KHz 以下(多为 100kHZ),且引出 IO 通过内部寄存器存取,因此无法实现高级 IO 功能,但适用于使能、LED、按键的外设拓展。设计要点如下:
- 通过配置 A0-A2 引脚高低以确定芯片 I2C 地址。
- 由于芯片 IO0 用作输出模式时是开漏输出,控制 LCD 的片选引脚时高电平输出需要加上拉电阻。
- 芯片 IO1-IO4 为输出模式, IO5-IO6 设计为按键,软件操作时应为输入模式。
3. Layout 设计
- 将电源部分、数字部分、模拟部分分开放置,单独设计供电及接地,然后单点相接。如下图中,左上部分为数字地,下侧为功率地,右侧为模拟地,三者通过 0 欧电阻相接。
- DCDC 的电路设计要注意纹波干扰,做到两点:一是大功率电路(Vin/Vout)电路要宽且通常;二是降低高频电路(高 dI/dt 区域,B 站唐老师的叫法)环路面积以降低电路干扰。本电路的 TP5400 和 SY8088 为 DCDC 升压、降压电路,前者应注意升压输出部位的回路面积,后者应注意降压输入部位的回路面积。两者的典型 Layout 布局如下(TP5400 技术手册未给出,参考普通升压芯片):
- 本项目添加了大量的过孔以保证共地效果,设计目的有以下几点:一是在 DCDC 电路中,功率地一定要靠近电容,因此要在电容附近打大量过孔;二是在发热量较大的芯片中要增加焊盘和过孔以导热;三是高速信号线(SD 卡、摄像头数据线)为防止导线之间的干扰要增加过孔隔离;四是模拟电路中进行类似的包地处理。
- 待改进之处:本项目的 I2C 总线电路设计的不够合理。I2C 电路由于是总线电路,因此电路设计要遵循串联规律(参考i2c总线高级篇: 典型问题/上拉电阻/原理图/PCB_哔哩哔哩_bilibili),尽量减少树形分叉处理。较大的树形分叉处理会增加电路的回路干扰,使 I2C 总线数据不稳定,尤其体现在 CLK 的时钟沿变化中。
- 有关差分式走线:这里在进行模拟输入输出电路设计时,对麦克风信号输入和功放喇叭信号输出进行了差分式走线,保证线路的稳定性。
- 最后,由于手头没有示波器和逻辑分析仪,本项目设计的电路纹波情况、I2C 的影响情况究竟如何暂未测试,以后有条件了将进行深入测试分析。
四、软件部分
1. 板级支持包(BSP)
熟悉乐鑫项目开发的伙伴会明白,乐鑫提供的很多案例工程,包括他们提供的开发板,均有板级支持包 BSP 提供。所有案例的实现均基于 BSP 提供的 API 接口进行直接调用,这样极大程度丰富了硬件适配度。由于本项目硬件除了可以实现本项目功能,还具备拓展学习功能,这里特地对其进行了 BSP 开发,并随项目同时开源,以减轻复刻者进行二次开发的难度。
1.1 BSP 及组件关系
功能 | 依赖组件 | 版本 |
---|---|---|
显示屏 | esp_lcd | - |
触摸 | espressif/esp_lcd_touch_ft5x06 | ^1.0.7 |
LVGL | espressif/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_sdmmc | SD 卡数据的读写测试和速度测试 | SDMMC模式 SD卡相关 |
03_camera | 拍摄一帧图像并保存到 SD 卡中 | SDMMC模式 SD卡相关 OV2640摄像头相关 |
04_audio | 播放 wav 格式的音频文件 | 音频编解码ES8311 相关 |
录制一段音频并保存到 SD 卡中 | SDMMC模式 SD卡相关 音频编解码ES8311 相关 | |
05_lcd | 启动显示屏并加载一个图片 | ST7789 触摸显示相关 |
启动 lvgl 并展示 music demo | lvgl 及触控相关 |
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。
测试结果如下:
2.2 关键结果分析
- SD 卡类型对效率的影响:SDSC 卡性能瓶颈明显,即使使用 4 线模式也依然。SDHC 卡更适合高负载场景(如视频录制),因其支持更高总线频率和容量。
- 数据宽度对效率的影响:4 线模式对 SDHC 卡(尤其是写入 IRAM)的性能提升显著,但对 SDSC 卡提升有限。
- 缓存区对效率的影响: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
格式录制。实验结果如下:
可以发现,在使用较低视频质量时可以达到摄像头的硬件采集帧率,而分辨率在 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 硬件所支持的最高视频帧率。
优化结果如下:
研究发现,优化后所有分辨率下均可达到硬件帧率。同时,在视频录制选用的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


工程成员



评论