SRAMが1MBに満たない省電力マイコンで、カメラから動画をリアルタイムにストリーミング配信してみました。
対象ボードは、Raspberry Pi Pico 2W、Nordic nRF54L15-DK、そしてArduino® UNO Qです。
今回は、Zephyr RTOSとArducam Mega 3MPを組み合わせ、20〜30 FPSという驚異的なフレームレートでPCへ動画を連続ストリーミングするリファレンス実装を紹介します。
カメラ内蔵ハードウェアJPEGエンコーダの活用法や、32KB×2面のPing-Pongダブルバッファリング、OSのバッファ詰まりを防ぐPython側の非同期受信アーキテクチャなど、実践的なノウハウを詳しく解説します。
Zephyr RTOS & SPIカメラで20〜30FPS動画ストリーミング!【Pico 2W / nRF54L15 / UNO Q 共通MJPEG編】
SRAMが1MBに満たない省電力マイコンで、カメラから動画をリアルタイムにストリーミング配信してみました。
対象ボードは、Raspberry Pi Pico 2W、Nordic nRF54L15-DK、そしてArduino® UNO Qです。
Pico2Wをメインに紹介します。


今回は、Zephyr RTOSとArducam Mega 3MPを組み合わせ、20〜30 FPSという驚異的なフレームレートでPCへ動画を連続ストリーミングするリファレンス実装を紹介します。
カメラ内蔵ハードウェアJPEGエンコーダの活用法や、32KB×2面のPing-Pongダブルバッファリング、OSのバッファ詰まりを防ぐPython側の非同期受信アーキテクチャなど、実践的なノウハウを詳しく解説します。
プロジェクト・GitHubのリンク先
GitHubのリンク先は下記です

実際のデモ動画は下記です
Raspberry Pi Pico 2W(USB経由のリアルタイム動画ストリーミング実演)
マルチボード構成とピンアサイン
本プロジェクトでは、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) |
| PC通信路 | - | 内蔵USB CDC ACM | オンボード J-Link VCOM1 | USART1シリアル (D0/D1) |
リアルタイム動画を実現する4つの技術的工夫
SRAMが数百KBしかないマイコンで高フレームレートな動画配信を行うため、4つの工夫を凝らしました。
ハードウェアJPEG圧縮の徹底活用
非圧縮のRAW画像(例えばVGA 640x480 RGB565)は1フレームあたり約614KBあり、マイコンのSRAM容量もUSB転送帯域も一瞬で破綻します。
そこで、Arducam Megaの内蔵ハードウェアJPEGエンコーダを活用しました。
画像データを撮影の瞬間に約1/25〜1/40に圧縮し、フレームサイズを3〜8KB程度に抑え込んでいます。
動的な解像度最適化(QVGAとQQVGA)
用途に応じて最適な画質と速度を選べるよう、2種類の解像度を用意しました。
* QVGA (320x240): フレームサイズ 4〜7 KB(約15〜25 FPS)
人物の表情や文字がくっきりと認識でき、滑らかな映像が得られます。
* QQVGA (160x120): フレームサイズ 1.5〜3 KB(約20〜30 FPS)
スポーツや高速移動体の監視にも追従できる、超高フレームレート表示が可能です。
静的ダブルバッファリング(Ping-Pong Buffer)
Zephyrの video_import_buffer() を利用し、.bss 領域に32KB × 2面(計64KB)の静的バッファを確保しました。
バッファAをPCへUSB送信している裏で、カメラ側がSPI経由でバッファBへ次フレームを並行キャプチャします。
パイプラインの待ち時間をゼロにすることで、フレーム落ちのないスムーズな動画伝送を実現しました。
ストリーミング専用軽量パケット(*VID)
静止画のキャプチャでは確実性を重視して4KBごとのACKハンドシェイクを用いましたが、動画配信では速度を優先し、連続ストリーミング対応のパケットプロトコルを新規設計しました。
24バイトの自己同期ヘッダー(マジックコード *VID、解像度、フレーム番号、タイムスタンプ)に続けてJPEGデータを流し込み、4バイトのフッター(VID*)で終了します。
パケットロスが発生した場合でも、次のヘッダーで即座に自動復帰できます。
各ボードのスペック比較とリソース消費
各マイコンボードにおける動画配信時の性能測定結果です。
| 項目 | Raspberry Pi Pico 2W | Nordic nRF54L15-DK | Arduino® UNO Q |
|---|---|---|---|
| SoC / コア | RP2350 (Dual M33 @ 150 MHz) | nRF54L15 (M33 @ 128 MHz) | STM32U585 (M33 @ 160 MHz) |
| 内蔵SRAM | 520 KB | 256 KB (最小環境) | 786 KB |
| 標準解像度 | 320 x 240 QVGA | 320 x 240 QVGA | 320 x 240 QVGA |
| 達成フレームレート | 20 〜 30 FPS | 15 〜 25 FPS | 20 〜 30 FPS |
| 平均フレームサイズ | 4.5 〜 7.0 KB | 4.5 〜 7.0 KB | 4.5 〜 7.0 KB |
| バッファ構成 | 32 KB x 2面 (計 64 KB) | 32 KB x 2面 (計 64 KB) | 32 KB x 2面 (計 64 KB) |
| SRAM使用量 | 約 18.5% (空き 420 KB) | 約 43.8% (空き 144 KB) | 約 10.4% (空き 686 KB) |
| Flash使用量 | 約 74 KB / 4 MB (1.8%) | 約 53 KB / 1.5 MB (3.5%) | 約 48 KB / 2 MB (2.3%) |
最もSRAMが小さいnRF54L15-DK(256KB)でも、SRAM使用率はわずか43.8%であり、余裕を持って動作しています。
ビルドと書き込み手順(West)
各ボード向けのファームウェアは、プロジェクト内のバッチファイル(build.bat)で簡単にビルドできます。
ソースコードはGitHubで公開しています。
Raspberry Pi Pico 2W のビルド手順
PowerShellで仮想環境を起動し、ビルドを実行します。
|
1 2 |
cd zephyr-spicam/unified_video_spicam .uild.bat pico |
生成された build\pico\zephyr\zephyr.uf2 を、BOOTSELモードで接続したPico 2Wのドライブへドラッグ&ドロップして書き込みます。
Nordic nRF54L15-DK のビルドと書き込み手順
nRF54L15-DKは、コマンドラインから直接書き込み可能です。
|
1 2 3 4 5 |
# ビルドの実行 .uild.bat nrf # ボードへの書き込み .uild.bat flash-nrf |
Arduino® UNO Q のビルド手順
Arduino® UNO QのSTM32マイコン向けファームウェアをビルドします。
|
1 |
.uild.bat uno |
Python低遅延ストリーミングビューワー
PC側で動画を表示するため、Python(PySerial + OpenCV)による高機能ビューワー(video_stream.py)を作成しました。
OSバッファの背圧を解消する非同期受信
通常のシリアル読み込みループでは、PC側の描画処理に引きずられて受信バッファが溜まり、数秒の表示遅延(ラグ)が発生しがちです。
本ビューワーでは、バックグラウンドスレッドが32KBのリングバッファを介してシリアルデータを常時一括読み込み(Bulk Read)します。
メインスレッドは常に「最新の完全なフレーム」だけをデコードして描画するため、遅延(レイテンシ)ほぼゼロのリアルタイム表示を実現しています。
ストリーミングの実行方法
付属のバッチファイルでワンクリック起動できます。
|
1 |
.\stream.bat |
またはPythonから直接実行します。
|
1 |
python video_stream.py |
画面上にはリアルタイムFPS、解像度、フレームサイズがHUD表示されます。
キーボード操作で一時停止(スペースキー)、解像度切替(数字の1キーでQVGA、2キーでQQVGA)、JPEG写真の保存(Cキー)が手軽に行えます。
まとめ
1MB未満のSRAMしか持たない省電力マイコン(Pico 2W / nRF54L15 / UNO Q)で、Zephyr RTOSとArducam Megaを活用した20〜30 FPSのリアルタイム動画ストリーミングを実現しました。
* ハードウェア圧縮: 内蔵JPEGエンコーダにより、フレームサイズを3〜7KBに圧縮。
* Ping-Pongダブルバッファ: 64KBの静的バッファでキャプチャと送信を完全並行化。
* ストリーミングプロトコル: 軽量パケット設計で高スループットと即時同期を両立。
* 超低遅延ビューワー: Python非同期受信により、PC側での描画遅延をゼロ化。
マイコンによる見守りカメラやロボットの視覚センサ、遠隔モニタリングなど、多彩なIoT応用に活用できる実用的なリファレンスです。
ソースコードはGitHubで公開していますので、ぜひ滑らかな動画ストリーミングを体験してみてください!

コメント