3. AE算法实现:目标亮度设定、曝光时间与增益的分配策略、防闪烁处理与收敛控制

自动曝光(AE)是3A算法里最贴近用户体验的一环。说白了,就是让摄像头在不同光照下都能输出一张“看着舒服”的图像。目标亮度定多少?曝光时间和增益怎么分?遇到50Hz/60Hz频闪怎么躲?收敛快了会不会来回晃?这些坑我基本都踩过一遍。

核心思路:AE不是简单的PID闭环,而是一个带约束的多目标优化问题。亮度要准、噪声要低、运动要清晰、频闪要消除——这四个目标往往互相打架。

3.1 目标亮度设定

目标亮度(Target Luminance)是AE的“靶心”。我个人习惯把亮度基准定在Y = 100~120(8bit,0~255范围)。但这不是死的,得看场景。

场景类型 推荐目标亮度 说明
室内/办公室 110 ~ 120 人脸肤色自然,背景不过曝
室外白天 100 ~ 110 保留高光细节,避免天空过曝
夜间/暗光 80 ~ 100 优先降噪,牺牲一点亮度
背光场景 130 ~ 150(人脸区域) 需要配合WDR或局部AE

你可能会问:为什么暗光反而目标亮度更低?因为增益拉上去之后噪声会爆炸,与其让画面全是噪点,不如稍微暗一点但干净。我在做一款低光IPC时试过把目标亮度设到120,结果画面亮是亮了,但全是雪花点——后来降到90,用户反馈反而更好。

经验值:目标亮度最好做成可配的,不同模组、不同sensor的灵敏度差异很大。我一般会在驱动层留一个AE_Target_Luminance的寄存器接口,方便调试时动态调整。

3.2 曝光时间与增益的分配策略

这是AE里最核心的决策。给定一个目标亮度,曝光时间和增益怎么组合?

基本公式:亮度 ≈ 曝光时间 × 增益 × 场景照度

分配策略的核心原则:优先调曝光时间,再调增益。为什么?因为增益会放大噪声,而曝光时间只影响运动模糊。具体策略我总结成三步:

  1. 先调曝光时间:从最小值开始往上加,直到达到目标亮度,或者达到最大曝光时间(通常受帧率限制,比如30fps下最大33ms)。
  2. 再调模拟增益:曝光时间到顶了还不够亮?加模拟增益。模拟增益的噪声比数字增益小,优先用。
  3. 最后调数字增益:模拟增益也到上限了?只能上数字增益了。但数字增益超过2倍时,噪声会非常明显,建议配合降噪算法。

我曾经在一个项目里遇到一个问题:曝光时间调得太长,导致运动物体拖影严重。后来我加了一个运动检测模块,检测到运动时主动限制最大曝光时间,把亮度缺口用增益补上。效果立竿见影。

注意:增益不是无限可调的。模拟增益一般上限在16x~32x,数字增益建议不超过4x。超过这个范围,图像质量会急剧下降。

3.3 防闪烁处理(Flicker Detection)

防闪烁是AE里最容易被忽视但又最容易出问题的环节。50Hz和60Hz的灯光频闪,会让画面出现明暗条纹,非常影响体验。

闪烁的根本原因:曝光时间不是光源周期的整数倍。比如50Hz光源周期20ms,如果你曝光时间设成25ms,就会拍到半个周期的亮和半个周期的暗。

防闪烁策略我分两步走:

  • 检测阶段:统计连续几帧的亮度变化,如果出现周期性波动,基本可以判定有频闪。我习惯用FFT做频域分析,看50Hz/60Hz分量是否突出。
  • 消除阶段:将曝光时间锁定为光源周期的整数倍。50Hz下锁定为10ms、20ms、30ms... 60Hz下锁定为8.33ms、16.67ms、25ms...
光源频率 周期 推荐曝光时间(整数倍)
50Hz 20ms 10ms, 20ms, 30ms, 40ms
60Hz 16.67ms 8.33ms, 16.67ms, 25ms, 33.33ms
避坑指南:我曾经在印度市场遇到一个项目,当地电网既有50Hz也有60Hz区域。我写了一个自动检测逻辑:先尝试锁定50Hz,如果亮度波动仍然存在,自动切换到60Hz。这个逻辑后来成了我们公司的标准模块。

3.4 AE收敛速度与稳定性

收敛速度和稳定性是一对矛盾。收敛太快容易过冲,画面会忽明忽暗;收敛太慢用户会觉得反应迟钝。

我常用的控制策略是分段PID:

  • 大偏差阶段(亮度误差 > 30%):用大步长快速逼近,步长可以设到当前值的20%~30%。
  • 中偏差阶段(亮度误差 10%~30%):用中等步长,5%~10%。
  • 小偏差阶段(亮度误差 < 10%):用微调步长,1%~3%,同时加入死区防止抖动。

另外,我强烈建议加一个收敛完成标志。当连续3~5帧的亮度误差都在死区范围内,就认为AE已经稳定,可以降低调节频率,减少CPU开销。

关键参数:
  • 死区(Dead Zone):±3 ~ ±5(8bit),防止微调导致来回跳
  • 步长上限:单次调节不超过当前值的30%,防止过冲
  • 稳定帧数:连续3帧在死区内才认为收敛完成

我记得有一次调试一个安防摄像头,AE总是在亮暗切换时来回震荡。查了半天发现是步长设得太大了,每次调节都过冲,然后反向调节又过冲。后来把步长上限从50%降到20%,问题解决。

3.5 AE算法整体流程

下面这张图是我自己总结的AE算法流程,基本覆盖了上面讲的所有环节:

AE算法整体流程图 AE算法整体流程图 输入图像帧 统计当前亮度Y_cur 计算误差 E = Y_tgt - Y_cur 防闪烁检测(50Hz/60Hz检测) 曝光时间分配(锁定整数倍) 增益分配(模拟增益优先) 关键参数 • 目标亮度:100~120 • 死区:±3~±5 • 步长上限:30% • 稳定帧数:3帧

3.6 工程实现要点

最后聊几个工程落地的细节:

  • 帧率同步:AE调节最好在VBlank期间完成,避免在帧中间修改曝光参数导致画面撕裂。
  • 平滑过渡:每次调节不要一步到位,而是分多步渐变。比如目标曝光时间从10ms变到20ms,中间插几帧15ms、18ms,用户几乎感觉不到变化。
  • 异常保护:当场景发生剧烈变化(比如从室内走到室外),AE可能会瞬间过曝或过暗。我习惯加一个最大调节幅度限制,单帧调节不超过当前值的30%。
  • 调试接口:一定要留log输出,把每一帧的Y_cur、Y_tgt、曝光时间、增益值都打出来。调试AE时这些数据比看图像直观得多。
血的教训:有一次我忘了在AE收敛后关闭调节,结果画面在稳定状态下还在微调,导致每几帧就有一个亮度跳变。用户反馈说“画面在呼吸”。后来加了收敛完成标志,问题解决。

AE算法说难不难,说简单也不简单。核心就是把握好目标亮度、曝光时间与增益的分配、防闪烁和收敛控制这四个维度。每个维度都有trade-off,没有银弹。多在实际场景里跑一跑,你自然就找到感觉了。