4、AE控制策略与收敛:PID控制与爬山算法实战
亮度收敛,说白了就是让画面从「过曝」或「欠曝」快速稳定到目标亮度,而且不能闪。我做了这么多年ISP,见过太多方案:有的收敛快但画面像呼吸灯一样忽明忽暗,有的稳定但调半天才到位。这里面的核心,就是控制策略的选择。
今天咱们重点聊两种主流方法——PID控制和爬山算法。它们各有脾气,用好了是利器,用不好就是坑。
4.1 为什么需要控制策略?
AE的本质是一个闭环反馈系统。传感器输出图像,统计模块算出当前亮度,算法计算与目标亮度的误差,然后调整曝光参数。但直接拿误差去调,会出问题。
核心矛盾:响应延迟 + 环境变化 + 噪声干扰 = 容易振荡
传感器从设置曝光到输出图像,至少需要1帧的延迟。如果环境光在变,或者场景里有闪烁光源,直接反馈就会来回震荡。
我早期做过一个项目,用的就是最粗暴的「误差直接映射法」。结果呢?画面在室内灯光下像波浪一样起伏,客户直接投诉。从那以后,我老老实实研究控制理论。
4.2 PID控制:经典中的经典
PID(比例-积分-微分)控制,在工业界用了上百年。在AE里,它把亮度误差拆成三部分来处理。
| 分量 | 作用 | AE中的含义 |
|---|---|---|
| P(比例) | 对当前误差做出反应 | 当前亮度与目标亮度的差值,直接决定调整幅度 |
| I(积分) | 消除稳态误差 | 累积过去的误差,解决「差一点点到目标」的问题 |
| D(微分) | 预测未来趋势 | 根据亮度变化速度,提前刹车或加速 |
公式很简单:
Output = Kp * error + Ki * ∫error dt + Kd * (derror/dt)
但在实际调参时,你会发现理论跟实践差距很大。
4.2.1 调参经验谈
我个人习惯先调P,再调I,最后碰D。为什么?
- P太大:画面快速到达目标,但会过头,然后来回震荡。我见过一个方案,P设到0.8,画面像抽风一样。
- P太小:反应迟钝,从暗到亮要好几秒,用户体验极差。
- I太大:容易产生「积分饱和」。场景突然变暗时,积分项还记着之前的误差,导致过冲。
- D太大:对噪声敏感。传感器噪声会被微分放大,画面反而更不稳定。
我的避坑指南:我曾经在一个低光照项目中,D项设得偏大,结果画面在暗处一直微抖。后来发现是传感器读出噪声被微分放大了。解决方案:对亮度统计值做3帧中值滤波,再送入PID。
4.2.2 离散化实现
ISP里都是离散采样,所以要用差分方程。我常用的位置式PID:
// 位置式PID
static float integral = 0;
float error = target_luma - current_luma;
integral += error * dt;
float derivative = (error - prev_error) / dt;
float output = Kp * error + Ki * integral + Kd * derivative;
prev_error = error;
// 限幅
if (output > max_step) output = max_step;
if (output < -max_step) output = -max_step;
注意那个限幅。没有限幅,积分项可能累积到天文数字,场景一变就炸。
4.3 爬山算法:另一种思路
PID是连续控制,爬山算法是离散搜索。它的逻辑很简单:往亮度增加的方向走,直到找到峰值。
具体做法:
- 先朝一个方向调整曝光(比如增加增益)
- 观察亮度变化:如果变亮了,继续同方向
- 如果变暗了,反向调整
- 当亮度在目标值附近来回跳动时,认为收敛
爬山算法的优势:
- 不需要精确的系统模型
- 对参数变化不敏感
- 实现简单,计算量小
劣势:
- 收敛速度慢(一步步试)
- 容易陷入局部最优
- 在平坦区域(亮度变化不明显)会迷失方向
4.3.1 改进版:自适应步长爬山
纯爬山算法太笨了。我做过一个改进:根据误差大小动态调整步长。
// 自适应步长爬山
float step = base_step;
if (abs(error) > 20) step = base_step * 3; // 误差大,大步快跑
else if (abs(error) > 10) step = base_step * 2;
else step = base_step; // 接近目标,小步微调
if (current_luma < target_luma) {
exposure += step;
} else {
exposure -= step;
}
这样既保证了快速收敛,又避免了在目标附近来回震荡。
4.4 无闪烁收敛:关键在「平滑」
闪烁的根源是什么?曝光参数变化太快,或者变化幅度太大。人眼对亮度变化非常敏感,尤其是低频闪烁。
我总结了几条铁律:
- 帧间变化限幅:单帧曝光变化不超过总范围的5%
- 增益与曝光时间协同:不要同时大幅调整两个参数
- 避开50/60Hz频闪:曝光时间要避开工频周期的整数倍
重要提醒:千万不要在AE收敛过程中同时调整增益和曝光时间。我见过一个方案,增益跳了6dB,曝光时间同时变了2ms,画面直接闪成频闪灯。正确的做法:先调曝光时间,再调增益,最后调数字增益,分阶段进行。
4.5 混合策略:取长补短
在实际产品中,我很少只用一种算法。更常见的做法是:
| 阶段 | 算法 | 原因 |
|---|---|---|
| 启动/场景切换 | 爬山算法(大步长) | 快速找到大致亮度范围 |
| 接近目标 | PID控制 | 精确收敛,消除稳态误差 |
| 稳定状态 | PID(小增益)+ 死区 | 防止微小噪声引起抖动 |
这种混合策略,我称之为「粗调+精调」模式。爬山算法负责把亮度拉到目标附近(误差<10%),然后PID接手做精细调整。既保证了速度,又保证了精度。
4.6 核心流程图
下面这张图展示了AE控制策略的整体流程,从亮度统计到参数输出,每一步都对应着今天讲的内容。
4.7 实战中的坑与对策
最后分享几个我踩过的坑,希望能帮你少走弯路。
坑1:积分饱和
场景从暗室切换到室外,积分项还记着暗处的累积误差,导致过曝好几帧。对策:积分限幅 + 场景切换时重置积分项。
坑2:爬山算法在平坦区迷失
纯色墙壁场景,亮度变化极小,爬山算法不知道该往哪走。对策:加入随机扰动,或者切换到PID的P控制。
坑3:50/60Hz频闪
室内灯光下,曝光时间如果落在工频周期的非整数倍,画面会闪烁。我曾经在客户现场调试到半夜,最后发现是曝光时间设成了1/45秒(50Hz地区)。对策:曝光时间锁定为工频周期的整数倍,比如1/50、1/100。
嗯,AE控制策略就聊到这儿。核心就三句话:爬山算法负责快,PID负责准,平滑限幅负责稳。三者配合,才能做到快速且无闪烁的亮度收敛。