Schlauer Zwerg: ML mit dem Raspberry Pi Pico, Teil 2: Modelltraining

Seite 3: Umsetzung in C++

Inhaltsverzeichnis

Das GitHub-Projekt zum Artikel enthält die komplette XOR-Beispielanwendung. Sie basiert auf dem Raspberry-Pi-Projekt pico-tflite. Das example-Verzeichnis enthält die Anwendung. Im src-Verzeichnis befindet sich der TensorFlow-Micro-Sourcecode mit den benötigten Bibliotheken, die ebenfalls als Quellcode vorliegen. Um die Beispielanwendung übersichtlich zu halten, sind Tests und die ursprünglichen Beispiele nicht mehr im Projekt vorhanden.

Visual Studio Code fragt beim ersten Öffnen zunächst nach einem Compiler. Zusätzlich gilt es den PICO_SDK_PATH in den Settings für Cmake: Build Environment und Cmake: Configure Environment zu konfigurieren. Anschließend lässt sich mit Build die XOR-Anwendung erstellen und auf den Raspberry Pi Pico übertragen.

Im Verzeichnis example/xor befindet sich die Datei CMakeList.txt, die das Projekt für CMake definiert. Die Datei main_functions.cpp und der zugehörige Header enthalten die Methoden setup und loop. In main.cpp findet sich wie im ersten Teil des Artikels die main-Funktion. Sie ruft einmalig die setup-Methode auf und anschließend in einer Endlosschleife loop. Daneben existiert die model.cpp-Datei. Wie oben beschrieben, erstellt das Jupyter Notebook diese Datei.

Die setup-Methode besteht nach wie vor aus der Definition der Taster und der Leuchtdiode. Zusätzlich dient ein umfangreicher Codeabschnitt der Initialisierung von TensorFlow. Zwei Codestellen sind hervorzuheben:

model = tflite::GetModel(model_data);

GetModel lädt die Modelldaten, die in dem model_data-Array in model.cpp definiert sind. Es bietet den Aufbau des Modells und die berechneten Gewichtungen.

input = interpreter->input(0);
output = interpreter->output(0);

Diese beiden Aufrufe setzen die Ein- und Ausgabe als Pointer in ihre jeweiligen Variablen. Dadurch lassen sich innerhalb der loop-Methode die beiden Werte der Taster setzen und das Ergebnis lesen.

  int button1 = gpio_get(BUTTON1_PIN);
  int button2 = gpio_get(BUTTON2_PIN);

  input->data.f[0] = button1;
  input->data.f[1] = button2;

  TfLiteStatus invoke_status = interpreter->Invoke();

  printf("%f\n", output->data.f[0]);

  if (output->data.f[0] > 0.5)
  {
    gpio_put(LED_PIN, true);
  }
  else
  {
    gpio_put(LED_PIN, false);
  }

Die loop-Methode liest die Werte der Taster und setzt mit ihnen die beiden Eingabewerte für das Modell. Invoke führt die Berechnung aus. Das Ergebnis ist über die output-Variable erreichbar, die printf ausgibt. Eine if-Abfrage prüft den Wert: Liegt er über 0,5, schaltet der Code die Leuchtdiode ein, sonst schaltet er sie aus.

Ergebnisausgabe in der Arduino-Monitorausgabe

Abbildung 2 zeigt den Übergang beim Drücken des ersten Tasters. Dabei sind dieselben krummen Werte zu sehen, die beim Training entstanden sind.