マイコン単体でカメラ画像からリアルタイムに物体を検出する「TinyMLエッジAI」を構築してみました。
対象ボードは、Raspberry Pi Pico 2W、Nordic nRF54L15-DK、そしてArduino® UNO Qの3種類です。
今回は、Zephyr RTOSとArducam Mega 3MPを組み合わせ、Edge Impulseの超軽量物体検出モデル「FOMO」を完全共通のC++コードで実行するシステムを紹介します。
カメラ側の96x96解像度設定によるリサイズ負荷ゼロ化や、RAMの消費を抑えるゼロコピー変換、ARM Cortex-M33のCMSIS-NN(SIMD)による高速推論など、実践的な組み込みAIのテクニックを詳しく解説します。
Zephyr RTOS & Arducam MegaでFOMO物体検出
マイコン単体でカメラ画像からリアルタイムに物体を検出する「TinyMLエッジAI」を構築してみました。
対象ボードは、Raspberry Pi Pico 2W、Nordic nRF54L15-DK、そしてArduino® UNO Qの3種類です。



今回は、Zephyr RTOSとArducam Mega 3MPを組み合わせ、Edge Impulseの超軽量物体検出モデル「FOMO」を完全共通のC++コードで実行するシステムを紹介します。
カメラ側の96x96解像度設定によるリサイズ負荷ゼロ化や、RAMの消費を抑えるゼロコピー変換、ARM Cortex-M33のCMSIS-NN(SIMD)による高速推論など、実践的な組み込みAIのテクニックを詳しく解説します。

プロジェクト・GitHubのリンク先
GitHubのリンク先は下記です

実際のデモ動画は下記です
Nordic nRF54L15-DK(CMSIS-NN @ 128 MHz によるリアルタイム物体検出実演)
Raspberry Pi Pico 2W(CMSIS-NN @ 150 MHz によるリアルタイム物体検出実演)
マルチボード構成とピンアサイン
本プロジェクトでは、いずれもARM Cortex-M33コアを搭載した3種類の最新マイコンボードを使用しています。
Arducam Mega 3MP(SPI接続カメラ)を以下のピン配置で接続しました。
ハードウェア接続一覧表
| 信号名 | カメラピン | Raspberry Pi Pico 2W | Nordic nRF54L15-DK (P1) | Arduino® UNO Q |
|---|---|---|---|---|
| 電源 (VCC) | Pin 1 (VCC) | 3V3 (Pin 36) | Pin 1 (VDDIO / 3.3V) | 3.3V / 5V |
| グランド (GND) | Pin 2 (GND) | GND (Pin 23) | Pin 2 (GND) | GND |
| SPI MOSI | Pin 3 (MOSI) | GP19 (Pin 25) | Pin 6 (P1.06 / SPI22) | D11 (PB15 / SPI2) |
| SPI MISO | Pin 4 (MISO) | GP16 (Pin 21) | Pin 7 (P1.07 / SPI22) | D12 (PB14 / SPI2) |
| チップセレクト (CS) | Pin 6 (CS) | GP17 (Pin 22) | Pin 10 (P1.10 / GPIO) | D10 (PB9 / GPIO) |
| SPI クロック (SCK) | Pin 5 (SCK) | GP18 (Pin 24) | Pin 11 (P1.11 / SPI22) | D13 (PB13 / SPI2) |
| コンソール通信 | - | 内蔵USB CDC ACM | オンボード J-Link VCOM1 | USART1シリアル (D0/D1) |
エッジAIパイプラインと軽量化の工夫
マイコンで機械学習を実行する際、処理速度とRAM容量が最大のネックとなります。
本システムでは、以下の工夫により極小リソースでのリアルタイム物体検出を実現しました。
カメラ解像度とモデル入力の完全一致(96x96)
Arducam Megaのハードウェア解像度設定を「96x96 RGB565」に直接指定しました。
一般的なシステムのように「QVGAで撮ってからCPUソフトウェアで96x96へバイリニア縮小する」という処理が不要になります。
画像縮小の演算負荷とワークスペースメモリ(約18KB)を完全にゼロ化できました。
ゼロコピーによるストリーム画像変換(signal_t)
Edge Impulseの推論エンジンは、ピクセルデータを正規化されたRGB float配列として要求します。
これを愚直に実装すると、SRAM上に別途27KBもの展開バッファが必要になってしまいます。
そこで、推論エンジンのコールバック関数 signal_t の中で、18.4KBのRGB565バッファからオンザフライで1画素ずつRGBを展開して推論テンソルへ直接流し込む「ゼロコピー変換」を採用しました。
余分な中間バッファを一切消費しません。
CMSIS-NNによるCortex-M33ハードウェアアクセラレーション
畳み込みニューラルネットワーク(CNN)の演算には、ARM公式の最適化ライブラリ「CMSIS-NN」を組み込みました。
Cortex-M33が持つDSP拡張命令およびSIMD命令をフル活用することで、INT8量子化されたMobileNetV2の積和演算を驚異的な速度で処理します。
各ボードでのメモリ消費量と推論性能
内蔵SRAMが256KBしかないnRF54L15をはじめ、各ボードで余裕を持って動作しています。
| 対象ボード | コア周波数 | 内蔵SRAM | RAM使用量 | SRAM使用率 | Flash使用量 |
|---|---|---|---|---|---|
| Raspberry Pi Pico 2W | 150 MHz | 520 KB | 240.2 KB | 45.11% (空き 279 KB) | 161.3 KB (3.8%) |
| Nordic nRF54L15-DK | 128 MHz | 256 KB | 223.5 KB | 85.25% (空き 32.5 KB) | 141.4 KB (9.1%) |
| Arduino® UNO Q | 160 MHz | 786 KB | 222.9 KB | 28.34% (空き 545 KB) | 135.0 KB (6.4%) |
すべてのボードにおいて、Tensor Arena(モデル推論領域)やカメラバッファ、スタック領域を含めてSRAM内にぴったり収容されています。
Edge Impulseモデルの準備と組み込み
Edge Impulseのモデルライブラリをプロジェクトへ配置する手順です。
モデルの学習とエクスポート
Edge Impulse Studio(https://studio.edgeimpulse.com/)にて、入力解像度を「96x96」、学習ブロックに「Object Detection(FOMO)」を選択して学習を行います。
学習完了後、「Deployment」タブから「C++ Library」を選択し、CMSIS-NNを有効にしてビルドします。
プロジェクトへの展開
ダウンロードしたzipファイルを、unified_edgeai_spicam/edge-impulse/ ディレクトリへ展開します。
|
1 2 3 4 5 6 |
unified_edgeai_spicam/edge-impulse/ ├── CMakeLists.txt ├── README.md ├── edge-impulse-sdk/ ├── model-parameters/ └── tflite-model/ |
CMakeLists.txt が自動的にモデルソースコードとCMSIS-NNカーネルをZephyrビルドシステムへリンクします。
ビルドと書き込み手順(West)
各ボード向けのビルドは、付属の build.bat でワンストップで実行できます。
ソースコードはGitHubで公開しています。
Raspberry Pi Pico 2W のビルド
PowerShellで仮想環境を起動し、ビルドを実行します。
|
1 2 |
cd zephyr-spicam/unified_edgeai_spicam .uild.bat pico |
生成された build\pico\zephyr\zephyr.uf2 を、BOOTSELモードのPico 2Wへドラッグ&ドロップして書き込みます。
Nordic nRF54L15-DK のビルドと書き込み
nRF54L15-DKは、内蔵J-Link経由でコマンドラインから書き込みます。
|
1 2 3 4 5 |
# ビルドの実行 .uild.bat nrf # ボードへの書き込み(west flash / nrfutilRunner) .uild.bat flash-nrf |
Arduino® UNO Q のビルド
Arduino® UNO QのSTM32マイコン向けファームウェアをビルドします。
|
1 |
.uild.bat uno |
Python AIツールによるバウンディングボックス可視化
マイコンが推論した物体検出結果をPC上でグラフィカルに可視化するため、Pythonスクリプト(capture_ai.py)を作成しました。
可視化ツールの実行
コマンドプロンプトまたはPowerShellで以下を実行します。
|
1 |
python capture_ai.py |
スクリプトが自動的にマイコンを検出し、推論トリガー('D')を送信します。
マイコンから送られてくる検出ラベル、信頼度スコア、物体座標(x, y, 幅, 高さ)、およびカメラの元画像(96x96 RGB565)を受信し、PC画面上にカラー矩形を描画したプレビュー画像を表示します。
|
1 2 3 4 5 6 7 |
Auto-detected device on COM33: Nordic nRF54L15-DK (M33 App Core / VCOM1) Sending AI capture command 'D'... Received AI Header: format=1 (RGB565), 96x96, detections=1 [1] label='pico', conf=0.82, bbox=(x=28, y=36, w=16, h=16) Timing: DSP=0ms, Inference=128ms, Anomaly=0ms Received image: 18432 / 18432 bytes (100.0%) Saved annotated result to 'detection_result_preview.png' |
推論時間や検出枠が即座に確認できるため、モデルのチューニングや現地テストに非常に便利です。
まとめ
Cortex-M33を搭載した3種類のマイコンボード(Pico 2W / nRF54L15 / UNO Q)において、Zephyr RTOS上で同一のC++コードによる本格的なエッジAI物体検出システムを構築しました。
* 解像度完全一致: カメラ側で96x96を出力し、CPUによるリサイズ処理を完全排除。
* ゼロコピー展開: signal_t コールバックにより、中間バッファ不要で推論テンソルへ直結。
* SIMD高速推論: ARM CMSIS-NNの活用により、Cortex-M33コアで高レスポンスな推論を実現。
* PC可視化連携: Pythonツールにより、推論座標とカメラ画像を重ね合わせたバウンディングボックス表示に対応。
内蔵SRAMが数百KBクラスの省電力マイコンでも、アーキテクチャを工夫すれば本格的なビジョンAIが快適に動作します。
ソースコードはGitHubで公開していますので、ぜひ自分だけのAIモデルを組み込んで動かしてみてください!


コメント