單元 16 · 多感測器與實作案例

客製 OV 接入、完整案例

16.1 多相機

RPi5 兩組 CSI 可同時跑兩顆;libcamera 以多 Camera 呈現,各自獨立控制。

列出所有相機
rpicam-hello --list-cameras   # 0: ov5647(1)  1: ov5647(0) 等

16.2 客製 OV 感測器接入(四步,含細節)

  1. 硬體:CSI 2-lane、I2C、reset/clock GPIO 對齊 15-pin。
  2. DT overlay:宣告 I2C 位址、clocks、reset-gpios、port{csi-lane=2}。
  3. 驅動drivers/media/i2c/ 有對應驅動(ov5647/5640/9281)。
  4. 出圖 → 調校:先 RAW 確認,再做黑位/LSC/色彩(本課全套流程)。
NDA 提醒:完整 register 表常需 NDA;本站只用公開可查 register。正式開發以 OVT 官方文件為準。

16.3 完整案例:調校一顆 OV5647(七步)

接板+overlay出圖RAW 檢查黑位/LSCAWB/CCMNR/Sharpen回歸
  1. rpicam-hello --list-cameras 確認偵測。
  2. rpicam-still --raw -o t.dng 出圖 + 存 DNG。
  3. 解析 RAW:Bayer order、黑位、缺陷像素(單元 7/8)。
  4. 灰卡校正黑位與 LSC(單元 7/12)。
  5. 色卡校正 AWB 與 CCM(單元 11)。
  6. 實景微調 NR ↔ Sharpen(單元 13/14)。
  7. 室內/日光/逆光回歸(單元 14)。

16.4 最佳實踐總清單

課程完成:你已具備從感測器初始化、串流到完整調校的能力,可獨立處理一顆 OV 感測器。

16.5 深入:客製感測器接入的除錯策略

常見死法:「一次改了一堆」→ 出問題無法歸因。接新感測器時,嚴格「一次一變數」。

16.6 Worked Example:新感測器 bring-up 檢查清單

checklist
□ 硬體:CSI lane / I2C / reset / clock 腳位對齊
□ DT overlay:compatible / reg / clocks / reset-gpios
□ 驅動:是否有現成 / 是否需改 init table
□ dmesg:probe 成功?讀到 ID?
□ 出圖:--list-cameras / 預覽有畫面?
□ RAW:Bayer order / 黑位 / 缺陷像素
□ 調校:黑位 → LSC → AWB/CCM → NR/Sharpen

16.7 總複習:把你的完整流程寫成文件

  1. 寫一份「你的感測器 bring-up SOP」。
  2. 寫一份「你的調校流程與參數」。
  3. 兩份都放進 repo,成為團隊資產。
看完這單元你應該能說出:
  • RPi5 多相機操作。
  • 客製 OV 接入四步。
  • 完整調校案例七步。
  • 調校最佳實踐。

進階真實情境 Worked Example:從零建立 OV5647 的完整 tuning 檔

場景:你拿到一顆全新的 OV5647,RPi5 的預設 tuning 不理想(偏暗、偏黃)。需要從零開始,按正確順序建立一份完整的 tuning JSON。

完整 tuning 建立流程
#!/bin/bash
# === 階段 1:備份預設 tuning ===
cp /usr/share/libcamera/ipa/rpi/pisp/ov5647.json ./tuning_v0.json

# === 階段 2:黑位校正(遮光拍攝)===
rpicam-still --raw --shutter 33000 --gain 1 \
  --awb off --awbgains 1,1 -o black.dng
python3 - <<'EOF'
import rawpy, numpy as np
raw = rawpy.imread('black.dng').raw_image[500:1500,500:1500]
print(f"BlackLevel: R={raw[0::2,0::2].mean():.0f} "
      f"Gr={raw[0::2,1::2].mean():.0f} "
      f"Gb={raw[1::2,0::2].mean():.0f} "
      f"B={raw[1::2,1::2].mean():.0f}")
EOF
# → 將量測值填入 tuning JSON 的 BlackLevel 區塊
# → 另存為 tuning_v1_black.json

# === 階段 3:LSC 校正(均勻灰拍攝)===
rpicam-still --raw --shutter 20000 --gain 1 \
  --awb off --awbgains 1,1 -o lsc_gray.dng
# → 用白牆/灰卡推算 LSC 網格 → tuning_v2_lsc.json

# === 階段 4:AWB/CCM 校正(色卡拍攝)===
rpicam-still --raw --shutter 20000 --gain 1 \
  --awb off --awbgains 1,1 -o colorchart.dng
# → 用色卡各色塊計算 CCM → tuning_v3_ccm.json

# === 階段 5:NR/Sharpen 微調(實景)===
rpicam-still --tuning-file tuning_v3_ccm.json \
  --shutter 20000 --gain 4 -o real_scene.jpg
# → 調整 Denoise strength + Sharpen → tuning_v4_final.json

# === 驗證 ===
rpicam-still --tuning-file tuning_v4_final.json \
  --shutter 20000 --gain 1 --raw -o verify.dng
為什麼選這條路徑:五階段的順序嚴格遵循 ISP 管線的校正依賴:黑位是地基(影響所有統計)→ LSC 是亮度(影響色彩量測)→ AWB/CCM 是色彩(依賴正確的亮度基準)→ NR/Sharpen 是美化(最後處理)。跳步驟(如先調 NR 再校黑位)會導致前面的校正被後面的改動覆蓋——這是新手最常犯的錯誤。

深入原理擴充:OV5647 的 ISP 處理鏈與 BCM2712 PiSP 的硬體管線

RPi5 的 BCM2712 內建 PiSP(Programmable ISP),其硬體管線有以下特點:

大家以為沒问题但其實是陷阱:很多人以為「tuning JSON 可以控制 ISP 的每個細節」,但 PiSP 的硬體管線有一些不可調的內部參數(如 demosaic 的 kernel 大小、NR 的空間 kernel 形狀)。這些參數在硬體中固化,tuning JSON 只能調整「強度」旋鈕。若你需要完全控制管線的每個細節,RPi5 不是正確的平台——這時需要 Orin Nano 或客製 SoC。

診斷式疑難排解表

症狀可能原因解決方案
從零建立的 tuning 效果比預設更差跳過了校正順序(如先調 CCM 再校黑位)嚴格按黑位→LSC→AWB/CCM→NR 順序重做
色卡校正後室內場景色彩正確但日光場景偏暖CCM 只在一個色溫下校正,未涵蓋其他光源在至少 2-3 個色溫下分別校正 CCM,建立分段表
LSC 校正用白牆但效果不如積分球白牆的均勻度不夠(有陰影、反射、污漬)用更均勻的光源;或增加曝光範圍取平均
NR 開到最強仍無法消除低光雜訊增益太高(雜訊源頭過強)回到源頭:降增益、加曝光、補光
tuning JSON 修改後 libcamera 報錯JSON 語法錯誤或缺少必要欄位用 python3 -m json.tool 驗證;對照預設 JSON 的結構

進階挑戰題

  1. 設計一個「tuning 質量自動評分器」:輸入一張色卡 RAW,自動計算平均 ΔE、暗角比、NR σ,輸出綜合評分。用這個評分器量化比較「預設 tuning」vs「你的手動 tuning」。
  2. 若要將 OV5647 的 tuning 參數移植到 OV5640(不同的感測器),需要修改哪些區塊?哪些區塊可以直接複製?
  3. 研究 libcamera 的 IPA 模組原始碼(src/ipa/rpi/pisp/),分析 AE/AWB 演算法的核心邏輯。若要自訂 AE 演算法,需要修改哪些檔案?

延伸閱讀

專案級端到端 Worked Example:雙鏡頭立體視覺 bring-up 專案

場景:建立一套「雙 OV5647 立體視覺」系統:兩顆相機同步曝光、各自調校、輸出校正後的成對圖。整合單元 16(多感測器)、1(雙鏡頭)、4(overlay)、11/12/13(調校)、15(平台對照)知識。

雙鏡頭同步 bring-up
# 1. 確認兩顆相機(Camera 0 + Camera 1)
rpicam-hello --list-cameras
# 預期:0 : ov5647  1 : ov5647

# 2. picamera2 同步配置(同尺寸/同參數)
python3 - <<'EOF'
from picamera2 import Picamera2
cam0 = Picamera2(0); cam1 = Picamera2(1)
cfg = lambda c: c.create_still_configuration(main={"size":(1296,972),"format":"RGB888"})
cam0.configure(cfg(cam0)); cam1.configure(cfg(cam1))
# 固定相同曝光/增益/AWB,確保兩眼一致
for c in (cam0, cam1):
    c.set_controls({"AeEnable":False,"ExposureTime":20000,
                    "AnalogueGain":1.0,"AwbEnable":False})
cam0.start(); cam1.start()
cam0.capture_file("left.jpg"); cam1.capture_file("right.jpg")
cam0.stop(); cam1.stop()
print("立體對輸出完成")
EOF

# 3. 驗證兩眼一致性
python3 - <<'EOF'
import cv2, numpy as np
l = cv2.imread('left.jpg'); r = cv2.imread('right.jpg')
print("亮度差(mean abs):", np.abs(l.astype(float)-r.astype(float)).mean())
# 目標 < 3(同場景兩眼亮度應幾乎一致)
EOF
專案規模與跨單元整合:立體視覺的關鍵是「兩眼一致性」:同步曝光用單元 16 的多相機能力、兩眼參數相同用單元 10/11 的控制、各自的 LSC/CCM 用單元 12/11 校正(每顆鏡頭各自校正後亮度才會一致)。這是最後一個整合專案——它用到本課程幾乎每一單元。

量測/驗證 SOP:整合系統驗收

  1. 逐一 bring-up 兩顆相機(autobringup.sh 各跑一次)。
  2. 確認兩顆在 list-cameras 同時出現。
  3. 同步拍攝,驗證兩眼亮度差 <3(灰度)。
  4. 各跑 RAW 健康報告與黑位(兩顆各自校正)。
  5. 色彩/暗角/雜訊各達標後,做場景回歸。
  6. 輸出完整 bring-up + 調校文件。
判讀指標:兩眼亮度差 >5 = 曝光/增益未同步或 LSC 未各自校正;一顆色彩偏差 = 該顆的 CCM/黑位未對;任一顆失敗 = 用該顆的診斷表定位。

平台間對照:多感測器能力

面向RPi5Orange PiOrin NanoThor
同步相機數2(雙 CSI)1-2(依板型)多路(GMSL 可到 8+)多路(HSB 乙太)
同步機制軟體同步(誤差較大)硬體同步(GPIO/trigger)硬體同步(乙太精準)
調校工具每顆各自 tuning JSONNVIDIA 批次工具Holoscan pipeline
雙顆同 I2C 位址要分 bus能力有限成本高、NDA生態新

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:RPi5 雙攝影機 I2C 地址配置

步驟操作位元級說明
1. 確認 sensor 0 I2C 地址i2cdetect -y 10x3C (OV5647 default)
2. 確認 sensor 1 I2C 地址i2cdetect -y 00x3C on bus 0(不同 I2C bus)
3. config.txt 設定加入兩個 dtoverlaydtoverlay=ov5647,cam0 + dtoverlay=ov5647,cam1
4. 驗證 media topologymedia-ctl -d /dev/media0 -p確認兩個 sensor 出現在 topology 中

② 多層疑難排解決策樹

決策樹 A:多感測器只有一個能用

只有一個能用
├─ 檢查 A:兩個 sensor 是否在不同 I2C bus
│  ├─ 同 bus 同地址 → 地址衝突 → 調整其中一個的地址(如有 OTP)
└─ 不同 bus → 繼續 ├─ 檢查 B:config.txt overlay 是否正確指定 cam0/cam1 │ ├─ 未指定 → 加 ,cam0 / ,cam1 參數 │ └─ 已指定 → 繼續 ├─ 檢查 C:CSI lane 分配是否正確 │ ├─ cam0 佔用全部 lane → cam1 無 lane 可用 → 調整 lane 分配 │ └─ 正確分配 → 繼續 └─ 檢查 D:ISP 是否有足夠 bandwidth 處理雙路 ├─ 無 → 降低其中一路解析度 └─ 有 → 檢查驅動程式 multi-camera 支援

③ 量測驗證完整 SOP:多感測器同步性量測

  1. 方法:LED 閃爍 + 雙攝影機同時擷取 + frame timestamp 比對
  2. 步驟:
    a. 設定 LED 以已知頻率(如 100Hz)閃爍
    b. 雙攝影機同步擷取 1000 幀
    c. 比對對應幀的 timestamp 差值
  3. 量測指標:
    a. 平均時間差(offset)
    b. 時間差的標準差(jitter)
    c. 最大時間差
  4. 判讀:offset <1ms, jitter <0.5ms = 同步良好
  5. 常見偏差:I2C 時序差異 → 兩 sensor 啟動時間不同 → 首幀不同步

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
最大感測器數2(官方)1-24+ (GMSL)多路(乙太網路)Orin 規模最大
多攝同步有限(GPIO sync)困難硬體 sync 支援網路時間協議Orin/Thor 最佳
帶寬2×4-lane MIPI2×2-lane4× GMSL2乙太網路無限Thor 天生分散
應用場景立體視覺前後鏡頭多角度監控大型部署依需求選擇
複雜度中等中等很高RPi5 適合入門

⑤ 完整 Bring-up 專案 Checklist