)
从Sensor到算法手把手带你拆解手机PDAF相位对焦的完整校准流程含Gainmap与DCC详解先交代一下背景。我之前在一颗RV1126B方案的IPC模组上调试PDAF对焦sensor是一颗支持2x2 OC的1/2.7英寸CMOS项目都快量产了客户反馈近景对焦慢、远景拉风箱。按老经验去查几家主流平台得到的回答基本都是“相位对焦不准你重新标一下Gainmap和DCC”但真正能把这套标定流程讲清楚的人很少。当时我手上只有一份sensor的datasheet、一个半残的调试工具还有一个快要到期的交付节点。这篇文章就是那段时间的总结。我会从PDAF的基本原理讲起重点拆解Gainmap和DCC这两个参数在整条链路里的作用然后给出一套可以复现的完整校准流程。内容不挑具体平台无论是手机、行车记录仪、智能摄像头还是机器人视觉模块只要你的sensor支持PDAF这套思路都可以直接套用。1. 内容整体设计与思路拆解为什么PDAF不是“拿到sensor就能用”很多人第一次接触PDAF以为这是一个sensor出厂就固化的功能接上模组就能自动对焦事实远不是这样。PDAF能够工作依赖的是sensor上特殊的像素结构和后端算法的配合而连接两者的桥梁就是Gainmap和DCC。这两个参数通常不是sensor出厂默认值就能直接用的它们和模组的镜头、VCM马达、IR滤光片、甚至排线布局都有关系必须针对具体模组做一次标定。1.1 相位对焦的核心工作原理先帮大家把PDAF成像底层补明白否则后面讲标定全是空的。普通CMOS的每个像素上都覆盖着微透镜microlens用来把入射光汇聚到光电二极管上尽量提高量子效率。PDAF sensor的做法是在部分像素上做文章将这些像素的微透镜遮挡一半或者做半圆形微透镜偏移使得它们只接收来自特定方向的光线。通俗地说这些“特殊像素”被分成了左眼看左半边光、右眼看右半边光的成对结构。当镜头处于合焦位置时这对像素接收到的光强度应该对称一致当镜头离焦时两个像素接收到的光强会产生差异相位差就出来了。对比度对焦CDAF是在像平面上逐渐移动镜头来找“最锐”的位置本质上是一个搜索过程搜索就需要来回扫所以慢。PDAF直接给出一幅图里各区域的相位差偏移量再通过映射关系换算成当前的离焦方向和大小镜头一步就能跳到目标位置附近。速度快的原因就在这里——它把“搜索”变成了“测量”。但问题来了。这个相位差通常叫PD value是个无量纲量它对应的是两个半像素之间的光强偏移并不是实际的物理离焦量。要把PD value转换成VCM马达需要走的步数也就是defocus值就需要DCC。而左右像素哪怕设计上是对称的实际制造中也很难做到完全一致尤其是微透镜偏移量和光电二极管有效面积的差异会导致同一光照下左右像素输出本身就有一个固定的增益差这个差值由Gainmap来补偿。1.2 为什么“默认系数”不可信我见过不少工程师在第一次调试时直接拿sensor vendor提供的demo settings烧进模组就指望PDAF跑通结果往往是近处对不上、远处疯狂来回抽动。原因在于sensor vendor提供的参数是在他们自己的reference模组上标定的镜头型号、VCM行程、马达灵敏度都和你手上的模组不一样。举一个具体例子DCC曲线里最关键的一个参数是“PD value到defocus的斜率转换”它直接取决于镜头的光学设计——同样的物理离焦量在不同镜头上产生的PD value偏移是不同的。哪怕你换了同一家镜头厂的不同批次焦距差了0.1mmDCC的斜率就必须重新拟合。更隐蔽的是温度影响VCM马达的弹簧弹性和磁力都会随温度变化导致同一个DCC值在25度和50度下的实际对焦位置不同。这些都是在实际量产中必须考虑的因素如果只知道拿默认参数就谈不上真正掌握PDAF。1.3 整体校准流程的设计框架一个完整的PDAF标定流程我建议这样拆分第一步是环境准备确定标定环境的光源、物距、标板类型第二步是PD值采集在多个已知距离下记录PD值、VCM位置、镜头温度等数据第三步是Gainmap计算用均匀光照下的左右像素输出拟合增益补偿系数第四步是DCC拟合建立PD值与defocus之间的映射关系第五步是写入和验证把参数写入驱动并做实拍测试。这套流程看起来不复杂但每一步都有很多坑。比如采集PD值时环境光不均匀直接导致左右像素灵敏度差异拟合错误又比如拍摄的标板本身纹理方向单一导致某个方向的PD值异常。后面我会把每个环节的细节都摊开来讲。2. 核心细节解析Gainmap和DCC到底是什么如果只看各家平台的文档Gainmap和DCC的概念很容易被绕晕。有的SDK里叫PD calibration、有的叫DCC table、有的把Gainmap混在AWB统计里。这里我把两者的物理意义、作用机制、计算方法掰开揉碎讲清楚。2.1 Gainmap校正左右像素的先天“视力差”前面说过PDAF像素是由一对分别接收左右光线的子像素构成的。理想情况下当一个均匀照亮的目标成像在所有像素上时左右子像素的输出应该完全一致。但实际制造中两个子像素的光电二极管面积不可能完全相等微透镜的偏移量也有容差所以即使在完全均匀的光照下左右子像素的输出也会有一个固定的比例差这个差异就是Gainmap要补偿的东西。Gainmap的计算逻辑其实不复杂。选择一块均匀照明的白色板子在自动白平衡稳定后连续抓取若干帧取左右子像素输出各自的平均值然后算出它们的比值。这个比值通常是一个接近1.0的数但会随sensor温度、曝光时间略有漂移。所以在量产标定中通常会在至少两个不同光照强度下分别采集然后用线性插值生成一张二维查表横轴是亮度、纵轴是增益比。实际应用中Gainmap是逐像素计算的。因为PDAF像素在sensor上不是成片排列而是稀疏分布在几个特定行每一对像素的工艺偏差都不一样所以标准的做法是存储一张与PDAF像素位置一一对应的增益表而不是一个全局标量。有些平台把这张表简化为按行存储这会在边缘区域引入少量误差但通常可以接受。2.2 DCC把“相位差”翻译成“镜头步数”DCC全称是Defocus Conversion Coefficient翻译过来是“离焦转换系数”。这是PDAF里最核心、最容易被误解的参数。它做的事情可以用一句话概括把从图像中计算出的PD值映射成VCM马达需要运动的步数也就是驱动电流从而让镜头移动到合焦位置。为什么需要这个转换因为PD值反映的是光线到达sensor时在水平方向上的偏移量这个偏移量的大小和离焦量之间的关系取决于镜头的焦距、光圈、光学畸变、sensor的像素尺寸等多个因素。不同的镜头模块同样的离焦量产生的PD值可能完全不同。甚至同一个镜头模块在画面中心区域和边缘区域因为入射角不同PD值和defocus的对应关系也不同。所以DCC通常不是一个简单的常数而是一组分区、分距离的拟合曲线。在一次完整的标定中你需要在特定物距比如10cm、30cm、1m、无穷远下手动精确控制VCM位置记录下每个位置对应的PD值然后用最小二乘法或者分段线性拟合得到一条PD-defocus映射曲线。这条曲线就是整个PDAF算法的“灵魂”。这里需要特别说明一个初学者常踩的坑DCC和“对焦目标距离”是两回事。有人拿PDAF当测距仪用问为什么PD值不能直接换算成实际物距。实际上PD值只能告诉你镜头当前离开合焦位置多远、往哪个方向走它无法直接告诉你目标物体离你多少米。特别是在大光圈镜头上PD值随距离的变化是非线性的越远越不敏感无限远附近甚至会饱和。把DCC理解成“对焦收敛用的反馈增益”才是正确的打开方式。2.3 Gainmap和DCC在算法链路中的协作关系两者在PDAF算法中的协作可以用一个例子说清楚。假设你在暗光环境下拍一个纹理细致的物体sensor左右子像素的原始输出一个是100 ADU一个是110 ADU。如果Gainmap是1.05那么加权后的PD值就是100×1.05-110-5 ADU表示镜头需要往后移动。如果Gainmap错误地设成了0.9那么PD值就变成100×0.9-110-20 ADU算法就会认为离焦量大得离谱给VCM一个很大的步进电压结果镜头冲过头再反向修正来回震荡几次才勉强收敛。这就是很多模组“对焦慢”的真正原因很多时候并不是算法参数没调好而是Gainmap本身就是错的。DCC则负责第二步。当PD值被Gainmap修正后DCC把它折算成VCM的目标步数。如果DCC斜率过陡算法会认为很小的PD偏移也需要移动很长距离导致过冲如果斜率过缓算法会反应迟钝需要多次迭代才能收敛。一个理想的DCC曲线应该是接近线性的、过零点的、并且在小PD值和中等PD值区域都保持较高的斜率精度。后面我给的标定方法就是围绕这一目标展开的。3. 实操过程与核心环节实现一套可复现的PDAF标定流程现在进入本文最硬核的部分。下面这套流程是我在RV1126B方案上反复验证过的也适配其他主流平台。每一步我都会注明操作目的、需要记录的数据、以及我踩过的坑。3.1 环境准备与设备搭建做PDAF标定实验室环境远比想象中重要。我第一次是在普通办公室灯光下做的结果拟合出的Gainmap在户外场景下偏差很大。原因很简单不同色温的光谱分布会影响左右子像素的响应差异黄光下拟合出的增益比和自然光日光下完全不同。建议的标定环境是三要素第一使用标准D65光源或者至少是色温可控的LED面光源确保整个标板平面的照度均匀性在±5%以内第二标板要与镜头完全垂直如果有俯仰角画面边缘的PD值会引入系统性偏差第三整个系统放在防振台上或者至少在曝光期间无振动。这里还要提一个很多人忽视的环节IR滤光片。PDAF像素的响应通常比普通像素更广的光谱范围如果模组里的IR滤光片质量不好红外光会严重干扰左右像素的平衡。所以标定环境的灯光尽量少含红外成分或者干脆用监控级模组的标准环境光源。否则你拟合出的Gainmap会带一个伪红外偏置白天户外还行一到室内红外人造光源多的场景就翻车。在硬件层面VCM马达的状态也要确认。用驱动工具让VCM从0跑到满行程多跑两个来回确保机械结构没有钝涩、磁缸没有卡滞。记录VCM的阻尼时间也就是从设定位置到稳定位置的时间常数。这个数据在后续DCC拟合中会用到因为如果你在VCM还没稳定时就采集图像PD值里会混入动态误差。3.2 PD值采集多距离、多分区、多光照采样PD值采集是整个标定的基础数据质量决定参数质量。我在第一次标定时用了一个40cm×30cm的灰阶板发现数据噪声特别大后来换成高频纹理的ISO 12233标板才行。原因很简单PDAF算法是基于局部边缘的相位计算标板必须提供足够的空间频率信息纯灰阶板在均匀区域根本算不出PD值。采集流程建议按以下步骤操作第一步把标板固定在滑轨上从最远距离开始。对每个距离点需要使用分辨率足够高的对位标记保证标板中心在画面中心的±5%误差范围内。第二步在每个距离点让VCM从最近焦到最远焦步进步长建议设在满行程的1%到2%。每走一步等待马达稳定抓拍一帧并记录当前的VCM code、该帧的PD值左右子像素的原始差、sensor温度、环境亮度曝光时间增益的换算值)。第三步至少采集3个距离点近距离如镜头最小物距、中距离如30-50cm、无限远或者镜头对焦到无限远的VCM位置。如果镜头行程比较长建议在5-7个距离点采集覆盖从最近到无限远的整个对焦范围。第四步在暗光和亮光两种条件下各做一遍。因为PD值在低亮度下噪声会显著上升亮光条件下则容易饱和两套数据可以验证Gainmap和DCC的动态范围。在采集过程中有一个细节特别提一下。PD值和VCM位置并不是一对一的映射关系它受当前帧的曝光参数影响。如果自动曝光和自动白平衡开着你采集到的一组PD值里可能夹杂着曝光变化带来的增益变化。所以标定时的可靠做法是固定曝光时间、固定模拟增益、关掉自动白平衡只改变光源强度来适应环境亮度变化。手动模式下采集的数据拟合出来的DCC才稳定可用。3.3 Gainmap计算与补偿验证得到采集数据后第一步先算Gainmap。我在上一节提过基本逻辑这里给一个更具体的计算过程。假设sensor上有N对PDAF像素每对像素在第k帧均匀光照下的输出分别是L(i,k)和R(i,k)。Gainmap的作用是找到一个系数G(i)使得所有帧的R(i,k)×G(i)约等于L(i,k)。最简单的方式是每个PDAF像素位置独立做线性回归计算均值比G(i) sum_k L(i,k) / sum_k R(i,k)但这样的结果容易受个别噪声帧影响。更稳的做法是取中位数或者去掉最大最小各5%的数据再做均值。我在实践中发现取中位数对应对焦失败场景的鲁棒性更好因为那些VCM失步的帧会造成统计离群值。Gainmap计算完后不能急着写死。要做一次验证重新在均匀光照下采集一批新数据把补偿后的左右像素残差计算出来。如果残差的均值超过原始信号的1%说明Gainmap拟合有问题需要检查光源均匀性或者sensor散热干扰。验证中发现一个很典型的坑很多工程师在算完Gainmap之后直接把系数写进driver就结束不做“暗电流补偿”。在长曝光、高增益的暗光场景下sensor自身的暗电流会在左右像素上产生不同的固定偏置这个偏置会叠加在光信号上导致Gainmap在暗光下严重高估。如果在标定中发现暗场景下PDAF容易误判方向要特别留意是不是这个原因。暗电流的补偿方式是在完全遮光、相同曝光参数下采集一帧纯噪声数据记为D(i)。修改Gainmap的计算公式为G(i) sum_k (L(i,k) - D_L(i)) / sum_k (R(i,k) - D_R(i))这样得到的增益系数才是真正意义上的“光响应增益差”暗光下的补偿效果会好很多。3.4 DCC曲线拟合与分区建模DCC拟合是本流程中最需要细心的一步。我们前面说过PD值到defocus的映射不一定是线性的所以在获取足够多的采样点后通常的做法是用分段线性插值来拟合整条曲线。具体操作是把VCM的整个行程等分成若干段比如16段或32段在每个段的端点位置精确采集PD值。采集方法要先对焦上目标然后手动让VCM越过合焦位置正负10%的区间记录对应的PD值。连续的PD-defocus曲线会呈现一个典型的S形中心区域斜率最大两端趋于饱和。在中心区域设置更密的采样点有助于提高对焦收敛精度。拟合结束后还需要做“线性化检查”。把拟合出的DCC曲线反向映射看PD值到defocus的变换是否依然保持单调性。如果不单调说明某个采样点采集时VCM没稳定或者标板表面有局部反光。这种时候千万不能图省事直接平滑数据因为单调性是PDAF算法能正确判断方向的数学前提。我的经验是遇到多个采样点违背单调性直接重测更可靠。还有一个值得说明的点DCC曲线的中心区域斜率决定了算法在对焦临界区域的灵敏度。斜率太小算法会感觉“对焦已经完成了”而提前停止斜率太大算法在临界点附近容易过冲抖动。最好的情况是让中心区域斜率保持在0.8到1.2之间这是实际落地时比较安全的区间。3.5 参数写入与端到端验证参数计算完毕以后就可以写进sensor的驱动或ISP的寄存器配置了。Gainmap通常放在sensor驱动的PD校准区域或者ISP的统计模块DCC曲线一般放在AF算法的配置文件中有些平台支持分场景多组DCC比如normal模式、macro模式、infinity模式各存一组。写入参数后要做四件事来验证效果。第一在近、中、远三个距离分别做对焦测试记录从触发对焦到锁定焦点的时间必须小于产品规格要求。第二反复对焦同一目标10次看对焦位置是否稳定在一个小范围内如果不稳定说明DCC曲线在某个区域斜率过陡、增益太大。第三在暗光和亮光下分别测试确保Gainmap动态范围覆盖到位。第四用手遮挡镜头一半后放开模拟目标切换场景观察算法是否能够快速收敛到新目标的距离上。这四轮验证里最容易暴露问题的就是第二项对焦稳定性。如果你发现同一个目标的最终位置出现了明显的来回抖动先不要急着去调DCC回头检查一下Gainmap的逐像素一致性。很多时候抖动不是DCC斜率的问题而是某几个PDAF像素的Gainmap错误导致统计结果被带偏。4. 常见问题与排查技巧实录我踩过的那些坑标定PDAF和写普通代码不一样它的问题是“层叠式”的——你可能费了很大力气调好了Gainmap结果发现DCC还是要重新拟合等你重新拟合了DCC又发现边缘PDAF区域的精度不够必须回去改标定环境。下面是把我在实际项目中遇到的典型问题整理成表供大家快速对照。问题现象最可能原因排查方向解决建议对焦来回抽动、无法锁定DCC中心区域斜率过陡检查临界区间的PD-defocus映射是否精度足够在合焦点附近加密采样点降低中心区域增益近距离对焦慢VCM行程末端非线性检查DCC长距离端是否饱和增加近距离采样点尝试分段曲线分离标定远距离对焦失败无限远PD值饱和检查镜头是否真正到达远焦位置确认VCM infinity位置寄存器值必要时重新校准暗光下对焦误判方向暗电流未补偿检查Gainmap在低亮度下的残差增加暗电流标定使用去偏置后的公式同一目标多次对焦位置漂移特定PDAF像素的Gainmap异常检查逐像素残差矩阵重新采集均匀光照数据单独修正异常像素系数画面边缘区域对焦不准边缘单分区DCC不准检查边缘PD值分布增加画面分区建议至少5X5分区拟合独立DCC4.1 对焦来回抽动干扰来自身边抽动问题很经典尤其在VCM驱动电流比较大的模组上。我遇到过一次很“邪门”的现象标定台上所有指标都正常一装到整机里就开始来回抽动。排查到最后发现是模组的FPC排线布局靠近VCM驱动走线驱动电流瞬间变化时产生了电磁干扰影响了PD值采集的模拟信号。重新走线后问题消失。建议大家在遇到“实验室正常、装机异常”的时候先检查一下模组线缆的屏蔽和走向再去动算法参数。这类干扰问题靠参数是调不好的只会越调越乱。4.2 暗光下的方向误判暗光下的误判很多情况是真冤枉了算法问题出现在sensor的噪声模型上。PDAF左右像素的输出差值本身就包含了几种噪声光子散粒噪声、读出噪声、暗电流散粒噪声。在暗光低曝光条件下读出噪声占主导它不服从零均值高斯分布特别是列并行ADC方案中每一列的偏置电压略有不同会直接导致特定列区域内的左右像素输出差出现系统性偏移。解决方法是在标定流程中增加一个“噪声底部”的数据采集——在完全遮光的条件下用和实际场景相同的曝光时间和增益采集一段数据把各像素的平均噪声底部作为偏置减去。很多SDK里把这个功能叫做PD dark calibration或者black level calibration不同平台叫法不同但作用都是一样的给PD值做一次“零位校正”。4.3 多片模组一致性问题量产阶段最常见的抱怨是“这批模组里随机抽几片PDAF效果各不相同”。这个问题的本质在于VCM马达的灵敏度个体差异、镜头组装的倾斜公差、sensor封装应力的释放程度都会让Gainmap和DCC产生差异。如果在产线上只标定一片“黄金模组”再把参数复制到所有模组效果必然打折扣。我的建议是量产标定至少要做到两件事一是在产线上增加一个简单的Gainmap标定工位用标准光源和灰板自动计算每片模组的Gainmap写入设备端非易失存储二是对DCC做抽检每批次至少抽5片做全流程标定统计DCC曲线斜率的方差如果方差超过±10%就要反馈给镜头或马达供应商要求他们改善一致性。4.4 RV1126B上的一个具体排查案例最后分享一个发生在RV1126B平台上的具体案例这个对参考价值帮助最大。现象是PDAF在10cm近景时对焦速度明显变慢偶尔会拉到整个VCM行程走一遍才回来。我一开始怀疑是DCC近距离段拟合不准但重新标定后问题依旧。后来我直接在ISP的raw域dump了PD区域的像素数据发现一个细节近景对焦时镜头在某个VCM位置附近PD值的变化率明显下降几乎进入了一个“平台期”。追查原因是镜头在近物距时光线透过镜头的入射角度发生了明显变化导致左右子像素的遮蔽结构产生的PD值不再随离焦量线性变化。解决方法是把近物距区间单独做一组DCC拟合并和正常物距的DCC做平滑过渡而不是用一条曲线硬套整个行程。改完之后近景对焦速度从原来的2000ms降到了400ms以内。这个案例给我们的提醒是任何一条DCC曲线都有自己的适用范围不要指望一条曲线覆盖从微距到无穷远的全部场景。如果你的产品经常在近景和远景之间切换至少要有两到三组DCC做分场景切换。结尾做PDAF标定这件事表面上是改参数、跑曲线、写寄存器实际上做的是“光学、半导体、机械、算法的交叉拧螺丝”。那块PD像素阵列可能是整个sensor里最敏感的区域标定环境的一点温度漂移、VCM的一丝机械滞回到最后都会转变成实拍画面里的一次对焦失败。我个人实际操作中最大的体会是不要迷信vendor的默认参数也不要跳过任何一步看起来“可以凑合”的校准环节。Gainmap不准确后面优化再多DCC都是白搭DCC不拟合当前模组对焦速度再快的算法也会被糟蹋成“拉风箱”。标定的每一步本质都是在为算法提供可靠的数据源把这个源头守住了后面的事才会顺。最后再分享一个小技巧。如果你在产线上做PDAF标定一定把每一次标定的原始数据PD值、VCM位置、温度、曝光参数全部留存下来最好能做到按模组SN号归档。一旦某批模组在用户端出现对焦故障你回查数据要比重新架起标定台快得多。这个习惯救过我很多次也建议你从现在开始养成。