Podczas uruchamiania systemu wczesnego wykrywania awarii łożysk na silniku przemysłowym inżynierowie stają przed typowym dylematem. Na biurku stacji roboczej sieć konwolucyjna analizująca widmo wibracji osiąga 99% dokładności w środowisku PyTorch. Jednak próba wgrania tego samego algorytmu do mikrokontrolera STM32 z 64 KB pamięci RAM natychmiast kończy się błędem HardFault wywołanym przepełnieniem stosu lub alokacją tablicy tensora przekraczającą fizyczne możliwości układu. Różnica między teoretycznym modelem uczenia maszynowego a jego stabilną pracą w pętli pomiarowej układu wbudowanego to sedno inżynierii TinyML.
Uruchomienie sztucznej inteligencji na krawędzi (Edge AI) bezpośrednio na mikrokontrolerach wymaga radykalnej zmiany paradygmatu programowania. Nie dysponujemy tutaj systemem operacyjnym z pamięcią wirtualną, wielordzeniowym GPU ani nielimitowanym źródłem zasilania. Sukces wdrożenia zależy od rygorystycznego audytu zasobów, dokładnej kwantyzacji wag oraz optymalnego zarządzania pamięcią statyczną.
Architektura TinyML a ograniczenia mikrokontrolerów: RAM, Flash i moc obliczeniowa
Mikrokontrolery operują w skrajnie ograniczonym środowisku sprzętowym. Podczas gdy serwerowe systemy AI liczą zasoby w gigabajtach, w świecie mikrokontrolerów waluta projektowa to kilobajty pamięci operacyjnej oraz ułamki miliamperów poboru prądu. Zrozumienie fizycznego podziału ról między pamięcią nieulotną a operacyjną stanowi pierwszy krok każdego audytu wykonalności.
Pamięć SRAM vs Pamięć Flash w cyklu inferencji
W procesie inferencji pamięć Flash przechowuje skompilowany kod binarny, strukturę topologii sieci neuronowej oraz stałe wagi modelu. Jest to pamięć tylko do odczytu, która nie degraduje się po wyłączeniu zasilania. Z kolei SRAM jest zasobem krytycznym – to w niej alokowane są tymczasowe tensory pośrednie (aktywacje warstw), bufory wejściowe z czujników (np. próbek z akcelerometru czy mikrofonu) oraz stos wykonawczy programu.
Jeżeli architektura modelu generuje mapy cech o rozmiarze 40 KB w warstwie ukrytej, a układ dysponuje 32 KB wolnego SRAM, model nie uruchomi się bez względu na to, jak duża jest pamięć Flash mikrokontrolera. Pamięć operacyjna definiuje maksymalny rozmiar największego pojedynczego tensora aktywacji w łańcuchu przetwarzania sieci.
Architektura procesorów brzegowych: Cortex-M0+, M4, M7 a dedykowane akceleratory NPU
Wydajność obliczeniowa zależy bezpośrednio od zestawu instrukcji procesora. Proste rdzenie, takie jak ARM Cortex-M0+, wykonują operacje matematyczne sekwencyjnie w wielu cyklach zegara i nie posiadają jednostki zmiennoprzecinkowej (FPU). Modele na nich uruchamiane muszą być bezwzględnie skwantowane do liczb całkowitych.
Rdzenie Cortex-M4 i Cortex-M7 wprowadzają instrukcje SIMD (Single Instruction, Multiple Data) oraz jednostkę FPU, co pozwala na jednoczesne przetwarzanie wielu operacji mnożenia i akumulacji (MAC). Na szczycie hierarchii znajdują się nowoczesne mikrokontrolery hybrydowe (np. układy z rdzeniami Cortex-M55 i koprocesorem Ethos-U55), które dysponują dedykowanymi blokami NPU (Neural Processing Unit) sprzętowo przyspieszającymi operacje na macierzach.
Sygnały ostrzegawcze przed startem projektu
Przed przystąpieniem do implementacji należy ocenić następujące punkty kontrolne:
- Rozmiar bufora aktywacji przekracza 70% dostępnego SRAM: Ryzyko kolizji ze stosem aplikacji i buforami komunikacyjnymi (BLE, Wi-Fi, CAN).
- Zależność od operacji zmiennoprzecinkowych na rdzeniu M0/M3: Drastyczny spadek przepustowości (nawet 50-krotnie dłuższy czas inferencji).
- Brak obsługi DMA dla transferu danych z sensorów: Procesor marnuje cykle na odpytywanie czujnika zamiast wykonywać inferencję lub przechodzić w tryb uśpienia.
Jeśli dostępna pamięć SRAM układu jest mniejsza niż suma bufora wejściowego i dwóch największych kolejnych warstw aktywacji sieci, architektura sprzętowa wymaga natychmiastowej zmiany lub model musi zostać poddany głębokiej redukcji. W przeciwnym razie projekt utknie na etapie alokacji pamięci runtime.
Przygotowanie i dobór architektury sieci neuronowej pod mikrokontroler
Projektowanie modelu dla urządzeń brzegowych różni się od standardowego deep learningu. Zamiast budować głębokie sieci o milionach parametrów, inżynier TinyML dąży do maksymalizacji wskaźnika dokładności w przeliczeniu na pojedynczy parametr wagowy. Kluczem jest przeniesienie części obciążenia obliczeniowego na klasyczne algorytmy cyfrowego przetwarzania sygnałów (DSP).
Warstwy i operatory obsługiwane sprzętowo
Frameworki uruchomieniowe dla mikrokontrolerów wspierają jedynie ograniczony podzbiór operatorów dostępnych w pełnych wersjach TensorFlow czy PyTorch. Przed zaprojektowaniem architektury należy zweryfikować listę wspieranych warstw w docelowym środowisku uruchomieniowym.
Standardowo bezpiecznymi i zoptymalizowanymi operatorami są: sploty 2D (Conv2D), sploty separowalne głębokościowo (DepthwiseConv2D), warstwy gęste (Fully Connected), pooling (MaxPool2D, AveragePool2D) oraz aktywacje ReLU i Softmax. Użycie niestandardowych funkcji aktyw
acji (np. Swish czy GELU) zazwyczaj wymusza ich kosztowną, programową emulację zmiennoprzecinkową, co drastycznie obniża przepustowość pętli głównej mikrokontrolera.
W praktyce przemysłowej zamiast surowych danych czasowych z czujników (np. 1000 próbek z akcelerometru trójosiowego) do sieci przekazuje się ich reprezentację częstotliwościową. Zastosowanie szybkiej transformacji Fouriera (FFT) lub banków filtrów mel-spektralnych (MFCC) redukuje wymiarowość tensora wejściowego nawet o 90%, przenosząc ciężar wstępnej filtracji szumu na wysoce zoptymalizowane pod kątem DSP biblioteki matematyczne.
Kwantyzacja: przejście z FP32 do INT8
Natywne modele trenowane w środowiskach takich jak PyTorch czy TensorFlow zapisują wagi w formacie zmiennoprzecinkowym pojedynczej precyzji (32-bitowy float, FP32). Wdrożenie takiego formatu na małym mikrokontrolerze jest nieefektywne: czterokrotnie zwiększa zapotrzebowanie na pamięć Flash i SRAM oraz uniemożliwia wykorzystanie instrukcji wektorowych SIMD operujących na rejestrach stałoprzecinkowych.
Kwantyzacja potreningowa (PTQ) vs kwantyzacja świadoma treningu (QAT)
Kwantyzacja do formatu 8-bitowej liczby całkowitej ze znakiem (INT8) mapuje ciągły zakres wartości wag i aktywacji na 256 dyskretnych stanów całkowitych zgodnie z zależnością:
r = S * (q - Z)
gdzie r to rzeczywista wartość zmiennoprzecinkowa, S (scale) to współczynnik skali, q to skwantowana wartość całkowita, a Z (zero-point) reprezentuje punkt zerowy siatki kwantyzacji.
W podejściu Post-Training Quantization (PTQ) konwersja odbywa się na gotowym modelu. Wymaga ona jedynie tzw. zbioru kalibracyjnego (reprezentatywnej próbki danych wejściowych bez etykiet), na bazie którego konwerter wyznacza dynamiczny zakres aktywacji każdej warstwy. Jeżeli architektura jest wrażliwa na utratę precyzji i wskaźnik dokładności spada o więcej niż 2–3%, stosuje się Quantization-Aware Training (QAT). W tym trybie szum kwantyzacji jest modelowany już na etapie wstecznej propagacji błędu podczas douczania sieci, co pozwala wagom dostosować się do ograniczeń stałoprzecinkowych.
Frameworki uruchomieniowe i generowanie kodu C/C++
Modele po procesie kwantyzacji i optymalizacji grafu (usunięciu zbędnych węzłów treningowych, połączeniu warstw Batch Normalization z wagami splotu) muszą zostać przetłumaczone na postać binarną lub kod źródłowy w języku C/C++. Na rynku mikrokontrolerów dominują trzy podejścia do silników inferencyjnych:

1. TensorFlow Lite for Microcontrollers (TFLM) – interpretator uruchomieniowy o minimalnym narzucie, który wykonuje spłaszczony bufor binarny FlatBuffers (plik .tflite przekształcony w tablicę const unsigned char model_data[] w pamięci Flash). Silnik dynamicznie odczytuje kolejne warstwy i wywołuje odpowiadające im kernele obliczeniowe.
2. CMSIS-NN i STMicroelectronics X-CUBE-AI – podejście oparte na bezpośredniej generacji kodu. Model nie jest interpretowany przez zewnętrzny silnik runtime, lecz tłumaczony wprost na sekwencję wywołań funkcji w C. Kernele CMSIS-NN wykorzystują specyficzne instrukcje asemblera Cortex-M (np. __SMLAD – podwójne mnożenie z akumulacją w jednym cyklu), co zapewnia maksymalną wydajność kosztem elastyczności modyfikacji modelu po kompilacji.
3. Edge Impulse EON Compiler – technologia kompilacji grafu, która eliminuje narzut pamięciowy tradycyjnych silników interpretowanych. Kod wynikowy sieci neuronowej jest generowany jako czysty kod źródłowy C++, co pozwala zaoszczędzić od 25% do 55% pamięci RAM w porównaniu z TFLM.
Zarządzanie pamięcią: Tensor Arena i determinizm wykonania
W krytycznych systemach wbudowanych dynamiczna alokacja pamięci za pomocą funkcji malloc() lub operatora new jest niedopuszczalna ze względu na ryzyko fragmentacji sterty (heap) oraz nieprzewidywalny czas wykonania. Środowiska TinyML rozwiązują ten problem za pomocą mechanizmu Tensor Arena.
Tensor Arena to statycznie zaalokowana, ciągła tablica bajtów w pamięci SRAM mikrokontrolera:
constexpr int kTensorArenaSize = 24 * 1024; // 24 KB SRAM
alignas(16) uint8_t tensor_arena[kTensorArenaSize];
Wyrównanie do 16 bajtów (alignas(16)) jest wymagane przez instrukcje wektorowe SIMD. Podczas inicjalizacji silnik inferencyjny (np. tflite::MicroInterpreter) analizuje graf obliczeniowy modelu i planuje nakładające się na siebie bufory aktywacji. Jeśli warstwa trzecia nie potrzebuje już danych z warstwy pierwszej, ten sam fizyczny obszar pamięci wewnątrz Tensor Areny jest natychmiast przydzielany dla warstwy czwartej. Dzięki temu do wykonania całej sieci potrzebny jest jedynie taki rozmiar bufora, jaki wyznacza najbardziej pamięciożerny punkt przecięcia dwóch sąsiadujących warstw.
Integracja z peryferiami i pętlą pomiarową
Prawidłowo zintegrowany model TinyML nie powinien blokować jednostki centralnej w oczekiwaniu na dane z przetworników. Standardem w architekturze systemów brzegowych jest wykorzystanie kontrolera bezpośredniego dostępu do pamięci (DMA) w połączeniu z podwójnym buforowaniem (double-buffering / ping-pong buffer).
W takim modelu przetwornik ADC lub interfejs cyfrowy I2S/SPI przesyła surowe dane z czujników bezpośrednio do bufora A w pamięci RAM bez udziału rdzenia CPU. Gdy bufor A zostaje zapełniony, sprzętowe przerwanie DMA przełącza strumień danych na bufor B, a procesor rozpoczyna ekstrakcję cech (DSP) i inferencję na danych z bufora A. Taka izolacja czasowa gwarantuje brak utraty próbek pomiarowych przy zachowaniu pełnej przepustowości obliczeniowej.
W systemach opartych na systemie czasu rzeczywistego (FreeRTOS lub Zephyr RTOS) inferencja uruchamiana jest w dedykowanym wątku o niskim lub średnim priorytecie. Wątki komunikacyjne (np. obsługa stosu Bluetooth Low Energy lub magistrali CAN) zachowują wyższy priorytet, co zapobiega zrywaniu połączeń bezprzewodowych podczas wykonywania ciężkich operacji macierzowych.
Profilowanie i optymalizacja poboru energii
Kluczowym wskaźnikiem efektywności wdrożenia TinyML na zasilaniu bateryjnym nie jest liczba klatek na sekundę, lecz całkowity koszt energetyczny pojedynczej inferencji wyrażony w mikrodżulach (μJ). Optymalizacja opiera się na strategii race-to-sleep: mikrokontroler powinien wykonać obliczenia z maksymalną dopuszczalną częstotliwością taktowania zegara w jak najkrótszym czasie, po czym natychmiast przejść w głęboki tryb uśpienia (Stop lub Standby), gdzie pobór prądu spada z kilkunastu miliamperów do pojedynczych mikroamperów.
Dokładny pomiar czasu wykonania inferencji w kodzie mikrokontrolera wykonuje się przy użyciu sprzętowego licznika cykli rdzenia (DWT – Data Watchpoint and Trace):
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
uint32_t start_cycles = DWT->CYCCNT;
TfLiteStatus invoke_status = interpreter->Invoke();
uint32_t total_cycles = DWT->CYCCNT - start_cycles;
Dzieląc liczbę cykli przez częstotliwość rdzenia, inżynier uzyskuje precyzyjny czas trwania samej fazy obliczeniowej. Jeśli czas ten przekracza budżet czasowy aplikacji, pierwszym krokiem optymalizacyjnym jest redukcja liczby filtrów w warstwach splotowych (pruning) lub zwiększenie kroku splotu (stride), co radykalnie zmniejsza liczbę operacji mnożenia z akumulacją (MACC) przy minimalnym wpływie na ostateczną metrykę modelu.
Efektywne wdrożenie TinyML na urządzeniu brzegowym wymaga precyzyjnego balansu pomiędzy strukturą sieci neuronowej a fizyczną architekturą układu scalonego. Znajomość ograniczeń pamięci SRAM, sprawna kwantyzacja do INT8 oraz eliminacja dynamicznej alokacji pamięci pozwalają zamienić surowy model uczenia maszynowego w niezawodny, energooszczędny komponent oprogramowania wbudowanego.
Walidacja modelu na fizycznym krzemie (Hardware-in-the-Loop)
Zbieżność metryk w środowisku symulacyjnym na komputerze deweloperskim nie gwarantuje identycznego zachowania sieci po kompilacji na docelową architekturę. Różnice w implementacji operacji stałoprzecinkowych, zaokrągleniach w instrukcjach SIMD czy specyfice kernelów asemblerowych mogą prowadzić do subtelnych rozbieżności numerycznych. Z tego powodu kluczowym etapem wdrożenia jest walidacja typu Hardware-in-the-Loop (HIL).
Procedura polega na przesłaniu reprezentatywnego zbioru testowego przez interfejs UART lub debuger SWD bezpośrednio do pamięci RAM mikrokontrolera, uruchomieniu inferencji na układzie scalonym i pobraniu tensora wyjściowego z powrotem na stację roboczą. Porównanie wyników z wyjściem referencyjnym modelu w formacie FP32 pozwala wyznaczyć błąd średniokwadratowy (MSE) kwantyzacji oraz zweryfikować, czy funkcja Softmax nie faworyzuje fałszywych klas na skutek kumulacji błędów nasycenia w rejestrach akumulatora.
Architektura aktualizacji OTA i zarządzanie wagami
Wdrożenie modelu na urządzeniu brzegowym rzadko jest procesem jednorazowym. W miarę zużywania się komponentów mechanicznych lub zmian w profilu szumów otoczenia, model może wymagać doregulowania. Ponieważ pamięć mikrokontrolera jest ograniczona, architektura oprogramowania układowego powinna rozdzielać sam silnik wykonawczy od wag sieci neuronowej.
Zastosowanie relokowalnej struktury danych w wydzielonym sektorze pamięci Flash pozwala na aktualizację Over-the-Air (OTA) wyłącznie tensora wag, bez konieczności ponownego flashowania całego firmware’u aplikacji. W takim scenariuszu procesor weryfikuje sumę kontrolną nowego bloku wagowego, a wskaźnik modelu w strukturze TFLM zostaje zaktualizowany na nowy adres w pamięci nielotnej. Taki podział skraca czas transmisji radiowej (BLE, LoRaWAN lub NB-IoT) z minut do kilku sekund, drastycznie zmniejszając zużycie baterii podczas zdalnego utrzymania floty urządzeń.
Ewolucja sprzętu: dedykowane mikro-NPU
Chociaż klasyczne rdzenie Cortex-M4 i M7 z rozszerzeniami DSP dobrze radzą sobie z prostymi zadaniami detekcji anomalii czy rozpoznawania pojedynczych słów kluczowych (Keyword Spotting), rozwój TinyML zmierza w kierunku wyspecjalizowanych akceleratorów NPU (Neural Processing Unit) zintegrowanych bezpośrednio w strukturze mikrokontrolera.
Nowoczesne układy wyposażone w rdzenie ARM Cortex-M55 z technologią wektorową Helium (MVE) oraz koprocesory takie jak ARM Ethos-U55 przenoszą obliczenia tensorowe całkowicie poza główny potok wykonawczy CPU. Dedykowane jednostki MAC w akceleratorach mikro-NPU wykonują setki operacji splotowych w jednym cyklu zegara przy zachowaniu znikomego poboru prądu. Otwiera to drogę do uruchamiania na urządzeniach brzegowych znacznie głębszych modeli wizyjnych (np. MobileNetV2 lub person detection) z częstotliwością odświeżania rzędu 15–30 klatek na sekundę, zachowując restrykcyjny budżet energetyczny zasilania bateryjnego.
