Schlauer Zwerg: Maschinelles Lernen mit dem Raspberry Pi Pico, Teil 1

Mit TinyML lässt sich der Raspi Pico ohne Internetanbindung für Machine-Learning-Anwendungen nutzen.

Lesezeit: 13 Min.
In Pocket speichern
vorlesen Druckansicht Kommentare lesen 39 Beiträge
Künstliche Intelligenz, KI

(Bild: Gerd Altmann, gemeinfrei)

Von
  • Lars Gregori
Inhaltsverzeichnis

Der Raspberry Pi Pico eignet sich gut für Machine-Learning-Anwendungen. Im Rahmen dieser dreiteiligen Artikelserie entsteht als Beispielanwendung eine einfache XOR-Schaltung (Exklusiv Oder) mit zwei Tastern und einer Leuchtdiode. Anschließend folgt das Training eines ML-Modells, das als TinyML-Anwendung auf dem Pico läuft. Das Modell erkennt die unterschiedlichen Zustände der Taster und lässt passend eine Leuchtdiode leuchten. Der erste Teil der Serie widmet sich dem Aufbau und dem Test der Schaltung, bevor im zweiten Teil das Modelltraining ansteht.

Das Auswerten der Taster ist rein exemplarisch und kein typischer Anwendungsfall für Machine Learning. Es soll als Beispiel für konkrete Anwendungen im Internet der Dinge dienen. Dort lassen sich unterschiedliche Sensordaten und Messwerte lesen. Darüber hinaus kann eine Anwendung die Daten von Sprache, Bilder, Videos und Gesten verarbeiten. Dadurch ergeben sich diverse Anwendungsszenarien unter anderem in den Bereichen Einzelhandel, Gesundheitsversorgung, Smart Home, industrielle Verarbeitung, Landwirtschaft und autonomes Fahren. Ebenso wie das IoT (Internet of Things) in unterschiedliche Feldern Einzug hält, kann das maschinelle Lernen auf den Endgeräten in Zukunft ein Teil davon sein.

Im Gegensatz zu den anderen Raspberry-Pi-Boards besitzt der Raspberry Pi Pico mit dem RP2040 einen Mikrocontroller (MCU). Dabei handelt es sich um einen 32-Bit Dual ARM Cortex M0+. Er wurde von der Raspberry-Pi-Foundation entwickelt, hat zwei MByte Flash-Speicher und lediglich 264 KByte RAM – zum Vergleich: Ein MS-DOS-Rechner hatte seinerzeit 640 KByte.

Das reicht jedoch aus, um unterschiedliche, insbesondere kleine Modelle im Bereich des Machine Learning lokal auf dem Raspberry Pi Pico auszuführen. Dadurch kann er Sensordaten lesen und direkt verarbeiten, ohne eine Internetverbindung zu benötigen. Daraus ergibt sich zum einen ein Geschwindigkeitsvorteil, da in dem Fall die Verarbeitung der Daten ohne Umweg durch das Internet in Echtzeit erfolgt. Zum anderen ist die Verarbeitung unabhängig von der Verfügbarkeit der Verbindung und einem Server.

Schließlich verlassen keine potenziell sensiblen Daten Das Gerät. Im Fall des Raspberry Pi Pico entfällt die zusätzliche Hardware für die Kommunikation, wodurch sich der Preis und der Energiebedarf reduziert.

Grundsätzlich benötigt das Internet der Dinge, wie der Name nahelegt, eine Internetverbindung. Da der Pico im Gegensatz zum regulären Raspberry Pi von Haus aus keine solche Verbindung mitbringt, lässt er sich nicht direkt als IoT-Gerät verwenden. Er kann jedoch auf Ereignisse reagieren und ein anderes Gerät aktivieren. Dieses Vorgehen nutzen beispielsweise Sprachassistenten: Ein Mikrocontroller erkennt das zum Aktivieren genutzte Wake Word und weckt das Hauptgerät auf, das anschließend über das Internet kommuniziert. In diesem Fall steht die Privatsphäre mehr im Vordergrund als der Energiebedarf, da ein Sprachassistent meist permanent mit der Steckdose verbunden ist.

Anders verhält es sich bei Geräten, die autonom und mit geringem Energiebedarf Messwerte auf Anomalien prüfen und nur in wenigen Fällen oder gar nicht kommunizieren müssen. Sie lassen sich mit einer Batterie betreiben, wobei zu beachten ist, dass bei einem größeren Modell der Mikrocontroller mehr Rechenschritte ausführt und somit mehr Energie benötigt. Daher spielt bei kleinen Gräten die Größe des Modells eine entscheidende Rolle in Bezug auf Speicherplatz, Energiebedarf und Geschwindigkeit. Die Genauigkeit wiederum reduziert sich mit einem kleineren Modell und hat deshalb meist einen geringeren Stellenwert. Ist sie jedoch wie bei bestimmten Bereichen des autonomen Fahrens notwendig, greifen die Hersteller auf eine stärkere Hardware zurück. Dabei spielt wiederum der Speicherplatz und Energiebedarf eine untergeordnete Rolle.

TinyML ist kein eigenes Framework, sondern der Begriff steht für Tiny Machine Learning. Viele denken bei ML an die Cloud beziehungsweise leistungsstarke Rechner und spezielle Systeme, die Modelle trainieren und ausführen. Inzwischen können allerdings sogar Smartphones mit speziellen Chips Aufgaben im Bereich des Machine Learning direkt auf dem Telefon ausführen. Allerdings beschränken sich deren Aufgaben auf die Ausführung. Das Training des Modells erfolgt vor allem bei großen Datenmengen nicht auf dem Telefon, sondern auf leistungsfähigen Rechnern – typischerweise in der Cloud. Dasselbe ist auch bei TinyML der Fall, bei dem Mikrocontroller lediglich die trainierten Modelle ausführen und nicht trainieren.

Das weit verbreitete ML-Framework TensorFlow deckt sowohl das Training als auch die Ausführung ab. Dabei kann es Graphics Processing Units (GPU) und Tensor Processing Units (TPU) nutzen, die für die ML-Berechnungen optimiert sind. TensorFlow Lite ist ein Unterprojekt von TensorFlow, das für Smartphones konzipiert ist und künftig standardmäßig in Android enthalten sein wird. Daneben existiert eine Auslagerung in das separate GitHub-Projekt tflite-micro.

TensorFlow Micro ist unterhalb von TensorFlow Lite angesiedelt und konzentriert sich auf kleine Geräte, auf die der Code angepasst ist. Die für das Training benötigten Teile fehlen komplett und der für die Ausführung notwendige Teil implementiert nicht den kompletten Befehlssatz. Die reduzierte Codegröße ist wichtig, da die "Tiny"-Geräte im Speicherplatz begrenzt sind. Die Reduktion kann allerdings dazu führen, dass nicht jedes Modell auf dem Gerät lauffähig ist. Das kann auch für Modelle gelten, die grundsätzliche von der Größe auf das Gerät passen, aber Operatoren verwenden, die in der Implementierung fehlen. Dazu gehörten bis vor kurzem die Recurrent Neural Networks (RNN) wie LSTM (Long short-term memory) und GRU (Gated recurrent unit), die bei der Vorhersage von Sequenzen beispielsweise in natürlicher Sprache Anwendung finden. Fehlen die benötigten Operatoren, müssen andere Modelle herhalten. Für das Exklusiv-Oder-Beispiel kommt ein einfaches neuronales Netz zum Einsatz, das sich mit TensorFlow Micro verträgt.

Vor dem Blick auf das Modell steht zunächst ein Einblick in die Hardware und Schaltung an.

Der Aufbau ist bewusst einfach gehalten und kommt ohne Mikrofon, Kamera oder Sensoren aus. Er benötigt neben dem Raspberry Pi Pico lediglich ein paar Kabel, zwei Taster und eine Leuchtdiode mit einem Vorwiderstand.

Um das Modell zu trainieren verwendet der Autor wie bereits in seinem Artikel über ML in der mobilen App auf heise Developer ein XOR-Gatter. Die Taster dienen als Eingabe und die Leuchtdiode als Ausgabe. Das Exklusiv-Oder- beziehungsweise Entweder-Oder-Gatter schaltet die Ausgabe auf HIGH, wenn an einem der beiden Eingänge ein HIGH-Signal anliegt und an dem anderen ein LOW-Signal. Sind beide Eingänge auf LOW oder auf HIGH, schaltet der Ausgang auf LOW. Die Taster steuern die Eingabewerte, und den Ausgabewert zeigt die Leuchtdiode an.

Zunächst entsteht die Schaltung mit den zwei Tastern und der Leuchtdiode, und die XOR-Entscheidung übernimmt eine klassisch programmierte if-Abfrage: Die Leuchtdiode leuchtet lediglich, wenn der Status der beiden Taster unterschiedlich ist. Wenn beide Taster deaktiviert oder aktiviert sind, bleibt die Leuchtdiode aus. Gerade der Fall, dass beide Taster gedrückt sind, gestaltet das Training des Modells und die Berechnung der Gewichtung der einzelnen Knoten innerhalb des Modells interessant, aber das wird ebenso Bestandteil des zweiten Teils der Artikelserie wie das Umwandeln des ML-Modells in C-Code.

Das Foto zeigt den Aufbau der XOR-Schaltung (Abb. 1).

Die in Abbildung 1 gezeigten Taster lassen sich im Zweifel durch Kabelenden oder Jumper ersetzen. Die Farbe der Leuchtdiode (LED) spielt für die Anwendung grundsätzlich keine Rolle, aber eine blaue LED benötigt mehr als vier Volt, während der Raspberry Pi Pico nur 3,3 Volt liefert. Für den Nachbau lässt sich alternativ die auf dem Pico verbaute LED nutzen.

Für die externe LED ist ein Vorwiderstand erforderlich – im Beispielaufbau hat er 56 Ohm. An der Stelle soll eine kurze Auffrischung der Vorwiderstandsberechnung helfen: Der Pico liefert 3,3 Volt, und an der LED liegen 2,2 Volt an. Damit liegen die übrigen 1,1 Volt am Vorwiderstand an. Für die maximale Stromstärke, die durch die LED fließen kann, seien 20 mA (Milliampere) verwendet, die ebenso durch den mit der LED in Reihe geschalteten Widerstand fließen. Dadurch lässt sich der Widerstand nach dem Ohmschen Gesetz als Quotient aus Spannung und Stromstärke berechnen:

R = U / I = 1,1 V / 0,02 A = 55 Ohm.

Als handelsüblicher Widerstand steht der 56-Ohm-Widerstand in der sogenannten E12-Reihe zur Verfügung. Sollte er nicht vorhanden sein, lässt sich ein 100-Ohm- oder 120-Ohm-Widerstand verbauen, wodurch die LED etwas schwächer leuchtet. Das Weglassen des Vorwiderstands ist nicht zu empfehlen, da der Pico darauf empfindlich reagieren könnte.

Die Skizze der Schaltung ist mit dem Tool Fritzing erstellt worden (Abb. 2).

Eine Seite der beiden Taster ist jeweils mit 3,3 Volt am Pin 3V3 (OUT) des Picos verbunden (s. Abb. 2). Die andere Seite geht von dem einem Taster zu Pin GP11 und von dem anderen zu Pin GP12. Der Aufbau und somit die Beschaltung unterscheiden sich von Taster zu Taster. Bei der Leuchtdiode ist darauf zu achten, dass die Kathode, die an der LED mit der flachen Seite gekennzeichnet ist, mit Minus beziehungsweise GND verbunden ist. Der Vorwiderstand ist auf der einen Seite mit der Anode der LED verbunden und auf der anderen mit Pin GP16. Die interne LED des Pico ist mit Pin GP25 verbunden. Wer sie nutzen möchte, muss den Code entsprechend anpassen.

Die endgültige Anwendung wird aus dem TensorFlow-Micro-Code, dem Modell, dem Pico SDK und der Rahmenanwendung zum Ausführen bestehen. Zuvor dient ein einfaches Programm dazu, den Aufbau der Schaltung zu testen. Der Code ist bewusst einfach gehalten, aber das Programm dient als Grundgerüst, in das später der TinyML-Code und das trainierte Modell einfließen. Als Entwicklungsumgebung bietet sich das quelloffene und kostenlose Visual Studio Code an, wie Michael Stal in einem Blogbeitrag auf heise Developer erläutert hat.

Als erstes gilt es die Datei XOR.cpp anzulegen. Im Folgenden sind die darin einzufügenden Codepassagen aufgeschlüsselt.

#include "pico/stdlib.h"

const uint BUTTON1_PIN = 11;
const uint BUTTON2_PIN = 12;
const uint LED_PIN = 16; 
// const uint LED_PIN =  PICO_DEFAULT_LED_PIN;

Die eingebundene stdlib.h enthält das Software Development Kit (SDK) für den Raspberry Pi Pico mit den Grundfunktionen, die beispielsweise die Pins des Pico für die Ein- und Ausgabe festlegen. Die Pins für die Taster und Leuchtdiode sind anschließend als Konstanten definiert. Dabei ist darauf zu achten, dass es sich nicht um die physikalischen Werte des Pico-Boards handelt, sondern um die Werte der GPIO-Pins. GPIO steht für General Purpose Input / Output und kennzeichnet Pins, die für die Ein- oder Ausgabe konfigurierbar sind. Wer die interne LED nutzen möchte, verwendet statt des GPIO-Pins 16 den vordefinierten Wert PICO_DEFAULT_LED_PIN, der mit dem Wert 25 konfiguriert ist.

int main(int argc, char* argv[]) {
  setup();
  while (true) {
    loop();
  }
}

Die Aufteilung der main-Funktion in die Methoden setup und loop entspricht dem Vorgehen der TinyML-Beispiele. Wer bereits Erfahrung mit der Arduino-Entwicklung gesammelt hat, dürfte die Aufteilung ebenfalls wiedererkennen. Dadurch ist das Programm in zwei Bereiche aufgeteilt: Der setup-Part legt einmalig beim Start Werte und Konfigurationen fest. Später lädt er das trainierte Modell. Der loop-Aufruf hingegen läuft in einer Endlosschleife und führt immer wieder denselben Code aus.

void setup() {
  gpio_init(BUTTON1_PIN);
  gpio_set_dir(BUTTON1_PIN, GPIO_IN);
  gpio_init(BUTTON2_PIN);
  gpio_set_dir(BUTTON2_PIN, GPIO_IN);
  gpio_init(LED_PIN);
  gpio_set_dir(LED_PIN, GPIO_OUT);
}

Die setup-Methode definiert mit gpio_init und gpio_set_dir die Pins für die beiden Taster und die Leuchtdiode. Sie setzt die Taster als Eingaben und die Leuchtdiode als Ausgabe.

void loop() {
  int button1 = gpio_get(BUTTON1_PIN);
  int button2 = gpio_get(BUTTON2_PIN);

  if (button1 != button2) {
    gpio_put(LED_PIN, true);
  } else {
    gpio_put(LED_PIN, false);
  }
  sleep_ms(10);
}

Die loop-Methode liest den Status der jeweiligen Taster. Sollten deren Werte unterschiedlich sein, weil genau einer der beiden Taster gedrückt ist, setzt sie den Pin, an der sich die Leuchtdiode befindet, auf true: Die LED leuchtet. Sind die Tasterwerte gleich, weil beide oder keiner gedrückt ist, setzt sie den Pin auf false: Die LED bleibt dunkel. Am Ende wartet die Methode zehn Millisekunden, um zu verhindern, dass die mechanischen Taster während des Schaltvorgangs mehrmals auslösen.

Nach dem Kompilieren, Bauen und Hochladen des Programms auf den Raspberry Pi Pico, lässt sich die Schaltung und Anwendung testen.

Das gezeigte Beispiel vermittelt den Aufbau der Hardware und Schaltung sowie zunächst eine Anwendung, die auf herkömmliche Weise die Abfrage der XOR-Logik umsetzt. Die einfache Testanwendung dient als Basis für die Umsetzung mit Machine Learning im zweiten Teil der Artikelserie.

Dort stehen zunächst das Training des Exklusive-Oder-Modells mit einem Jupyter Notebook und die Umwandlung des Modells in C-Code an. Anschließend wird der TensorFlow-Micro-Code eingebunden und das Programm zum Verwenden des trainierten Modells angepasst.

Lars Gregori
arbeitet als Technology Strategist bei SAP CX im Customer Experience Umfeld. Innerhalb des SAP CX Labs Teams im CX CTO Office betrachtet er neuste Technologien und beschäftigt sich mit Machine Learning, Embedded Devices, dem Internet der Dinge, Daten und vielem mehr. Er ist Sprecher auf nationalen und internationalen Konferenzen, Buchautor zum Thema Machine Learning und bringt seine Erfahrung im Elektronikumfeld in Lernvideos ein.

(rme)