Bayer、global shutter、OV9281
OV9281 / OV5640 都是標準 Bayer 排列,由 ISP demosaic 重建全彩。
| 型態 | 特性 | OV9281 | OV5640 |
|---|---|---|---|
| Rolling | 逐列曝光 | — | ✅ |
| Global | 全像素同時曝光 | ✅ | — |
| 項目 | 規格 |
|---|---|
| 解析度 | 1 MP(1280×800) |
| 像素 | 3.0 µm |
| 快門 | Global shutter |
| 輸出 | MIPI CSI-2 |
| I2C 位址 | 0x36 |
| ID register | 0x300A=0x92 / 0x300B=0x81 |
每個像素 = 微透鏡 + 彩色濾波片(CFA)+ 光電二極體 + 讀出電路。光打到二極體產生電子,累積成電壓,再由 ADC 量化成 RAW 數字。
標準 Bayer 2×2 塊有 RGGB / BGGR / GRBG / GBRG 四種起點。OV9281 的 Bayer order 決定 RAW 要怎麼 demosaic;設錯整個色彩翻轉。
| 位元深 | 用途 | Orin 上的表示 |
|---|---|---|
| 8-bit | 最終輸出 | NV12 / YUV |
| 10-bit | 感測器原始(OV9281 主流) | SRGGB10(依 order) |
| 12-bit | 高階感測器 | SRGGB12 |
10-bit RAW 在 CSI-2 以「10-bit packing」傳輸(每 4 像素塞 5 bytes),解析時若當成 8-bit 或 12-bit 讀,會看到條紋狀假影。
Rolling shutter(OV5640/IMX219)逐列開始曝光,高速物體會扭曲;Global shutter(OV9281)全像素同時曝光,無扭曲但低光靈敏度較低(通常 pixel 較大補償)。
# OV9281(位址 0x36,ID register 0x300A/0x300B) sudo i2cget -y 0 0x36 0x300a # 0x92 sudo i2cget -y 0 0x36 0x300b # 0x81 # IMX219(位址 0x10,ID register 0x0000/0x0001) sudo i2cget -y 0 0x10 0x00 # 0x02 sudo i2cget -y 0 0x10 0x01 # 0x19
讀到「0x92 0x81」與「0x02 0x19」就代表兩顆感測器都活著——這是平台能力的第一個證明。
感測器把影像時間切成「行」。一幀 = 有效行 + vertical blanking;曝光時間通常以「行數」設定,實際秒數 = 行數 × 每行時間。每行時間又受輸出解析度與時脈影響。
| 量 | 關係 | 除錯意義 |
|---|---|---|
| 每行時間 | 由解析度/時脈決定 | 換模式後曝光感變 = 每行時間變 |
| 垂直 blanking | 留給讀出與曝光調度 | 影響可達最大幀率 |
| 最大曝光 | ≈ 幀時間 − 讀出時間 | 低幀率才能長曝光 |
| 項目 | OV9281 | OV5640 | IMX219 |
|---|---|---|---|
| 解析度 | 1280×800 | 2592×1944 | 3280×2464 |
| 像素 | 3.0 µm | 1.4 µm | 1.12 µm |
| 快門 | Global | Rolling | Rolling |
| I2C 位址 | 0x36 | 0x3C | 0x10 |
| ID register | 0x300A/B | 0x300A/B | 0x0000/1 |
# 各自取一幀 RAW(回顧單元 8)
# OV9281: 1280x800 10-bit, I2C 0x36
# IMX219: 3280x2464 10-bit, I2C 0x10
python3 - <<'EOF'
import numpy as np
for name, f, w, h in [('ov9281','ov.raw',1280,800),
('imx219','imx.raw',3280,2464)]:
raw = np.fromfile(f, dtype=np.uint16).reshape(h, w)
print(name, 'min', raw.min(), 'max', raw.max(),
'mean', round(raw.mean(),1))
EOF場景:工業傳送帶需要「線掃描」(line-scan)效果來檢測產品表面缺陷。OV9281 是 area sensor,但利用其 global shutter 特性,可以用軟體模擬 line-scan:只取每幀的中間一行 pixel。
# 1. 設定 OV9281 為全解析度 1280x800, 120fps v4l2-ctl -d /dev/v4l-subdev0 --set-fmt-video=width=1280,height=800,pixelformat=GREY # 2. 用 GStreamer 擷取並只取中間行(800/2=400) gst-launch-1.0 v4l2src device=/dev/video0 ! \ video/x-raw,format=GRAY8,width=1280,height=800,framerate=120/1 ! \ videobox left=0 right=0 top=399 bottom=-400 ! \ multifilesink location=line_%04d.raw # 3. 逐行拼成完整影像(Python) python3 - <<'EOF' import numpy as np, glob lines = [np.fromfile(f, dtype=np.uint8) for f in sorted(glob.glob('line_*.raw'))] np.array(lines).tofile('linescan.raw') print(f'拼接完成:{len(lines)} 行 x {lines[0].shape[0]} 像素') EOF
設計決策:OV9281 的 global shutter 確保每幀的行間無果凍效應(rolling shutter 會扭曲),120fps 提供高密度取樣。Orin 的 VI channel 只需一路,ISP 不必啟用(直接取灰階 RAW)。
Global shutter 感測器的每個 pixel 同時開始曝光、同時結束曝光(這是它比 rolling shutter 的優勢),但讀出(readout)仍然是逐列依次進行的。讀出速度受感測器內部 ADC 與匯流排頻寬限制。
| 概念 | Global Shutter | Rolling Shutter |
|---|---|---|
| 曝光起始 | 所有 pixel 同時 | 逐列遞延 |
| 曝光結束 | 所有 pixel 同時 | 逐列遞延 |
| 讀出 | 逐列依次(但曝光已結束) | 逐列同時曝光+讀出 |
| 對運動物體 | 整幀一致,無果凍 | 果凍效應(skew/rolling) |
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| OV9281 出圖有明顯果凍(直線彎曲) | 感測器並非真的 global shutter 模式(DTB mode 設定錯) | 確認 OV9281 的 mode 設為 global shutter(register 0x0106);用高速運動物體測試 |
| Bayer 偽影(灰卡拍出彩色) | DTB 或 Argus 設定的 Bayer order 與感測器實際不符 | 查感測器 datasheet 的輸出格式;嘗試 4 種 Bayer 起點(RGGB/BGGR/GRBG/GBRG) |
| 暗部雜訊嚴重 | 感測器增益過高 或 黑位偏高 | 用全遮測黑位(參照單元 8);降低 analog gain 設定 |
| 不同 mode 切換後色彩偏差 | binning/crop 改變了 effective Bayer pattern 或 lens shading | 每次切 mode 後重新做白平衡校正;鎖定調校用的 mode |
| OV9281 在低光下幀率驟降 | auto exposure 拉長曝光時間以補償亮度 | 固定曝光時間(手動 AE);或接受低光降幀是正常行為 |
場景:你負責驗收一批新的 OV9281 模組,需要建立一座「感測器特性實驗台」,把單元 2 的光→RAW、Bayer、global shutter 知識變成可重複的量測流程,最終產出一份「每顆模組的感測器特性報告」。
# Step 1 · 上電 + 讀 ID(驗證感測器活著) sudo i2cget -y 0 0x36 0x300a # 0x92 sudo i2cget -y 0 0x36 0x300b # 0x81 # Step 2 · 全遮鏡頭:量黑位 argus_camera --mode 0 --capture-auto 1 --raw-file blk.raw --duration 1 python3 - <<'EOF' import numpy as np raw = np.fromfile('blk.raw', dtype=np.uint16) print('黑位 mean', round(raw.mean(),1), 'max', raw.max()) # 預期接近 datasheet 標稱 EOF # Step 3 · 雷射亮點:判定 Bayer order(回顧 8.17 方法) # Step 4 · 高速轉盤:確認 global shutter 無果凍 # (若出現傾斜/彎曲 → 可能被切成 rolling 模式) # Step 5 · 曝光階梯:確認響應線性(回顧 10.9) for e in 1000 2000 4000 8000 16000; do v4l2-ctl -d /dev/v4l-subdev0 -c exposure=$e argus_camera --mode 0 --capture-auto 1 --raw-file e$e.raw --duration 1 done
專案輸出:每顆模組一份 sensor_report.csv(ID、黑位、Bayer order、shutter 型態、曝光線性斜率)。這份報告就是後續 ISP 調校(單元 11–14)的「感測器本質」基線。
| 面向 | Orin Nano | RPi5 | Orange Pi | Thor |
|---|---|---|---|---|
| 讀 ID 工具 | i2cget / i2ctransfer | i2ctransfer | i2ctransfer | i2ctransfer |
| 取 RAW | argus --raw-file | rpicam --raw | v4l2-ctl --stream-to | argus / Holoscan |
| 曝光控制 | tegracam exposure | libcamera ExposureTime | V4L2 exposure | SensorMode |
| 感測器 register | 完全相同(0x300A ID、0x3500 曝光、0x350A 增益) | |||
| 特性分析 | RAW 分析腳本可 100% 共用(平台無關) | |||
| 步驟 | 操作 | 暫存器 | Bit Field | Mask | Expected |
|---|---|---|---|---|---|
| 1. 讀 chip ID 高位 | i2cget | 0x300A | chip_id[15:8] | 0xFF | 0x92 |
| 2. 讀 chip ID 低位 | i2cget | 0x300B | chip_id[7:0] | 0xFF | 0x81 |
| 3. 設曝光高位 | i2cset | 0x3500 | exp[19:16] | 0x0F | 依需求 |
| 4. 設曝光中位 | i2cset | 0x3501 | exp[15:8] | 0xFF | 依需求 |
| 5. 設曝光低位 | i2cset | 0x3502 | exp[7:4] | 0xF0 | 依需求 |
| 6. 設曝光手動模式 | i2cset | 0x3503 | aec_manual | 0x01 | 0x01 |
# Step 1: Read — 讀目前曝光高位 cur=$(i2ctransfer -y 0 w2@0x36 0x35 0x00 r1) echo "current exp hi = 0x$cur" # Step 2: Modify — 曝光 = 0x0500(5 行) exp_hi=0x05; exp_mid=0x00; exp_lo=0x00 # Step 3: Write — 依序寫入三個位元組 i2ctransfer -y 0 w2@0x36 0x35 0x00 r1 # 確認讀回 i2ctransfer -y 0 w3@0x36 0x35 0x00 $exp_hi i2ctransfer -y 0 w3@0x36 0x35 0x01 $exp_mid i2ctransfer -y 0 w3@0x36 0x35 0x02 $exp_lo # Step 4: Verify — 回讀三個位元組組合成完整值 r_hi=$(i2ctransfer -y 0 w2@0x36 0x35 0x00 r1) r_mid=$(i2ctransfer -y 0 w2@0x36 0x35 0x01 r1) r_lo=$(i2ctransfer -y 0 w2@0x36 0x35 0x02 r1) echo "verify: 0x$r_hi 0x$r_mid 0x$r_lo"
1. 灰卡拍出彩色? ├─ 是 ┐ │ 2. 嘗試 4 種 Bayer 起點(RGGB/BGGR/GRBG/GBRG) │ ├─ 有一種正確 → DTB pixel_phase 設錯 │ └─ 全部不對 ┐ │ 3. 感測器實際輸出格式與 DTB 宣告不符? │ ├─ 是 → 查 datasheet 確認真正的 Bayer order │ └─ 否 → ISP demosaic 問題(回歸單元 7) └─ 否 → 色彩正常
1. 高速移動物體拍出有果凍(直線彎曲)? ├─ 是 ┐ │ 2. OV9281 register 0x0106 設定為 global shutter 模式? │ ├─ 否 → 設為 0x01(global 模式) │ └─ 是 ┐ │ 3. DTB mode 是否宣告正確的 shutter 型態? │ ├─ 否 → DTB mode 改為 global │ └─ 是 → 硬體問題:serializer 或 GMSL link 延遲 └─ 否 → 無果凍
i2ctransfer -y 0 w2@0x36 0x30 0x0a r1 → 預期 0x92;r1 at 0x300B → 0x81。raw.min();與 datasheet 黑位標稱差 > 10% 需追查。mean vs exposure 之 R²;R² > 0.98 = 線性良好。| 面向 | Orin Nano | RPi5 | Orange Pi | Thor | 推薦 |
|---|---|---|---|---|---|
| 讀 ID 工具 | i2cget / i2ctransfer | i2ctransfer | i2ctransfer | i2ctransfer | i2ctransfer 全平台共通 |
| 取 RAW | argus --raw-file | rpicam --raw | v4l2-ctl --stream-to | argus / Holoscan | 依平台選工具 |
| 曝光控制 | tegracam exposure | libcamera ExposureTime | V4L2 exposure | SensorMode | 寄存器相同,介面不同 |
| Bayer 驗證 | 雷射亮點法全平台共用 | 平台無關 | |||
| Global Shutter 驗證 | 高速物體拍照全平台共用 | 平台無關 | |||
| 雜訊分析 | Python / numpy 全平台共用 | 平台無關 | |||
| 感測器特性報告 | 格式相同(ID/黑位/Bayer/shutter/線性斜率) | 可跨平台攜帶 | |||