Freifach Informatik: Projekte mit KI und Hardware

Etappe 2: KI im Browser – Schere-Stein-Papier mit Handzeichen

Ziel: Am Ende zeigst du ein Handzeichen vor der Kamera, eine KI erkennt es, und du spielst damit gegen den Computer, gegen den micro:bit und zum Schluss gegen eine andere Person per Funk.

Die Idee: Die Kamera ersetzt die Taste

In Etappe 1 war die Eingabe eine Taste. Jetzt ist es ein Kamerabild. Die Verarbeitung ist neu eine KI, die das Bild einer Klasse zuordnet (Stein, Papier, Schere). Die Ausgabe bleibt: Display, Ton, Ergebnis.

Eingabe Verarbeitung Ausgabe
Kamerabild KI-Modell: „Das ist Papier, zu 97 %“ Browser-Seite, micro:bit-Display

Wie lernt eine KI? Du zeigst dem Modell viele Beispiele pro Klasse (die Trainingsdaten). Daraus lernt es, neue Bilder einer Klasse zuzuordnen. Es hat keine Regel wie „Papier hat fünf Finger“, es vergleicht nur mit dem, was es gesehen hat. Darum sind gute Beispiele wichtiger als alles andere.

Vorbereitung

  1. Chrome oder Edge (Web Serial gibt es nur dort).
  2. VS Code mit der Erweiterung Live Server. Öffne deinen ganzen Arbeitsordner schere-stein-papier-code in VS Code, nicht nur eine einzelne Datei (Anleitung). Die Seiten brauchen die gemeinsamen Dateien im Ordner ki/gemeinsam/.
  3. Eine Webcam (die eingebaute reicht) und für Schritt 3 und 4 den micro:bit mit USB-Kabel.
  4. Für das Zusatz ★ in Schritt 3 ein Neopixel-Streifen (höchstens 8 Pixel), den bekommst du von der Lehrperson.

So öffnest du eine Seite: Rechtsklick auf index.html → Open with Live Server.


Schritt 1: Modell trainieren (Teachable Machine)

  1. Öffne teachablemachine.withgoogle.com → Bildprojekt → Standard-Bildmodell.
  2. Lege vier Klassen an und benenne sie genau so: stein, papier, schere, nichts. (Die Seite erkennt diese Wörter im Namen. Gross und klein ist egal.)
  3. Nimm pro Klasse mit der Webcam mindestens 50 Bilder auf (Knopf gedrückt halten):
    • stein, papier, schere: dein Handzeichen, nur die Hand
    • nichts: keine Hand im Bild, und auch die Hand in Ruhe (unten, in der Hosentasche, halb im Bild)
  4. Modell trainieren drücken und warten. Das Tab dabei offen lassen.
  5. Teste im Vorschaufenster alle vier Klassen.
  6. Modell exportieren → Tab Tensorflow.js → Modell hochladen → den Link kopieren. Er endet auf /. Speichere ihn in einer Textdatei oder Notiz, du brauchst ihn in jedem Schritt.

Datenschutz: Ein hochgeladenes Modell ist über seinen Link öffentlich abrufbar. Halte nur die Hand vor die Kamera, nicht dein Gesicht. Sieh vor dem Aufnehmen, dass hinter dir nichts Privates im Bild ist (Bildschirme, Wandplakate, andere Personen).

Gute Trainingsdaten

Funktioniert es?

Aufgaben

  1. Teste dein Modell vor einem anderen Hintergrund und mit der anderen Hand. Was passiert?
  2. Verbessere es mit neuen Beispielen und trainiere neu. Wurde es besser?
  3. ★ Tausche mit einer Nachbarin oder einem Nachbarn die Modell-Links. Funktioniert ihr Modell bei dir? Woran liegt das?

Schritt 2: Nur Browser – erkennen und selbst die Spiellogik schreiben

Code: ki/01-erkennen/ · öffnen mit Live Server

Teil A: erkennen. Füge den Modell-Link ein und drücke Kamera + Modell starten (Kamera erlauben). Du siehst die erkannte Klasse mit der Sicherheit und einen Balken pro Klasse.

Funktioniert es?

Teil B: Du schreibst die Spiellogik. Die Seite kann Kamera, Modell und Spielfeld. Was in einer Runde passiert, schreibst du in ki/01-erkennen/runde.js. Dort stehen fünf TODOs. Arbeite sie der Reihe nach ab und lade die Seite nach jedem Teil neu (F5):

TODO Aufgabe
1 Countdown 3, 2, 1 anzeigen (eine Sekunde pro Zahl)
2 Das Handzeichen mit handzeichenJetzt() holen
3 Der Computer wählt zufällig
4 Das Ergebnis mit ergebnis(ich, computer) berechnen
5 „Sieg!“, „Verloren“ oder „Gleich“ anzeigen und Punkte zählen

Funktioniert es?

Hängst du fest? Die Lösung steht in ki/01-erkennen/loesung/runde.js. Versuche es zuerst selbst: Lies die Hinweise in der Datei, schau in die Konsole (F12) und auf die Meldung unter dem Spielfeld. Bei „Nicht sicher erkannt“ steht dort, woran es liegt.

Was handzeichenJetzt() bei 0 macht:

  1. Die Seite wartet noch 0,25 s (NACHFRIST_MS), falls die Hand etwas spät kommt. Dann schaut sie auf die letzten ca. 500 ms zurück.
  2. Sie zählt nur Kamerabilder, bei denen das Modell mindestens 80 % sicher war und ein Handzeichen erkannt hat. nichts stimmt nicht mit ab.
  3. Das häufigste Handzeichen gewinnt. Gibt es zu wenige sichere Bilder (weniger als 3) oder keinen klaren Sieger, ist die Erkennung „nicht sicher“ (null), und die Runde zählt nicht.

Zeige dein Handzeichen schon bei 1 und halte es ruhig, bis das Ergebnis erscheint.

Aufgaben

  1. Öffne ki/gemeinsam/logik.js und ändere SCHWELLE auf 0.95. Was ändert sich beim Spielen? Und bei 0.5?
  2. Ändere FENSTER_MS auf 100 und auf 2000, dann NACHFRIST_MS auf 0 und auf 1000. Wann klappt es besser, wann schlechter? Warum?
  3. Lass die Seite „Spiel gewonnen!“ anzeigen, wenn jemand 3 Punkte hat.
  4. ★ Der Computer schummelt: Er wählt mit 40 % Wahrscheinlichkeit das Zeichen, das dein Zeichen schlägt. (Tipp: (ich + 1) % 3 schlägt ich.)

Schritt 3: Browser + micro:bit – der micro:bit als Spielgerät

Code: microbit/06_ssp_ki_vorlage.py und ki/02-microbit/

Jetzt spielst du gegen den micro:bit. Er ist das Spielgerät: Er zählt mit Display und Ton 3-2-1, fragt dann über das USB-Kabel den Browser nach deinem Handzeichen, wählt zufällig und zeigt dir Ergebnis und Punkte. Der Browser ist hier nur die Kamera mit KI, die dem micro:bit das Handzeichen liefert. Das ist dieselbe Idee wie bei „KI bewegt etwas“: Die KI erkennt, und die Hardware reagiert.

Die Vorlage hat sechs TODOs, du schreibst die Funktion runde_spielen(). USB, Lesen der Antwort und das Protokoll sind schon fertig.

TODO Aufgabe Bauteil
1 Countdown: C3/C2/C1 senden, Zahl auf dem Display, kurzer Ton, 1 Sekunde warten Display, Lautsprecher (V2)
2 Bei keiner Antwort oder -1 (nicht sicher): X senden, verwirrtes Gesicht, Runde beenden Display
3 Der micro:bit wählt zufällig Zufall
4 Ergebnis berechnen und dem Browser als R … melden USB
5 Beide Handzeichen zeigen, Ergebnis mit Melodie, Punkte zählen und senden Display, Lautsprecher (V2)
6 Die Runde auch durch Schütteln starten Beschleunigungssensor

So gehst du vor (die Reihenfolge ist wichtig):

  1. Arbeite TODO 1 ab und lade das Programm im Editor python.microbit.org auf den micro:bit. Teste den Countdown mit Taste A.
  2. Arbeite weiter und teste nach jedem TODO. Ab TODO 2 brauchst du die Seite:
    1. Trenne den Editor vom micro:bit (im Editor Disconnect, oder den Tab schliessen). Sonst ist der USB-Port besetzt und die Seite kann ihn nicht öffnen.
    2. Öffne die Seite ki/02-microbit/: mit Live Server oder direkt online unter masta-nksa.github.io/schere-stein-papier/ki/02-microbit/. Füge den Modell-Link ein und starte Kamera + Modell.
    3. Mit micro:bit verbinden drücken und den micro:bit in der Liste wählen.
  3. Drücke Taste A (oder schüttle). Taste B setzt die Punkte auf 0.

Zum neuen Hochladen: Auf der Seite zuerst Trennen, dann im Editor hochladen, dann wieder verbinden.

Hängst du fest? Die Lösung ist microbit/06_ssp_ki.py. Versuche es zuerst selbst: Das Log der Seite zeigt dir, was dein Programm schickt.

Funktioniert es?

Zusatz ★: Neopixel-Ampel

Ein Neopixel-Streifen zeigt Countdown und Ergebnis: Beim Countdown leuchten 3, 2, 1 Pixel gelb, am Ende ist alles grün (du gewinnst), rot (du verlierst) oder orange (unentschieden).

Anschluss: Datenleitung (Eingang) an Pin 1, dazu 3V und GND des micro:bit. Nicht Pin 0: Den braucht der Ton. Am 3V-Anschluss dürfen höchstens 8 Neopixel hängen. Prüfe den Anschluss deines Streifens im Hardware-Katalog, bevor du das Kabel ansteckst, und lass die Lehrperson einmal draufschauen.

Programm: Setze oben N_PIXEL auf die Anzahl deiner Pixel (z. B. 8). Die Funktion ampel(r, g, b, anzahl) ist schon da. Du musst sie nur an den richtigen Stellen aufrufen, die Hinweise stehen bei TODO 1 und TODO 5. Halte die Farbwerte klein (z. B. 40 von 255): Sie sind sonst sehr hell und brauchen viel Strom.

Funktioniert es?

Das Protokoll: was über das Kabel geht

Browser und micro:bit schicken sich Textzeilen (Ende der Zeile: \n, Geschwindigkeit: 115200 Baud). Die Seite zeigt sie im Log. Ein gemeinsames Protokoll heisst: beide Seiten sind sich einig, was jede Zeile bedeutet.

micro:bit → Browser

Zeile Bedeutung
C3 C2 C1 Countdown steht bei 3, 2, 1
? Bei 0: „Welches Handzeichen zeigst du?“
R <ich> <gegner> <ergebnis> Resultat der Runde, z. B. R 1 0 1
P <ich> <gegner> Punktestand, z. B. P 2 1
X Runde abgebrochen (nicht sicher erkannt oder keine Antwort)

Browser → micro:bit

Zeile Bedeutung
G0 G1 G2 Handzeichen: 0 = Stein, 1 = Papier, 2 = Schere
G- „Ich bin nicht sicher“

„ich“ ist immer die Person vor diesem Browser, „gegner“ der micro:bit oder die andere Person. ergebnis: 1 = du gewinnst, 0 = unentschieden, -1 = du verlierst. Die Kodierung ist dieselbe wie in Etappe 1.

Beispiel einer Runde

← C3   ← C2   ← C1        der micro:bit zählt
← ?                        der micro:bit fragt
→ G1                       der Browser antwortet: Papier
← R 1 0 1                  Papier gegen Stein: du gewinnst
← P 1 0                    Stand 1 : 0

Aufgaben

  1. Schau im Log zu, bei welcher Zeile der Browser rechnet und wann der micro:bit entscheidet. Wer wählt das Zeichen des micro:bit?
  2. Ändere in deinem Programm den Countdown auf (5, 4, 3, 2, 1). Was zeigt das Log? Warum ist C5 eine „unbekannte Zeile“? Wo müsste man den Browser anpassen? (Tipp: parseVonMicrobit in logik.js.)
  3. Spiele eine eigene Melodie für den Sieg. Unter Reference im Editor findest du music.POWER_UP, music.NYAN und mehr.
  4. ★ Der micro:bit schummelt wie in Schritt 2: Er wählt mit 40 % das Zeichen, das dein Handzeichen g schlägt. Wie gehst du vor? Merkt man es nach 10 Runden?
  5. ★★ Was passiert, wenn der Browser zu spät antwortet? Wo steht im Programm, wie lange der micro:bit wartet?

Schritt 4 (Finale): Duell per Funk

Code: microbit/07_ssp_duell.py und dieselbe Seite ki/02-microbit/

Zwei Personen stehen Rücken an Rücken. Jede sitzt oder steht vor dem eigenen Laptop (mit eigener Kamera) und hat einen micro:bit. Taste A startet bei beiden gleichzeitig den Countdown. Bei 0 holt jeder micro:bit das Handzeichen vom eigenen Browser, die micro:bits tauschen sie per Funk aus und beide zeigen das Ergebnis.

Dieses Programm ist fertig und etwas länger. Du schreibst es nicht selbst, sondern lernst es lesen, erklären und verändern. Die Aufgaben unten führen dich durch.

So gehst du vor

  1. Stellt in 07_ssp_duell.py bei beiden dieselbe GRUPPE ein. Jedes Paar braucht eine eigene Zahl (Absprache mit der Lehrperson).
  2. Beide laden das Programm auf den eigenen micro:bit und trennen den Editor wieder.
  3. Beide öffnen die Seite ki/02-microbit/ (mit Live Server oder online), starten Kamera + Modell und verbinden den micro:bit.
  4. Stellt euch Rücken an Rücken. Eine Person drückt Taste A. Ihr könnt auch beide gleichzeitig drücken.

Funktioniert es?

Das Funk-Protokoll

Auch der Funk schickt Text. Jede Nachricht hat drei Teile, getrennt durch Kommas:

Nachricht Bedeutung
S,<runde>,<n> Start: Runde Nummer <runde>, der Countdown steht bei <n>
G,<runde>,<g> Handzeichen <g> (0, 1, 2 oder -) in dieser Runde

Funk ist unzuverlässig: Nachrichten können verloren gehen oder verspätet ankommen. Darum steckt im Programm:

Aufgaben

  1. Suche im Programm die Stelle, die entscheidet, ob eine Funknachricht zur laufenden Runde gehört. Was passiert ohne die Rundennummer? Spiele das gedanklich mit einer verspäteten Nachricht durch.
  2. Was passiert, wenn ihr beide gleichzeitig Taste A drückt und zwei verschiedene Rundennummern würfelt? (Tipp: Suche nach „die grössere Nummer gewinnt“.)
  3. ★ Spielt „Best of 5“: Wer zuerst 3 Runden gewinnt, bekommt ein Siegerbild, danach beginnt ein neues Spiel.
  4. ★ Baue Ton und Neopixel-Ampel aus Schritt 3 in 07_ssp_duell.py ein. Du kannst ampel() und die music-Aufrufe aus 06_ssp_ki.py kopieren. Wo im Programm gehören sie hin?
  5. ★★ Ein drittes Paar in derselben Gruppe funkt dazwischen. Was geht schief? Wie könnte man das verhindern?

Zusatz ★: ml5 handPose statt Teachable Machine

Teachable Machine braucht Training. ml5 handPose ist ein fertiges Modell: Es findet die Hand im Bild und liefert 21 Punkte (Fingerspitzen, Gelenke). Aus diesen Punkten kannst du selbst ableiten, welches Handzeichen es ist, z. B. durch Zählen der ausgestreckten Finger. Das braucht keinen Trainingsdatensatz, dafür eine eigene Regel.

Idee: Fingerspitze weiter von der Handfläche weg als das mittlere Gelenk = Finger ausgestreckt. 0 Finger = Stein, alle = Papier, zwei = Schere. Wie würdest du nichts erkennen? Ein Beispiel für ml5 handPose findest du im Beispielprojekt „Fang das Licht“.


Rückblick

Wenn etwas nicht klappt

Problem Lösung
„Failed to fetch“ beim Start Modell-Link kopiert? Er endet auf /. Modell wirklich hochgeladen (Tensorflow.js → „Modell hochladen“)?
Kamera startet nicht Seite über Live Server öffnen (nicht die Datei doppelklicken). Kamera erlauben. Läuft die Kamera in einem anderen Programm?
Die Kamera ist schwarz oder die Seite hängt Anderes Programm (Zoom, Teams) schliesst die Kamera? Seite neu laden.
Rote Meldung „Fehler in runde.js“ Tippfehler in deinem Code. Konsole mit F12 öffnen: Dort steht die Zeile. Häufig: ein vergessenes await, ein falsch geschriebener Name oder eine fehlende Klammer.
Nach TODO 2 steht „die Kamera sieht … aber das Programm hat sie nicht gefragt“ In runde.js steht noch const ich = null;. Ersetze null durch handzeichenJetzt().
Erkennung wechselt dauernd Mehr und vielseitigere Trainingsdaten. Gleicher Abstand und gleiches Licht wie beim Training.
Immer „Nicht sicher erkannt“ Hand deutlich zeigen und ruhig halten. Zu streng? SCHWELLE in logik.js senken. Heisst die Klasse wirklich stein, papier, schere?
Zeichen wird erst nach dem Wechseln erkannt Zeige das Handzeichen schon bei 1 und halte es bis zum Ergebnis.
„Mit micro:bit verbinden“ zeigt nichts Chrome oder Edge? Kabel mit Datenleitung? Anderer USB-Anschluss?
„Port nicht verfügbar“ (Fehler beim Öffnen) Der Python-Editor ist noch verbunden. Im Editor Disconnect drücken oder den Tab schliessen. Auch andere Tabs und Programme mit serieller Verbindung (Serial Monitor, Thonny, Arduino IDE) belegen den Port.
Neues Programm hochladen geht nicht mehr Zuerst auf der Seite Trennen, dann im Editor hochladen, dann wieder verbinden.
Im Log erscheint nichts Läuft 06_ssp_ki.py bzw. 07_ssp_duell.py auf dem micro:bit? Taste A gedrückt? Reset-Taste hinten probieren und neu verbinden.
Im Log stehen graue Zeilen Die Seite kennt die Zeile nicht (Tippfehler im Programm, oder ein anderes Programm läuft). Sie wird ignoriert.
Kein Ton Nur der micro:bit V2 hat einen eingebauten Lautsprecher (steht hinten drauf).
Die Runde startet beim Tragen von selbst Schütteln startet die Runde. Halte den micro:bit ruhig, oder lass TODO 6 weg.
Die Neopixel leuchten nicht N_PIXEL oben im Programm gesetzt? Daten an Pin 1, dazu 3V und GND? Streifen in der richtigen Richtung angeschlossen (Eingang, nicht Ausgang)? Nicht Pin 0 benutzen.
Die Neopixel sind zu hell oder flackern Kleinere Farbwerte (z. B. 40 statt 255). Höchstens 8 Pixel am 3V-Anschluss.
Der micro:bit zeigt „Browser?“ Die Seite antwortet nicht. Kamera gestartet? Der Tab muss im Vordergrund sein (im Hintergrund pausiert der Browser die Erkennung).
Der micro:bit zeigt ein verwirrtes Gesicht Handzeichen nicht sicher erkannt. Runde mit Taste A nochmals starten.
Duell: kein Countdown auf dem anderen micro:bit Beide dieselbe GRUPPE? Beide das Programm neu geladen? Beide micro:bits eingeschaltet?
Duell: der micro:bit zeigt ein trauriges Gesicht Die Funk-Antwort der anderen Person fehlt. Läuft deren Seite und ist ihr micro:bit verbunden?
Ein fremdes Paar startet meine Runde Gleiche GRUPPE wie ein anderes Paar. Neue Zahl absprechen.

Weiter zu Etappe 3: Projektstart