Benutzer-Werkzeuge

Webseiten-Werkzeuge


Seitenleiste

techniken:tensorflownodebluetooth


TensorFlow & Kommunikation mit dem Arduino per Bluetooth

1. Einführung

TensorFlow ist ein Framework, welches eine vereinfachte Anwendung von Algorithmen aus dem maschinellen Lernen ermöglicht. Damit kann man z.B.:

  • Posenet: die Stellung von einem oder mehreren Menschen erkennen,
  • Body-Pix: den menschlichen Körper segmentieren,
  • Facemesh: markante Punkte im Gesicht erkennen,
  • Handpose: die Handstellung erkennen usw.

Es gibt TensowFlow-Implementationen für verschiedene Sprachen, z.B. Python, C++, Java und JavaScript. Die JavaScript-Variante scheint einfacher für den Start zu sein. Euer TensorFlow-Code läuft dabei im Browser.

Probiert die TensorFlow-Beispiele aus, indem ihr auf die entsprechenden Links in der Liste oben klickt!

TensorFlow ist rechenintensiv. Deswegen ist es (zu diesem Zeitpunkt im Januar 2021) ziemlich langwierig, es auf einem Smartphone oder Raspberry Pi zu betreiben (bzw. wir haben noch keine gute Lösung gefunden). Es gibt zwar die abgespeckte Variante TensorFlow Lite, jedoch scheint es momentan einfacher, TensorFlow auf einem PC zu laufen und nur die Ergebnisse bei Bedarf über serielle Kommunikation an den Arduino zu schicken. Bei mobilen Robotern kann die serielle Kommunikation über Bluetooth laufen. Diesem Thema (TensorFlow & Kommunikation über die serielle Schnittstelle) widmet sich dieser Artikel.

Bei mobilen Robotern kann man über die App DroidCam die Handy-Kamera als externe kabellose Webcam nutzen. Wenn der Rechner und das Handy beide im gleichen WLAN-Netzwerk sind, kann man sie kabellos über WLAN verbinden. Die dafür erforderliche IP-Adresse wird in der App angezeigt.

2. Einfacher(er) Einstieg in TensorFlow

Da ihr euch im Crashkurs mit Processing vertraut gemacht habt, könnt ihr p5.js nutzen – die Processing-Variante für JavaScript. Hier gibt es eine gute Einleitung, wie man die TensorFlow-Bibliothek Posenet (für die Einschätzung der menschlichen Stellung) in p5.js verwendet.

In dieser Video-Einleitung wird das Programm im p5.js-editor ausgeführt. Alternativ könnt ihr Dasselbe in Textdateien auf eurem Rechner abspeichern und ausführen. Dass es verschiedene Text-Dateien gibt, wird auch im Einleitungsvideo (ab ca. 26. Min.) erwähnt. Die .html-Datei beinhaltet das Grundgerüst der Webseite, in der .js-Datei läuft euer Code.

Informiert euch, warum JavaScript überhaupt in Webseiten verwendet wird und wozu der <script>-Tag dient.

Aktuell gibt es eine neuere Version von p5.js als im Einleitungsvideo erwähnt, ihr könnt den folgenden Code in eurer .html-Datei nutzen (wir nennen diese Datei z.B. index.html):

index.html

index.html

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
    <script src="sketch.js"></script>
  </head>
</html>


In Zeile 5 bezieht sich der html-Code auf die JavaScript-Datei script.js. Diese Datei müsst ihr selbst erstellen, in den selben Ordner wie index.html legen und dorthin den Code aus dem p5.js-editor kopieren. (Wenn ihr dieser Datei einen anderen Namen wie script.js vergeben habt, müsst ihr die entsprechende Stelle in der html-Datei ändern.) Wenn ihr nun index.html in eurem Browser öffnet, soll dort die selbe Anwendung wie auf der p5.js-editor-Zeichenfläche laufen. Testet das, bevor ihr zum nächsten Punkt dieses Artikels übergeht.

3. Lokalen Server einrichten

3.1. Node.js-Installation

Node.js auf Windows: Posenet mit lokal gehosteter p5.js Instanz

Wir konnten schon über den p5.js-editor die Ergebnisse von einem TensorFlow-Modell bekommen, warum brauchen wir den Server? Grund ist, wir wollen auf den seriellen Port zugreifen, um Daten an den Arduino zu schicken, und das geht nicht von der Client-Seite (Browser). In unserem Fall wird der Server auf dem gleichen Rechner laufen wie der Client (Browser), deswegen sagen wir, dass wir einen lokalen Server einrichten. Eine Internetverbindung brauchen wir trotzdem, da unsere Datei index.html Links zu den Bibliotheken p5.js und ml5.js beinhaltet.

Informiert euch darüber, was ein Server und ein Client sind.

Es gibt viele Möglichkeiten, einen Server zu programmieren, z.B. mit Java, PHP oder Python. Wir werden einen Node.js-Server einrichten, u.A. weil Node.js ebenso auf JavaScript geschrieben ist.

Node.js verfügt über den Paket-Manager npm, über welchen man zusätzliche Node-Module installiert. Ladet Node.js und npm herunter und installiert sie. Anschließend könnt ihr mit den Befehlen node --v und npm --v auf dem Terminal (bzw. in der Anwendung cmd) überprüfen, ob alles geklappt hat. (Die Node/npm-Version wird angezeigt ⇒ geklappt.)

3.2. Hallo Welt

Erstellt einen Ordner (ich nenne ihn NodeOrdner – andere Namen möglich), wo ihr Dateien für euren Server ablegen werdet. Es hilft, wenn dieser Ordner auf dem Laufwerk ist, wo ihr Anwendungen ausführen dürft. Wechselt über den cd-Terminal-Befehl zu diesem Ordner. (Bei Bedarf sucht online, wie man mit cd zwischen Ordnern wechseln kann.) Wenn ihr im richtigen Ordner seid, führt diesen Befehl aus – er initialisiert euer Projekt:

npm init

Bestätigt mit der Enter-Taste (mehrfach) die Standard-Einstellungen. Die Datei package.json wird automatisch angelegt, wo diese Einstellungen gespeichert sind.

Installiert auch die Node-Module, die wir später brauchen werden:

  • express (minimalistische Node-Variante): npm install express --save
  • body-parser (analysiert HTTP-Requests): npm install body-parser --save
  • serialport (ermöglicht den Zugriff auf serielle Ports): npm install serialport --save

Erstellt im Ordner die Datei server.js (andere Namen möglich) mit dem folgenden Inhalt:

Code server.js

Code server.js

var express = require('express'); // den Node-Modul express einbinden
 
const hostname = '127.0.0.1'; // localhost
const port = 3000; // Portnummer, kann geaendert werden
const folderName = 'public'; // nutze Dateien aus diesem Ordner
 
var app = express();
app.use(express.static(folderName)); // nutze Dateien aus diesem Ordner
var server = app.listen(port, function () { // starte den Server
  console.log("Example app listening at http://%s:%s", hostname, port) // console.log() ist wie println()
})


In diesem Beispiel starten wir den Server auf der lokalen Maschine (127.0.0.1, oder man kann stattdessen auch localhost schreiben) auf dem Port 3000 (andere Portnummer möglich). Dieser Server wird Dateien aus einem konkreten Ordner bereitstellen. Im Code haben wir diesen Ordner public genannt (andere Namen möglich).

Diesen Ordner gibt es noch nicht. Deswegen müssen wir im Ordner NodeOrdner einen Unterordner public erstellen. In diesem Unterordner erstellt bitte eine Datei index.html (andere Namen möglich), öffnet diesen und speichert dort eine Zeile: Hallo Welt.

Jetzt startet den Server. Im Terminal im Ordner NodeOrdner führt dafür den folgenden Befehl aus:

node server.js

Geht im Browser auf die Seite http://localhost:3000/index.html (oder http://127.0.0.1:3000/index.html, was das Selbe ist). Wenn ihr „Hallo Welt“ seht, funktioniert alles richtig.

Was haben wir gerade gemacht? Wir haben einen Server eingerichtet, der in der Lage ist, Dateien (bzw. Inhalte dieser Dateien) aus dem bestimmten Ordner (in unserem Fall public) bereitzustellen. Dann haben wir einen Browser gestartet, der die konkrete Datei index.html angefordert hat. Diese Datei befand sich in dem Ornder, und so wurde ihr Inhalt an den Browser bereitgestellt: „Hallo Welt“. Das war einfacher Text, damit kann der Browser nicht viel mehr anfangen. Wenn die Datei stattdessen html-Code beinhaltet hätte, könnte der Browser diesen entsprechend verarbeiten – das machen wir im nächsten Schritt.

3.3. Eure Dateien über den Server bereitstellen

Löscht die Datei index.html aus dem Ordner public und fügt die im Punkt 2 erstellten Dateien index.html und script.js hinzu. Ruft im Browser den Link http://localhost:3000/index.html auf. Wenn alles richtig ist, seht ihr euer Programm mit TensorFlow/Posenet.

4. GET & POST Requests

Der Server und der Client (Browser) kommunizieren über das Hypertext Transfer Protocol, HTTP (kurze Erklärung). Der Client kann an den Server bestimmte Anfragen schicken, am häufigsten kommen der GET- und der POST-Request vor (kurze Erklärung). In unserem Beispiel könnten wir sowohl GET als auch POST verwenden; Im folgenden Beispiel wird ein POST-Request verschickt.

Der POST-Request kann im Code so aussehen (es kann auch was anderes statt „distance“ und „dist“ stehen, aber es muss der gleiche String an beiden Seiten sein):

Code: POST-Request im Browser schicken

Code: POST-Request im Browser schicken

    var distance = 42; // der Wert, den man an den Server schickt
    var xhttp = new XMLHttpRequest();
    xhttp.open("POST", '/dist', true); 
    //Den richtigen POST-Request-Header schicken:
    xhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 
    xhttp.onreadystatechange = function() { // Diese Funktion wird aufgerufen, sobald der Zustand sich aendert
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
            // Request ausgefuehrt. Weitere Aktionen koennen ausgefuehrt werden.
        }
    }
    xhttp.send("distance=" + distance); // den Wert als Eigenschaft distance schicken
 

Code: POST-Request auf dem Server verarbeiten

Code: POST-Request auf dem Server verarbeiten

const bodyParser = require("body-parser"); // Node-Modul body-parser einbinden
app.use(bodyParser.urlencoded({extended : true}));
 
app.post('/dist', (req, res) => {
    console.log('Got distance:', req.body.distance);
    res.sendStatus(200);
    // TODO: weitere Aktionen 
});


5. Seriellen Bluetooth-Port einrichten

Überpfüft, dass auf eurem Rechner ein Bluetooth-Port verfügbar ist. Auf einem Linux-Rechner kann man das z.B. so überprüfen (das Sternchen * steht für eine beliebige Nummer/Index des Ports, der Befehl wird im Terminal ausgeführt):

ls /dev/rfcomm*

Falls Bluetooth-Ports verfügbar sind, werden ihre Namen angezeigt, z.B. /dev/rfcomm0, /dev/rfcomm1 usw. Wenn „No such file or directory“ zurück kommt, sind keine Bluetooth-Ports verfügbar, und wir müssen den Port extra einrichten (oder euer Rechner ist nicht Bluetooth-fähig). Um das zu tun, müssen wir schauen, welche Bluetooth-Geräte überhaupt empfangen werden, das machen wir mit dem folgenden Befehl:

sudo bluetoothctl

Es werden Adressen (z.B. „F4:5E:AB:AA:BB:CC“) und Namen (z.B. „HC-05“) von den verfügbaren Geräten angezeigt. Notiert euch die Adresse vom Gerät, mit welchem ihr die serielle Kommunikation aufbauen wollt. Anschließend erstellen wir den Port mit dem Namen z.B. /dev/rfcomm0, über welchen wir uns mit diesem Gerät verbinden werden:

sudo rfcomm bind 0 F4:5E:AB:AA:BB:CC 1

Überpfüft mit ls /dev/rfcomm*, ob das geklappt hat. Den Portnamen /dev/rfcomm0 werden wir im nächsten Teil im Node.js-Code benutzen. Zum Testen ist es manchmal hilfreich, wenn man direkt im Terminal die serielle Kommunikation betreiben kann. Dafür könnt ihr z.B. das Programm picocom nutzen (Installation: sudo apt-get install picocom):

 
picocom /dev/rfcomm0


6. Daten über den seriellen Port vom Server zum HC-05 schicken

Wenn wir den seriellen Port erstellt haben und seinen Namen kennen, können wir Daten vom Server verschicken (dieser Code gehört in die server.js-Datei):

Code: einen String über den seriellen Port schicken

Code: einen String über den seriellen Port schicken

const SerialPort = require('serialport'); // Node-Modul serialport einbinden
const arduinoSerialPort = new SerialPort('/dev/rfcomm0', { baudRate: 9600 }); // Den seriellen Portnamen und Baudrate festlegen
arduinoSerialPort.write("Hello..."); // Daten schicken (Strings oder Zahlen)


In diesem Beispiel wird der Port /dev/rfcomm0 verwendet – der Bluetooth-Port auf einem Linux-PC. Wenn ihr die serielle Kommunikation über einen USB-Kabel betreibt, nennt sich euer Port evtl. /dev/ttyUSB0, COM3 oder Ähnliches. Das sind die gleichen Namen, die in der Arduino IDE unter Werkzeuge⇒Port zu finden sind.

techniken/tensorflownodebluetooth.txt · Zuletzt geändert: 2021/02/12 15:44 von SabidJFejzula