Arduino® UNO Qの強力なLinux側コア(Qualcomm QRB2210)を使い、MIPI-CSI2カメラとUSBカメラを使った、高速なエッジAI(YOLO-Proでの物体検出)を実行します。
また、MIPIカメラの画質調整や、ハードウェアの各種性能測定(消費電流やメモリ割り当て)といった、UNO Qを使いこなすための測定結果もまとめて紹介します。
UNOQでMIPI-CSI2とUSBのカメラを使ってみた
Arduino® UNO Qの強力なLinux側コア(Qualcomm QRB2210)を使い、MIPI-CSI2カメラとUSBカメラを使った、高速なエッジAI(YOLO-Proでの物体検出)を実行します。
また、MIPIカメラの画質調整や、ハードウェアの各種性能測定(消費電流やメモリ割り当て)といった、UNO Qを使いこなすための測定結果もまとめて紹介します。




プロジェクト・GitHubのリンク先
コンテストに提出したプロジェクトのリンク先は下記です
GiHubのリンク先は下記です

UNOQ用のMIPI-CSI2の追加ボード
UNOQにMIPI-CSI2のカメラを使うには追加アドオンボードが必要です。
下記オープンソースのボードを利用しました。




実際に使うカメラはラズパイのカメラでOKです。
IMX219が搭載されているV2カメラを利用しています。
PCBWayでのボード作成
今回MIPI-CSI2のボード作成するにあたり、PCBWay様で作成させていただきました。
PCBWayで実装含めて対応していただき、正常に動作が出来ました。
この場を借りてお礼申し上げます。

納品状態も問題なく、実装含めて約1~2週間で対応していただきました。


MIPI-CSI2カメラ(IMX219)のLinuxセットアップ
Qualcomm Linux側でPi Camera v2を動かすには、適切なデバイスツリーオーバーレイと、カーネルの選択が必要です。
デバイスツリーオーバーレイ(Powerfix)の適用
電源制御とエラーを解消するため、オーバーレイ(.dtbo)を結合して適用します。
|
1 2 3 4 5 6 7 8 |
# 1. DTSオーバーレイをDTBOにコンパイル dtc -@ -I dts -O dtb -o unoq-imx219-powerfix.dtbo unoq-imx219-powerfix.dts # 2. ベースとなるDTBにオーバーレイを結合 fdtoverlay -i qrb2210-arduino-imola-camera-rpiv2.dtb unoq-imx219-powerfix.dtbo -o qrb2210-arduino-imola-camera-rpiv2-r1b.dtb # 3. 結合されたDTBをブートローダーのディレクトリに配置 sudo cp qrb2210-arduino-imola-camera-rpiv2-r1b.dtb /boot/efi/dtb/r1b/ |
カーネルのロールバックと検証
不具合を防ぐため、実績のある安定したカーネルバージョン 6.16.7 に固定して再起動します。
|
1 2 |
# /boot/efi/loader/loader.conf default 3e660e15577e4d88ad85a3673a183368-6.16.7-g0dd6551ae96b.conf |
再起動後、必要なツールをインストールして検証します。
|
1 2 |
sudo apt install libcamera-tools gstreamer1.0-libcamera QT_QPA_PLATFORM=offscreen qcam |
PythonによるEdge Impulseモデル(.eim)の実行
Linux Host側では、Edge Impulse Studioで出力したコンパイル済みの実行モデル(.eim)を使用します。
.eimモデルのロードと画像データのパック
Python SDKを使用して .eim を初期化し、フレームをモデル要求フォーマット(32ビット浮動小数点数)に変換します。
推論プログラムのコードの詳細は、下記GitHubで公開しています。

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
from edge_impulse_linux.runner import ImpulseRunner import cv2 import numpy as np # 1. EIMモデルの初期化 runner = ImpulseRunner("camera-test-linux-aarch64-v10-impulse-#1.eim") model_info = runner.init() input_w = model_info["model_parameters"]["image_input_width"] input_h = model_info["model_parameters"]["image_input_height"] # 2. FRAMEをリサイズし、RGBに変換 frame_resized = cv2.resize(frame, (input_w, input_h)) img_rgb = cv2.cvtColor(frame_resized, cv2.COLOR_BGR2RGB) # 3. 特徴量を 0x00RRGGBB 形式にパック img_u32 = img_rgb.astype(np.uint32) packed_features = ((img_u32[:, :, 0] << 16) | (img_u32[:, :, 1] << 8) | img_u32[:, :, 2]).astype(np.float32).reshape(-1) # 4. ローカル推論を実行 res = runner.classify(packed_features) |
WebUIからのリアルタイム画像キャリブレーション
生のカメラモジュールは、照明環境によって、「緑被り」や「コントラスト低下」が発生しがちです。
そこで、Web画面から動的にパラメータを調整できる画像処理フィルターを実装しました。


CLAHEとRGBゲインの調整コード
OpenCVを使い、暗室や曇ったレンズ用の除霧フィルター(CLAHE)と、手動のRGBゲイン調整をフレーム適用前に掛け合わせます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
def apply_user_image_tune(frame_bgr, r_gain, g_gain, b_gain, defog_val, bright_val, contrast_val): img = frame_bgr.copy() # 1. CLAHEによるデフォグ(コントラスト強調) if defog_val > 0: lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) clip_lim = 1.5 + float(defog_val) * 0.4 clahe = cv2.createCLAHE(clipLimit=clip_lim, tileGridSize=(8, 8)) l = clahe.apply(l) img = cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR) # 2. 自動ホワイトバランス基準 + 手動RGBゲイン補正 img = img.astype(np.float32) b, g, r = cv2.split(img) b_avg = np.mean(b) + 1e-5 g_avg = np.mean(g) + 1e-5 r_avg = np.mean(r) + 1e-5 k = (b_avg + g_avg + r_avg) / 3.0 # ユーザー調整値の反映 b *= (k / b_avg) * float(b_gain) g *= (k / g_avg) * float(g_gain) r *= (k / r_avg) * float(r_gain) # 3. 明るさ・コントラストの適用 img = cv2.merge([b, g, r]) img = img * float(contrast_val) + float(bright_val) return np.clip(img, 0, 255).astype(np.uint8) |
実際のデモ動画は下記です。
徹底検証:ボトルネックと性能測定結果
本プロジェクトの構築を通して得られた、ハードウェアの各種測定データを紹介します。
① SPIクロックの違いによる転送速度(4MHz vs 8MHz)
SPIカメラから320x240 RGB565の画像を、マイコンのRAMに吸い上げる速度を測定しました。
* SPIクロック 4MHz: 984 ms
* SPIクロック 8MHz: 677 ms (31.2% の高速化)
マイコン側で動画レートでの取得を目指すには、Linux kernel側へ高速なSPI slaveドライバーを設定するなどの、さらなる最適化が必要です。

② マイコンSRAM(768KB)の割り当て内訳
STM32U585のRAM使用状況の内訳です。
* ビデオダブルバッファ: 440,000 Bytes (57.3%)
* TFLite テンソルアリーナ: 153,936 Bytes (20.0%)
* メインスレッドスタック: 32,768 Bytes (4.3%)
* 画像縮小用ワークスペース: 18,432 Bytes (2.4%)
* 合計RAM使用量: 87.6%
このため、メモリを静的に割り当て、平均化プーリングによって96x96に縮小して入力する設計が、非常に有効であることが分かります。

③ 各カメラ動作時の消費電流
5V給電下におけるシステムの全体的な消費電流を測定しました。
* UNO Q アイドル状態: 85 mA (~0.43 W)
* SPIカメラ動作中 (MCU + 推論): 296 mA (~1.48 W) — 差分 211 mA
* MIPI-CSI2カメラ動作中 (Linux + YOLO): 491 mA (~2.46 W) — 差分 406 mA
* USBカメラ動作中 (Linux + YOLO): 730 mA (~3.65 W) — 差分 645 mA
* 3カメラ同時配信中 (推論なし): 913 mA (~4.57 W) — 差分 828 mA
結果からの洞察:
同じYOLOモデルを実行する状況において、MIPIカメラはUSBカメラと比べて消費電力を約37%削減できました。
MIPIカメラはUSBコントローラ層を介さずに、低オーバーヘッドのDMA転送を行うため、省電力設計が求められる用途で非常に有利です。

まとめ
Arduino® UNO Qという、Linuxとマイコンが1つになったユニークなボードを駆使した、マルチカメラAIシステムを紹介しました。
* STM32側: 低消費電力を活かしたPIRセンサー起動とマイコンAI
* Linux側: 高画質な複数ストリーミングと、YOLOによる高速物体検出
エッジAI、RTOS、Linuxカメラ制御など、多くの面白い機能が体験できる構成となりました。
みなさんもぜひ、Arduino UNO Qを動かしてみてください!

コメント