Ich möchte heute gerne über ein kleines Projekt berichten, dass ich in der letzten Woche durchgeführt habe und da es hier nicht mehr um Bluetooth geht, habe ich mal ein neues Thema gestartet.
Mein Kumpel hat einen Lego EV3 Brick mitgebracht und wir wollten dann mal schauen wie sich ft TXT Controller, Lego EV3 Brick und ein Computer im Vergleich bei der Umsetzung einer einfachen Programmierung schlagen.
Der Text ist recht lang geworden, daher hier die Kurzfassung: der TXT Controller ist im Vergleich deutlich schneller als ich angenommen hätte.
Testaufbau:
Auf dem ft TXT läuft natürlich die CFW 0.9.4.
Auf dem Lego Ev3 Brick habe ich mit EV3dev das Gegenstück zur FT CFW installiert, sodass hier jetzt Python3 und Debian Linux läuft.
Der EV3 Brick wurde für diesen Test zunächst von 300 MHZ auf 456MHz übertaktet (das laut Datenblatt spezifizierte Maximum für diesen Prozessor), um ein bisschen näher an den 600 MHZ starken TXT Controller zu kommen.
Zuletzt kommt noch ein PC zum Einsatz mit Windows 10 und Pycharm.
Als Benchmark habe ich ein Skript mit Rechenaufgaben aus dem Bereich LinA gefunden, dass ich gut für die beiden Controller anpassen konnte und dass zunächst erstmal vollkommen ohne Systemeigenheiten wie Motorklassen etc. auskommt. Nach der Berechnung gibt das Skript die benötigte Zeit für die Berechnungen per print aus:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# Roughly based on: http://stackoverflow.com/questions/11443302/compiling-numpy-with-openblas-integration
# and adapted after: https://gist.github.com/SwimmingTiger/7dbea709d052a127135569d9af950b20
import numpy as np
from time import time
# Let's take the randomness out of random numbers (for reproducibility)
np.random.seed(0)
size = 128
A, B = np.random.random((size * 2, size * 2)), np.random.random((size * 2, size * 2))
C, D = np.random.random((size * 256,)), np.random.random((size * 256,))
E = np.random.random((size, size))
F = np.random.random((size, size))
F = np.dot(F, F.T)
G = np.random.random((size, size))
# Matrix multiplication
N = 20
t = time()
for i in range(N):
np.dot(A, B)
delta = time() - t
print('Dotted two %dx%d matrices in %0.2f s.' % (size, size, delta / N))
del A, B
# Vector multiplication
N = 5000
t = time()
for i in range(N):
np.dot(C, D)
delta = time() - t
print('Dotted two vectors of length %d in %0.2f ms.' % (size * 128, 1e3 * delta / N))
del C, D
# Singular Value Decomposition (SVD)
N = 3
t = time()
for i in range(N):
np.linalg.svd(E, full_matrices = False)
delta = time() - t
print("SVD of a %dx%d matrix in %0.2f s." % (size / 2, size / 4, delta / N))
del E
# Cholesky Decomposition
N = 3
t = time()
for i in range(N):
np.linalg.cholesky(F)
delta = time() - t
print("Cholesky decomposition of a %dx%d matrix in %0.2f s." % (size / 2, size / 2, delta / N))
# Eigendecomposition
t = time()
for i in range(N):
np.linalg.eig(G)
delta = time() - t
print("Eigendecomposition of a %dx%d matrix in %0.2f s." % (size / 2, size / 2, delta / N))
print('')
Und hier die Ergebnisse:
Ergebnis Computer Intel Pentium:
Dotted two 128x128 matrices in 0.00 s.
Dotted two vectors of length 16384 in 0.02 ms.
SVD of a 64x32 matrix in 0.01 s.
Cholesky decomposition of a 64x64 matrix in 0.00 s.
Eigendecomposition of a 64x64 matrix in 0.02 s.
Ergebnis FT TXT Controller:
$ python3 bench.py
Dotted two 128x128 matrices in 1.39 s.
Dotted two vectors of length 16384 in 2.26 ms.
SVD of a 64x32 matrix in 1.09 s.
Cholesky decomposition of a 64x64 matrix in 0.04 s.
Eigendecomposition of a 64x64 matrix in 1.97 s.
Ergebnis EV3 Brick Lego, übertaktet:
robot@ev3dev:~/bench$ python3 bench.py
Dotted two 128x128 matrices in 10.58 s.
Dotted two vectors of length 16384 in 23.06 ms.
SVD of a 64x32 matrix in 8.23 s.
Cholesky decomposition of a 64x64 matrix in 0.25 s.
Eigendecomposition of a 64x64 matrix in 17.92 s.
Wie man sieht ist dieser Test zu wenig fordernd um für den PC interessante Ergebnisse zu produzieren, doch für die beiden Controller war er schon eine Herausforderung und die Ausführung des Skripts dauerte auf dem Lego Gerät etwa 15 Minuten. Fazit TXT vs EV3 Brick:
Der TXT benötigt für die Berechnungen nur etwa 10% der Zeit die der EV3 Brick benötigt.
Ich fand das schon ziemlich beachtlich doch mein Kumpel war nicht wirklich überzeugt. Schließlich ist dieser Test ja noch weit von einem realen Setting entfernt und testet zunächst erstmal nur numpy. Diese zutreffende Kritik stimmt aber sicher nur zum (überwiegenden) Teil. Mit dem hier verwendeten numpy Modul könnte man z.B. einen Kalman Filter für den ft-Gyro Sensor umsetzen, was doch eigentlich sehr nützlich wäre, sodass es auf dessen Leistungsfähigkeit schon ankommen könnte. Wir haben uns dennoch lieber einen Test überlegt, der etwas näher an einer konkreten Anwendung liegt, z.B. einer Situation wie sie bei einem Roboterwettkampf vorliegen könnte:
Mit einem Ultraschallsensor und 2 Motoren wird eine einfache Logik programmiert, die auf Gegenstände die weit entfernt sind zunächst zusteuert, letztlich aber v.a. durch rechtzeitiges Aufstoppen oder Bremsen eine Kollision verhindert. Diese kleine Logikschleife wird 40.000 Mal laufen gelassen und geschaut wie lange die Geräte dafür brauchen, bzw. wie viele Loops die Geräte pro Sekunde schaffen.
Beide Geräte sind dabei möglichst identisch programmiert, und nutzen die jeweils im System vorhandenen großen Motoren + einen US Sensor.
Die Überlegung ist, dass ein Gerät dass in diesem Test mehr Loops pro Sekunde schafft dann auch in einem realen Anwendungsfall z.B. bei Verwendung eines PID Controllers in Verbindung mit 2 Farbsensoren (verb. Linienfolger) oder einem Gyro deutlich schneller bzw. häufiger Korrekturen ausführen könnte. Ein optimales Ergebnis würde demnach theoretisch immer dann vorliegen wenn die loops pro Sekunde mindestens der schnellsten Samplerate des abgefragten Sensors entspräche, wobei schneller auch nicht schadet, letztlich erhält man dann eben alte Sensorwerte.
Auf dem TXT Controller sieht der Testcode so aus:
Code: Alles auswählen
from time import perf_counter
import ftrobopy
txt = ftrobopy.ftrobopy('192.168.178.51')
lMotor = txt.motor(1)
rMotor = txt.motor(2)
ultraschall = txt.ultrasonic(1)
lMotor.setSpeed(0)
rMotor.setSpeed(0)
lMotor.setDistance(0)
rMotor.setDistance(0)
geschw = 0
LOOPS = 40000
txt.updateWait()
startTime = perf_counter()
for a in range(0,LOOPS):
if ultraschall.distance() >= 80:
geschw = 508
elif ultraschall.distance() >=32:
geschw = 10 * ultraschall.distance()
elif ultraschall.distance() >= 20:
geschw = 260
elif ultraschall.distance() >= 15:
geschw = 180
elif ultraschall.distance() >= 8:
geschw = 0
elif ultraschall.distance() >= 4:
geschw = -350
else:
geschw = 0
if geschw > 507:
geschw = 508
else:
pass
lMotor.setSpeed(geschw)
rMotor.setSpeed(geschw)
endTime = perf_counter()
lMotor.stop()
rMotor.stop()
print("Loops pro Sekunde: " + str(round(LOOPS / (endTime - startTime))))
print("Benoetigte Zeit fuer " + str(LOOPS) + " Loops betraegt " + str(round((endTime - startTime),2))+ " Sekunden")
Auf dem Lego EV3 Brick sieht es dann so aus:
Code: Alles auswählen
from time import sleep
from time import perf_counter
#from ev3dev2.motor import *
#from ev3dev2.sensor.lego import UltrasonicSensor
from ev3fast import *
lMotor = LargeMotor('outA')
rMotor = LargeMotor('outB')
us = UltrasonicSensor()
us.mode='US-DIST-CM'
geschw = 0
LOOPS = 40000
time.sleep(0.5)
startTime = perf_counter()
for a in range(0,LOOPS):
if us.distance_centimeters >= 80:
geschw = 1000
elif us.distance_centimeters >=32:
geschw = 20 * us.distance_centimeters
elif us.distance_centimeters >= 20:
geschw = 510
elif us.distance_centimeters >= 15:
geschw = 350
elif us.distance_centimeters >= 8:
geschw = 0
elif us.distance_centimeters >= 4:
geschw = -300
else:
geschw = 0
if geschw > 1000:
geschw = 1000
else:
pass
lMotor.speed_sp = round(geschw)
rMotor.speed_sp = round(geschw)
lMotor.run_forever()
rMotor.run_forever()
endTime = perf_counter()
lMotor.stop()
rMotor.stop()
print("Loops pro Sekunde: " + str(round(LOOPS / (endTime - startTime))))
print("Benoetigte Zeit fuer " + str(LOOPS) + " Loops betraegt " + str(round((endTime - startTime),2))+ " Sekunden")
Und hier wieder die Ergebnisse:
Messung am PC mit dem Txt Controller (online) ferngesteuert:
Connected to TX2013 firmware version 4.6.6
Loops pro Sekunde: 226800
Benoetigte Zeit fuer 40000 Loops betraegt 0.18 Sekunden
Messung am TXT Controller direct mode:
$ python bench2.py
Connected to TXT direct firmware version not detected
Loops pro Sekunde: 2663
Benoetigte Zeit fuer 40000 Loops betraegt 15.02 Sekunden
Messung mit EV3dev-lang unter Verwendung der orig. Python Bibliothek:
robot@ev3dev:~/bench$ python3 bench2.py
Loops pro Sekunde: 57
Benoetigte Zeit fuer 40000 Loops betraegt 703.8 Sekunden
Messung mit EV3dev-lang-python-fast, eines alternativen Python Moduls für EV3dev:
robot@ev3dev:~/bench$ python3 bench2.py
Loops pro Sekunde: 348
Benoetigte Zeit fuer 40000 Loops betraegt 114.81 Sekunden
und zuletzt noch EV3dev mit RPYC ausgestattet und dann vom PC ferngesteuert, in der Hoffnung ein Ergebnis analog zum Ftrobopy online Modus zu bekommen:
Loops pro Sekunde: 5 (!)
Die benoetigte Zeit fuer 40000 Loops habe ich dann bei zu erwartenden 2h / 8000s nicht mehr getestet..
Fazit: Die Ergebnisse sind wieder beachtlich. Der Txt Controller schafft mind. 7x mehr Loops als der EV3 Brick, je nach Szenario aber auch deutlich mehr. Es scheint außerdem, dass die Möglichkeiten nochmals deutlich gesteigert werden können wenn das Programm im online modus über den PC läuft. Dass das nicht selbstverständlich ist, zeigt der Vergleich mit RPYC auf dem Lego Brick.
Mein Kumpel war ziemlich überrascht und wir haben überlegt wie diese Ergebnisse zu erklären sind. Ich kann nur vermuten, dass die Ergebnisse nicht ausschließlich mit dem größeren Speicher und den 144 Mhz Taktdifferenz zu erklären sind die der Txt dem EV3 vorraus hat, sondern dass auch bei FTrobopy, CFW, ft etc. schon bei der Implementierung gute Arbeit geleistet wurde um die knappen Ressourcen des Controllers einzusetzen.
Im Anhang noch ein Bild vom Testaufbau nebst herbeigerufenen Schiedsrichtern.
Ich spare jetzt bis Weihnachten auf den tx-pi und dann will ich mal gucken wie dieser sich schlägt. Außerdem würde ich dann mal testen wie sich die Geräte schlagen wenn im Online Modus statt eines PCs ein aktuelles Handy für die Berechnungen eingesetzt wird.
VG, Dennis