75 KiB
Bluetooth Data Exchange App
Dieses Repository enthält Informationen und Code aus dem BOGY (Berufsorientierung für Gymnasien), das in Kooperation zwischen dem Leibniz Gymnasium in Rottweil (https://www.lg-rw.de/) und der Firma Mitutoyo CTL Germany GmbH (https://www.mitutoyo-ctl.de/) im Frühjahr 2024 stattfand.
Es handelt sich um ein erweitertes Praktikum. Das bedeutet, dass es neben der üblichen Praktikums-Woche noch sechs vorbereitende Nachmittage gibt. An diesen Nachmittagen bringen wir den Schülern die Grundlagen bei, damit in der Praktikumswoche auch ein sichtbares Produkt entstehen kann.
BOGY Woche, Donnerstag 21.3.2024
Wir haben herausgefunden, warum das LocalStorage die Daten nicht speichern konnte. Die Erklärung haben wir in DatenSpeichern (PowerPoint) aufgeschrieben.
BOGY Woche, Dienstag 19.3.2024
Hier der Link zum Beispielcode (C++) wie man die DateTime Werte umrechnen kann: https://godbolt.org/z/K453oPdov
Bluetooth Gatt-XML: https://github.com/oesmith/gatt-xml/blob/master/org.bluetooth.characteristic.date_time.xml
Ergebnis nach dem heutigem Tag:
BOGY Woche, Montag 18.3.2024
Wir haben nochmal das Thema Objektorientierung wiederholt, weil es bei Flutter so wichtig ist.
Danach haben wir die ersten Schritte mit Flutter (Powerpoint) gemacht.
Nachmittag 6, Mittwoch 13.3.2024
Wir haben die Dart (Powerpoint) abgeschlossen. Das wichtigste Thema dabei war sicherlich die Objektorientierung. Die wird uns das BOGY über ständig begleiten.
Da noch ein wenig Zeit übrig war, haben wir uns zudem noch ein wenig den Aufbau des Flutter Demo Projekts in Android Studio angeschaut, um eine bessere Vorstellung davon zu bekommen, wie sich eine Smartphone-App mit Flutter aufbauen lässt. Zuletzt haben wir noch probiert, die gesehene Struktur anhand eines kleinen Beispiels selbst zu programmieren.
Nachmittag 5, Mittwoch 6.3.2024
Auch an diesem Nachmittag haben wir weiter mit der Dart (Powerpoint) gearbeitet. Neu waren:
- Listen
- Maps
- Methoden
- Funktionen
- Named Arguments
- Scope
- Callbacks
Wir sind mit allen behandelten Themen fertig geworden und steigen nächste Woche mit Lambdas ein. Wir sind bis einschließlich Folie 70 gekommen.
Nachmittag 4, Mittwoch 28.2.2024
Wir haben mit der Dart (Powerpoint) weitergemacht. Neu waren:
- Strings
- Templates
- Schleifen (
forundwhile) - Wahrheitswerte
- Verzweigungen
Die Idee von Listen haben wir am Ende noch kurz angesprochen und werden mit dem Thema Listen am nächsten Mittwoch wieder einsteigen. Wir kamen bis einschließlich Folie 40.
Nachmittag 3, Mittwoch 21.2.2024
Wir haben heute die Entwicklungsumgebung Android Studio (Powerpoint) installiert. Das ist gar nicht so trivial, da es mehrere Komponenten gibt:
- Amazon Corretto, eine JRE (Java Runtime Environment) als Voraussetzung, dass Android Studio überhaupt läuft
- Android Studio selbst
- SDKs und Plugins für Android Studio
- den Emulator für Android-Geräte auf Windows
- Flutter als Framework für plattformunabhängige Apps
Nachdem uns diese Installation fast den gesamten Nachmittag gekostet hat, konnten wir noch mit Dart (Powerpoint) beginnen und tatsächlich noch ein paar Befehle programmieren. Wir sind bis zur Aufgabe auf Folie 20 gekommen.
Nachmittag 2, Mittwoch 7.2.2024
Wir haben uns heute mit der Programmierung des ESP32, insbesondere der Bluetooth-Anbindung beschäftigt. Den Code dazu haben wir in esp_32_tutorials abgelegt.
Das lief leider nicht ganz so gut, wie erwartet. Da das Thema BLE (Bluetooth Low Energy) auch für uns noch neu ist, werden wir uns das mal anschauen und fertigstellen. Nächstes Mal beginnen wir daher mit Android Studio.
Nachmittag 1, Mittwoch 31.1.2024
Wir haben uns das Gebäude angeschaut, damit sich die Schüler auch zurechtfinden. Mit Hilfe der Firmenpräsentation (Powerpoint) haben wir einen Überblick über den Aufbau der Firma und deren Produkte erhalten. Auch der Ablauf des Studiums wurde besprochen. Da unsere Partner-Hochschule die DHBW in Stuttgart ist, haben wir in Stuttgart eine Studentenwohnung (Powerpoint) gemietet, die wir den Studenten zur Verfügung stellen.
Wir haben ganz kurz besprochen, was wir programmieren:
- einen Microcontroller (Typ ESP32), der Daten von einem Temperatursensor per Bluetooth BLE liefert
- eine Smartphone App, die diese Daten empfängt und darstellt
Wir haben darüber hinaus einige Ideen gesammelt, welche Features die App noch haben könnte:
Weiterhin haben wir besprochen, wie man sich am Laptop und am WLAN anmeldet. Wir haben Discord Accounts eingerichtet und sind dem Kanal für das BOGY beigetreten. Wir haben Github Accounts angelegt und mit diesem Repository verknüpft. Außerdem haben wir die Arduino IDE und Visual Studio Code installilert, mit denen wir den Microcontroller programmieren möchten, der uns die Daten per Bluetooth liefern soll.
Bluetooth Data Exchange App
Dieses Repository enthält Informationen und Code aus dem BOGY (Berufsorientierung für Gymnasien), das in Kooperation zwischen dem Leibniz Gymnasium in Rottweil (https://www.lg-rw.de/) und der Firma Mitutoyo CTL Germany GmbH (https://www.mitutoyo-ctl.de/) im Frühjahr 2024 stattfand.
Es handelt sich um ein erweitertes Praktikum. Das bedeutet, dass es neben der üblichen Praktikums-Woche noch sechs vorbereitende Nachmittage gibt. An diesen Nachmittagen bringen wir den Schülern die Grundlagen bei, damit in der Praktikumswoche auch ein sichtbares Produkt entstehen kann.
BOGY Woche, Donnerstag 21.3.2024
Wir haben herausgefunden, warum das LocalStorage die Daten nicht speichern konnte. Die Erklärung haben wir in DatenSpeichern (PowerPoint) aufgeschrieben.
BOGY Woche, Dienstag 19.3.2024
Hier der Link zum Beispielcode (C++) wie man die DateTime Werte umrechnen kann: https://godbolt.org/z/K453oPdov
Bluetooth Gatt-XML: https://github.com/oesmith/gatt-xml/blob/master/org.bluetooth.characteristic.date_time.xml
Ergebnis nach dem heutigem Tag:
BOGY Woche, Montag 18.3.2024
Wir haben nochmal das Thema Objektorientierung wiederholt, weil es bei Flutter so wichtig ist.
Danach haben wir die ersten Schritte mit Flutter (Powerpoint) gemacht.
Nachmittag 6, Mittwoch 13.3.2024
Wir haben die Dart (Powerpoint) abgeschlossen. Das wichtigste Thema dabei war sicherlich die Objektorientierung. Die wird uns das BOGY über ständig begleiten.
Da noch ein wenig Zeit übrig war, haben wir uns zudem noch ein wenig den Aufbau des Flutter Demo Projekts in Android Studio angeschaut, um eine bessere Vorstellung davon zu bekommen, wie sich eine Smartphone-App mit Flutter aufbauen lässt. Zuletzt haben wir noch probiert, die gesehene Struktur anhand eines kleinen Beispiels selbst zu programmieren.
Nachmittag 5, Mittwoch 6.3.2024
Auch an diesem Nachmittag haben wir weiter mit der Dart (Powerpoint) gearbeitet. Neu waren:
- Listen
- Maps
- Methoden
- Funktionen
- Named Arguments
- Scope
- Callbacks
Wir sind mit allen behandelten Themen fertig geworden und steigen nächste Woche mit Lambdas ein. Wir sind bis einschließlich Folie 70 gekommen.
Nachmittag 4, Mittwoch 28.2.2024
Wir haben mit der Dart (Powerpoint) weitergemacht. Neu waren:
- Strings
- Templates
- Schleifen (
forundwhile) - Wahrheitswerte
- Verzweigungen
Die Idee von Listen haben wir am Ende noch kurz angesprochen und werden mit dem Thema Listen am nächsten Mittwoch wieder einsteigen. Wir kamen bis einschließlich Folie 40.
Nachmittag 3, Mittwoch 21.2.2024
Wir haben heute die Entwicklungsumgebung Android Studio (Powerpoint) installiert. Das ist gar nicht so trivial, da es mehrere Komponenten gibt:
- Amazon Corretto, eine JRE (Java Runtime Environment) als Voraussetzung, dass Android Studio überhaupt läuft
- Android Studio selbst
- SDKs und Plugins für Android Studio
- den Emulator für Android-Geräte auf Windows
- Flutter als Framework für plattformunabhängige Apps
Nachdem uns diese Installation fast den gesamten Nachmittag gekostet hat, konnten wir noch mit Dart (Powerpoint) beginnen und tatsächlich noch ein paar Befehle programmieren. Wir sind bis zur Aufgabe auf Folie 20 gekommen.
Nachmittag 2, Mittwoch 7.2.2024
Wir haben uns heute mit der Programmierung des ESP32, insbesondere der Bluetooth-Anbindung beschäftigt. Den Code dazu haben wir in esp_32_tutorials abgelegt.
Das lief leider nicht ganz so gut, wie erwartet. Da das Thema BLE (Bluetooth Low Energy) auch für uns noch neu ist, werden wir uns das mal anschauen und fertigstellen. Nächstes Mal beginnen wir daher mit Android Studio.
Nachmittag 1, Mittwoch 31.1.2024
Wir haben uns das Gebäude angeschaut, damit sich die Schüler auch zurechtfinden. Mit Hilfe der Firmenpräsentation (Powerpoint) haben wir einen Überblick über den Aufbau der Firma und deren Produkte erhalten. Auch der Ablauf des Studiums wurde besprochen. Da unsere Partner-Hochschule die DHBW in Stuttgart ist, haben wir in Stuttgart eine Studentenwohnung (Powerpoint) gemietet, die wir den Studenten zur Verfügung stellen.
Wir haben ganz kurz besprochen, was wir programmieren:
- einen Microcontroller (Typ ESP32), der Daten von einem Temperatursensor per Bluetooth BLE liefert
- eine Smartphone App, die diese Daten empfängt und darstellt
Wir haben darüber hinaus einige Ideen gesammelt, welche Features die App noch haben könnte:
Weiterhin haben wir besprochen, wie man sich am Laptop und am WLAN anmeldet. Wir haben Discord Accounts eingerichtet und sind dem Kanal für das BOGY beigetreten. Wir haben Github Accounts angelegt und mit diesem Repository verknüpft. Außerdem haben wir die Arduino IDE und Visual Studio Code installilert, mit denen wir den Microcontroller programmieren möchten, der uns die Daten per Bluetooth liefern soll.
Android Timer App
Freitag 31.3.2023
Abend der Technik
Der Abend der Technik findet am Mittwoch, 3.5.2023, statt. Die dort vorgestellte Präsentation arbeitet ihr bitte bis Freitag 28.4.2023 aus. Didi und Thomas kommen am 28.4. ins LFZ. Dort könnt ihr die Präsentation einmal probbehalten.
Unsere Vorgaben für den Abend der Technik:
-
Poloshirts anziehen
-
Präsentation auf Basis der Vorlage von Mitutoyo erstellen
-
Lasst euch vorher die Bühne zeigen, damit ihr wisst, wo es hinauf und hinunter geht
-
Aufbau der Präsentation
- Zuschauer motivieren, dass sie die nächsten 5-10 Minuten auch zuhören
- Interesse wecken: "Hatten Sie nicht auch schon das Problem, dass ..."
- Nicht die Lösung verraten
- Firma vorstellen: 1 Folie genügt
- Ablauf des Praktikums erklären
-
Nachmittage: Rundgang, Installation Android Studio, Dart, Flutter
Fachbegriffe kurz erklären
Dart: Programmiersprache, ähnlich wie Arduino / C++
Flutter: Bibliotheken (geschrieben in Dart) für die Oberfläche, z.B. Buttons, Icons, Scrollbalken, Text, Farben, ...
-
BOGY-Woche: Ein Problem gelöst / App programmiert
-
- Details
- Bildschirme auf Papier aufgemalt (Bild/Foto vom gescannnten Papier)
- In einem Online-Kollaborationstool (Figma) etwas realistischer gezeichnet (Bild zeigen)
- Umsetzung in Widgets (Programmierarbeit) (Screenshot von Timer anlegen)
- Code austauschen über Github (Push + Pull),
- Github ist eine Versionsverwaltung
- Github ist auch ein Kollaborationstool (Screenshot zeigen von Github Issues)
- Wiederverwendbare Unter-Widgets
- Daten + Business Logik (dass die App was tut)
- Testen (im Browser + im Emulator) + Fehler finden + Fehler eintragen (bei Github) + Fehler beheben + Abläufe optimieren
- Lösung / Ergebnis: hier die coolsten und besten Bilder zeigen
- Settings Dialog
- Categories
- Video von einem Timer der abläuft
- ...
- Vielen Dank
- Fragen: bitte kommt zu unserem Stand
- Zuschauer motivieren, dass sie die nächsten 5-10 Minuten auch zuhören
Messestand:
- Messestand bringen wir mit
- Laptops zum Ausprobieren bringen wir mit
Donnerstag 30.3.2023
Vereinfachung des Datenaustauschs
Wir haben darauf verzichtet, die Daten eines StatefulWidget an seinen State durchzureichen. Stattdessen geben wir die Daten nur noch an das Widget und greifen dann im State mittels widget.data auf die Daten zu.
Für die Subwidgets haben wir eigene Datenklassen erstellt und Logik ergänzt, damit diese Daten auch benutzt werden.
Notifications auf dem Sperrbildschirm
Um Nachrichten über abgelaufene Timer auf dem Sperrbildschirm anzuzeigen haben wir eine Bibliothek installiert (pubspec.yaml):
flutter_local_notifications: ^9.7.0
https://pub.dev/packages/flutter_local_notifications
Dazu muss auch in der Datei android\app\src\main\AndroidManifest.xml folgendes eingetragen werden:
<!-- Permissions options for the `notification` group -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
App Icon
Um Icons für verschiedene Plattformen generieren zu lassen kann man folgendes Package verwenden: https://pub.dev/packages/flutter_launcher_icons
In unserem Projekt haben wir die Konfiguration in die pubspec.yaml integriert. Die Beschreibung dafür findet man auf der Projektseite des Packages.
Hinweis:
Wenn man weitere Daten zu den Resourcen für Android hinzufügt, muss man darauf achten, dass die Dateinamen klein geschrieben sind. Sonst bekommt man folgende Fehlermeldung:
ERROR:.\BOGY-App-2023-04\multitimer\android\app\src\main\res\drawable\Logo.png: Resource and asset merger: 'L' is not a valid file-based resource name character: File-based resource names must contain only lowercase a-z, 0-9, or underscore
FAILURE: Build failed with an exception.
Mittwoch 29.3.2023
Wir haben weitere Widgets programmiert, die Themes angepasst und schon erste Sub-Widgets erstellt.
Wir haben die Daten an die Widgets durchgereicht, damit diese irgendwann die echten Daten anzeigen können.
Fehlermeldung beim Bauen des APK
Falls diese Fehlermeldung beim Bauen des APKs (Menü: Build / Flutter / Build APK) erscheint:
Running Gradle task 'assembleRelease'...
FAILURE: Build failed with an exception.
* What went wrong:
The supplied javaHome seems to be invalid. I cannot find the java executable. Tried location: C:\Program Files\Android\Android Studio\jre\bin\java.exe
dann hilft folgende Vorgehensweise:
- Windows Kommandozeile als Administrator starten
cd /d C:\Program Files\Android\Android Studioren jre jre_oldmklink /D jre jbr
Dienstag 28.3.2023
Wir haben mit der Implementierung von Widgets weitergemacht.
Speichern von Daten als JSON
Wir Betreuer haben festgestellt, dass unser Wissen über Dart schon wieder veraltet ist. Das alte flutter generate wurde mittlerweile durch dart pub run build_runner ersetzt. Das ist allerdings auch schon wieder veraltet und heißt nun dart run build_runner build. Mit dem Befehl werden dann Teile von Klassen automatisch generiert.
Klassen, die als JSON gespeichert werden sollen, brauchen:
-
das Paket
import 'package:json_annotation/json_annotation.dart'; -
einen anderen, automatisch generierten Teil der Klasse
part 'Klassenname.g.dart'; -
direkt oberhalb der Klasse
@JsonSerializable(explicitToJson: true, includeIfNull: true) -
@override String toString() => toJson().toString(); factory Klassenname.fromJson(Map<String, dynamic> json) => _$KlassennameFromJson(json); Map<String, dynamic> toJson() => _$KlassennameToJson(this);
Nach diesen Änderungen muss der Befehl dart run build_runner build ausgeführt werden, um die Datei Klassenname.g.dart zu erzeugen, die mit Punkt 2 der obigen Liste eingebunden wird.
Versionsnummern in pubspec.yaml
^ bedeutet: es sind auch alle neueren Versionen dieser Bibliothek zugelassen, die garantiert rückwärtskompatibel mit dieser Version sind.
>= bedeutet: diese Version oder neuer
Themes
Bei der Erstellung von Themes ist uns aufgefallen, dass die Hintergrundfarbe des Themes nicht angewandt wurde. Als Lösung haben wir die Eigenschaft scaffoldBackgroundColor gefunden.
Navigation
Neben .push(...) und .pop() haben wir noch pushReplacement(...) entdeckt, das verhindert, dass man z.B. wieder zum Splashscreen zurück navigieren kann.
Flutter Upgrade
Manche Betreuer mussten eine bestehende Flutter Installation mit flutter upgrade --force aktualisieren.
Montag 27.3.2023
Nach einigen organisatorischen Dingen wie z.B. der Bestellung von Mittagessen haben wir das Widget von Folie 26 fertiggestellt, Navigation eingebaut und gelernt, wie wir Daten von einem Widget zum nächsten transferieren.
Um unsere Arbeit zu vereinfachen, haben wir uns ein paar Live Templates erstellt:
-
Wir haben das Live Template
stlessso modifiziert, dass wir in Zukunft automatisch ein Scaffold, eine AppBar und eine Column bekommen. -
Wir haben uns Live Templates für TextField und den dazugehörigen TextEditingController angelegt.
Damit wir eine grobe Vorstellung haben, was wir diese Woche programmieren müssen, haben wir zunächst die Widgets von Hand gezeichnet.
Folgende Widgets haben wir identifiziert:
- SplashScreen mit dem Logo unserer App
- Startbildschirm (Main menu)
- Button für Einstellungen
- Button für Benutzerprofile (?)
- Navigation zu den verfügbaren Kategorien
- Widget für Einstellungen
- Dark Mode / Light Mode
- Spende ans Entwicklerteam
- Widget für Benutzerprofile (?)
- ?
- Anzeige der verfügbaren Kategorien
- Liste der Kategorien
- je Button zum Löschen einer Kategorie
- je 1 Button zum Editieren einer Kategorie (Bleistift Icon)
- 1 Button zum Anlegen eines neuen Timers
- Anlegen/Bearbeiten eines Timers
- Textfeld zum Eingeben des Namens des Timers
- Auswahl oder Eingabe der Kategorie für diesen Timer
- je Abschnitt 1 Eingabefeld zum Festlegen der Dauer dieses Timers
- Textfeld für die auszuführende Aktion
- Button zum Hinzufügen einer weiteren Dauer (zusätzlicher Abschnitt mit +/- Button)
- Button zum Speichern
- Anzeige der Timer innerhalb einer Kategorie
- Button zum Starten eines Timers
- Button zum Editieren eines Timers
- Button zum Löschen eines Timers
- Anzeige bei Ablauf eines Timers
- Liste der abgelaufenen Timer
- Name der auszuführenden Aktion
- je 1 Bestätigungsbutton zum Fortsetzen bzw. Beenden des Timers
- Anzeige der aktiven Timer
- Name und Dauer des Timers
- Abbrechen eines Timers
- Meldungen auf dem Sperrbildschirm
Die Aufgaben zur Erstellung dieser Widgets haben wir als Issues in Github hinterlegt.
Mittwoch 22.3.2023
Zum Einstieg haben wir eine Dart-Aufgabe von Codewars gelöst.
Anschließend haben wir Flutter wiederholt und uns nochmal ein paar Widgets angeschaut.
Wir haben und dann weitere Beispiele programmiert, die etwas fortgeschrittenere Konzepte mit Layouts benutzen. Bis Folie 26 sind wir gekommen.
Mittwoch 15.3.2023
Wir haben heute das Thema Dart abgeschlossen. Neu waren:
- Lambdas
- Klassen und Objekte
Wir konnten dann mit Flutter beginnen und haben uns bis Folie 8 durchgearbeitet. Die Themen waren:
- Was ist ein Widget?
- Beispiele für Widgets
- Unser erstes stateless Widget
Mittwoch 8.3.2023
Da wir beim letzten Mal ziemlich viele Themen behandelt haben, haben wir heute nochmal eine Wiederholung durchgeführt für:
for-Schleifen- Verzweigungen
- Listen
- Methoden und Funktionen
Danach haben wir in der Dart weitergemacht und die nächsten Themen angeschaut:
- Named Arguments
- Scope
- Shadowing
- Callbacks
Wir sind bis Folie 70 gekommen.
Mittwoch 1.3.2023
Nachdem wir beim letzten Mal das Android Studio installiert haben, konnten wie heute damit beginnen, uns die Programmiersprache Dart anzuschauen.
Dart ist von der Syntax her ähnlich zu Arduino, weswegen viele Dinge schon bekannt vorkamen.
Die behandelten die Themen:
- kurzer Überblick über unterschiedliche Programmiersprachen
- Dateiformat von Dart
- Rechtschreibprüfung in Android Studio
- Rechnen in Dart
- Bibliotheken
- Code Formatierung (Coding Guidelines)
- Strings (Texte) verarbeiten
- Code Templates
for-Schleife- Booleans (Wahrheitswerte)
- Verzweigungen
- Listen
- Maps / Dictionaries
- Methoden und Funktionen
Wir haben es heute geschafft bis Folie 51 und machen dort nächstes Mal wieder weiter.
Mittwoch 15.2.2023
Zu Beginn des BOGYs steht natürlich die Vorstellungsrunde, damit sich die Teilnehmer des Praktikums und Betreuer kennenlernen. Direkt danach haben wir eine Führung durch das Gebäude gemacht, damit die örtlichen Gegebenheiten bekannt sind.
Nach dem üblichen Formalismus wie Besucheranmeldung haben wir zunächst einen Überblick über die Firma Mitutoyo gegeben. Außerdem haben wir einen Einblick in unsere Studentenwohnung gezeigt.
Anschließend haben wir kurz die Idee der App vorgestellt, damit klar ist, wo das Praktikum hingeht: die App soll benannte, im Vorhinein definierbare Timer zur Verfügung stellen, von denen mehrere gleichzeitig gestartet werden können.
Anwendungsbeispiele:
- Kochen
- Eier: 5 Minuten
- Nudeln kochen: 2 Minuten (Wasser erhitzen) + 10 Minuten (kochen)
- Brötchen aufbacken: 3 Minuten (Herd vorheizen) + 10 Minuten (backen)
- Pommes in der Heißluftfriteuse: 8 Minuten, dann wenden + nochmal 8 Minuten
- Suppenmaultaschen: 2 Minuten (Wasser erhitzen) + 8 Minuten (kochen)
- Tee ziehen lassen: 7 Minuten
- Pizza backen: Ofen vorheizen + Pizza belegen + Pizza backen
- Sport
- Hometrainer: 15 Minuten
- Joggen: 20 Minuten, dann umdrehen, nochmal 20 Minuten
- Workout: wiederholende Übungen mit Pausen dazwischen
- Haushalt
- Waschen: 2:36 Stunden
- Spülmaschine: 3:12 Stunden
- Zähneputzen: 2:30 Minuten
- Kinderbetreuung:
- Hausaufgaben machen: 45 Minuten
- Computer spielen oer Fernsehen: 30 Minuten
- Fremdsprachen lernen: 15 Minuten
- Lesen: 20 Minuten
- Klavier spielen: 15 Minuten
- Arbeit
- Pomodoro-Timer: 45 Minuten + 5 Minuten Reflexion + 10 Minuten Pause
- Lüftungs-Timer: 1:30 Stunden
Diese Uhrzeiten sollen natürlich nicht fest hinterlegt sein, sondern jeder Anwender soll seine eigenen Timer erstellen und abspeichern können.
Ideen für weitere Features:
- Am Ende des Timers einen Webhook auslösen, z.B. um ein Licht anzuschalten oder eine Discord Nachricht verschicken.
Wir haben dann Android Studio installiert.
Hausaufgaben für die Teilnehmer bis zum nächsten Mal:
- Fotofreigabe ausfüllen und von den Eltern unterschreiben lassen
- Unserem Discord Server beitreten, damit ihr uns Fragen stellen könnt
- Github Account anlegen und Einladung zum Repository (diesem hier) akzeptieren
Checklisten-App-2022.1
Checklisten erstellen und ausfüllen mit einer App für Smartphones 📱.
Donnerstag 7.4.2022
Neben mehreren internen Fortschritten (Code) haben wir ein bisschen was über Globalisierung (PPTX⇓) gelernt, um unsere App zu übersetzen.
Mittwoch 6.4.2022
Wir haben uns um die bisher eingetragenen Issues gekümmert und ein bisschen "echte" Funktionalität ergänzt, d.h. dass die App sich erstmals Eingaben merkt.
Die Umschaltung zwischen Themes klappt schon. Es gibt ein dunkles Theme und ein helles Theme.
Ein nicht unerheblicher Teil der Arbeit floss auch in das Icon der App, bis es rund und mit dem richtigen Hintergrund angezeigt wurde:
Die übrigen Arbeiten fanden unsichtbar im Hintergrund statt: es entstand Programmcode für das Speichern von Daten usw. Wir stellen fest, dass das Pareto-Prinzip hier greift: am ersten Tag (20% des Aufwandes) entstand ein großer Teil der Oberfläche (80%). Möglicherweise verbringen wir das restliche BOGY (80%) ohne größere sichtbare Änderungen.
Dienstag 5.4.2022
Wir haben uns auf einen Namen für unsere App geeinigt: Voodoo-List.
Unsere Prioritäten für die Umsetzung:
- Icon und Name
- übersichtliches Menü (neutraler Hintergrund)
- Checklisten-Name (eintippen)
- Dark Mode
- Checklisten-Editor, evtl. mit tabellarischer Darstellung
- mehrere Checklisten erstellen und verwalten
- Fortschrittsanzeige
- Wiederholung einer Checkliste in einem bestimmten Intervall (täglich / wöchentlich)
- Kalender: diese Checkliste brauche ich an diesem Tag / spezialisierter Kalender, Checkliste für einen Tag (Termine; Hausaufgaben)
- Erinnerung an eine Checkliste oder einzelne Einträge davon
- Checklisten miteinander verbinden (Checkliste in Checkliste; Ober-Checkliste mit Unter-Checklisten)
- Übersetzung in mehrere Sprachen
- Warnsystem für nicht ausgefüllte Checklisten
- Hilfe-Funktion / Tutorial oder Einführung
- Pro-Version: unbegrenzte Anzahl Listen und Einträge
- Checkliste mit jemandem teilen / Checkliste an jemanden senden
- Handschrifterkennung
- Bilder an einen Checklisten-Eintrag anhängen
- Gratisversion: nur eine Liste o.ä.
- Werbung einblenden (könnte Beliebtheit reduzieren)
- Geo-Fencing: Checkliste an einem bestimmten Ort aufrufen
- Belohnungssystem / Prokrastinations-Modus
- Sollte flott sein (reagiert in kurzer Zeit)
- für mehrere Geräte (Smartphone, Tablet, Watch), Android und iOS
- Flutter macht das schon
Wir haben folgende Seiten in unserer App designt:
Splashscreen für unsere Voodoo-App:
Sprachauswahl:
Theme-Auswahl:
Erstellen der ersten Checkliste:
Editieren einer Checkliste:
Ansicht der verfügbaren Checklisten:
Montag, 4.4.2022
Wir haben uns die Gestaltung von Android Apps mit Hilfe von Flutter (PPTX⇓) angeschaut ein ein paar Beispiel-Oberflächen inklusive Navigation entwickelt. Wir haben uns auch noch ein wenig mit Gestalt Principles (Wikipedia) beschäftigt und ein paar Beispiele besprochen:
- Proximity (Gruppierung über Nähe bzw. Abstand)
- Similarity (Ähnlichkeit, z.B. Farbe oder Form)
- Continuity (Zusammenhang)
Mittwoch 30.3.2022
Wir haben den Nachmittag begonnen mit der Kata (Programmierübung) "Is it a palindrome" bei Codewars.
Im Anschluss sind wir die letzten Folien der Dart (PPTX⇓) Präsentation durchgegangen und haben die Objektorientierung behandelt. Damit haben wir die Programmiersprache ziemlich vollständig durchdrungen und können uns in der BOGY-Woche direkt mit der Programmierung einer Oberfläche beschäftigen.
Mittwoch 23.3.2022
Weiter ging es mit der Programmiersprache Dart (PPTX⇓). Die Konzepte aus diesem Nachmittag brauchen wir für die Flutter Programmierung:
- Named Arguments
- Scope
- Callbacks
- Lambdas
- Async / Await
Wir haben uns bis Folie 79 durchgearbeitet.
Hausaufgaben (freiwillig):
- Programmier-Aufgaben bei Codewars lösen
Mittwoch 16.3.2022
Wir haben die Lösung der Aufgabe von letztem Mal besprochen und dabei Funktionen des Debuggers kennengelernt (Breakpoints, Variableninspektion, ...). Danach haben wir uns die Programmiersprache Dart (PPTX⇓) weiter zu Gemüte geführt und die Kapitel
- Listen
- Maps (Dictionaries)
- Methoden
- Funktionen
bearbeitet. Die Aufgaben auf Folie 51 und 52 haben wir noch gelöst.
Aufgabe: Programmiere eine Funktion, die das gleiche Ergebnis liefert wie math.pow()
Aufgabe: Schreibe eine Funktion, die aus einer Liste mit Zahlen die kleinste heraussucht.
Mittwoch 9.3.2022
Wir haben uns weiter mit der Programmiersprache Dart (PPTX⇓) beschäftigt und dort die Kapitel
- Strings
- Wiederholungen
- Wahrheitswerte
- Verzweigungen
durchgearbeitet. Wir sind bis Folie 37 gekommen.
Für ein paar Aufgaben stellen wir Euch eine mögliche Lösung auf DartPad vor:
- Lösung Übung 1: Berechne 356*4³
- Lösung Übung 2: 36² -> WXYZ -> XY -> XY²
- Aufgabe 3: Wie viele Zahlen zwischen 100 und 999 enthalten die Ziffer 3?
Hausaufgaben:
- freiwillig: Wiederholung der Android Studio Installation auf dem eigenen Rechner daheim.
- freiwillig: Aufgaben nochmal nachvollziehen, Lösung vereinfachen oder verbessern.
Mittwoch 23.2.2022
Heute beschäftigten wir uns mit der Installation von allem, was man zur Entwicklung einer Smartphone-App braucht. Die Android Studio Präsentation (PPTX⇓) erklärt die einzelnen Schritte.
Die Downloads haben wir bereits erledigt und auf einem USB Stick zur Verfügung gestellt:
- Java SDK (JDK) namens Amazon Corretto
- Android Studio
- das Flutter-Plugin für Android Studio (Download innerhalb von Android Studio)
- Flutter
- Emulator / virtuelle Geräte (Download innerhalb von Android Studio)
Nach einer dreistündigen Installationsorgie blieb noch etwas Zeit für die ersten Beispiele mit der Programmiersprache Dart (PPTX⇓). Wir kamen bis Folie 17.
Hausaufgaben:
- Anmeldung im Discord abschließen
- freiwillig: Wiederholung der Installation auf dem eigenen Rechner daheim
- freiwillig: Programmierung eines Hello World Beispiels
Mittwoch 16.2.2022
In der Vorstellungsrunde haben wir erfahren, dass mehrere Teilnehmer bereits erste Programmiererfahrung mit dem Arduino gemacht haben. Beim Rundgang durch die derzeit recht leere Firma haben wir die Örtlichkeiten kennengelernt. Im Anschluss haben wir Laptops in Betrieb genommen.
Die Firmenpräsentation (PPTX ⇓) enthält auch Informationen über das Studium. Ein Blick in die Studentenwohnung (PPTX ⇓) war auch möglich. Zum Schluss haben wir Github Accounts eingerichtet und dem Projekt zugeordnet.
Hausaufgaben:
- Fotofreigabe ausfüllen und von den Eltern unterschreiben lassen
- Termin für den Abend der Technik erfragen
- Besuch des Betreuungslehrers klären
Ideen und Aufgaben für unsere Checklisten-App
- Icon und Name
- übersichtliches Menü (neutraler Hintergrund)
- mehrere Checklisten erstellen und verwalten
- Checklisten-Name (eintippen)
- Checklisten-Editor, evtl. mit tabellarischer Darstellung
- Kalender: diese Checkliste brauche ich an diesem Tag / spezialisierter Kalender, Checkliste für einen Tag (Termine; Hausaufgaben)
- Checklisten miteinander verbinden (Checkliste in Checkliste; Ober-Checkliste mit Unter-Checklisten)
- Erinnerung an eine Checkliste oder einzelne Einträge davon
- Warnsystem für nicht ausgefüllte Checklisten
- Dark Mode
- Gratisversion: nur eine Liste o.ä.
- Pro-Version: unbegrenzte Anzahl Listen und Einträge
- Werbung einblenden (könnte Beliebtheit reduzieren)
- Hilfe-Funktion / Tutorial oder Einführung
- Geo-Fencing: Checkliste an einem bestimmten Ort aufrufen
- Wiederholung einer Checkliste in einem bestimmten Intervall (täglich / wöchentlich)
- Checkliste mit jemandem teilen / Checkliste an jemanden senden
- Belohnungssystem / Prokrastinations-Modus
- Fortschrittsanzeige
- Sollte flott sein (reagiert in kurzer Zeit)
- für mehrere Geräte (Smartphone, Tablet, Watch), Android und iOS
- Handschrifterkennung
- Übersetzung in mehrere Sprachen
- berechnete Anzahl eines Checklisteneintrags: z.B. Anzahl der Urlaubstage geteilt durch 2 Pullover mitnehmen oder Menge für Rezept anhand der Personenzahl ausrechnen
- Bilder an einen Checklisten-Eintrag anhängen
Mögliche Checklisten
- Einkaufsliste: Wocheneinkauf, Geburtstagseinkauf
- Todo-Liste für einen Tag
- Schach-Verein: Mannschaft organisieren
- Klassenarbeiten / Hausaufgaben - Lernen auf eine Klausur
- Urlaubs-Checkliste
Snake AI 2021.1
Dieses Projekt dokumentiert die Entwicklung eines Machine Learning Algorithmus im Rahmen einer Berufsorientierung für Gymnasien (BOGY) für das Leibnitz-Gymnasium in Rottweil im Schuljahr 2020/2021. Als Firmenpartner stand Mitutoyo CTL in Oberndorf mit Hardware und Ansprechpartnern zur Verfügung.
Es handelt sich um eine erweiterte Berufsorientierung, d.h. zusätzlich zur üblichen BOGY-Woche stehen noch sechs Nachmittage zur Verfügung, um die Schüler auf das Praktikum vorzubereiten. Aufgrund von CODIV-19 finden diese Nachmittage online statt. Wir danken Discord für die Nutzungserlaubnis.
Inspiration
Inspiration für dieses Projekt war das Leibniz Forschungszentrum mit einer Idee, die Bewegung von Ameisen vom Computer vorherzusagen. Die Original-Idee beinhaltete ein Terrarium mit echten Ameisen, Kamera usw. Eine solch reale Umgebung birgt jedoch Schwierigkeiten, die mit den Rahmenbedingungen eines Praktikums schlecht vereinbar sind, z.B.:
- wer kümmert sich um die Ameisen? Möglicherweise sterben sie ausgerechnet alle am ersten Tag der Praktikumswoche.
- wie nehmen die Teilnehmer das Ergebnis samt Ameisen mit nach Hause, um es Eltern und Freunden zu zeigen?
- sind die Ergebnisse reproduzierbar? Wir können bei einer fehlerhaften Umsetzung nicht nochmal am gleichen Startpunkt beginnen.
- passt das Projekt in den Zeitrahmen?
Aus diesem Grund haben wir uns entschlossen, zwar ein Machine Learning Projekt durchzuführen, aber die Bedingungen zu unseren Gunsten anzupassen. Entstanden ist ein Snake-Spiel, bei dem der Computer selbst die Spielregeln erlernen soll und dann die richtigen Aktionen durchführt.
Projekt-Umgebung
Software
Wir verwenden kostenlose Software:
- das Betriebssystem Raspberry Pi OS für den Raspberry Pi 4. Wir verwenden die Version mit 4 GB Speicher, da wir für ein Experiment viel RAM benötigen.
- Kitty für den Zugriff auf den Raspberry per SSH
- Bash als Kommandozeile
- xRDP für den Zugriff auf die Benutzeroberfläche des Raspberry
- die Programmiersprache Python
- die Entwicklungsumgebung PyCharm von JetBrains (Community Edition)
- die Bibliothek OpenAI Gym
- die Versionsverwaltung Git mit dem Provider Github
- ggf. unter Windows den Editor Notepad++ und das Difftool Winmerge
Daten
Im Rahmen des Projekts erzeugen sich die Daten aus dem Spielverlauf selbst.
Vorbereitung / Einführung
An den sechs vorbereitenden Nachmittagen können Grundlagen vermittelt werden. Dadurch läuft die Praktikumswoche einfach flüssiger und die Schüler erreichen auch echte Ergebnisse.
Erster Nachmittag, 3.2.2021
Am ersten Nachmittag haben wir uns vorgestellt. Normalerweise führen wir am ersten Tag durch unser Gebäude, um die Räumlichkeiten kennenzulernen. Dies werden wir nachholen, falls wir uns zur Praktikumswoche vor Ort sein können.
Die Firmenpräsentation erklärt unser Firmen-Motto, nennt die von uns entwickelte Software, erklärt das duale Studium und zeigt Beispiele von Praktikumsprojekten.
Wir arbeiten mit der DHBW Stuttgart zusammen und stellen den Studenten eine Studentenwohnung zur Verfügung, um die Fahrtzeiten während den Theoriephasen zu verkürzen, so dass sie sich auf das Studium vorbereiten können.
Dann haben wir uns mit dem Thema der Berufsorientierung auseinandergesetzt. Das Spielprinzip ist vermutlich hinreichend bekannt: es handelt sich um ein Snake-Spiel. Die Schlange (grün) frisst mit ihrem Kopf (blau) einen Apfel (rot) und wächst dabei.
Zum Glück sind wir hier nicht an fächerübergreifenden Unterricht gebunden, ansonsten müsste man sich fragen, seit wann Schlangen vegetarisch sind (Biologie), ob nicht Adam und Eva den Apfel gegessen haben, anstatt der Schlange (Religion) und ob Schlangen mit künstlicher Intelligenz ein Bewusstsein haben, und somit überhaupt in Tierversuchen einsetzbar sind (Ethik).
Die von uns bereitgestellte Spieleumgebung ist bereits auf KI-Experimente vorbereitet, d.h. ein beliebiger Algorithmus kann in der Umgebung mehrere Spiele nacheinander ohne menschliches Zutun spielen. Zur Spieleumgebung gibt es eine Visualisierung, die folgendermaßen aufgebaut ist:
- der linke Bereich liefert statistische Daten
- grün: Daten zur Visualisierung, derzeit nur die aktuelle Visualisierungsgeschwindigkeit in Bildern pro Sekunde (fps; frames per second)
- hellblau: Daten zum Training, d.h. mehrere Spiele übergreifend
- violett: Daten zum aktuell laufenden Spiel. Ein Teil dieser Daten könnte als Input für Neuronen dienen.
- der rechte Bereich visualisiert das Spielfeld
- rot: das Futter (angeblich ein Apfel)
- blau: der Kopf der Schlange
- grün: Körper der Schlange, wobei die hellere Teile früher verschwinden als dunklere Teile
Im Bild sieht man einen von Mitutoyo programmierten Algorithmus, der noch keine künstliche Intelligenz nutzt. Dabei handelt es sich bewusst um einen Algorithmus, der nicht mathematisch als perfekt bewiesen ist. Unsere KI wird sich mit diesem Algorithmus messen müssen. Bei 1000 Spielen erreicht er eine Länge von bis zu 80 Kästchen, was einer Abdeckung von 40% der Fläche entspricht.
Die Hardware, ein Raspberry Pi 4, haben wir uns zunächst nur auf Bildern angeschaut. Die echte Hardware ist bereits verschickt, wird aber erst im Laufe der Woche eintreffen. Dank der Speichererweiterung auf 4 GB können auch größere Datenmengen verarbeitet werden, wie sie bei Machine Learning auftreten.
Um auf den Raspberry zugreifen zu können, wenn er zugestellt wurde, haben wir SSH grob erklärt und Kitty installiert.
Um für eine spätere Zusammenarbeit vorbereitet zu sein, haben wir Accounts bei Github angelegt und Zugriff auf dieses Repository gewährt.
Zweiter Nachmittag, 10.2.2021
Raspberry Pis anschließen und finden
Ein Raspberry Pi kam bereits im Laufe der letzten Woche an. Nach dem Anschluss im LAN gab es zunächst Schwierigkeiten, diesen aufzufinden. Selbst der Advanced IP Scanner half nicht.
Beim Einsatz solcher Tools ist eine Aufklärung über §202c StGB angebracht, auch bekannt als "Hackerparagraph". Für private Zwecke im eigenen Netzwerk sind solche Tools zulässig. In fremden Netzwerken, z.B. dem Schulnetz, könnte die Ausführung als Vorbereitung des Ausspähens von Daten gewertet werden und damit strafbar sein.
Wo wir gerade beim Thema Recht sind: "Unwissenheit schützt vor Strafe nicht" sagt man. Das ergibt sich aus §17 StGB. Dort heißt es "[...] handelt er ohne Schuld, wenn er diesen Irrtum nicht vermeiden konnte". Allerdings lassen sich durch das Lesen von Gesetzen Irrtümer vermeiden, so dass man wahrscheinlich schlechte Karten hat.
Letztlich konnte der Raspberry Pi dann doch noch gefunden werden. Grund war, dass der Raspberry lediglich eine IPv6 Adresse und keine IPv4 Adresse bekommen hatte. Damit haben wir nicht gerechnet, sonst hätten wir den Raspberry so konfiguriert, dass er nur IPv4 Adressen akzeptiert.
Allerdings war der Raspberry Pi über seinen Namen ansprechbar, so dass die Suche nach der IPv6-Adresse gar nicht erforderlich war. Wir haben Euch den Raspberry mit seinem Standard-Namen raspberry zugeschickt. Unter "Host Name (or IP address)" trägt man daher raspberry ein.
Die Nutzung von Kitty ist in manchen Ländern übrigens auch reglementiert, da es Verschlüsselung beinhaltet. In Deutschland ist es jedoch unproblematisch.
Sobald die Verbindung vom Computer mittels Kitty zum Raspberry hergestellt ist, haben wir einen wichtigen Schritt erreicht: wir haben jetzt Zugriff auf einen Linux-Computer mit Hilfe der Bash. Beides erklären wir am heutigen Nachmittag.
Linux
Linux ist ein Betriebssystem für Computer, also eine Alternative für Windows. Die Linux-Präsentation geht auf einige Unterschiede ein. Die Folien sind eher theoretischer Natur, weswegen wir viele Folien ausgeblendet haben. Wer mehr Interesse am Dateisystem hat, kann sich gern die versteckten Folien ansehen.
Wer sich langfristig mit dem Thema Software-Entwicklung auseinandersetzen möchte, kommt unserer Meinung nach nicht um Linux herum.
Bash
Die Bash ist eine Kommandozeile von Linux. Sie ähnelt der Eingabeaufforderung von Windows, ist jedoch wesentlich mächtiger. Die Bash-Präsentation ist weniger theoretisch und enthält viele praktische Übungen.
Dritter Nachmittag, 24.2.2021
Remote Desktop
Nachdem wir am letzten Nachmittag eine Verbindung per SSH zur Bash aufgebaut hatten, haben wir heute eine Verbindung zur grafischen Oberfläche mittels Remote Desktop verwendet.
Dies hat leider nicht auf Anhieb bei allen Systemen geklappt, so dass wir Fehlersuche betreiben mussten. Ursache war in diesem Fall, dass der Raspberry per LAN und der PC per WLAN angebunden waren, diese beiden Netze jedoch vom Router aus Sicherheitsgründen separiert wurden, so dass ein gegenseitiger Zugriff nicht möglich war.
Ein Zugriff per Remote Desktop ist bei Raspberry Pi OS nicht automatisch möglich. Damit es klappt, musste vorher auf der Shell der entsprechende Dienst installiert werden mit sudo apt install xrpd. Dies bestätigt wieder einmal, wie wichtig die Shell (in unserem Fall die Bash) bei Linux ist.
PyCharm
Pycharm hatten wir bereits für Euch heruntergeladen und abgelegt. Wir haben dann gemeinsam PyCharm installiert und uns einen ersten Überblick verschafft.
Danach haben wir es auch für Windows heruntergeladen und ebenfalls installiert, so dass ihr es auch mal ohne Raspberry benutzen könnt.
Python
Wir haben dann eine Einführung in Python durchgearbeitet, die viele Möglichkeiten für eigenes Ausprobieren bot. Wir sind bis Folie 31 gekommen und machen da nächste Woche weiter.
Hausaufgaben
Bei Interesse könnt ihr ein paar Aufgaben von Project Euler lösen.
Vierter Nachmittag, 3.3.2021
Wir haben die Einführung in Python durchgearbeitet.
Fünfter Nachmittag, 10.3.2021
Wir haben uns das Programmierparadigma Objektorientierung angeschaut, inklusive Übungen zur Umsetzung in Python.
Sechster Nachmittag, 17.3.2021
Bei umfangreichen Softwareprojekten ist es nicht mehr möglich, allein an einem Programm zu arbeiten, da es sonst nicht oder nicht schnell genug fertig wird. Daher arbeiten mehrere Programmierer in einem Team zusammen. Dann wiederum muss jeder Programmierer Zugriff auf den Code seiner Teammitglieder haben. Dieses Aufgabe (und noch ein paar mehr) löst ein Versionskontrollsystem.
Wir haben den Nutzen von Versionskontrolle allgemein allgemein erklärt. Danach sind wir die Grundlagen von Git erklärt, einem von mehreren kostenlosen Versionsverwaltungssystemen.
BOGY Woche
Montag, 22.3.2021, Vormittag
Repository klonen
Wir haben die Datenbank von Github auf den Raspberry geklont mit
git clone https://github.com/BogyMitutoyoCTL/Snake-AI-2021.1.git snake
Beim Ausprobieren ist uns aufgefallen, dass für NumPy und Python noch zwei Bibliotheken fehlen. Diese beiden Bibliotheken konnten wir mit folgenden Befehlen nachinstallieren:
sudo apt-get install libatlas-base-dev
sudo apt install libsdl2-ttf-2.0-0
Erläuterung des bestehenden Codes
Da wir uns auf das Machine Learning konzentrieren wollen, hat Mitutoyo das Snake-Spiel bereits implementiert. Über diese Implementierung haben wir uns einen Überblick verschafft.
Snake
Der Kern des Programms, das Spiel, ist in der Klasse Snake untergebracht. Das Spiel akzeptiert 7 mögliche Bewegungen:
north, um nach oben zu laufeneast, um nach rechts zu laufensouth, um nach unten zu laufenwest, um nach links zu laufenturn left, um in Laufrichtung der Schlange links abzubiegenturn right, um in Laufrichtung der Schlange rechts abzubiegenstraight, um weiter geradeaus in Laufrichtung der Schlange zu laufen
Die Klasse Snake nutzt eine andere Klasse Field, um sich zu zeichnen. Dabei handelt es sich um ein zweidimensionales Array, das wir als Spielfeld bezeichnen. Field enthält bereits die Farben, wie sie später abgebildet werden sollen.
Normalerweise würde das Snake Spiel von einem Menschen mit einem Controller bedient. Das ist in unserem Fall unpraktisch. Daher gibt es um die Klasse Snake herum noch ein sogenanntes Gym (englisch gymnasium = Sporthalle), also einen Ort, in der die künstliche Intelligenz trainieren kann. Dieses Gym ist kompatibel zu der Definition eines Gym von OpenAI. Die Klasse dafür bei uns heißt SnakeGym.
Algorithmen
Damit beim Programmieren von unterschiedlichen Strategien der Schlange weder das Gym, noch das Spiel selbst geändert werden muss, haben wir eine Klasse Algorithm definiert. Diese Klasse ist vorbereitet auf Machine Learning, d.h. sie hat Methoden und Eigenschaften, die wir am Anfang noch nicht brauchen, sondern erst, wenn wir tatsächlich Machine Learning mit neuronalen Netzen betreiben. Mit dieser Klasse Algorithm ist es sehr einfach, selbst eine Idee zu verwirklichen, wie die Schlange sich bewegen soll.
Ein Beispiel für einen solchen Algorithmus ist RotateForever. Dieser Algorithmus basiert auf der Idee, dass Snake möglichst lang gespielt werden soll. Die einfachste Art, ewig zu spielen ist, sich immer im Kreis zu drehen. Leider bekommt man dafür keine Punkte. Die Implementierung dieser Idee ist beinahe trivial:
from Algorithms.Algorithms import Algorithm
from GameData import GameData
class RotateForever(Algorithm):
def __init__(self):
super().__init__()
def decide(self, info: GameData) -> str:
return "turn left"
Die ersten Zeilen sind immer identisch. Lediglich die Funktion decide() muss angepasst werden.
Von diesen sehr einfachen Algorithmen haben wir einige zusammengestellt:
RotateForever: dreht sich immer im KreisRandomChoice: wählt eine Zufallsaktion, also ob man einfach blind auf dem Controller herumdrückt
Entscheidungsgrundlagen für Algorithmen
Das Spielfeld ist folgendermaßen aufgebaut:
Diese Richtung der Achsen ist in der Bildverarbeitung üblich. Euer Monitor hat z.B. ebenfalls die Ecke P(0|0) oben links und Q(1920|1080) unten rechts.
Damit man sich nicht blind für eine Aktion entscheiden muss, bekommt man für die Entscheidung ein paar Grundlagen, und zwar im Parameter info vom Typ GameData. Darin sind allerhand Informationen zu finden, die man für Entscheidungen braucht:
head_xbzw.head_y: wo der Kopf der Schlange sich befindet. Das Ergebnis ist eine Zahl, entsprechend der Koordinate.snake_length: Länge der Schlangedirection: Aktuelle Laufrichtung der Schlange. Das Ergebnis ist ein String mit den Werten"north","east","south"oder"west".food_xbzw.food_y: wo sich das Futter befindet. Das Ergebnis ist eine Zahl, entsprechend der Koordinate.food_direction: Richtung, in der sich das Futter befindet. Die Winkel sind dabei wie folgt:
food_distance_in_steps: Schritte bis zum Futter (kürzester Weg, ohne Berücksichtigung von Hindernissen)air_line_distance: Abstand zum Futter in Kästchen (diagonal, Pythagoras)walldistance_...: Abstand zur Wand (vom Kopf aus)- u.a.
Ebenfalls nützlich sind einige Funktionen:
can_move_to(x,y): findet heraus, ob an diese Position gelaufen werden kann, ohne zu sterben. Für X und Y setzt man dabei am besten eine Koordinate ein, die sich in der Nähe des Kopfes befindet, also, z.B.
if info.can_move_to(info.head_x - 1, info.head_y): # Ist links vom Kopf Platz?
return "west" # Dann kann man nach Westen fahren
-
body_age(x,y): findet heraus, wie bald sich der Körper an dieser Stelle hier wegbewegt -
is_body(x,y),is_food(x,y)undis_head(x,y): um abzufragen, um welche Sorte Kästchen es sich handelt
Die Anzeige
Damit wir eine hübsche Anzeige mit allerhand Statistik bekommen, gibt es die Klasse Visualization. Diese nutzt die Bibliothek PyGame, um ein Fenster zu zeigen.
Die Daten der Statistik kommen aus der Klasse TrainingData.
Zum lauffähigen Programm zusammengestellt
Das Programm main.py fügt alle Dinge zusammen:
- es baut das Gym auf
- es zeigt alle Algorithmen an und lässt den Benutzer einen auswählen
- es lässt den Algorithmus in einigen Runden (
max_epochs) spielen - zeigt am Ende die Statistik auf der Konsole an.
Auch das Programm main.py ist schon auf Machine Learning vorbereitet. Deshalb gibt es dort auch schon ein Belohnungssystem vom Typ RewardSystem und einen Algorithmus für Zufallsentscheidungen, aus denen die KI später lernen wird.
Die Klassen Snake, Field,SnakeGym, Algorithm, RotateForever, RandomChoice und GameData müssen im Laufe des Praktikums nicht geändert werden.
Aufgabe: schreibe einen Algorithmus
Die Aufgabe für diesen Vormittag ist, einen eigenen Algorithmus zu schreiben, der hoffentlich schon besser funktioniert als der Algorithmus, der per Zufall entscheidet. Dazu verwenden wir noch keine KI. Wir möchten zunächst herausfinden, wie schwierig es eigentlich ist, gut Snake zu spielen.
Das Grundgerüst sieht so aus:
from Algorithms.Algorithms import Algorithm
from GameData import GameData
class B⸻(Algorithm): # Passe den Klassen-Namen hier an
def __init__(self):
super().__init__()
def decide(self, info: GameData) -> str:
# Programmiere hier
Montag, 22.3.2021, Nachmittag
Hamiltonweg
Wir haben uns eine einfache aber perfekte Lösung für das Snake-Spiel ausgedacht: im Zickzack das Feld nach Futter absuchen, so dass man am Ende wieder am Anfang ankommt.
Diese Art der Lösung ist ein Hamiltonweg. Dazu gibt es bei Wikipedia noch ein paar Hinweise. In unserer Spielumgebung funktioniert ein Hamiltonweg nur bedingt, da die Schlange nach 117 Schritten verhungert. Ihr bleibt also gar nicht genug Zeit, alle Kästchen nach Futter abzusuchen.
Um den definierten Anfangspunkt O(0|0) zu erreichen kann man folgenden Code verwenden:
class ZickZack(Algorithm):
def __init__(self):
super().__init__()
self.fahre = ["north"] * 10 + ["west"] * 5
def decide(self, info: GameData) -> str:
# Fahre zu Beginn einer Runde nach oben links
if len(self.fahre) > 0:
action = self.fahre[0]
del self.fahre[0]
return action
# Fahre im Zickzack nach unten
else:
...
Aufgabe: vervollständige den Weg
Vervollständige den obigen Code an der Stelle ..., so dass die Schlange im Zickzack nach unten fährt und auf der rechten Seite ein Kästchen übrig lässt, um wieder nach oben zu kommen.
Github Token für den Zugriff einrichten
Damit der Zugriff auf Github einfacher wird und wir uns nicht ständig einloggen müssen, richten wir uns ein Github Token ein. Das geht folgendermaßen:
-
Logge Dich auf Github ein und gehe zu den Einstellungen Deines Profils.
-
Gehe zu Developer Settings und dann Personal Access Tokens
-
Klicke auf Generate New Token
-
Gib dem Token einen Namen als Bedeutung, z.B. "Snake bei Mitutoyo".
-
Setze ein Häkchen bei: repo, read:org und gist.
-
Klicke auf Generate Token
-
Klicke auf das Icon, um die Zahlenfolge in die Zwischenablage zu kopieren
-
In PyCharm: gehe zu File / Settings
-
Gehe zu Version Control / GitHub
-
Klicke auf
+und wähle "Login with Token..." -
Füge die Zahlenfolge in das Feld ein.
Dienstag, 23.3.2021, Vormittag
Am Vormittag haben wir uns ganz allgemein mit Künstlicher Intelligenz befasst.
Dienstag, 23.3.2021, Nachmittag
Wir haben gemeinsam verglichen, was die Ergebnisse unserer eigenen Algorithmen sind.
-
I⸻: bestes Ergebnis 29 in 100 Spielen, max. Schritte 558, Gesamtmenge Futter: 1212, Gesamt-Schritte: 20433, Anzahl der
if-Anweisungen: 9 -
J⸻: bestes Ergebnis 27 in 100 Spielen, max. Schritte 298, Gesamtmenge Futter: 802, Gesamt-Schritte: 8442, Anzahl der
if-Anweisungen: 4 -
J⸻: bestes Ergebnis 32 in 100 Spielen, max. Schritte 324, Gesamtmenge Futter: 1156, Gesamt-Schritte: 12116, Anzahl der
if-Anweisungen: 8 -
T⸻: bestes Ergebnis 51 in 100 Spielen, max. Schritte 709, Gesamtmenge Futter: 2419, Gesamt-Schritte: 28963, Anzahl der
if-Anweisungen: 20
Grob kann man erkennen, dass das Ergebnis besser wird, je mehr Bedingungen oder Situationen im Algorithmus berücksichtigt werden. Das drückt sich häufig durch die Anzahl der if/elif Abfragen aus.
Spaghetti-Code?
Gleichzeitig merkt man beim Programmieren aber auch, dass es immer schwieriger wird, die richtige Stelle zu finden, an der man noch weiter verbessern kann. Bei Programmierern mit wenig Erfahrung kann das schnell zu so genanntem Spaghetti Code führen. Als Spaghetti Code wird Quellcode bezeichnet, der in sich verstrickt ist.
Bei Spaghetti Code ist oft nicht klar, was alles passiert, wenn man an einer Stelle etwas ändert. Und man muss sich fragen, ob man selbst das Programm noch verstehen würde, wenn man es in einem halben Jahr noch einmal liest.
Damit das nicht passiert, haben sich ein paar Regeln und Konzepte gebildet. Einerseits gibt es so genannte Entwurfsmuster (engl. patterns), mit denen man bestimmte Probleme lösen kann, zum anderen gibt es eine Initiative namens Clean Code, die zumindest Hinweise gibt, was man tun bzw. nicht tun sollte. Und wenn man dann nicht weiter weiß, sollte man einen Entwickler fragen, der mehr Erfahrung hat und vielleicht einen gute Tipp auf Lager hat, wie der Spaghetti Code ordentlicher aussehen könnte.
Die Präsentation Von der Spaghetti-Schlange zu Q-Tables stellt die Problematik vor und schlägt eine Lösung vor.
Aufgabe: Umrechnung einer Situation in eine Nummer
Die Aufgabe ist bereits in der Präsentation beschrieben: schreibe eine Funktion, die
- einen Ausschnitt aus dem Spielfeld um den Kopf herum in Betracht zieht
- die Situation aus leeren und belegten Feldern in eine Zahl umrechnet
- und dabei bestimmte Felder ausmaskieren (auslassen) kann
Ausgangspunkt für die Implementierung ist folgende Klasse:
from Algorithms.Algorithms import Algorithm
from GameData import GameData
class QTable⸻(Algorithm): # Passe den Klassen-Namen hier an
def __init__(self):
super().__init__()
def decide(self, info: GameData) -> str:
situationsnummer = self.umrechnen(info, 3, "111 101 111")
return "north"
def umrechnen(self, info, kantenlaenge, maske):
# hier rechnen
return situationsnummer
Mittwoch, 24.3.2021, Vormittag
Aufgabe: Entscheidungen sammeln
Wir haben ein Programm geschrieben, das Entscheidungen für bestimmte Situationen aufzeichnen und abspeichern kann. Dieses Programm ist kompatibel zu der Art und Weise, wie wir die Situation in eine Nummer umrechnen. Allerdings gilt es nun, 2⁸ Felder * 5 Richtungen, also für 1280 Fälle die Entscheidungen einzusammeln. Diese Arbeit teilen wir uns.
Bei einem 5x5 Ausschnitt und 5 Richtungen hätten wir schon 83 Millionen Entscheidungen zu treffen. Die wollen wir selbst mit einem Programm nicht durchgehen - das soll der Computer dann lieber selbst lernen.
Die grafische Oberfläche zum Einsammeln von Entscheidungen sieht folgendermaßen aus:
Zu sehen ist ein 3x3 Ausschnitt aus dem Spielfeld. Der dunkelrote Winkel zeigt an, in welcher Richtung sich das Futter befindet. In diesem Fall liegt das Futter auf jeden Fall südlich, es könnte aber zusätzlich noch ein bisschen östlich liegen, vielleicht aber auch westlich. Dass der rote Ausschnitt eher nach Westen zeigt, ist dabei kein Anzeichen für eine höhere Wahrscheinlichkeit, dass das Futter im Westen liegt.
Welche Aktion die Schlange in dieser Situation auslösen soll, bestimmst Du. Folgende Kriterien sind zu beachten:
- Die Schlange soll keinesfalls sterben. Fahre also nicht in den Schwanz der Schlange.
- Die Schlange sollte möglichst schnell zum Futter kommen.
Da der rote Bereich sowohl westlich als auch östlich liegt, ist weder Osten noch Westen im gezeigten Fall eine gute Wahl. Wenn die Schlange allerdings nach Süden fährt, wird irgendwann die Ausprägung zwischen Ost oder West deutlicher:
-
Verschiebung nach Osten, falls das Futter tatsächlich östlich lag:
-
Verschiebung nach Westen, falls das Futter tatsächlich westlich lag:
Da das Programm alle Möglichkeiten durchgeht, können Situationen angezeigt werden, die im Spiel nie vorkommen können, beispielsweise:
Es muss mindestens ein grünes Kästchen an das blaue Kästchen anschließen, da die Schlange nicht diagonal laufen kann. Für diesen Fall ist der Button "Impossible" gedacht.
Beachte aber, dass nicht alle diagonal aussehenden Felder unmöglich sind. Folgendes Feld beispielsweise ist legal:
weil das Spielfeld so aussehen könnte:
Damit wir eine gegenseitige Kontrolle haben, sollten wir jeden Fall zweimal aufzeichnen (1280 → 2560). Zusammen mit einem Mitarbeiter von Mitutoyo haben wir 4 Personen. Somit ergeben sich für jeden Schüler 640 Entscheidungen.
Die Einstellung erfolgt über die Datei configuration.json im Order decisionrecorder. Im oben beschriebenen Fall muss sie so aussehen:
{
"mask": "000 010 000",
"food_directions": 5,
"field_size": 3,
"number_parallel_workers": 2,
"number_worker": <zahl>
}
Anstelle von <zahl> tragen wir folgende Nummer ein:
- I⸻: 0 d.h. I⸻ bearbeitet die ersten 640 Fälle
- Ja⸻: 1 d.h. Ja⸻ bearbeitet die zweiten 640 Fälle
- Ju⸻: 0 d.h. Ju⸻ bearbeitet die ersten 640 Fälle, zum Gegencheck von I⸻
- D⸻: 1 d.h. D⸻ bearbeitet die zweiten 640 Fälle, zum Gegencheck von Ja⸻
Um das Tool laufen zu lassen öffnen wir main.py aus dem Ordner decisionrecorder.
JSON
Das Programm hat die Datei in einer Art Textdatei mit der Endung JSON abgespeichert. Dieses Dateiformat haben wir gewählt,
- weil man es (im Gegensatz zu Excel o.ä.) auch in PyCharm noch anschauen und ggf. auch nachträglich bearbeiten kann.
- weil sich Textdateien bei Änderungen gut vergleichen lassen, z.B. mit einem Programm wie KDiff3 und man mit Hilfe von Git gut nachvollziehen warum es Änderungen gab.
Ein Ausschnitt aus der Datei könnte folgendermaßen aussehen.
{
"field": 128,
"food": 0,
"decision": "-"
},
{ ... } kennzeichnet ein Objekt.
field ist eine Eigenschaft des Objekts und kennzeichnet den hier Ausschnitt aus dem Spielfeld und entspricht der von uns berechneten Situationsnummer.
food bestimmt die Richtung zum Futter. Bei 5 Richtungen geht diese Zahl von 0 bis 4.
decision ist die aufgezeichnete Antwort. N, E, S und W sind die Himmelsrichtungen. - bedeutet "Impossible" und nullsagt aus, dass keine Antwort abgegeben wurde.
Dateien lesen und verarbeiten
Um Dateien zu lesen und speziell JSON zu verarbeiten brauchen wir folgende Bibliotheken:
import glob
import json
Die Bibliothek glob erlaubt uns, Dateien zu finden, zum Beispiel
dateinamen = glob.glob("./decisionrecorder/*.json")
Um eine Datei zu lesen wird keine Bibliothek benötigt. Eine Datei kann "von Hand" geöffnet und geschlossen werden mit
datei = open(dateiname, "r")
# Daten lesen
datei.close()
oder geöffnet und automatisch geschlossen werden:
with open(dateiname, "r") as datei:
# Daten lesen (eingerückt)
Die Bibliothek json ermöglicht uns, Daten im JSON-Format zu lesen., zum Beispiel
daten = json.load(datei)
Aufgabe: Die gespeicherten Entscheidungen einlesen
Die abgespeicherten JSON Dateien sind quasi das Langzeitgedächtnis unserer Schlange. Das Langzeitgedächtnis des PCs ist die Festplatte. Dort überleben Daten auch einen Neustart des PCs.
Damit die Schlange diese Daten auch abrufen und verarbeiten kann, müssen sie in das Kurzzeitgedächtnis überführt werden. Das Kurzzeitgedächtnis des PCs ist das RAM (Random Access Memory). Variablen von Python befinden sich im RAM.
Erweitere den bestehenden Algorithmus, so dass er die aufgezeichneten Entscheidungen aus den JSON Dateien einliest und in (einer) Variablen speichert. Dann kann die Schlange später gemäß diesen Entscheidungen spielen.
Das Lesen der Daten macht die Schlange am besten nur einmal, sofern die Datenmenge dies zulässt. Der geschickteste Ort dafür ist die __init__() Methode der Schlange.
def __init__(self):
super().__init__()
# Schritt 0: Gedächtnis auffrischen
# TODO: hier Daten einlesen
Mittwoch, 24.3.2021, Nachmittag
Aufgabe: Winkel in Bereiche einteilen
Der DecisionRecorder hat die Richtung des Futters ziemlich grob in 5 Richtungen eingeteilt. GameData liefert uns allerdings einen gradgenauen Winkel zwischen -180° und 180°.
Schreibe eine Funktion, die den gradgenauen Winkel in n Bereiche einteilen kann und eine Zahl zwischen 0 und n-1 liefert.
def decide(self, info: GameData) -> str:
# Schritt 1: Situationsnummer ausrechnen
situationsnummer = self.umrechnen(info, 3, "111 101 111")
# Schritt 2: Nummer für die Richtung ausrechnen
# TODO: hier grobe Bereichsnummer ermitteln
Aufgabe: vervollständige den Algorithmus
Unser Algorithmus hat jetzt ein "Gehirn" mit den nötigen Entscheidungen, er kann die Situation in eine Zahl umrechnen und die Richtung des Futters grob in Bereiche aufteilen.
Jetzt muss er "nur noch" diese Informationen alle zusammenbringen und die richtige Aktion auswählen.
def decide(self, info: GameData) -> str:
# Schritt 1: Situationsnummer ausrechnen
situationsnummer = self.umrechnen(info, 3, "111 101 111")
# Schritt 2: Nummer für die Richtung ausrechnen
richtung = self.grobe_richtung(info.food_direction)
# Schritt 3: Greife auf eine Tabelle von Entscheidungen zu
# und suchen uns die Aktion raus, die ausgeführt werden soll
# TODO: Entscheidung raussuchen
Fun!
Endlich ist der Algorithmus fertig. Die Methode decide() kann auf 1280 Entscheidungen zugreifen, hat also quasi 1280 if-Anweisungen eingebaut.
Wie gut macht sich die Schlange? Wir stellen vergleichen Ergebnisse her, indem wir die Schlange 100 Epochen lang beobachten.
Ergebnisse:
- I⸻: bestes Ergebnis 48 in 100 Spielen, max. Schritte 674, Gesamtmenge Futter: 2406, Gesamt-Schritte: 29447
- J⸻: bestes Ergebnis 49 in 100 Spielen, max. Schritte 708, Gesamtmenge Futter: 2484, Gesamt-Schritte: 31200
- J⸻: bestes Ergebnis 54 in 100 Spielen, max. Schritte 612, Gesamtmenge Futter: 2417, Gesamt-Schritte: 29835
- D⸻: bestes Ergebnis 55 in 100 Spielen, max. Schritte 701, Gesamtmenge Futter: 2512, Gesamt-Schritte: 30649
- T⸻: bestes Ergebnis 47 in 100 Spielen, max. Schritte 654, Gesamtmenge Futter: 2360, Gesamt-Schritte: 29085
Übersicht:
| Name | I⸻ vorher | J⸻ vorher | J⸻ vorher | T⸻ vorher | aufgezeichnete Entscheidungen |
|---|---|---|---|---|---|
| Beste Länge | 29 | 27 | 32 | 51 | 47 ... 55 |
| Max. Schritte | 558 | 298 | 324 | 709 | 612 ... 708 |
| Futter gesamt | 1212 | 802 | 1156 | 2419 | 2360 ... 2515 |
| Schritte gesamt | 20433 | 8442 | 12116 | 28963 | 29085 ... 31200 |
| Anzahl if-Anweisungen | 9 | 4 | 8 | 20 | 1175* |
| *) 2⁸ * 5 = 1280, abzgl. 105 unmögliche Situationen |
Warum erreicht unser Algorithmus keine besseren Ergebnisse?
- Kurzsichtigkeit: die Schlange kann nur 1 Kästchen weit sehen
- Die Anzahl der Richtungen ist gering: es ist manchmal unklar, was die beste Entscheidung ist
- Bedingt dadurch: Fehler in unseren aufgezeichneten Daten
Donnerstag, 25.3.2021, Vormittag
Kleinere Umstrukturierungen
Ein paar Kleinigkeiten sind mir noch aufgefallen:
- Der Name der Klasse
QTableTWist noch nicht aussagekräftig. Ja, sie arbeitet irgendwie in Richtung QTables, aber der Begriff QTable sagt eigentlich aus, dass die Schlange selbst lernt. Das ist noch nicht der Fall. Es handelt sich eher um eine Art Record-Playback: wir haben Entscheidungen aufgezeichnet und spielen sie wieder ab. - Die Methode
lies_alle_dateien()ist nicht wahrheitsgemäß: sie liest nicht alle Dateien, sondern nur JSON-Dateien und davon auch nur die mit Entscheidungen. Besser wärelies_aufgezeichnete_entscheidungen().
Erweiterung des Blickfeldes
Die Aufzeichnung unserer Entscheidungen für ein 3x3 Sichtfeld waren in Ordnung, brachten aber keine große Verbesserung in der Leistung der Schlange. Sie schneidet nur marginal besser ab als eine selbst programmierte Schlange mit 20 if-Anweisungen. Wir erhöhen daher die Sichtweite auf 5x5. Bei 83 Millionen Kombinationen aus Spielfeld und Futter soll der Computer dann selbst herausfinden, was gute und schlechte Entscheidungen sind.
Wir haben dazu unsere 2D Struktur so überarbeitet, dass eine 3D-Struktur entstanden ist, die Werte für die Zuversichtlichkeit von Aktionen aufnimmt. Diese Werte haben wir mit einem Mittelwert von 0,5 initialisiert und in der Feedback-Phase entweder erhöht oder erniedrigt, je nach dem, ob eine Aktion erfolgreich war oder sich schlecht auf die Lebensdauer der Schlange auswirkte.
Das Feedback erfolgt über eine Methode train() mit folgender Struktur:
def train(self, info: GameData, action, reward) -> None:
pass
info beinhaltet die Angaben zum Spielfeld, wie es vor der Aktion aussah.
action ist die durchgeführte Aktion.
reward ist eine Zahl, deren Maßstab wir einstellen können. In der Standard-Einstellung bedeuten negative Werte schlechte Erlebnisse, positive Werte gute Erlebnisse. Es handelt sich um eine Summe von Erfahrungen wie z.B. Entfernung zum Futter (gering positiv), Fressen von Nahrung (hoch positiv) und Sterben (negativ, abhängig davon wie die Schlange stirbt).
Der Algorithmus kann auch sein eigenes Belohnungssystem bestimmen. Dazu definiert man die Eigenschaft self.reward_system , z.B.
from RewardSystem import RewardSystem
...
self.reward_system = RewardSystem()
# Belohnungen
self.reward_system.reward_eat_food = 1
self.reward_system.reward_win = 1
self.reward_system.reward_closer_function = lambda closer: 1 if closer > 0 else -1
# Bestrafungen
self.reward_system.reward_killed_by_wall = -1
self.reward_system.reward_killed_by_tail = -1
self.reward_system.reward_impossible_move = -1
self.reward_system.reward_killed_by_starving_function = lambda step, length: 0
Es ist sinnvoll, die 3D-Struktur abzuspeichern und wieder zu laden, so dass die Schlange nicht immer von Neuem anfangen muss zu lernen. Das Dateiformat JSON ist dafür zu langsam. Deshalb nutzen wir bei pickle und speichern erst nach einer gewissen Anzahl von Epochen, anstatt am Ende jedes Spiels. Die Dateigröße beträgt bei einem 5x5 Spielfeld und 5 Blickrichtungen ca. 230 MB.
Speichern und Laden funktioniert grundsätzlich so:
import pickle
...
# Speichern
with open(dateiname, "wb") as datei:
pickle.dump(daten, datei)
...
# Laden
with open(dateiname, "rb") as datei:
daten = pickle.load(datei)
Zum Speichern bietet unsere Umgebung noch eine Methode an, die am Ende jeder Trainingsrunde aufgerufen wird. Die Rückgabewerte sind das Modell und dessen Fitness. Da wir kein neuronales Netz haben und auch keine Fitnessfunktion erstellt haben, können wir hier leere Werte zurückgeben.
def epochfinished(self) -> (object, float):
# Hier Speichern oder auch nicht
return None, 0.0
Aufgabe: Passe eine Kopie des 3x3 Algorithmus auf Selbstlernen an
Kopiere den Algorithmus für das 3x3-Feld. Passe den Namen und die Werte auf ein 5x5-Feld an. Entferne die Funktion zum Einlesen von Daten aus JSON-Dateien. Erweitere dann den Algorithmus so, dass er Wahrscheinlichkeiten berücksichtigt und seine gelernten Erfolge alle 50 Epochen abspeichert.
Donnerstag, 25.3.2021, Nacht
In diesem BOGY wurde sogar nachts gearbeitet. Unermüdlich trainierten zwei Schlangen mehrere Stunden lang.
Freitag, 26.3.2021, Vormittag
Auswertung
Die Schlange, die auf dem Rechner von T⸻ trainierte, hatte diesen Trainingsverlauf über 380000 Epochen:
Zu Beginn des Trainings hat die Schlange scheinbar recht schnell gelernt, danach flachte die Lernkurve ab. Bei ca. 120000 Epochen sieht es dann nochmal nach einem "Aha-Effekt" aus.
Allerdings zeigt die grafische Oberfläche auch die Menge des durchschnittlich gefressenen Futters an - und diese Zahlen sehen nicht besonders beeindruckend aus. Der gleitende Durchschnitt über die letzten 10 Epochen liegt knapp unter 14, entsprechend einer Schlangenlänge von 17.
Abgespeichertes Gehirn nutzen
Das trainierte Gehirn schaffte eine maximale Länge von 34 in 100 Epochen und einen Durchschnittswert von 14 gegessenen Äpfeln pro Epoche.
Ideen für bessere/andere Ergebnisse
Es gibt noch allerhand Möglichkeiten, andere und ggf. bessere Ergebnisse zu erzielen:
- Wir könnten die Trainingsdauer verlängern. Es ist anzunehmen, dass noch nicht alle möglichen Möglichkeiten ausreichend von der Schlange erkundet wurden, insbesondere die Situationen, in denen sehr viele Kästchen belegt sind.
- Das Sichtfeld der Schlange ist auch mit 5x5 noch ziemlich klein.
- Die Einschätzung der Lage des Futters in 5 groben Richtungen reicht evtl. nicht aus, um eine gute Entscheidung zu treffen.
- Wir könnten die Belohnungen und Bestrafungen anders verrechnen oder das Belohnungssystem anpassen.
- Hin und wieder zufällige Aktionen ausführen, um neue Wege zu entdecken, die die Schlange noch nie beschritten hat. Dieser Ansatz ist im Machine Learning bekannt und wird über die Variable ε (Epsilon) gesteuert.
- Die letzte Aktion eines Spiels führt zwar zum unmittelbaren Tod der Schlange und wird bestraft. Die fatale Entscheidung wurde vielleicht jedoch schon zuvor getroffen, als die Schlange z.B. in eine Sackgasse lief. Die "Bestrafung" für den Tod der Schlange könnte auf die vorherigen Aktionen übertragen werden.
Den Epsilon-Ansatz haben wir schon vorbereitet. Er kann bei einem Algorithmus ergänzt werden, z.B.
def epsilon(self, epoch: int, maxepochs: int) -> float:
if epoch < maxepochs/2:
# In der ersten Trainingshälfte 30% Zufall verwenden
return 0.3
else:
# In der zweiten Trainingshälfte kein Zufall mehr
return 0.0
Eine wesentliche Beschränkung bei dem von uns gewählten Ansatz ist allerdings die benötigte Speichermenge. Unser "Gehirn" der Schlange war so ausgelegt, dass sie alle Möglichkeiten für den Ausschnitt des Spielfelds im Kopf haben musste. Eine wesentliche Errungenschaft von Gehirnen ist allerdings das Vergessen. Dadurch wird Speicherplatz eingespart.
Neuronale Netze
Neuronale Netze kommen mit deutlich weniger Speicher aus. Sie arbeiten anhand der Idee einer Nervenzelle.
Anhand des sehr anschaulichen Beispiel auf playground.tensorflow.org und der Excel-Tabelle Neuronales Netz Mathematik haben wir die Funktion eines Neurons und von Netzen hoffentlich gut verstanden und konnten die Berechnungen der Webseite nachvollziehen.
Schon schwieriger wird es mit der Darstellung von Netzen zur Bildverarbeitung. Die Webseite von Adam Harley hat dies mal für Handschrifterkennung von Ziffern versucht.
Sophy
Softwareunterstützter Physikunterricht Wie schnell ist eine Kugel auf der Kugelbahn?
Measurements
The length of the distance has to be stated in millimeters.
How to install the light barriers
The light barriers have to be installed at a position in which they can't be interrupted by an object which should not interrupt them. Especially at the "Aquasol", the light barriers should not be reachable for a person which isn't in the slide yet. If the light barrier isn't installed in the right way, we can't guarantee the accuracy of our measurement results.
CalculatorCV
CalculatorCV is a calculator which has a gesture interface for input. It shall recognize fingers as numerical input and have buttons for the math operations.
It's written in Python and uses OpenCV for image processing.
Riesen-Tetris
Riesen Tetris Spiel mit pygame und ws2812 leds mit python













