常见画面亮度闪烁,AE层面排查思路
Q:摄像头画面出现规律性的亮暗闪烁,自动曝光反复调节、稳定不下来,怎么快速定位?
A:按 「先定性 → 查配置 → 看现成信息 → 再改源码」 四级递进。前三步用调试工具、串口和现成节点就能完成,都不用改代码。

第 0 步:先确认是不是 AE 引起的
用调试工具把 AE 切到 Manual 模式,固定曝光时间、模拟增益、数字增益:
| 结果 | 判断 |
|---|---|
| 闪烁消失 | 是 AE 在反复调节,继续往下查 |
| 仍然闪烁 | 大概率与 AE 无关,转查 3DNR 调试开关、ISP 其他模块或显示端 |
这一步只点几下工具,不用改代码,先排除误判。
第 1 步:查配置类原因(工具、串口可见)
按现场现象直接查,多数问题在这一步就能定位:
| 现象 | 检查点(怎么查) | 处理方向 |
|---|---|---|
| 开了曝光表但效果不对 / 曝光不生效 | 工具看 AE 模式;切 Manual 或普通模式对比 | 修正曝光表;曝光表模式不支持半行曝光,确认 sensor 是整行曝光 |
| 抗频闪(flicker)不生效、滚动水波纹 | 串口读 sensor 实际 FPS、VTS(命令见下方) | 修正驱动传入的 VTS/FPS,两者要匹配 |
| again/dgain 在两组值之间来回变、不收敛 | gain 表精度:小值达不到目标亮度、大值又超 | 提高 gain 表精度,或增大进入稳定状态的阈值 |
| 收敛越冲越亮、亮暗来回摆 | 收敛步长 / 收敛速度上限参数/强光抑制过大 | 限制收敛速度上限,避免步长过大或为负,降强光抑制强度 |
| 双摄主/次摄闪烁 | 隔帧生效参数、expo_fs | expo_fs 设为 0;隔帧生效按双摄配置 |
| 高增益段亮度突跳 | 驱动 again 上限是否等于 gain 表档位值 | again 上限改为 gain 表档位值 |
| 30 fps 高分辨率下闪,降帧率后消失 | 带宽 / 帧率 | 降帧率复测确认,说明问题不在 AE 参数 |
经验:先看 FPS/VTS 是否传对、曝光表模式是否配错、隔帧生效参数这三项,能覆盖相当一部分现场问题。
读 sensor 实际 FPS / VTS(T33 实测可用):
echo "w dbgmode 0 items 0x2 interval 0 vinum 0" > /proc/jz/isp/isp-m0 cat /proc/jz/isp/isp-m0 | grep -E "FPS|HTS|VTS"
实测输出(T33 + SC4336P):
Sensor OUTPUT FPS : 20 / 1 Sensor HTS : 2800 Sensor VTS : 2250
第 2 步:先看现成的 AE 信息(不用改代码)
T33 的 AE 算法核心在库文件(fast_ae_472.a / fast_ae_540.a)里,客户一般拿不到驱动源码。但 AE 实时信息有两个现成口子,一行代码都不用改。
2.1 方式一:proc 节点(零改动,优先)
核心 ISP 节点:/proc/jz/isp/isp-m0(T33 板端实测确认,ls /proc/jz/isp/ 可见 isp-m0 / isp-m1 等)
# items 的 bit9(0x200)选中 AE 信息,vinum 0 = 主摄 echo "w dbgmode 0 items 0x200 interval 0 vinum 0" > /proc/jz/isp/isp-m0 cat /proc/jz/isp/isp-m0
串口上全量输出太长,建议只取关键几行(注意 AeAGain : 带空格,否则会匹配到 AeAGainManualMode):
cat /proc/jz/isp/isp-m0 | grep -E "AeLuma|AeAGain :|AeDGain :|AeIspDGain :|AeIntegrationTime :|AeStable"
连续采样看是否跳变:
for i in 1 2 3 4 5; do cat /proc/jz/isp/isp-m0 | grep -E "AeLuma|AeStable" | tr "\n" " "; echo; sleep 1; done
实测输出片段:
********* Ae Info ********* AeIntegrationTime : 1415 AeAGain : 1024 AeDGain : 0 AeIspDGain : 1024 ExposureValue : 1415 AeStable : 0 AeTarget : 62 AeMean : 55 AeLuma : 56
输出里直接看这些字段是否来回跳:
| 字段 | 含义 |
|---|---|
AeLuma | 当前统计亮度 |
AeAGain / AeDGain / AeIspDGain | 模拟 / 数字 / ISP 数字增益 |
AeIntegrationTime | 曝光时间 |
AeTarget / AeMean / AeStable | 目标亮度 / 加权亮度 / 稳定标志 |
ExposureValue / EVLog2 | 曝光量 |
2.2 方式二:应用层调 API(SDK 带库就能用)
节点被裁剪时,在客户应用层直接调接口打印:
tisp_ae_exprinfo_t info; tisp_g_ae_exprinfo_attr(0, &info); printf("it=%d ag=%d dg=%d ispdg=%d EV=%d\n", info.AeIntegrationTime, info.AeAGain, info.AeDGain, info.AeIspDGain, info.ExposureValue); tisp_ae_scence_attr_t sc; tisp_g_ae_scence_attr(0, &sc); printf("luma=%d\n", sc.luma);接口声明在 lib_tisp_api.h。注意实现包在 #ifdef TISP_AE_API_CTRL 里,没开这个宏时函数不填数据,先确认客户 SDK 的配置。
2.3 判断:luma / 增益是否跳变
| 观察结果 | 下一步 |
|---|---|
| luma 或增益有跳变 | 定位跳变位置,用 Manual 固定该组参数,确认画面效果 |
| 没有跳变,但画面仍闪 | AE 算得没问题,是驱动没正确执行 → 进第 3 步对比 |
第 3 步:还定位不了,再改驱动加打印
3.1 / 3.2 需要 T33 内部 AE 固件源码(客户 SDK 一般没有,找原厂支持);3.3 改的是 sensor 驱动,客户 SDK 自带,可以自己加。只有前两步都看不出问题时才需要。
3.1 打印 luma
luma 存在 AE 结果结构体 ae_context->results[index] 里:
nLuma:原始统计亮度nLumaFinal:加权 / ROI 后的亮度
打印点:opensource/internal/t33/t33_tiziano/src/tiziano_ae.c 的 tisp_ae_calc_process(约 413 行)——在库函数 normal_ae_calc_process / fast_ae_calc_process 调用之后、tisp_ae_set_algo_index 之前,此时 results[index] 已被本次统计填充。normal AE 和 fast AE 两种模式都覆盖。
3.2 打印 it / ag / dg
在同一位置打印 results[index] 的 nExpt / nAGain / nAGainReg / nDGain / nIspDGain。
两个注意点:
tiziano_ae.c约 343 行有一段现成注释 printk(ag_index / again / ispd),取消注释可用,但它在 fast AE 分支,normal 模式在 306 行就提前 return 了;tisp_ae_interrupt_sync_result(约 356–409 行)整段被#if 0禁用,不要在那里打点。
实测可用的打印代码(T33 + SC4336P 验证通过,EV 变化时逐条打印、稳定后每 50 帧心跳,避免刷屏):
{ static uint64_t dbg_last_ev = ~0ULL; static uint32_t dbg_cnt = 0; uint64_t cur_ev = ae_context->results[index].nEV; if (cur_ev != dbg_last_ev || (dbg_cnt++ % 50) == 0) { dbg_last_ev = cur_ev; printk("JZ_AE: v%d it=%u ag=%u ispdg=%u luma=%u ev=%llu\n", vinum, ae_context->results[index].nExpt, ae_context->results[index].nAGain, ae_context->results[index].nIspDGain, ae_context->results[index].nLuma, (unsigned long long)cur_ev); } }为什么打ispdg而不打dg:nDGain(sensor 数字增益)多数场景不用、恒为 0,AE 的实际增益调节落在 ISP 数字增益nIspDGain上(实测在 1024~1054 之间变化)。打dg会看不到调节过程。
实测稳定段:
JZ_AE: v0 it=896 ag=1024 ispdg=1024 luma=55 ev=917504 JZ_AE: v0 it=896 ag=1024 ispdg=1024 luma=55 ev=917504
对照关系:
| JZ_AE | m0 节点 | 说明 |
|---|---|---|
it | AeIntegrationTime | 曝光行数(AeIntegrationTimeUnit : 0 时两者相等) |
ag | AeAGain | 模拟增益,1024 = 1x |
ispdg | AeIspDGain | ISP 数字增益,1024 = 1x |
luma | AeLuma | 统计亮度 |
ev | ExposureValue | ev 是 10bit 精度,ExposureValue = ev >> 10 |
上电前几帧 luma 会剧烈波动(如 101→1),属出图瞬态,不要拿启动那几帧当判断依据,看稳定后的值。
看luma和ev是否在相邻帧之间来回跳、it/ag/ispdg是否成对反向变化,就能判断是 AE 侧振荡还是下游执行问题。
3.3 对比 sensor 驱动侧输入值
在 sensor 驱动里打印——这个源码客户 SDK 自带,路径 opensource/drivers/sensors/<sensor>/,不用改内部固件。
以 T33 + SC4336P 为例,走的是 SENSOR_EXPO 模式(sc4336p.c 头部 #define SENSOR_EXPO),it 和 again 打包在一个 int 里传给 sc4336p_set_expo(约 2008 行):
static int sc4336p_set_expo(struct tx_isp_subdev *sd, int value) { int it = value & 0xffff; /* 曝光行数 */ int again = (value >> 16) & 0xffff; /* again 寄存器值 */ printk("JZ_SENSOR: it=%d again=0x%x\n", it, again); /* again 用十六进制,和 gain 表对照 */ /* 原有写寄存器代码不动 */ }如果 sensor 驱动没开 SENSOR_EXPO(头文件里没有 #define SENSOR_EXPO),打点改在这两个函数:
sc4336p_set_integration_time(约 2044 行)sc4336p_set_analog_gain(约 2076 行)
WDR 短帧对应 sc4336p_set_expo_short(约 2243 行),要一起打。与第 2 步 proc 节点 / API 拿到的 AeIntegrationTime / AeAGain 对比(这条链路客户自己就能走完,不依赖内部固件源码):
实测(T33 + SC4336P,收敛过程中同时抓):
# sensor 驱动打印 JZ_SENSOR: it=598 again=0x80 JZ_SENSOR: it=600 again=0x80 JZ_SENSOR: it=602 again=0x80 # 同刻 proc 节点 AeIntegrationTime : 595 AeAGain : 1024
| 对比项 | 能否直接比 | 说明 |
|---|---|---|
it ↔ AeIntegrationTime | ✅ 直接比 | 都是曝光行数。上例 598 vs 595 的差值是抓取时刻不同(AE 正在收敛),取同一帧或稳定态应相等 |
again ↔ AeAGain | ⚠️ 需查表 | AeAGain 是倍数(1024 = 1x),经 sc4336p_alloc_again 查 again_lut 表得到寄存器值。上例 1024 → 0x80(表首项 {0x0080, 0},即增益下限档);增益升高时按表逐档递增,只看趋势是否跟得上,不要比数值 |
| 对比结果 | 结论 |
|---|---|
| 一致(it 相等、again 按 AE 分配同步变化) | 闪烁与 AE 算法无关,查 sensor 驱动:增益表(again_lut)、增益上限、隔帧生效、寄存器写入时机 |
| 不一致(sensor 收到值和 AE 分配不符) | 问题在 AE 算法侧或参数传递,向上排查分配逻辑 |
避免误判
先切 Manual 定性,快速定位模块;
FPS/VTS 必须核对,flicker 相关几乎都和它有关;
注意隔帧生效:双摄和单摄配置不同,参数照搬单摄会错;
曝光表和驱动限制要对齐:表里设的值超过驱动能力时,串口会有提示,不要只看工具显示;
结论
先切 Manual 确认是不是 AE;再用工具和串口查模式、帧率、曝光表、隔帧生效;然后 cat proc 节点或调 API 看AeLuma和增益是否跳变;都不行才改驱动加打印,与 sensor 驱动输入值对比——一致查驱动,不一致查算法。
