單元 10 · 曝光與自動曝光(AE)

sensor AEC/AGC

曝光三要素與 OV5640

要素register注意
快門時間0x3500–0x3503太長模糊/banding
類比增益0x350A–0x350B太高雜訊
數位增益平台/後處理最後手段

透過 V4L2 控制(正確方式)

V4L2 exposure controls
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0     # 關自動
v4l2-ctl -d /dev/v4l-subdev0 -c exposure=1000       # 曝光(依驅動單位)
v4l2-ctl -d /dev/v4l-subdev0 -c analogue_gain=200

單位與範圍看 --list-ctrls 的 min/max/step。

AEC/AGC 收斂

調適要點:OV5640 的 AEC 依場景亮度調整曝光與增益。除錯「太暗」先確認 AEC 是否到快門/增益上限——受限就補光,別硬調。

10.6 深入:曝光 register 與 Worked Example

讀目前曝光
for r in 0x3501 0x3502 0x3503; do h=${r:2:2}; l=${r:4:2}; echo -n "$r: "; sudo i2ctransfer -y 3 w2@0x3c 0x$h 0x$l r1; done

曝光值依感測器位元布局組合(單元 3 的 16-bit 位址規則)。正式流程用驅動/平台控制,這裡只是理解。

控制曝光
v4l2-ctl -d /dev/v4l-subdev0 -c exposure=1000
順序鐵則:先快門 → 類比增益 → 數位增益。用增益補曝光犧牲畫質。

10.8 深入原理:曝光 register 的位元展開與 AEC 演算法

OV5640 的曝光時間由 0x3500–0x3503 四個 byte 組合,且受 0x3503 的 bit 開關控制:

Register欄位說明
0x3500exposure[19:16]曝光最高位元
0x3501exposure[15:8]中位元組
0x3502exposure[7:0]低位元組
0x3503[1]manual exposure en1 = 手動(AEC 關)
0x3503[2]manual gain en1 = 手動(AGC 關)

曝光值單位是「感測器 row 週期」(1 line)。AEC 收斂策略:先加曝光到接近幀上限(避免運動模糊),再升增益,最後才用數位增益——這就是「先快門 → 類比 → 數位」鐵則的由來。

為什麼曝光上限有 ceiling:曝光太長會超過一幀的 row 週期 → 畫面變黑/幀率下降(frame rate 與 exposure 共享時間預算,單元 11 會算)。

10.9 完整 Worked Example:從讀取到設定的完整流程

步驟 1|讀目前曝光與增益
for r in 0x3500 0x3501 0x3502 0x3503 0x350a 0x350b; do
  h=${r:2:2}; l=${r:4:2}
  echo -n "$r: "; sudo i2ctransfer -y 3 w2@0x3c 0x$h 0x$l r1
done
步驟 2|用 V4L2 關 AEC/AGC
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0
v4l2-ctl -d /dev/v4l-subdev0 -c auto_gain=0
步驟 3|設定曝光並驗證
v4l2-ctl -d /dev/v4l-subdev0 -c exposure=2000
v4l2-ctl -d /dev/v4l-subdev0 -C exposure    # 讀回,應等於 2000
步驟 4|對比亮度變化
# 依序取 exposure=500/2000/6000 三幀,統計平均亮度
for e in 500 2000 6000; do
  v4l2-ctl -d /dev/v4l-subdev0 -c exposure=$e
  v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=e$e.yuv
done
完成標準:你能展示「曝光值↑ → 平均亮度↑」的單調關係,且讀回值與設定一致。若無單調關係,檢查 AEC 是否真的關了。

10.10 疑難排解決策樹:曝光問題

現象方向
太暗、調 exposure 無效AEC 沒關 / 已到曝光上限 → 補光
太亮、clipping曝光/增益太高或場景反光 → 降曝光
日光燈下橫條紋banding:曝光非 50/60Hz 倍數 → 曝光調成 10ms/8.3ms 倍數
設定後立刻跳回自動值另一層 3A 在跑(平台/後處理)→ 關所有 auto
亮暗閃爍AEC 收斂慢/oscillation → 鎖定曝光改調增益
常見錯誤與陷阱:① 曝光單位誤解(line 週期 vs µs)——先看 --list-ctrls 的說明;② 只調 exposure 不關 auto_exposure,設了等於沒設;③ banding 時猛拉增益——先對齊電源頻率倍數;④ 在低光硬拉曝光造成 motion blur——先想「要不要補光」。

10.11 練習

  1. 完成 Worked Example 四步,記錄每步輸出。
  2. 在日光燈下找出「不 banding」的曝光值區間。
  3. 用 SNR 觀念(單元 13)決定「這場景該用多少曝光/增益」。
  4. 寫一支 script:exposure_sweep.sh,自動掃曝光並輸出亮度曲線。

10.12 回顧練習

  1. 讀目前曝光/增益值。
  2. 手動設定並確認生效。
  3. 日光燈下檢查 banding。

10.13 進階真實情境 Worked Example:OV5640 在 RK3588 上的曝光時間預算計算

場景:Orange Pi 5 Plus(RK3588)接 OV5640,設定 30fps,計算最大可用曝光時間與 frame rate 的 trade-off。

步驟 1|確認 frame rate 與 row 週期
# OV5640 30fps, 1080p: 1125 lines/frame (含 blanking)
# row 週期 = 1/30/1125 ≈ 29.6 µs/line
# 最大曝光 ≈ 1125 lines (但會降到 ~15fps)
步驟 2|設定不同曝光值並量測實際 frame rate
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0
for e in 500 1000 2000 4000 8000; do
  v4l2-ctl -d /dev/v4l-subdev0 -c exposure=$e
  v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=30 --stream-to=exp${e}.yuv
  echo "exposure=$e fps=$(ls -l exp${e}.yuv | awk '{print $5}')"
done
設計決策:OV5640 的曝光時間與 frame rate 共享時間預算——曝光越長,可用 frame rate 越低。在需要固定 frame rate 的應用(如影像分析),必須計算「最大曝光上限」,超過就要改用增益補。

10.14 深入原理擴充:OV5640 曝光 register 的位元組序陷阱

OV5640 的曝光值由 0x3500–0x3503 四個 byte 組合,但位元組序容易搞錯:

Register欄位位元組序
0x3500exposure[19:16]最高 byte(先送)
0x3501exposure[15:8]中 high
0x3502exposure[7:0]中 low
0x3503exposure[3:0] + 控制位元最低 byte(後送)

容易忽略的邊界案例:OV5640 的曝光值是 20-bit(不是 16-bit),最高 4 個 bit 在 0x3500 的 bit[3:0]。如果你只讀 0x3501/0x3502(16-bit),會漏掉最高位元——在長曝光場景下曝光值被截斷到 65535 lines,實際應該是 1048575 lines(20-bit)。

除錯陷阱:如果你用 i2ctransfer 只讀 0x3501/0x3502 來判斷曝光值,在長曝光時會看到「曝光設定 8000 但讀回只有 65535」的假象——實際是 20-bit 被截斷。

10.15 診斷式疑難排解表

症狀可能原因解決方案
設定 exposure=8000 但讀回 65535只讀了 16-bit(0x3501/0x3502),漏掉 20-bit 高位讀全部四個 byte(0x3500–0x3503)組合成 20-bit 值
日光燈下曝光設 10ms 仍有 banding10ms 是 50Hz 倍數但 OV5640 的 banding filter 未啟用設 0x3013[7]=1 啟用 banding filter;或用 8.3ms(60Hz)
AEC 收斂後曝光跳動AEC 演算法在兩種曝光/增益組合間震盪設 AEC 收斂速度(0x350E)或鎖定增益只調曝光
低光場景拉增益後雜訊暴增類比增益超過安全上限先算 SNR 預算(單元 13),設增益上限
曝光設為 0 但畫面仍亮AEC 沒關或數位增益在補確認 auto_exposure=0 且 auto_gain=0

10.16 進階挑戰題

  1. 計算 OV5640 在 1080p@30fps 下的「曝光時間 vs frame rate」曲線,畫出圖表並找出最佳工作點。
  2. 實作一個 20-bit 曝光讀寫腳本(處理 0x3500–0x3503 的位元組序),驗證與 V4L2 exposure control 的一致性。
  3. 在日光燈下,找出 OV5640 在 50Hz 與 60Hz 電源下「不 banding」的曝光值區間,並解釋為什麼 banding filter 在某些值無效。

10.17 專案級 Worked Example:曝光預算與自動曝光調校專案

把單元 10 的曝光知識做成完整曝光調校專案:為你的應用(固定幀率、日夜模式)訂出曝光/增益上限、驗證 AEC 收斂、建立「場景 → 曝光參數」對照表。這是從「能拍照」走向「曝光可控」的關鍵專案。

階段 1|訂曝光預算(單元 10/11)
python3 - <<'EOF'
fps = 30
lines_per_frame = 1125        # 1080p@30fps 含 blanking
row_period_us = 1e6 / fps / lines_per_frame
max_exposure_lines = lines_per_frame * 0.9   # 留 10% blanking
print(f'row 週期 = {row_period_us:.1f} µs')
print(f'最大曝光 = {max_exposure_lines:.0f} lines ≈ {max_exposure_lines*row_period_us:.0f} µs')
EOF
階段 2|驗證 AEC 收斂(單元 5/10)
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=1
for i in 1 2 3 4 5; do
  sleep 1
  v4l2-ctl -d /dev/v4l-subdev0 -C exposure
  v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=aec_$i.yuv
done
階段 3|掃曝光曲線(單元 10.9)
v4l2-ctl -d /dev/v4l-subdev0 -c auto_exposure=0
for e in 500 1000 2000 4000 8000 16000; do
  v4l2-ctl -d /dev/v4l-subdev0 -c exposure=$e
  v4l2-ctl -d /dev/video0 --stream-mmap=3 --stream-count=1 --stream-to=exp_$e.yuv
done
階段 4|產出曝光參數表
python3 - <<'EOF'
import numpy as np
for e in [500,1000,2000,4000,8000,16000]:
    buf = np.fromfile(f'exp_{e}.yuv', dtype=np.uint8)
    y = buf.reshape(720,1280,2)[...,0]
    print(f'exposure={e:6d}  Y mean={y.mean():6.1f}  peak={y.max():3d}')
EOF
專案完成標準:你產出「曝光曲線」與「AEC 收斂時間」兩份數據,並訂出本應用的曝光/增益上限。之後任何場景都能快速套用——而不是每次從零摸索。

10.18 量測/驗證 SOP:曝光與頻閃驗證

步驟作法通過判據
1. 確認曝光單位--list-ctrls 看 exposure 的 min/max/step知道單位(lines 或 µs)
2. 驗證手動生效關 auto 設值後讀回讀回與設定一致
3. 驗證單調性掃描曝光觀察亮度亮度隨曝光單調上升
4. 頻閃測試日光燈下掃描曝光找 banding找出 50/60Hz 安全區間
5. AEC 收斂場景切換後量收斂時間記錄收斂到穩定所需的幀數
SOP 紀律:曝光調校的常見誤區是「不關 auto 就手動設值」——設了等於沒設。所有驗證第一步都是把 auto_exposure / auto_gain 明確關閉或開啟。

10.19 平台間對照:曝光控制

面向Orange PiRPi5Orin NanoThor
曝光 controlV4L2 exposureExposureTime(libcamera)SensorMode exp_timeSensorMode exp_time
單位lines(OV)µsµsµs
AEC感測器內建libcamera AENVIDIA AENVIDIA AE
增益上限自訂(後處理輔助)libcamera 限制NVIDIA 設定NVIDIA 設定
頻閃/banding手動控制libcamera 支援NVIDIA 支援NVIDIA 支援
核心領悟:「先快門 → 類比增益 → 數位增益」是物理鐵律,四平台皆然。Orange Pi 用 lines 為單位、RPi5/NVIDIA 用 µs——換算透過 datasheet 的 row 週期即可(1 line ≈ 29.6 µs @ 1080p30)。

10.20 互動式檢核清單:本單元進階驗收

checklist
- [ ] 我能完成曝光調校專案並產出曝光曲線。
- [ ] 我能換算 lines ↔ µs 的曝光單位。
- [ ] 我已訂出本應用的曝光與增益上限。
- [ ] 我能用掃描找出日光燈下的安全曝光區間。
- [ ] 我理解「先快門 → 增益 → 數位增益」的物理依據。
- [ ] 我能驗證 AEC 收斂並記錄收斂時間。

10.21 Register 位元級完整工作流

曝光控制是 ISP 調校的核心。以下是 OV5640 曝光控制的位元級完整序列:

步驟暫存器/位址位元欄位操作預期值
1. 開啟手動 AEC0x3503[0] AEC manuali2cset -y 3 0x3c 0x3503 0x080x08(手動)
2. 設定 exposure high0x3500[7:4] exposure[19:16]i2cset -y 3 0x3c 0x3500 0x01依場景
3. 設定 exposure mid0x3501[7:0] exposure[15:8]i2cset -y 3 0x3c 0x3501 0xF4500 lines
4. 設定 exposure low0x3502[7:0] exposure[7:0]i2cset -y 3 0x3c 0x3502 0x000x00
5. 設定 analog gain0x350A[1:0] gaini2cset -y 3 0x3c 0x350a 0x001× gain
6. 回讀驗證0x3503[0]i2cget -y 3 0x3c 0x35030x08
7. 取幀驗證v4l2-ctlv4l2-ctl --stream-mmap=1 --stream-count=1亮度符合預期
位元級關鍵: OV5640 的曝光是 20-bit(0x3500~0x3502),500 lines = 0x01F400。每 line ≈ 29.6µs @ 1080p30。先快門→類比增益→數位增益的順序是物理鐵律。

10.22 多層疑難排解決策樹

決策樹 A:AEC 收斂太慢

決策樹 A
AEC 收斂時間異常?
├─ 太慢(>500ms)
│  ├─ 增加 AEC 響應速度:0x3A0F/0x3A10
│  └─ 減少收斂範圍:0x3A1D/0x3A1E
├─ 太快(oscillating)
│  ├─ 降低響應速度
│  └─ 增加收斂範圍
└─ 不收斂 → AEC 被覆蓋
   └─ 檢查 0x3503 是否為 0x00(auto)

決策樹 B:日光燈頻閃

決策樹 B
影像有橫向條紋?
├─ 50Hz 頻閃 → 曝光非整數倍
│  ├─ 計算:1/50s = 20ms → 20ms / 29.6µs ≈ 676 lines
│  └─ 設定 exposure = 676 × N(N=1,2,3...)
├─ 60Hz 頻閃 → 同理
│  └─ 1/60s = 16.67ms → 563 lines × N
└─ 無頻閃 → 正常

10.23 量測驗證完整 SOP

步驟指令預期輸出判讀標準
1. 開啟手動 AECi2cset -y 3 0x3c 0x3503 0x08手動模式0x3503 = 0x08
2. 設定 exposurei2cset ... 0x3500 0x01; i2cset ... 0x3501 0xF4500 lines對應亮度
3. 設定 gaini2cset ... 0x350a 0x001× gain最低雜訊
4. 取幀v4l2-ctl --stream-mmap=1 --stream-count=1成功亮度符合預期
5. 掃描增益上限遞增 0x350a 值雜訊增加訂出增益上限
6. 測 AEC 收斂切換 auto(0x3503=0x00)收斂時間<500ms

10.24 四平台終極對照

面向Orange PiRPi5Orin NanoThor推薦
曝光單位linesµsµsµs先換算 row 週期
增益控制0x350A(整數)AnalogueGain(浮點)NVIDIA AENVIDIA AE依平台
AEC感測器內建libcamera AENVIDIA AENVIDIA AE底層→感測器
頻閃/banding手動控制libcamera 支援NVIDIA 支援NVIDIA 支援底層→手動
收斂速度可調(register)tuning 可調封閉封閉學習→Orange Pi

10.25 完整 Bring-up 專案 Checklist

checklist
- [ ] 完成「曝光調校專案」並產出曝光曲線
- [ ] 換算 lines ↔ µs(用 row 週期:1 line ≈ 29.6µs @ 1080p30)
- [ ] 訂出本應用的曝光上限(exposure_max lines)
- [ ] 訂出類比增益上限(gain_max)
- [ ] 測 AEC 收斂時間(< 500ms 為佳)
- [ ] 測日光燈頻閃:找安全曝光區間(676 × N lines @ 50Hz)
- [ ] 驗證「先快門 → 增益 → 數位增益」的順序
- [ ] 產出「exposure tuning log」(含 gain/exposure/time/quality)
- [ ] 確認 AEC 收斂後亮度穩定無 oscillation
- [ ] 測試「補光 vs 硬調」取捨並記錄
看完這單元你應該能說出:
  • 曝光三要素與 register。
  • V4L2 controls 控制曝光。
  • AEC 收斂與上下限。
  • 「補光 vs 硬調」取捨。

延伸閱讀