Commit ccd8de6b authored by 尹洪福's avatar 尹洪福

初始提交:S3/S5 新对焦架构设计文档

包含:
- 主架构文档(s3 s5 新对焦架构.md)
- 会议简版(新对焦架构-会议简版.md)
- 关键信息记录、业界调研、旧架构参考
- libcamera 参考资料
- 参考图片
Co-Authored-By: 's avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
parents
# Raspberry Pi 自动对焦(AF)算法类总结
## 核心功能
这是一个**自动对焦控制算法实现**,支持两种对焦方式:
- **PDAF**(相位对焦) - 快速、精确
- **CDAF**(对比度对焦) - 备用方案
---
## 主要类结构
### 配置参数类
```
CfgParams
├── RangeDependentParams (3个范围)
│ ├── AfRangeNormal - 正常范围
│ ├── AfRangeMacro - 微距范围
│ └── AfRangeFull - 全范围
└── SpeedDependentParams (2个速度)
├── AfSpeedNormal
└── AfSpeedFast
```
---
## 核心算法流程
### 1️⃣ **对焦模式** (mode_)
| 模式 | 行为 |
|------|------|
| **Manual** | 手动设置镜头位置,不自动对焦 |
| **Auto** | 触发一次扫描完成对焦 |
| **Continuous** | 持续监测场景变化并自动调整 |
### 2️⃣ **扫描状态机** (scanState_)
```
Idle ──触发──> Trigger ──PDAF启动──> Pdaf
掉帧或场景变化
Coarse1 ↔ Coarse2 ──> Fine ──> Settle ──> Idle
(粗扫) (粗扫) (细扫) (稳定)
```
### 3️⃣ **核心处理流程**
```cpp
prepare() ──PDAF数据处理──> doAF() ──┬─> doPDAF() (闭环控制)
└─> doScan() (扫描)
process() ──获取CDAF统计──> updateLensPosition()
```
---
## 关键算法
### **PDAF处理** `doPDAF()`
1. 应用环路增益
2. 在连续模式下抑制抖动(低置信度时)
3. 在触发模式下逐步缩放(确保稳定)
4. 施加最大滑动率限制
5. 更新目标镜头位置 `ftarget_`
### **CDAF扫描** `doScan()`
1. 记录扫描数据(位置、对比度、相位、置信度)
2. **粗扫**(`Coarse1/2`):
- 如果首次扫描未括住峰值,反向扫描
- 对比度下降时结束
3. **细扫**(`Fine`):
- 在粗扫峰值附近细化搜索
- 用抛物线插值找到最优位置
4. **稳定**(`Settle`):
- 验证对焦质量
- 成功→`AfState::Focused` / 失败→`AfState::Failed`
### **早期终止** `earlyTerminationByPhase()`
- 利用两个PDAF样本直接计算零相位位置
- 避免完整扫描,加速对焦
---
## 权重系统 `computeWeights()`
支持**窗口化对焦**:
- 计算AF窗口内各统计区域的权重
- 融合多个窗口(最多10个)
- 默认使用中央1/2宽、中央1/3高的区域
---
## 场景变化检测
### 连续模式(CAF)
```cpp
if (对比度变化 > 25% || 颜色变化 > 25%) {
sceneChangeCount_++;
if (sceneChangeCount_ >= retriggerDelay) {
startProgrammedScan();
}
}
```
---
## 红外检测 `getAverageAndTestIr()`
- 检查RGB是否在4:5比例范围内
- 若>50%区域都是"灰色"(RGB相等) → 红外补光
- IR模式下禁用PDAF置信度(避免干扰)
---
## 镜头控制
| 方法 | 功能 |
|------|------|
| `updateLensPosition()` | 应用斜率限制,从未知位置初始化 |
| `setLensPosition()` | 手动设置镜头位置(手动模式) |
| `getLensPosition()` | 获取当前镜头位置(单位:屈光度) |
| `getLensLimits()` | 获取硬件限制范围 |
---
## 关键状态变量
| 变量 | 含义 |
|------|------|
| `ftarget_` | 目标焦距(屈光度) |
| `fsmooth_` | 实际镜头位置(应用斜率限制后) |
| `prevContrast_` | 上一帧对比度 |
| `phase`, `conf` | PDAF相位和置信度 |
| `scanData_` | 扫描过程中的历史数据 |
---
## 特点总结
✅ **混合对焦**: PDAF优先,失败时自动回退到CDAF
✅ **自适应**: 可配置速度、范围、参数
✅ **鲁棒性**: 场景变化检测、IR补光识别、掉帧恢复
✅ **灵活控制**: 支持手动/自动/连续三种模式
✅ **高效**: 早期终止、抛物线插值加速收敛
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
对焦流程:
数据源:
pd -> pd 计算模块 -> 算出目标位置的 pd 值。之前是分成 5 block,算 5 个值。
由于性能限制 很多 pd raw 处理放在了 stream 中。并且只下采样并裁剪出来 一个大致 384x144 区域。
人脸模式、AFS 模式的 pd 计算就只算一个区域。并且使用的是另外的 pd raw
af stats -> 获取 fv 模块 -> 计算 block 对应 fv 值。
对焦触发检测:
根据 block lsit,选择一个 最近的 block,然后根据 pd 值的方差,fv 的变化等信息。决策是否触发对焦。
如果 检测到 pd 不可信。则还有个 caf 的逻辑。
不过 最新版本是准备替换 pd 算法的。 使用 ai计算 pd。所以置信度这个指标暂时没有了。
对焦执行:
也分为 2 个,一个就是 pd 可信的时候 pdaf
另外一个是 caf 模糊搜索。
对焦执行完成之后 会将结果发送给 对焦触发检测模块,更新这次对焦的结果,包括最终的 pd 值, fv 值。
镜头控制:
控制值 pulse 范围 0~1024 near 端应该再 800~900 far 端在 300~400
已知问题:
1 首先就是 dcc 模块,之前搞错了 用错了。引入了物距的概念。其实应该是 分段 dcc 常量。只用 pulse 值控制就好了。这样也好控制分步。
2 pd 计算结果准度不够,还有波动,以及 pd 0 并不是准焦点这个奇怪的现象。可能是 pd 取值问题或者其他我没想到的吧。
3 不管是传统 pd 算法还是 aipd 都有一定延迟 aipd 计算耗时更长。镜头推动之后的 pd 值相应是有延迟的。
4 fv 的计算逻辑写的比较乱,坐标的转换和映射难理解。
5 模糊搜索的处理也是填坑的处理,这样处理感觉并不好。并且只能向远处搜。其实就是规避了一下镜头在 near 处时 画面模糊 pd 失效,卡死在 near
以及可以在 pd 完全失效的时候 执行一次 caf 对焦。而且还要避免反复执行。
6 之前的对焦执行模块很多逻辑也不够完善。比如步长规划还依赖物距、caf 阶段的 fv 上升下降判定逻辑、pd 延迟的处理等等。不过好在多次调试,对焦效果还算 OK 了。
7 对焦触发检测的逻辑也不够好。经常会有不应该有的触发。虽然实际效果看着还行。
想到再补充吧
其他:
1 是否考虑,这次重新设计的架构,同时兼容 s3 s5(拍照相机)。整体的 数据流处理、PD算法、FV算法、触发模块、执行模块、对焦模式、对焦框模式、统一起来。
这样收益比较高,同时优化了 s3 的对焦效果,也开发推进了 s5 项目的对焦模块。同时统一的 pdaf 代码后续维护更友好。
而且 2 个项目的对焦,本质上差异其实不大。pdaf 作为一个专门处理对焦的 lib,适当的做好接口兼容问题应该不大。
2 这个 lib 的作用简化描述本质上就是:接受 pd fv 数据流,根据对焦模式和对焦框设置等配置执行推镜头操作。输入数据 -> 执行控制镜头。
3 pdaipd 算法的计算逻辑要做的兼容性强一点。不能出现之前那种 384x144 5 block 这种逻辑。
#开发计划:
# 1 首先这个对焦算法其实不算复杂。 我建议保持 大致架构不变,主要是:
# AFManager WindowManager ProcessModule PolicyModule ActuateModule StateMachineModule 这些定义应该是比较合理的。
# 当然你有好方案也可以改。
# 2 然后我们从头开始,一个一个模块完成,尽量做到每个模块完成后独立验证,单元测试。
# 3 初步计划是:
# 1 先修改 dcc 相关的。移除物距概念。这个比较独立。
# 2 测试 aipd 算法准度。优化 pd 值的处理。
# 3 窗口的管理,fv 获取优化下。更好理解点。
# 4 开发测试对焦触发模块。这里整体的触发检测算法都可以重新设计开发。这里本身也不算复杂。对焦效果想要好,肯定触发的要准,不能乱触发、不触发。
# 5 模糊搜索模块我没想好...
# 6 对焦执行模块的重新设计和开发,如何推镜头、步长规划和限制、如何通过 fv 以及 pd 信息将镜头推到最清晰为止。
开发计划:
架构:保持大致架构不变(AFManager / WindowManager / ProcessModule / PolicyModule / ActuateModule / StateMachineModule
方式:从头开始,逐模块完成,每个模块独立验证。
Step 1: DCC 简化
移除物距概念,改为分段 DCC 常量:deltaPulse = pd × dcc
dcc 大部分区间 80,近焦端更大(80~150)
最独立,改完立刻可验证。
Step 2: FV 获取优化
理清坐标映射,简化计算逻辑。
后面触发和执行都依赖 FV,先搞干净。
Step 3: AIPD 准度测试
写测试工具,固定不同位置采集 PD 值,画 PD-pulse 曲线。
摸清 PD=0 偏移规律。优化 PD 值处理。
Step 4: 触发模块重写
三种触发场景:
- 正常:PD 偏移 > 阈值,连续稳定 触发 PDAF
- PD 失效:连续 N 帧无有效 PD 触发 FV 爬峰(双向)
- near 端卡死:pulse near + PD 失效 强制往 far 方向爬峰
后续优化方向:focus peaking(单张图判断清晰/模糊)辅助触发。
Step 5: 对焦执行模块重写
PD 粗跳 + FV 验证精调。
步长规划基于 pulse(不依赖物距)。
PD 失效时降级为 FV 双向爬峰(替代之前单独的模糊搜索模块)。
Step 6: 集成测试 & 调优
批量场景测试(近→远、远→近、小幅移动、near端、低光等)。
af_summary.py 对比历史基线。
对焦架构:
┌─────────────────────────────────────────────────────────┐
│ ISP 硬件层 │
│ onPdFrameAvailable() onVideoFrameAvailable() │
└──────────┬───────────────────────────────┬──────────────┘
↓ ↓
┌──────────────────────┐ ┌─────────────────────┐
│ ProcessModule │ │ IQAModule │
│ (HPdProcessor) │ │ (ContrastAssessor) │
│ │ │ │
│ 模式分发: │ │ ISP AF统计 → FV值 │
│ ├─ STABILIZE (单点) │ │ H1/V1 低频 + H2/V2 │
│ ├─ FACE_TRACKING │ │ 高频 → 17×15 zone │
│ ├─ CONTINUE (5块) │ └─────────┬───────────┘
│ └─ AIPD (AI深度) │ │
│ │ │
│ PD计算 → 柯西滤波 │ │
│ → 时域滤波 → DCC转换 │ │
│ → 输出 PdData │ │
└──────────┬───────────┘ │
↓ │
┌──────────────────────────────────────────┴──────────┐
│ PolicyModule │
│ (SamplePolicy) │
│ │
│ 30Hz 轮询线程 │
│ ┌─────────────────────────────────────┐ │
│ │ TriggerComponent │ │
│ │ findBestBlock() → 选最优块 │ │
│ │ PdDetectUnit.detect(): │ │
│ │ ├─ PD 历史 (5帧) │ │
│ │ ├─ 方差 < 100 (稳定性) │ │
│ │ ├─ 方向一致性 │ │
│ │ ├─ 距离相关 DOF 阈值 │ │
│ │ └─ 累计帧数 ≥ triggerThreshold │ │
│ └──────────────┬──────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────┐ │
│ │ 触发决策 │ │
│ │ 有效PD + 偏移够大 → TYPE_CLEAR_PD │ │
│ │ 无有效PD(10帧) → TYPE_BLUR_CAF │ │
│ │ 否则 → TYPE_NONE │ │
│ └──────────────┬──────────────────────┘ │
│ ↓ │
│ FV 门控: shouldTriggerAgain() │
│ ├─ maxFv>2000 且 偏差<1% → 不触发 │
│ └─ |PD|<0.08 → 不触发 │
└──────────────────────┬──────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ StateMachineModule │
│ │
│ WAIT_FOR_TRIGGER ──→ STATUS_FOCUSING │
│ ↑ │ │
│ └────────────────────┘ (onFocusFinish) │
│ │
│ 新触发到达时 mTriggerSeq++ 可打断当前对焦 │
└──────────────────────┬───────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ ActuateModule │
│ │
│ TYPE_BLUR_CAF ──→ CafActuator │
│ │ │
│ ├─ COARSE: 扫描路径, 记录FV │
│ ├─ FINE: 峰值附近精搜 │
│ ├─ PD_FOCUS: 可选PD微调 │
│ │ │
│ └─ 如果扫到有效PD → 转PDAF ──┐ │
│ ↓ │
│ TYPE_CLEAR_PD ──→ V2PdAfActuator │
│ │ │
│ ├─ APPROACH: PD大跳 │
│ │ 收集PD → 计算目标 → 跳转 │
│ │ |PD|<0.25 → 进CONVERGE │
│ │ │
│ ├─ CONVERGE: EMA自适应小步 │
│ │ speed=|pd|×3, clamp[0.3,0.75]│
│ │ |PD|<0.10 连续2帧 → 收敛 │
│ │ 超20步 → 放弃 │
│ │ │
│ └─ returnToBest() 兜底 │
└──────────────────────┬───────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ LensMotor + DccCalculator │
│ │
│ distance → DCC曲线(OTP/文件) → pulse → 电机驱动 │
└──────────────────────────────────────────────────────┘
\ No newline at end of file
⏺ 好的,信息很关键。我整理一下你提到的要点,确认我理解对了:
核心纠正
1. DCC 应该简化 — 不要物距概念,直接用分段 DCC 常量,pulse 值控制。这样步长规划也更直接。
2. PD=0 ≠ 准焦 — 这是个大问题,意味着不能简单用 |PD|<阈值 判收敛,需要其他手段(FV)确认。
3. AIPD 会替代传统 PD — 但延迟更大,且暂时没有置信度指标。
架构层面
4. PD 计算受限 — 只有 384×144 下采样区域,人脸/AFS 模式用另外的 PD raw,只算一个区域。
5. FV 坐标映射混乱 — 需要重写清晰化。
6. 模糊搜索是打补丁 — 本质问题是 near 端 PD 失效 + 画面模糊,需要更好的方案而不是单方向扫描。
几个问题想确认:
1. 分段 DCC 常量 — 你的意思是,PD 值直接乘以一个分段系数得到 pulse 偏移量?比如 PD=0.2 在某个 pulse 区间对应移动 N 个 pulse?
2. PD=0 不是准焦 — 偏差大概多少?是固定偏移还是随位置变化?
3. 帧率 — PD 帧和视频帧都是多少 fps?AIPD 计算延迟大概几帧?
4. 电机响应 — 发 pulse 指令后,大约多久到位?移动过程中 PD/FV 数据可用吗?
这些会直接影响新算法的设计。
1 你可以看 DccCalculator::mGlodenParamMap 的定义 dcc 应该是这样算的 deltaPulse = pd * dcc。我这个镜头 Pulse 范围只 0~1024 全程。 dcc 大致值应该是 80 左右。 你理解的应该对吧。
2 偏的到时不多。可能在 0.03~0.08 不过这里可能是由于 pd 计算和取值 导致的。不确定了。
3 视频 是可以 30 60 帧切换。 aipd 最快 25 帧左右吧,是刚优化的我还没测试。 之前的 20帧不到。
4 多久到位我不太确定 这个是 vcm 马达应该算快的 不过计算镜头到位 那 pd 帧的延迟也存在。比如推完镜头后 获取的 pd 值 可能要 2~4 帧才准确。
看完了。你的计划很清晰,我有几点想法:
整体架构
同意保持大框架不变(AFManager / ProcessModule / PolicyModule / ActuateModule / StateMachineModule),分工合理,不需要推翻。
对开发顺序的建议
你的顺序基本是对的,我微调一下:
┌──────┬───────────────┬────────────────────────────────────────────────────────────────────────────┐
│ 步骤 │ 模块 │ 说明 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 1 │ DCC 简化 │ 移除物距,改为 deltaPulse = pd × dcc常量。最独立,改完立刻可验证 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 2 │ FV 获取优化 │ 理清坐标映射,简化计算逻辑。后面触发和执行都依赖 FV,先搞干净 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 3 │ AIPD 准度测试 │ 写个测试工具,固定不同位置采集 PD 值,画 PD-pulse 曲线,摸清 PD=0 偏移规律 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 4 │ 触发模块重写 │ 基于清晰的 PD+FV 数据重新设计触发条件 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 5 │ 对焦执行重写 │ PD 粗跳 + FV 验证精调 │
├──────┼───────────────┼────────────────────────────────────────────────────────────────────────────┤
│ 6 │ 模糊搜索 │ 最后再想 │
└──────┴───────────────┴────────────────────────────────────────────────────────────────────────────┘
调整理由:FV 提到 AIPD 前面,因为 FV 是后面所有模块的基础(触发要用、执行要用、模糊搜索也要用),先把它搞清楚。AIPD 测试可以和 FV 并行,但 FV 优先级更高。
关于模糊搜索(第5点)
我的想法:不需要单独的"模糊搜索模块"。可以这样处理:
- PD 有效时:正常 PDAF 执行
- PD 失效时:对焦执行模块内部切换到 FV hill-climbing 模式(双向探测,不限于只往远处搜)
- 本质上是对焦执行模块的一个子策略,而不是独立模块
这样就不用纠结"什么时候触发模糊搜索"和"怎么避免反复执行"的问题了 — 它就是对焦执行的降级路径。
---
你觉得这个顺序和模糊搜索的思路可以吗?如果可以我们就从 DCC 简化 开始。
===========================================
业界 PDAF 方案调研 & 与 S3 系统的差距分析
===========================================
一、业界主流方案
1. Canon — Dual Pixel CMOS AF
- 每个像素分左右两个光电二极管,全像素参与 AF(百万级对焦点)
- 对比左右信号 → 算方向 + 偏移量 → 一步到位驱动镜头,不需要 hunting
- 二代加入深度学习主体识别,软件可升级
2. Sony — Fast Hybrid AF
- PDAF + CDAF 混合:693 PDAF 点 + 425 CDAF 点(A9)
- PDAF 粗定位 → CDAF 精调(确认 FV 峰值)
- AF 计算频率 60Hz,光圈 ≥F13 时 PDAF 失效自动降级为纯 CDAF
3. Qualcomm(手机 ISP)— DCC 分区标定
- 核心公式:Defocus = K × PD(K = DCC 系数)
- 传感器分 9×7 = 63 个 block,每个 block 独立 K 值(因镜头场曲和 shading)
- 标定流程:已知距离拍图 → 对比 PDAF/CDAF 马达位置差 → 生成 K 值表
- K 可运行时动态更新(根据对焦结果微调)
4. 学术界通用模式 — Hybrid Coarse-Fine
Phase 1: PDAF 粗跳 → PD×DCC → 目标 pulse → 大步跳转
Phase 2: CDAF 精调 → 到达附近后小步 FV 爬峰 → 锁定最清晰位置
大厂也有延迟,但处理方式不同
物理延迟是不可避免的,大厂也一样存在:
- 推镜头 → 镜头到位 → 新帧曝光 → 读出 → PD 计算
- 这个链路至少 2-3 帧,谁都跑不掉
但大厂和你的关键区别不是"有没有延迟",而是"需不需要多次迭代":
大厂做法:开环单次跳转 + 闭环验证
帧 0: 读 PD → 算出 deltaPulse → 一次性跳到目标位置
帧 1-3: 等镜头到位 + PD 更新(这个延迟大家都有)
帧 3: 读新 PD → 确认是否合焦
└─ 如果偏差小 → 完成(总共 ~3-4 帧,100-130ms @30fps)
└─ 如果还有偏差 → 再修正一次
关键:他们的 DCC 标定够准,第一次跳就能到 90%+ 的位置,所以通常只需要 1 次跳转 + 1 次确认。
你的 S3 现状
帧 0: 读 PD → DCC 不准 → 跳到大致位置(可能只有 60-70% 精度)
帧 3: 读新 PD → 偏差还挺大 → 再跳
帧 6: 读新 PD → 还差一点 → 再调
帧 9: 读新 PD → 差不多了
...可能要 3-5 次迭代
每次迭代都要吃 2-3 帧延迟,3 次迭代就是 9 帧 = 300ms,5 次就是 500ms。
所以真正的差距是:
┌──────────┬─────────────────┬──────────────────────┐
│ │ 大厂 │ S3 │
├──────────┼─────────────────┼──────────────────────┤
│ 单次延迟 │ 2-3 帧 │ 2-3 帧(一样) │
├──────────┼─────────────────┼──────────────────────┤
│ 迭代次数 │ 1-2 次 │ 3-5 次 │
├──────────┼─────────────────┼──────────────────────┤
│ DCC 精度 │ 90%+ │ 60-70% │
├──────────┼─────────────────┼──────────────────────┤
│ 总耗时 │ 3-4 帧 (~130ms) │ 9-15 帧 (~300-500ms) │
└──────────┴─────────────────┴──────────────────────┘
结论:延迟本身不是问题,迭代次数才是。而迭代次数取决于 DCC 标定精度。
三、对重构计划的启示
- Step 1 DCC:分段常量是合理折中,但要注意 center vs edge 差异。
后续可考虑引入运行时自适应修正(每次对焦完对比预期 vs 实际位置微调 DCC)。
- Step 2 FV:必须先修好 zoom 坐标 bug,否则后续所有依赖 FV 的模块都不可靠。
- Step 5 执行模块:PDAF 粗跳 + FV 精调是业界验证方案,方向正确。
关键是精调阶段要做真正的 FV 爬峰,不能只靠 PD 收敛。
- 触发模块:短期用数值阈值,长期可考虑引入简单的图像特征辅助判断。
四、参考实现
libcamera (Raspberry Pi) af.cpp — 目前公开最完整的 PDAF+CDAF 混合对焦实现
源码:https://github.com/raspberrypi/libcamera/blob/main/src/ipa/rpi/controller/rpi/af.cpp
关键借鉴:
- FV 精调用三点抛物线拟合(findPeak),3 个采样点插值出精确峰值,不需要逐步爬峰
- CDAF 扫描中如果 PD 恢复有效,可提前终止(earlyTerminationByPhase)
- PDAF 闭环是每帧微调(类 PID),但 S3 因 AIPD 延迟大不适合,改用大跳 + 抛物线精调
- 场景变化检测:对比度或颜色变化 >25% 触发重对焦
五、待实测项
□ FV 延迟:推镜头后几帧 ISP AF stats (FV) 才稳定?预期 1~2 帧,需实测确认
□ FV 峰值宽度:不同 pulse 位置下,FV 峰的半宽是多少 pulse?决定 Phase 3 step 大小
□ DCC 实际精度:Phase 1 粗跳后偏差多大?决定 Phase 2 是否必要
各大厂 PDAF 对焦方案总结
1. Canon — Dual Pixel CMOS AF
核心思路: 每个像素分成左右两个光电二极管(Dual Pixel),独立读出产生视差图像。
- 对比左右信号 → 信号一致 = 合焦,有偏差 → 算出方向 + 偏移量 → 一步到位驱动镜头
- 全像素参与 AF(百万级对焦点),不是只有少量专用像素
- 第二代(Dual Pixel CMOS AF II)加入了深度学习主体识别,软件可升级
- 关键特点:不需要 hunting(搜索),直接计算目标位置
2. Sony — Fast Hybrid AF
核心思路: PDAF + CDAF(对比度检测)混合。
- 693 个 PDAF 点 + 425 个 CDAF 点(A9 系列)
- PDAF 做粗定位(快速跳到大致位置),CDAF 做精调(确认峰值)
- AF 计算频率可达 60Hz(A9)
- 光圈 ≥ F13 时 PDAF 失效,自动降级为纯 CDAF
- 关键特点:两阶段策略 — PDAF 粗跳 + CDAF 精确收敛
3. Qualcomm(手机 ISP 平台)— DCC 分区标定
核心公式: Defocus = K × PD
- K 就是 DCC(Defocus Conversion Coefficient),不是全局常量
- 传感器区域被分成 9×7 = 63 个 block,每个 block 有独立的 K 值
- 原因:镜头场曲和 shading 导致中心和边缘的 DCC 不一样
- 标定流程:在已知距离拍图 → 对比 PDAF 和 CDAF 的马达位置差 → 生成 K 值表
- 标定系数 K 可以在运行时动态更新(根据实际对焦结果微调)
- 关键特点:DCC 是空间分区的,需要标定,且可以自适应修正
4. 学术界/通用方案 — Hybrid Coarse-Fine
多篇论文描述的共同模式:
Phase 1: PDAF 粗跳
PD → DCC 转换 → 计算目标 pulse → 大步跳转
Phase 2: CDAF 精调
到达附近后 → 小步搜索 FV 峰值 → 锁定最清晰位置
- PDAF 单次就能到 90% 的位置,但最后 10% 靠 FV 更可靠
- PD=0 不代表完美合焦(标定误差、场曲等),需要 FV 验证
- 有论文用贝叶斯估计做 PD→Defocus 映射,更鲁棒
---
对你 S3 项目的参考价值
┌─────────────────────────────────────────┬───────────┬─────────────────────────────────────────────────────────────────────────┐
│ 启示 │ 来源 │ 对应你的问题 │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ PDAF 粗跳 + FV 精调 是业界通用方案 │ Sony/学术 │ 你的 Step 5 方向完全正确 │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ DCC 不应该是全局常量,应该按区域标定 │ Qualcomm │ 你的 DCC 简化方案(分段常量)是合理的折中,但要注意 center vs edge 差异 │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ DCC 可以运行时自适应修正 │ Qualcomm │ 每次对焦完成后,对比预期 vs 实际位置,微调 DCC 系数 │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ PD=0 ≠ 合焦是已知问题,不是你代码的 bug │ 通用 │ 所有系统都需要 FV 验证最终合焦,不能只信 PD │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ 光线不足 / 光圈太小时 PDAF 会失效 │ Sony │ 你的触发模块需要有 PDAF→FV 的降级路径(Step 4 已规划) │
├─────────────────────────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────┤
│ Canon 强调"一步到位" │ Canon │ 说明 DCC 标定准的话,单次跳转就够了,不需要多步迭代 │
└─────────────────────────────────────────┴───────────┴─────────────────────────────────────────────────────────────────────────┘
最核心的参考:Sony 的 Hybrid AF 策略和你的 Step 5 设计几乎一致 — PDAF 粗跳到目标附近,然后 FV 微调收敛。这是业界验证过的方案。
---
想深入讨论哪个方面?比如:
- Qualcomm 的 DCC 分区标定思路要不要借鉴到你的 DCC 设计里?
- Sony 的双阶段收敛策略具体怎么映射到你的 ActuateModule?
- DCC 运行时自适应修正要不要加入计划?
Sources:
- https://www.usa.canon.com/learning/training-articles/training-articles-list/canon-autofocus-series-dual-pixel-cmos-af-explained
- https://www.abelcine.com/articles/blog-and-knowledge/tech-news/the-core-technology-behind-canons-dual-pixel-af
- https://www.sony-semicon.com/en/technology/camera/index.html
- https://www.topteks.com/blog/sonys-fast-hybrid-af-merging-precision-and-swiftness-in-autofocus/
- https://patents.google.com/patent/US10387477B2/en
- https://patents.google.com/patent/US10313579B2/en
- https://www.researchgate.net/publication/305421139_Hybrid_Auto-Focusing_System_Using_Dual_Pixel-Type_CMOS_Sensor_With_Contrast_Det
ection_Algorithm
- https://blog.reikanfocal.com/2023/05/how-it-works-on-sensor-phase-detect-autofocus/
- https://openaccess.thecvf.com/content_ECCV_2018/papers/Abdullah_Abuolaim_Revisiting_Autofocus_for_ECCV_2018_paper.pdf
- https://patents.google.com/patent/CN106556960B/en
✻ Cogitated for 1m 49s
\ No newline at end of file
# S3/S5 新对焦架构 — 会议简版
## 一、目标
- 对焦速度:200-300ms(当前 400-700ms)
- 稳定收敛,无来回震荡
- S3/S5 统一一套 pdaf lib,降低维护成本
- 可调试:运行时调参,不依赖重编译
---
## 二、pdaf lib 定义
> **接受 PD 和 FV 数据流,根据对焦模式和对焦框配置,决策并执行推镜头操作。**
核心三段式:**输入 → 决策 → 执行**
```
┌─────────────────────────────────────────────────────────────────┐
│ pdaf lib │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ 输入 │ │ 决策 │ │ 执行 │ │
│ │ │ │ │ │ │ │
│ │ ProcessModule│──→│ PolicyModule │──→│ ActuateModule │ │
│ │ ├ PdProcessor│ │ (触发判定) │ │ (镜头控制) │ │
│ │ ├ FvProcessor│ │ │ │ │ │
│ │ └ TofProc...│──→│ WindowManager│ │ PdafActuator │ │
│ │ (预留) │ │ (目标选择) │ │ CdafActuator │ │
│ └─────────────┘ └──────────────┘ └────────┬────────┘ │
│ │ │
│ 配置输入: StateMachine ──┘ │
│ 对焦模式 / 对焦框 / 平台参数 │
└───────────────────────────────────────────────────┬─────────────┘
电机控制指令输出
```
| 阶段 | 模块 | 职责 |
|------|------|------|
| **输入** | ProcessModule(PdProcessor + FvProcessor + TofProcessor预留) | 接收 PD/FV 数据流,统一输出 12×6 zone 数据 |
| **决策** | PolicyModule + WindowManager | 选择对焦目标,判定是否触发对焦、用哪种策略 |
| **执行** | ActuateModule + StateMachine | PD 粗跳 + FV 精调,驱动镜头到位 |
### PDAF 决策链路
```
PD measure — PD 采集
temporal filter — 时域滤波(多帧平滑去噪)
confidence check — 置信度评估(方差、方向一致性、多 zone 投票)
dead zone check — 动态合焦容忍区(依赖 confidence + texture)
trigger decision — 触发决策(四层门控)
lens move — 推镜头(粗跳 + 精调)
```
---
## 三、输入层设计
### PD/FV 统一 12×6 zone,零坐标映射
PD 和 FV 共用 12×6 网格,独立存储、独立更新,用 zone index 关联。
```
PdZoneMap (12×6) FvZoneMap (12×6)
zone[i].pd zone[i].fv
zone[i].conf zone[i].timestamp
zone[i].timestamp
各自按自己帧率更新(PD ~25fps, FV ~30/60fps)
同一 zone index 直接取 PD 和 FV,零坐标映射
```
- PD 算法双轨:ZnccProcessor(传统)+ AipdProcessor(AI),统一输出 PdZoneMap
- zone 行列数配置化,不硬编码分辨率和 block 数量
### 坐标系与 zoom 处理
三个坐标系:
```
┌─────────────────────────────────┐
│ Sensor 全图坐标系 │ ← PD raw 始终基于此(不随 zoom 变化)
│ 3840 × 2160 │
│ │
│ ┌───────────────────┐ │
│ │ 可视区域坐标系 │ │ ← zoom 裁剪后的区域
│ │ (VPSS 输出) │ │ 人脸框、用户触摸点等基于此坐标系
│ │ │ │
│ │ AF stats 12×6 │ │ ← ISP 对可视区域重新配置 zone(暂定)
│ │ zone 覆盖此区域 │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────┘
```
**坐标转换链路**
```
VPSS 坐标(用户看到的画面)
↕ 偏移 + 缩放(zoom crop 参数)
Sensor 全图坐标
↕ zone 网格映射
Zone index (row, col)
→ 取 PD(需从全图 PD 中定位对应区域)
→ 取 FV(直接按 zone index,AF stats 已对齐可视区域)
```
**zoom 时的处理**
| 数据 | zoom 影响 | 处理方式 |
|------|----------|---------|
| AF stats (FV) | 重新配置到可视区域,zone 数量不变(12×6),单 zone 覆盖面积变小 | ISP 重配 w/h,保持 col=12, row=6 |
| PD raw | 始终全图,不随 zoom 变化 | 需要将可视区域映射到全图,裁剪出对应 PD 区域计算(备选:直接裁剪 PD raw) |
| VPSS 坐标(人脸框等) | 基于可视区域 | WindowManager 负责转换到 zone index |
> **注意**:zoom 后单 zone 内容变少,PD 计算精度可能受影响,需实测验证。
---
## 四、决策层设计
### 目标选择(WindowManager)
| 模式 | 选择策略 |
|------|---------|
| 人脸模式 | AI 人脸框 → 映射到 zone |
| 跟踪模式 | 跟踪目标框 → 映射到 zone |
| 手动选区 | 用户指定区域 → 映射到 zone |
| 中心点对焦 | 直接取中心 zone |
| 大区域自动 | 加权选择最优 zone |
### 触发决策(PolicyModule)— 四层门控
```
第一层:场景变化检测(FV 下降?PD 偏移?)→ 粗筛,大部分帧在此过滤
第二层:稳定性确认(连续 N 帧)→ 防误触发
第三层:PD 分析 → 触发 PDAF 或 FvScan
第四层:合焦门控 → 动态 dead zone,防重复触发
高置信+强纹理 → dead zone 小(精度高)
低置信+弱纹理 → dead zone 大(容忍偏差)
```
对焦抑制信号:AE/AWB/SAD 剧烈变化时暂停触发判定
---
## 五、执行层设计
### 为什么选「粗跳 + 精调」而不是「每步微调」
每步微调(每帧读 PD → 小步推镜头 → 等信号更新 → 再推)的问题:
- 每次推镜头后需等 2-4 帧 PD 信号才稳定
- DCC 精度不够时需多次迭代才能收敛
- 3-5 次迭代 × 每次 2-3 帧延迟 = 9-15 帧(300-500ms)
粗跳 + 精调的思路:
- **一次算好目标位置,直接大步跳过去**,只吃一次延迟
- 到位后用 FV 精调确认峰值,不依赖 PD 收敛
- 总共 1-2 次跳转 + 1 次精调,耗时大幅缩短
这也是业界通用方案(Sony Hybrid AF、libcamera PDAF+CDAF)。
### PD 稳定性 → 步长调节
粗跳前评估连续 N 帧 PD 稳定性:
- **stable**(+3, +3, +3):幅度方差小 → 正常步长
- **noisy**(+3, +1, +4):方向一致但幅度波动 → 降低步长,避免过冲
### 对焦执行流程
```
Phase 1 (PD 粗跳) → Phase 2 (PD 修正, 可选) → Phase 3 (FV 三点抛物线精调)
```
- Phase 1:`deltaPulse = pd × dcc + 重力补偿 + 温度补偿`,大步跳到目标附近
- Phase 2:粗跳后 PD 偏差仍大时修正一次(最多 1 次,不无限迭代)
- Phase 3:三点采样 + 抛物线拟合求 FV 精确峰值(参考 libcamera findPeak)
**预期耗时:200-300ms(典型),最长 400ms**
### 对焦过程数据记录
每次对焦执行过程中,记录完整的 PD/FV 变化序列(独立于历史数据)。
- 粗跳分步过程中虽不等信号同步,但**被动记录**每帧的 PD/FV
- 推镜头过程中数据不稳(运动模糊、PD 延迟),**记录但不依赖**
- 价值:
- DCC 校准:对比"PD 预测位置" vs "FV 实际峰值位置",累积修正 DCC
- 方向验证:分步中 FV 趋势至少可判断方向对不对
- 调试诊断:dump 完整对焦曲线,排查问题
- 异常检测:PD 突然反向或 FV 骤降时可提前中止
### DCC 补偿:温度 & 重力
VCM 电机受重力和温度影响,同样 pulse 对应不同实际位置:
| 因素 | 影响 | 补偿方式 |
|------|------|---------|
| **重力**(影响较大) | 镜头朝上/朝下/水平,磁力抵抗重力不同 | `sin(angle) × K_gravity`,K 需标定 |
| **温度** | VCM 磁力随温度变化 | `(T - T_ref) × K_temp`,K 需标定 |
补偿加入 DCC 计算:`deltaPulse = pd × dcc + gravityOffset + tempOffset`
### 镜头到位等待
VCM 马达无位置反馈,发 pulse 后不知道何时真正到位。
方案:实测不同移动量下的到位耗时,按移动量分档固定等待。VCM 响应特性稳定,同样移动量到位时间基本一致。
| 移动量 | 等待时间 | 备注 |
|--------|---------|------|
| 小步(<30 pulse) | 待测 | 精调阶段 |
| 中步(30~100) | 待测 | 修正阶段 |
| 大步(>100) | 待测 | 粗跳阶段 |
### 状态机:新增 TRACKING 状态
```
IDLE → FOCUSING → TRACKING → IDLE
↑ │
└─────────┘ (PD 大变 → 重新对焦)
```
- FOCUSING:完整 Phase 1/2/3
- TRACKING:对焦完成后持续监测,小步跟随移动目标
- 解决移动目标场景反复全流程对焦的问题
---
## 六、S3/S5 统一设计
### 一套 lib,两个产品
pdaf lib 是纯算法层,不直接操作硬件。S3/S5 差异通过配置解决。
```
pdaf lib(统一算法)
├── ProcessModule / PolicyModule / ActuateModule / WindowManager / ...
└── 平台配置:
├── S3: pulse 0~1024, DCC 表, 12×6 zone, AIPD ~25fps
└── S5: pulse 范围, DCC 表, zone 配置, ...
```
### lib 职责边界
> 接受 PD 和 FV 数据流,根据对焦模式和对焦框设置等配置,输出镜头控制指令。
| 归 pdaf lib | 不归 pdaf lib |
|-------------|--------------|
| PD/FV 数据处理 | PD raw 采集 |
| 触发判定 + 执行算法 | ISP 配置 / 电机驱动 |
| 模式/对焦框管理 | 人脸/目标检测 AI |
### 调试模式
S5 硬件未就绪前,在 S3 上通过 FIFO 命令通道模拟 S5 操作:
```bash
echo "mode afs" > /tmp/pdaf_cmd # 切换模式
echo "key half_press" > /tmp/pdaf_cmd # 模拟快门半按
echo "motor goto 500" > /tmp/pdaf_cmd # 控制镜头
echo "dump pd" > /tmp/pdaf_cmd # 查看数据
```
量产后同样可用于现场诊断。
### 日志与数据上报
**问题**:之前排查对焦问题只能远程看用户电脑屏幕,耗时耗人力,无法定位根因。
**方案**
- **调试日志分级开关**:可运行时开关,不同级别(关键事件 / 详细数据 / 全量 dump)
- **debug 绘制开关**:OSD 叠加 zone 网格、PD 方向箭头、FV 热力图等,开发调试用
- **对焦流程记录**:每次对焦完整记录一条结构化数据,包含:
- 触发原因、触发时的 PD/FV 快照
- 各 Phase 耗时、步数、PD/FV 变化序列
- 最终结果(成功/失败)、最终 pulse 位置
- **数据上报**:对焦流程记录用于远程数据分析,测试和用户反馈问题时直接拉数据定位,不再依赖远程桌面
---
## 七、开发计划
### 原则:模块独立验证,再组合集成
每个模块先单独测试确认可靠,再组合。避免混到一起后定位不了问题出在哪。
| 模块 | 独立验证内容 |
|------|-------------|
| PD 计算 | 固定镜头位置,采集各 zone 的 PD 值,验证准度和一致性 |
| FV 计算 | 固定镜头位置,验证各 zone 的 FV 值是否反映真实清晰度 |
| DCC | 已知 PD → `deltaPulse = pd × dcc` → 推镜头 → 验证到位精度 |
| 触发模块 | 制造场景变化(遮挡/移物),验证触发是否及时、是否误触发 |
| 执行模块 | 手动触发对焦,验证 Phase 1/2/3 各阶段行为和耗时 |
| 坐标映射 | 不同 zoom 倍数下验证 zone 对齐、VPSS→zone 转换正确性 |
### 开发步骤
| 步骤 | 模块 | 说明 |
|------|------|------|
| 1 | DCC 简化 | 移除物距概念,`deltaPulse = pd × dcc常量`,最独立 |
| 2 | FV 优化 | 统一 12×6 zone,理清坐标映射 |
| 3 | AIPD 测试 | 测试工具采集 PD-pulse 曲线,摸清精度 |
| 4 | 触发重写 | 四层门控,信号逐个验证后组合 |
| 5 | 执行重写 | PD 粗跳 + FV 抛物线精调 |
| 6 | 集成测试 | 全链路调优 |
---
## 八、预期收益
| 指标 | 目标 |
|------|------|
| 对焦速度 | 200-300ms |
| 代码维护 | S3/S5 统一 lib |
| 数据可靠性 | PD/FV 独立更新、零坐标映射 |
| 可调试性 | 命令通道实时调参 |
| 移动目标 | TRACKING 小步跟随 |
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment