4. 对焦曲线(Focus Curve)标定:VCM马达特性、标定流程、曲线拟合与参数存储

对焦曲线标定,说白了就是给镜头马达画一张「位置-清晰度」的地图。没有这张地图,AF算法就像蒙着眼睛走路——你永远不知道马达该转多少步才能对上焦。

我刚开始做RK平台相机时,就吃过这个亏。当时图省事,直接用了一组公版曲线,结果某款模组在微距端死活拉风箱。后来一查,VCM马达的磁滞效应把曲线拉偏了将近15%。从那以后,我再也不敢跳过标定这一步了。

4.1 VCM马达特性与标定必要性

VCM(音圈马达)本质上是个线性电机,靠电流驱动弹簧-透镜组移动。理想情况下,电流和位移是线性关系。但现实中有三个坑:

  • 磁滞效应:同一个电流值,从近端往远端走和从远端往近端走,透镜位置不一样。差值通常在5%~20%。
  • 弹簧非线性:在行程两端,弹簧力变化导致电流-位移曲线弯曲。
  • 个体差异:同一批次马达,阻尼、灵敏度也有±10%的波动。

核心结论:每颗模组必须单独标定,公版曲线只能用于原型验证。

我见过一个团队,量产时没做个体标定,结果1000台设备里有80台微距对焦不准。返工成本够买三套标定设备了。

4.2 标定流程:从硬件搭建到数据采集

标定流程分五步走。我习惯用下面这张图来跟硬件同事对齐:

VCM对焦曲线标定流程 1. 硬件准备 2. 行程扫描 3. 清晰度评估 4. 曲线拟合 5. 参数存储 关键说明 • 硬件准备:固定模组、安装标板、设置光源 • 行程扫描:从0到1023步进,正反双向各扫一次 • 清晰度评估:使用Sobel/Laplacian算子计算MTF值 • 曲线拟合:分段线性或多项式拟合,消除噪声

4.2.1 硬件准备

你需要:

  • 一个稳定的光源(色温5000K±200K,照度1000lux以上)
  • 一块高对比度标板(ISO 12233或自制的黑白条纹板)
  • 模组固定夹具(确保镜头光轴与标板垂直)
  • RK平台开发板(串口或ADB连接)

我的经验:标板距离取3个典型值——远景(3m以上)、中景(50cm)、微距(最近对焦距离)。每个距离扫一次,能覆盖大部分使用场景。

4.2.2 行程扫描与数据采集

扫描策略我推荐「双向扫描+过采样」:

// 伪代码:双向扫描逻辑
for (direction in [FORWARD, BACKWARD]) {
    for (step = 0; step <= MAX_DAC; step += STEP_SIZE) {
        set_vcm_position(step);
        delay(10);  // 等待马达稳定
        mtf_value = calculate_mtf(current_frame);
        record(direction, step, mtf_value);
    }
}

为什么要双向?因为磁滞效应。正向和反向的峰值位置可能差几十个DAC步。我建议步长取4~8个DAC码值,太粗会漏掉峰值,太细浪费时间。

4.3 曲线拟合:把离散点变成可用曲线

原始数据是一堆离散的(DAC, MTF)点,直接拿来用会有两个问题:

  • 噪声点导致峰值误判
  • 采样间隔导致精度不够

所以需要拟合。我常用的方法有三种:

方法 适用场景 优点 缺点
分段线性插值 快速原型、低算力平台 计算量小,实现简单 曲线不光滑,导数不连续
高斯拟合 单峰清晰度曲线 峰值定位准,抗噪好 多峰场景失效
多项式拟合(3~5阶) 通用场景 灵活,可处理多峰 阶数选择依赖经验

我个人习惯用分段3阶多项式。把行程分成远景段、中景段、微距段,每段单独拟合。这样既保证了精度,又避免了高阶多项式的龙格现象。

注意:拟合时一定要剔除异常点。我遇到过标板上有灰尘导致MTF值异常偏低的情况,如果不剔除,拟合出来的曲线峰值会偏移10个DAC以上。

4.4 参数存储:数据格式与校准文件

拟合完成后,需要把曲线参数存到设备里。RK平台通常用两种方式:

  • OTP(一次性可编程):模组出厂时写入,不可修改。适合量产。
  • 校准文件(.bin或.json):放在文件系统里,可更新。适合开发调试。

我推荐的数据结构如下:

// 对焦曲线参数结构体
typedef struct {
    uint16_t dac_min;           // 最小DAC值
    uint16_t dac_max;           // 最大DAC值
    uint16_t peak_dac_forward;  // 正向峰值DAC
    uint16_t peak_dac_backward; // 反向峰值DAC
    float    poly_coeff[4];     // 3阶多项式系数 [a0, a1, a2, a3]
    uint8_t  segment_count;     // 分段数
    uint16_t segment_boundary[4]; // 分段边界DAC值
    float    segment_coeff[4][4]; // 每段多项式系数
    uint16_t crc16;             // 校验和
} FocusCurveParams;

存储建议:OTP空间有限(通常只有512字节),所以参数要压缩。我一般只存分段边界和系数,峰值DAC在运行时实时计算。CRC校验必须加,否则OTP读出错误会导致AF完全失效。

4.5 避坑指南:我踩过的三个坑

  1. 温度漂移:VCM马达的线圈电阻随温度变化,导致同样DAC值下磁场强度不同。我曾经在冬天标定完,夏天发现对焦不准。解决方案是加温度补偿系数,每10°C修正一次曲线。
  2. 老化效应:弹簧用久了会疲劳,曲线会缓慢偏移。量产时建议做加速老化测试,确认100万次动作后曲线偏移量在允许范围内。
  3. 模组批次差异:不同批次的VCM马达,灵敏度可能差20%。我后来在产线加了抽检机制,每批次抽10颗,如果偏差超过阈值就调整标定参数。