光電物理、Bayer、快門、OV5647 內部
| R | Gr | R | Gr |
| Gb | B | Gb | B |
| R | Gr | R | Gr |
| Gb | B | Gb | B |
Rolling shutter:逐列「曝光→讀出」,且曝光與讀出流水線重疊(前一行還在讀、後一行已開始曝光)。因此每一列的曝光時間起點不同:
| 型態 | 時序特性 | OV5647 | 典型問題 |
|---|---|---|---|
| Rolling | 逐列曝光+讀出(有時間差) | ✅ | 高速物體扭曲、banding |
| Global | 全像素同時曝光→同時讀出 | — | 電路複雜、成本高 |
banding:若曝光時間不是「光源頻率週期的整數倍」,各列累積的交流成分不同 → 明暗條紋。50Hz 光源 → 曝光須為 10ms 整數倍;60Hz → 8.33ms(單元 10 深入)。
周邊區塊:Timing Generator(列/幀同步)、PLL(由 XCLK 產生內部時脈)、I2C 介面(register 控制)、上電管理(PWDN/RESET)。
| 項目 | 規格 | 工程意義 |
|---|---|---|
| 解析度 | 2592×1944 | 全解析輸出 |
| 像素尺寸 | 1.4 µm | 決定感光面積與滿阱 |
| 光學格式 | 1/4" | 鏡頭像圈匹配 |
| 快門 | Rolling | — |
| ADC | 10-bit | RAW 位元深 |
| SNR max | ~36 dB | 低光性能指標 |
| 動態範圍 | ~68 dB | — |
| 類比增益 | 2× 內建 | 可再數位延伸 |
| MIPI | 2-lane CSI-2 | RPi5 接法 |
| I2C 位址 | 0x36(7-bit) | 單元 3 實作 |
| XCLK | 6–27 MHz(典型 24) | 單元 6 PLL |
一張圖的訊號鏈:
# 場景:低光,訊號只有 N=400 e-,read noise σr=8 σ_shot = √400 = 20 σ = √(400 + 64) ≈ 21.5 SNR = 20·log10(400/21.5) ≈ 25.4 dB # 若把類比增益×4(訊號也×4,雜訊也×4) # SNR 不變:25.4 dB(雜訊被等比放大) # 若改曝光×4(真實訊號 N=1600) σ_shot = √1600 = 40;σ = √(1600+64) ≈ 40.8 SNR = 20·log10(1600/40.8) ≈ 31.9 dB # +6.5 dB! # 結論:曝光勝過增益
rpicam-still --raw -o t.dng)。場景:你正在為一個低光監控專案決定 OV5647 的增益上限。需要量化「gain 到多少時 SNR 降到不可接受」。
#!/bin/bash
# 用固定曝光、不同增益拍 RAW,量 σ
for GAIN in 1 2 4 6 8 10 12 16; do
rpicam-still --raw --gain $GAIN --shutter 33000 \
--awb off --awbgains 1,1 -o snr_g${GAIN}.dng 2>/dev/null
done
python3 - <<'EOF'
import rawpy, numpy as np
for g in [1,2,4,6,8,10,12,16]:
raw = rawpy.imread(f'snr_g{g}.dng').raw_image.astype(float)
roi = raw[600:1400, 600:1400]
signal = roi.mean()
noise = roi.std()
snr_db = 20 * np.log10(signal / noise) if noise > 0 else 0
print(f"gain={g:2d} signal={signal:7.1f} noise={noise:5.1f} SNR={snr_db:.1f}dB")
EOFOV5647 的 1.4µm 像素在 1/4" 光學格式下,滿阱容量(Full Well Capacity)約為 4800 e-。這直接限制了動態範圍:
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| 低光下色彩明顯偏綠 | Gr/Gb 不平衡——Bayer pattern 中綠色像素佔 50%,低光 shot noise 對綠色通道影響最大 | 量測 Gr vs Gb 均值差;若差異 >5% 需調整 ISP 的 Gr/Gb 平衡校正 |
| 增益加到 8× 以上畫面充滿彩色斑點 | shot noise σ = √N,低訊號下 σ/Signal 比值暴增 | 限制增益上限(建議 ≤8×);改用物理補光增加真實訊號 |
| 長曝光(>1s)暗部出現固定亮點 | hot pixel + dark current 累積 | 啟用 DPC(感測器端缺陷像素校正);長曝降溫或限制曝光 |
| 切換解析度後色彩偏移 | 不同 mode 的 PLL/timing 設定差異導致增益映射偏移 | 切換解析度後重新量測黑位與 AWB gains |
| 灰卡量測 R/G/B 不等(已關 AWB) | 光源色溫非中性 或 感測器光譜響應偏差 | 先確認光源色溫(用色溫計);若光源正確,需校正 CCM |
場景:datasheet 宣稱 OV5647 SNR max ≈ 36 dB、DR ≈ 68 dB。你為監控專案需要一份「實測規格表」,用來設定調校目標(gain 上限、曝光策略)。本專案整合單元 2(物理)、5(拍 RAW)、7(黑位)、13(雜訊)知識。
# 階段 1:量測黑位與飽和(決定動態範圍) # 遮鏡頭拍黑位;對強光源拍飽和 rpicam-still --raw --shutter 20000 --gain 1 -o black.dng rpicam-still --raw --shutter 2000 --gain 1 -o sat.dng # 階段 2:量測 SNR vs gain(固定曝光、不同增益) for GAIN in 1 2 4 8 16; do rpicam-still --raw --gain $GAIN --shutter 33000 --awb off --awbgains 1,1 -o spec_g${GAIN}.dng 2>/dev/null done # 階段 3:計算並產出規格表 python3 - <<'EOF' import rawpy, numpy as np def roi_stats(f): raw = rawpy.imread(f).raw_image.astype(float)[600:1400, 600:1400] return raw.mean(), raw.std() blk_m, _ = roi_stats('black.dng') sat_m, _ = roi_stats('sat.dng') print(f"BlackLevel = {blk_m:.0f} Saturation = {sat_m:.0f}") print(f"動態範圍 = {20*np.log10((sat_m-blk_m)/1):.1f} dB") for g in [1,2,4,8,16]: m, s = roi_stats(f'spec_g{g}.dng') print(f"gain={g:2d} signal={m:7.1f} σ={s:5.1f} SNR={20*np.log10(m/s):.1f} dB") EOF # 預期輸出:DR 接近 datasheet(~65-68 dB);SNR 隨 gain 上升而下滑
--shutter 33000 --gain 1 --awb off 拍基準 RAW。| 面向 | RPi5 | Orange Pi | Orin Nano | Thor |
|---|---|---|---|---|
| RAW 取得 | rpicam-still --raw(DNG) | v4l2-ctl + 自行解包 | argus raw(.nvraw) | argus / Holoscan raw |
| 黑位 metadata | DNG 內嵌 BlackLevel | 無(需自行量測) | ISP 統計可查 | ISP 統計可查 |
| 量測工具 | rawpy/numpy 全開源 | Python 自寫 | NVIDIA 工具(部分封閉) | NVIDIA 工具 |
| 坑 | DNG 黑位可能被誤寫 | 10-bit packing 要手解 | raw 格式私有 | 生態新、工具少 |
| 步驟 | 指令 | 位元級細節 |
|---|---|---|
| 1. 讀取 Chip ID | i2cget -y 1 0x3C 0x300A w | OV5647 chip ID 高位=0x56, 低位=0x47 → 回讀 0x5647 |
| 2. 讀取位元深度 | i2cget -y 1 0x3C 0x302E w | bit[1:0] 決定 RAW 輸出位元:0b00=10-bit, 0b01=8-bit |
| 3. 讀取曝光行數 | i2cget -y 1 0x3C 0x3500 w + 0x3501 w + 0x3502 w | 3-byte 曝光值:bit[19:16] | bit[15:8] | bit[7:0],行數 = value >> 4 |
| 4. 驗證 PLL | i2cget -y 1 0x3C 0x3034 w | bit[7:6]=PLL root divider, bit[5:4]=PLL pre-divider, bit[2:0]=sys divider |
i2cget 的 w flag 讀取 16-bit,實際只用低 8-bit。決策樹 A:Chip ID 讀取失敗(回讀 0xFF 或 0x00)
Chip ID 異常 ├─ 檢查 A:i2cget 本身是否成功(有無 "Error") │ ├─ Error: errno=5 → I2C 匯流排被佔用 → 移除 libcamera/cam 佔用 │ └─ 正常回覆但值異常 → 繼續 ├─ 檢查 B:A09 GPIO 是否為 High(供電使能) │ ├─ Low → 供電 IC 未啟用 → 檢查 Raspberry Pi 電源供應(5V/2.5A+) │ └─ High → 繼續 ├─ 檢查 C:PWRDN 排線時序 │ ├─ Reset GPIO 持 Low 超過 10ms → sensor 未脫離 reset │ └─ 時序正確 → 繼續 └─ 檢查 D:嘗試 0x3C 與 0x78 兩個地址 ├─ 兩者都無回應 → 排線問題 / 感測器損壞 └─ 有一個回應 → 用該地址
| 面向 | RPi5 | Orange Pi | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| 可選感測器 | OV5647/5640/9281 | OV5640/IMX219 等 | IMX219/477/577 等 | IMX 系列 + 自有 | Orin 感測器支援最廣 |
| 位元深度 | 10-bit RAW | 8/10-bit | 10/12-bit | 12/14-bit | Thor 位元深度最高 |
| 像素技術 | BSI (OV5647) | BSI | Stacked BSI | Stacked BSI | Orin/Thor 最先進 |
| 支援 ROI / 窗口 | 支援 binning | 部分支援 | 完整 ROI | 完整 ROI + HDR | Orin/Thor 最靈活 |
| 開源驅動 | ✓ mainline | 部分 | ✓ kernel | ✓ kernel | RPi5 + Orin 開源最好 |
sudo apt install i2c-toolsi2cdetect -y 1 確認感測器地址出現