嵌入式视觉采集模块的内存机制与稳定性设计
做物联网嵌入式开发时车牌抓拍、人脸核验、智能物品回收等项目都离不开摄像头拍摄模块。日常调试中总会遇到各类难以定位的故障时而摄像头初始化失常时而拍摄无法生成图像设备稳定运行一段时间后还会出现拍照卡死情况。经过排查发现这类疑难问题大多源于内存分配与释放不合理。excamera 库完成底层功能封装优化内存运行逻辑附带多种实用场景代码能有效减少调试工作量。一、常规模式长供电循环拍照在长供电场景下摄像头可以一直稳定上电为了确保摄像头业务的正常执行只需要做一次摄像头初始化后续重复调用excamera.photo获取照片结果即可——原理是将摄像头业务执行时所使用的内存区域占好确保摄像头业务正常执行。根据照片保存路径不同会有以下内存使用差异1.1 ZBUFF/RAM路径存储这个路径是将照片结果存放在sys ram中差异是使用ZBUFF方式存储时excamera会将预占用足够存储照片的空间确保照片可以输出RAM路径存储可能会因为其他业务的内存占用导致照片输出异常。因为两个方式都是存在内存中所以直接使用ZBUFF即可。**内存占用大小计算方式为**摄像头像素高 X 摄像头像素宽 X 3.5**以GC032A摄像头为例**像素高为640像素宽为480则640 X 480 X 3.5 1075200 (B) ≈ 1MB也就是说在使用GC032AZBUFF方式存储照片结果的环境下当调用excamera.open() 后sys ram就会被占用1MB。后续只需要循环调用excamera.photo()获取照片即可这1MB内存占用不会变大当出现拍照报错时再调用关闭接口重新初始化。参考代码如下-- 引入excamera扩展库模块localexcamerarequireexcamera-- 定义照片存储路径localsave_methodZBUFF--localsave_method/ram/test.jpgfunctioncamera_func()-- 出现异常后重新初始化whiletruedo-- 配置gc032a摄像头参数表localspi_camera_param{idgc032a, -- SPI摄像头仅支持gc032a、gc0310、bf30a2请带引号填写 i2c_id1, -- 模块上使用的I2C编号 work_mode0, -- 工作模式0为拍照模式1为扫描模式 save_pathnil, -- 扫描结果为字符串返回使用变量赋值既可 camera_pwr2, -- 摄像头使能管脚填写GPIO号即可无则填nil camera_pwdn5, -- 摄像头pwdn开关脚填写GPIO号即可无则填nil camera_lightnil -- 摄像头补光灯控制管脚填写GPIO号即可无则填nil}-- 初始化摄像头传入配置参数 resultexcamera.open(spi_camera_param)-- 记录摄像头初始化状态 log.info(初始化状态, result)-- 循环触发拍照任务whileresultdo-- 执行拍照操作 result, dataexcamera.photo()-- 判断拍照任务正常则上传错误则重新初始化摄像头ifresultthen-- 执行照片上传 --(upload...)end end -- 关闭摄像头重新初始化 excamera.close(true)end end sys.taskInit(camera_func)1.2 文件系统存储使用外挂SD卡或SoC内的FLASH来存储照片时因为excamera.photo() 是直接将照片结果输出到路径的内存不需要存储照片结果所以excamera.open() 初始化后只会占用摄像头业务所需要的内存。**内存占用大小计算方式为**摄像头像素高 X 摄像头像素宽 X 2**以GC032A摄像头为例**像素高为640像素宽为480则640 X 480 X 2 614400 (B) ≈ 620KB参考代码同上只需路将路径修改为文件路径-- 定义照片存储路径localsave_method/SD/ABC.jpg二、低功耗模式电池供电间歇拍照在低功耗应用中为了将功耗降到最低都会在拍摄任务完成后将摄像头完全关断等待下次唤醒后再上电重新初始化应用。这种场景下为了确保摄像头业务能够正常执行就必须要有足够的连续内存。因为脚本各类功能持续的工作内存被反复占用和释放后即便此时内存有足够的余量也没有连续的内存能够给摄像头使用所以此时的最优解是——重启设备把内存刷新并且在刚开机的时候就将摄像头初始化完成让摄像头业务先占用好足够的内存空间以便后续业务随时调用摄像头使用。参考代码如下-- 引入excamera扩展库模块localexcamerarequireexcamera-- 定义照片存储路径localsave_methodZBUFF--localsave_method/ram/test.jpgfunctioncamera_func()-- 配置gc032a摄像头参数表localspi_camera_param{idgc032a, -- SPI摄像头仅支持gc032a、gc0310、bf30a2请带引号填写 i2c_id1, -- 模块上使用的I2C编号 work_mode0, -- 工作模式0为拍照模式1为扫描模式 save_pathnil, -- 扫描结果为字符串返回使用变量赋值既可 camera_pwr2, -- 摄像头使能管脚填写GPIO号即可无则填nil camera_pwdn5, -- 摄像头pwdn开关脚填写GPIO号即可无则填nil camera_lightnil -- 摄像头补光灯控制管脚填写GPIO号即可无则填nil}-- 初始化摄像头传入配置参数localresultexcamera.open(spi_camera_param)ifnot resultthen-- 记录摄像头初始化状态 log.info(已无足够连续内存用于拍照业务执行重启刷新内存后重试, result)reboot()end -- 执行拍照操作 result, dataexcamera.photo()-- 判断拍照任务正常则上传错误则重新初始化摄像头ifresultthen-- 执行照片上传 --(upload...)end -- 关闭摄像头重新初始化 excamera.close(true)end sys.taskInit(camera_func)三、裁切照片应用扫码/人脸/车牌识别…在实际产品中我们往往不需要整张照片只关心其中一小块区域。3.1 常见应用场景例如二维码/条码识别码点在整图中占比很小裁切后可减少干扰、提升识别速度。人脸识别门禁只需人脸区域丢掉背景既保护隐私又降低传输流量。车牌识别路口监控只需截取车牌那一小条上传体积从几百KB降到几十KB。这时可以使用excamera.photo(x, y, w, h) 完成照片裁切只输出你需要的矩形区域。3.2 内存代价使用x,y,w,h参数裁切照片时内存会额外占用空间摄像头像素高 X 摄像头像素宽 X 1.5这个内存额外占用仅在执行excamera.photo(x,y,w,h) 期间当获取到照片结果时就会被释放掉根据实际拍照应用需求预备足够的内存余量再执行拍照业务。四、小结——拍照方案最优解根据上述的应用参考假设使用GC032A时内存会有1MB~1.5MB被占用在Air780EPM这类4MB4MB小内存的SoC上sys ram分配只有2.3MB 基本占用了一大半剩余1MB左右可以给其他业务使用极易造成内存爆满的情况。所以当有摄像头需求时选择Air8000系列、Air780EHM/EHV/EGH等8MB8MB及其以上大内存的SoC才是最优解。结合上述的应用参考在脚本中增加足够的保护机制就可以让摄像头业务稳定执行增加产品的稳定性和更长的使用生命周期。摄像头模块的稳定性高度依赖内存机制。RAM 存储、文件系统存储、图像裁切都会产生不同大小的内存占用。内存不足或碎片化会直接导致初始化失败、无图像、程序卡死等现象。保证足够连续内存、合理选择存储路径、正确控制初始化时机是提升长期稳定性的关键。今天的内容分享到这里。