14.2.2.2. ROMFSイメージのビルド

ROMFSイメージとは、ランタイムが /rom に自動マウントする、フラッシュ常駐の読み取り専用ファイルシステムです。前のページで締めくくったアセットの問題を解決します。機械学習のモデルファイル、ラベルテーブル、JSON設定、画像テンプレートなど、アプリケーションが開いて読み込むものの書き込むことは決してないものが、Pythonリテラルとして埋め込むコストを払うことなくビルドに乗ります。

ROMFSが出荷アセットに適したツールである理由は3つあります:

  • ファイルシステムはファームウェアイメージの一部です。エンドユーザーは /rom からファイルを削除したり、編集したり、自分のものに置き換えたりできません。

  • /rom 内のファイルはそのままの場所でアクセスできます。モデルファイルを読み込む ml モジュールのような利用側は、RAMへのコピーなしにフラッシュへの直接ビューを取得します。/rom 上の数メガバイトのモデルは実質的に無料で「ロード」されますが、/sdcard 上の同じファイルはロード時にRAMに読み込まれ、その参照が存続する限りそこに留まります。通常の open() + read は要求に応じてコピーします。各 read(n) 呼び出しは、その呼び出しの時点でフラッシュからRAMへ n バイトをコピーし、引数なしの read() はファイル全体を要求します。

  • /rom/rom/lib は起動時に sys.path に追加されます。イメージに入れたPythonパッケージは名前でインポートでき、呼び出し側で特別なことは何も必要ありません。

14.2.2.2.1. イメージのビルド

ROMFSイメージはIDEを通じて作成、編集、フラッシュされます。すべての出荷ROMFSパーティションの内容に関する信頼できる情報源として、IDEを使ってください。

これが重要な理由: モデルファイルにはアライメント要件があり、ランタイムのローダーがそれを強制します。.tflite ファイルは16バイト境界にパディングされなければならず、N6のNPUはコンパイル済みモデルに32バイトアライメントを要求します。IDEはイメージを書き込む際にそのパディングを自動的に適用します。パディングを適用せずにソースツリーを走査するツール(特に mpremote romfs)は、マウントは問題なくできるものの、モデルが最初の推論呼び出しで失敗するイメージを生成します。

IDEのROMFSエディタは、イメージの内容を対話的に表示するものです。ファイルとフォルダはメモリ上で追加、リネーム、削除でき、保存すると結果がフラッシュ可能な .img ファイルとして書き出されます。モデルといくつかのアセット、そしてPythonパッケージを出荷するアプリケーションの典型的な構造は次のようになります:

model.tflite
labels.txt
config.json
templates/
    calibration.jpg
lib/
    mylib/
        __init__.py
        helpers.py

Tip

IDEと mpremote のどちらも、ROMFSイメージに入れる途中で .py ファイルを .mpy バイトコードにクロスコンパイルするため、カメラはロード時に解析コストを払うことなくそれらをインポートします。エディタ内のソースファイルは .py のままですが、イメージには .mpy が含まれます。

イメージがフラッシュされると、ツリーはMicroPythonから /rom/ で見えるようになります:

>>> import os
>>> os.listdir('/rom')
['model.tflite', 'labels.txt', 'config.json', 'templates', 'lib']
>>> import mylib
>>> mylib.helpers
<module 'mylib.helpers' from '/rom/lib/mylib/helpers.mpy'>

14.2.2.2.2. アプリケーションの大半はROMFSに置く

ROMFSは、アプリケーションが出荷するほぼすべてのものにとって適切な置き場所です。インポートするライブラリ、ロードするモデルファイル、読み込む設定、ファイルツリーを出力するビルドツール(モデルコンバーター、画像パイプライン、アセットパッカー)の出力であるあらゆるアセット、そして、重要なことに、アプリケーションコードそのものです。

フリーズモジュール側は小さく保つべきです。REPL前のセットアップ用の boot.py、薄いエントリーポイントとしての main.py、そしてカメラが本当にそれなしには起動できないライブラリだけです。それ以外はすべてROMFSに入れます。ROMFS上での反復作業は、IDEから新しい .img を保存して再フラッシュするだけで済み、ファームウェアの再ビルドは不要で、それを行うためのツールチェーンも手元に要りません。

そこから導かれるパターンは、ROMFS常駐のアプリケーションに委譲するだけの main.py です:

# main.py (frozen)
import app
app.run()

# /rom/app/__init__.py (in ROMFS)
def run():
    ...

app への変更はROMFSの編集と再フラッシュです。フリーズ側で実際に何かを変更しなければならない場合を除き、ファームウェアのビルドは製品の存続期間中そのまま据え置かれます。