14.2.2.2. Budowanie obrazu ROMFS¶
Obraz ROMFS to rezydujący w pamięci flash, tylko-do-odczytu system plików, który środowisko uruchomieniowe automatycznie montuje w /rom. Rozwiązuje on problem zasobów, którym zakończyła się poprzednia strona: pliki modeli uczenia maszynowego, tablice etykiet, konfiguracja JSON, szablony obrazów – wszystko, co aplikacja otwiera i odczytuje, ale nigdy nie zapisuje – trafia do builda bez ponoszenia kosztu osadzania jako literały Python.
Trzy rzeczy sprawiają, że ROMFS jest właściwym narzędziem dla dostarczanych zasobów:
System plików jest częścią obrazu oprogramowania układowego. Użytkownicy końcowi nie mogą usunąć pliku z
/rom, edytować go ani zastąpić własnym.Pliki w
/romsą dostępne w miejscu. Konsumenci tacy jak modułmlładujący plik modelu otrzymują bezpośredni wgląd w pamięć flash bez kopii w RAM – wielomegabajtowy model na/rom„ładuje się” zasadniczo za darmo, podczas gdy ten sam plik na/sdcardjest wczytywany do RAM w momencie ładowania i tam pozostaje przez cały czas życia referencji. Zwykłeopen()+readkopiuje na żądanie: każde wywołanieread(n)kopiujenbajtów z pamięci flash do RAM w momencie wywołania, a samoread()żąda całego pliku./romoraz/rom/libsą dodawane dosys.pathpodczas rozruchu. Pakiety Python umieszczone w obrazie można importować po nazwie; nic szczególnego w miejscu wywołania.
14.2.2.2.1. Budowanie obrazu¶
Obrazy ROMFS są tworzone, edytowane i wgrywane za pomocą IDE. Używaj go jako źródła prawdy dla zawartości każdej dostarczanej partycji ROMFS.
Powód, dla którego to ma znaczenie: pliki modeli mają wymagania dotyczące wyrównania, które loader wymusza w czasie wykonywania. Pliki .tflite muszą być dopełnione do granic 16-bajtowych, a NPU układu N6 wymaga wyrównania 32-bajtowego dla skompilowanych modeli. IDE stosuje to dopełnienie automatycznie podczas zapisywania obrazu. Narzędzia, które przechodzą drzewo źródłowe bez zastosowania dopełnienia – w szczególności mpremote romfs – tworzą obraz, który montuje się poprawnie, ale którego modele zawodzą przy pierwszym wywołaniu wnioskowania.
Edytor ROMFS w IDE to interaktywny widok zawartości obrazu. Pliki i foldery można dodawać, zmieniać im nazwy i usuwać w pamięci; zapis wypisuje wynik jako plik .img gotowy do wgrania. Typowa struktura dla aplikacji dostarczającej model wraz z pewnymi zasobami i pakietem Python wygląda następująco:
model.tflite
labels.txt
config.json
templates/
calibration.jpg
lib/
mylib/
__init__.py
helpers.py
Wskazówka
Zarówno IDE, jak i mpremote kompilują krzyżowo pliki .py do kodu bajtowego .mpy w trakcie umieszczania ich w obrazie ROMFS, dzięki czemu kamera importuje je bez ponoszenia kosztu parsowania podczas ładowania. Pliki źródłowe w edytorze pozostają .py; obraz zawiera .mpy.
Gdy obraz zostanie wgrany, drzewo jest widoczne z poziomu MicroPython w /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. Większość aplikacji żyje w ROMFS¶
ROMFS jest właściwym miejscem dla niemal wszystkiego, co dostarcza aplikacja: bibliotek, które importuje, plików modeli, które ładuje, konfiguracji, którą odczytuje, dowolnego zasobu, którego wynik pochodzi z narzędzia budującego emitującego drzewo plików (konwertery modeli, potoki obrazów, pakowarki zasobów) oraz – co istotne – samego kodu aplikacji.
Strona zamrożonych modułów powinna pozostać niewielka: boot.py do konfiguracji przed REPL, main.py jako cienki punkt wejścia oraz tylko te biblioteki, bez których kamera naprawdę nie może się uruchomić. Wszystko inne trafia do ROMFS, gdzie iterowanie nad tym to świeży plik .img zapisany z IDE i ponownie wgrany – bez konieczności przebudowywania oprogramowania układowego, bez potrzeby posiadania pod ręką narzędzi do tego.
Wzorzec, który z tego wynika, to main.py, które nie robi nic poza delegowaniem do aplikacji rezydującej w ROMFS:
# main.py (frozen)
import app
app.run()
# /rom/app/__init__.py (in ROMFS)
def run():
...
Zmiana w app to edycja ROMFS i ponowne wgranie. Build oprogramowania układowego pozostaje niezmieniony przez cały czas życia produktu, chyba że coś po stronie zamrożonej faktycznie musi się zmienić.