Der Inhalt dieses Artikels befindet sich in der Beta-Phase und kann sich ändern. In diesem Artikel führen wir eine lineare Regression der Zeilenverfolgung unter Verwendung der Lernversion LEGO® MINDSTORMS EV3 (im Folgenden EV3) und der Python-Umgebung durch. Informationen zum Umgebungsbau finden Sie im vorherigen Artikel.
Maschinelles Lernen mit EV3 Teil 1 Umgebungskonstruktion: hier Maschinelles Lernen mit EV3 Teil 2 Lineare Regression: Dieser Artikel Maschinelles Lernen mit EV3 Teil 3 Klassifizierung: In Kürze erhältlich
PC Windows10 Python 3.7.3 Entwicklungsumgebung VisualStudioCode
EV3 ev3dev
Die lineare Regression ist eine Analysemethode, bei der die gerade Linie gefunden wird, die bei einer Datenverteilung am besten zur Datengruppe passt.
Wenn es zwei Arten von Datengruppen gibt, wie unten gezeigt, können Sie die Ergebnisse erhalten, indem Sie eine Linie zeichnen, die gut zu den Daten passt, und einige Vorhersagen auch für die Daten treffen, die Sie nicht haben.
Der Hauptzweck besteht darin, eine lineare Regression mit EV3 durchzuführen, aber es ist notwendig, die Operation von EV3 mit der Inferenz durch lineare Regression zu verbinden. Dieses Mal besteht das Ziel darin, eine Linienverfolgung mit reibungslosem Verlauf durchzuführen, während eine lineare Regression gemäß dem folgenden Verfahren durchgeführt wird.
Das diesmal verwendete EV3-Modell ist das folgende Modell. An der Vorderseite der Karosserie ist ein Farbsensor angebracht, der die Linienverfolgung ermöglicht. Ein Kreiselsensor ist an der Rückseite der Karosserie angebracht, um den Winkel zu erfassen, in dem die Karosserie zeigt. Der Fahrweg wird mit dem Wert dieses Winkels gezeichnet.
Der diesmal ausgeführte elliptische Kurs verwendet die folgenden Kurse.
Die Bibliotheken, die dieses Mal zusätzlich zu ev3dev zusätzlich verwendet werden sollen, sind wie folgt.
Numpy
Numpy ist eine sehr beliebte Bibliothek für numerische Berechnungen in Python.
Es wird möglich, auf einfache Weise eine Array-Berechnungsverarbeitung wie eine Vektor- und Matrixberechnung durchzuführen.
Dieses Mal wird es verwendet, wenn die erfassten Sensordaten in einem Array gespeichert oder eine Berechnung durchgeführt werden.
Installationsverfahren
pip install numpy
aus
matplotlib matplotlib ist eine Bibliothek, die häufig beim Zeichnen von Diagrammen in Python verwendet wird. Dieses Mal wird es verwendet, um die Reiseroute basierend auf den Kreiselsensordaten zu zeichnen. Unterhalb des Installationsvorgangs
pip install matplotlib
aus
Pandas
Pandas ist eine Bibliothek zum effizienten Umgang mit Daten in Python.
Dieses Mal wird es zum Lesen der CSV-Datei verwendet.
Unterhalb des Installationsvorgangs
pip install pandas
aus
sciki-learn scikit-learn ist eine Python-Bibliothek für maschinelles Lernen. Klassifizierung, Regression, Clustering usw. können relativ einfach implementiert werden. Unterhalb des Installationsvorgangs
pip install scipy
auspip install scikit-learn
aus
Erstellen Sie dieses Mal die folgenden drei Programme.
data_get_gyro.py
course.py
LinearRegression.py
Da die Spezifikationen von EV3 begrenzt sind, ist die Konfiguration so, dass die Berechnung numerischer Werte und die Verarbeitung der linearen Regression auf der PC-Seite durchgeführt werden und der Wert des Verarbeitungsergebnisses an EV3 gesendet wird. Die Beziehung zwischen den einzelnen Programmen ist in der folgenden Abbildung dargestellt.
Das EV3-Nebenprogramm ist ein Programm, das tatsächlich von EV3 ausgeführt wird. Es implementiert hauptsächlich die Linienverfolgung mit einem Wendewert, der als Betrieb von EV3 angegeben ist, die Übertragung eines Kreiselsensors über die Steckdosenkommunikation und den Empfang von Rückmeldungen an einem Wendepunkt.
Erstellen Sie das EV3-Seitenprogramm "data_get_gyro.py" im Arbeitsbereich von VSCode unten. Informationen zum Erstellen eines Arbeitsbereichs und zum Übertragen des Quellcodes auf EV3 finden Sie unter Vorheriger Artikel.
import time
import socket
import sys
from ev3dev2.button import Button
from ev3dev2.motor import LargeMotor, OUTPUT_B, OUTPUT_C
from ev3dev2.sensor import INPUT_2, INPUT_3
from ev3dev2.sensor.lego import GyroSensor, ColorSensor
power range:-1050 -> 1050, turn_ratio range:-100 -> 100
def linetrace_steer(power, turn_ratio):
global data_cycle
if color.reflected_light_intensity > 30:
ev3_motor_steer(power, turn_ratio*-1)
else:
ev3_motor_steer(power, turn_ratio)
time.sleep(0.1)
data_cycle += 1
def ev3_motor_steer(power, turn_ratio):
if turn_ratio < 0:
lm_b.run_forever(speed_sp=power*(1+turn_ratio/50))
lm_c.run_forever(speed_sp=power)
elif turn_ratio > 0:
lm_b.run_forever(speed_sp=power)
lm_c.run_forever(speed_sp=power*(1-turn_ratio/50))
else:
lm_b.run_forever(speed_sp=power)
lm_c.run_forever(speed_sp=power)
def gyro_reset():
time.sleep(1.0)
gyro.mode = 'GYRO-ANG'
gyro.mode = 'GYRO-RATE'
gyro.mode = 'GYRO-ANG'
time.sleep(1.0)
def dataget():
global fb_steer # feedback steering value
global set_steer
_gyro_data = gyro.value() # gyro data
_gyro_data_str = str(_gyro_data)
s.send(_gyro_data_str.encode())
print(_gyro_data_str)
if ROUND_CHECK < _gyro_data:
while not(button.up):
ev3_motor_steer(0, 0)
if fb_steer is None:
fb_steer = s.recv(1024).decode()
fb_steer_float = float(fb_steer)
print(fb_steer_float)
set_steer = set_steer - fb_steer_float
if button.backspace:
s.close
print('End program')
sys.exit()
print('set_steer = ' + str(set_steer))
# gyro reset
gyro_reset()
fb_steer = None
sensors&motors definition
button = Button()
color = ColorSensor(INPUT_3)
gyro = GyroSensor(INPUT_2)
lm_b = LargeMotor(OUTPUT_B)
lm_c = LargeMotor(OUTPUT_C)
gyro initialize
gyro_reset()
variable initialize
data_cycle = 1 # counter
fb_steer = None # Feedback turn
ROUND_CHECK = 355 # confirm one round
motor initialize
lm_b.reset()
lm_c.reset()
set_power = 200
set_steer = 70
get gyrodate and into array
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(('169.254.207.161', 50010)) # your PC's Bluetooth IP & PORT
while not(button.backspace):
linetrace_steer(set_power, set_steer)
if data_cycle % 4 == 0: # gyro_get_frequency
dataget()
lm_b.stop(stop_action='brake')
lm_c.stop(stop_action='brake')
print('End program')
sys.exit()
Im Vergleichskursdatenprogramm werden Daten ähnlich dem ursprünglichen Kurs zum Vergleich mit der Ellipse der von der Linienspur gezeichneten Trajektorie erzeugt. Dieses Programm wird nicht direkt ausgeführt, sondern für den Import in LinearRegression.py erstellt, das später erstellt wird. Erstellen Sie einen Ordner mit dem Namen "program" auf dem Desktop Ihres PCs, erstellen Sie "course.py" als Textdokument und beschreiben Sie die folgenden Inhalte.
import numpy as np
import matplotlib.pyplot as plt
def original_course(element_cnt, plot_interval):
_element_cnt_f = element_cnt % 10 # element count fraction
_element_cnt_unf = (element_cnt - _element_cnt_f)
_element_cnt_s = _element_cnt_unf / 10 # element count one course section
plot_interval = plot_interval + (_element_cnt_f*(plot_interval/_element_cnt_unf))
_xcount = 1
_ycount = 1
_rcount = 1
global P
P = np.zeros(0)
global Q
Q = np.zeros(0)
# 1
while not _xcount > _element_cnt_s:
_x1 = plot_interval * -1*_xcount
_y1 = 0
P = np.append(P, _x1)
Q = np.append(Q, _y1)
_xcount += 1
# 2
while not _xcount > _element_cnt_s * 2:
_x2 = plot_interval * -1*_xcount
_y2 = 0
P = np.append(P, _x2)
Q = np.append(Q, _y2)
_xcount += 1
# 3 cercle
_rcount = 0
while not _rcount > _element_cnt_s:
_a1 = plot_interval*(_element_cnt_s*2) * -1 # cercle centerX
_b1 = plot_interval*_element_cnt_s - plot_interval # cercle centerY & radius
_x3 = _a1 + _b1*np.cos(np.radians(270-(90 / _element_cnt_s*_rcount)))
_y3 = _b1 + _b1*np.sin(np.radians(270-(90 / _element_cnt_s*_rcount)))
P = np.append(P, _x3)
Q = np.append(Q, _y3)
_rcount += 1
# 4
while not _ycount > _element_cnt_s:
_x4 = _x3
_y4 = plot_interval*_ycount + _y3
P = np.append(P, _x4)
Q = np.append(Q, _y4)
_ycount += 1
# 5 cercle
_rcount = 0
while not _rcount > _element_cnt_s:
_a2 = _a1 # cercle centerX
_b2 = _y4 # cercle centerY
_x5 = _a2 + _b1*np.cos(np.radians(180-(90 / _element_cnt_s*_rcount)))
_y5 = _b2 + _b1*np.sin(np.radians(180-(90 / _element_cnt_s*_rcount)))
P = np.append(P, _x5)
Q = np.append(Q, _y5)
_rcount += 1
# 6
_xcount = 1
while not _xcount > _element_cnt_s:
_x6 = _x5 + plot_interval*_xcount
_y6 = _y5
P = np.append(P, _x6)
Q = np.append(Q, _y6)
_xcount += 1
# 7
_xcount = 1
while not _xcount > _element_cnt_s:
_x7 = _x6 + plot_interval*_xcount
_y7 = _y6
P = np.append(P, _x7)
Q = np.append(Q, _y7)
_xcount += 1
# 8 cercle
_rcount = 0
while not _rcount > _element_cnt_s:
_a3 = 0 # cercle centerX
_b3 = _y4 # cercle centerY
_x8 = _a3 + _b1*np.cos(np.radians(90-(90 / _element_cnt_s*_rcount)))
_y8 = _b3 + _b1*np.sin(np.radians(90-(90 / _element_cnt_s*_rcount)))
P = np.append(P, _x8)
Q = np.append(Q, _y8)
_rcount += 1
# 9
_ycount = 1
while not _ycount > _element_cnt_s:
_x9 = _x8
_y9 = plot_interval*_ycount*-1 + _y8
P = np.append(P, _x9)
Q = np.append(Q, _y9)
_ycount += 1
# 10 cercle
_rcount = 0
while not _rcount > _element_cnt_s:
_a4 = 0 # cercle centerX
_b4 = _b1 # cercle centerY
_x10 = _a4 + _b1*np.cos(np.radians(0-(90 / _element_cnt_s*_rcount)))
_y10 = _b4 + _b1*np.sin(np.radians(0-(90 / _element_cnt_s*_rcount)))
P = np.append(P, _x10)
Q = np.append(Q, _y10)
_rcount += 1
if __name__ == '__main__':
original_course(100, 30)
plt.figure()
plt.plot(P, Q, '-', color='blue')
plt.show()
Das PC-seitige Programm zeichnet die Reiseroute aus den von EV3 gesendeten Kreiselsensordaten und vergleicht sie mit den ursprünglichen Kursdaten, um den Fehler zu berechnen. Zusätzlich werden der berechnete Fehler und der Wendewert in einer CSV-Datei aufgezeichnet, und der entsprechende Wendewert wird als Ergebnis der Inferenz durch lineare Regression an EV3 zurückgemeldet.
Erstellen Sie "LinearRegression.py" als Textdokument im "Programmordner" auf die gleiche Weise wie "course.py" und beschreiben Sie den folgenden Inhalt.
import socket
import course # course.py
import sys
import csv
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import os.path
from sklearn import linear_model
clf = linear_model.LinearRegression()
Feedback phase function
def feedback(element_cnt,
plot_interval,
min_steer,
X,
cur_steer,
steers,
errors):
cnt = 1
limit = 100
# from course.py course coordinate point
if not element_cnt == 1:
course.original_course(element_cnt, plot_interval)
_X_abs = np.abs(X) # run coordinate point 'x'
_P_abs = np.abs(course.P) # from course.py course coordinate point 'x'
_X_ave = _X_abs.mean() # average
_P_ave = _P_abs.mean() # average
_point_error = np.abs(_X_ave - _P_ave) # point_average_error
# add feedback_data to csv
writedata = [cur_steer, _point_error]
with open('datafile.csv', 'a', newline='') as f:
writer = csv.writer(f)
writer.writerow (writedata) # Daten schreiben
steers = np.append(steers, cur_steer) # append steer data
errors = np.append(errors, _point_error) # append error data
print('steers = {}'.format(steers))
print('errors = {}'.format(errors))
print('len(errors) = {}'.format(len(errors)))
if len(errors) > 1:
if errors[-1] > errors[-2] and steers[-1] > min_steer:
min_steer = steers[-1]
errors = errors.reshape(-1, 1)
clf.fit(errors, steers) # linear regression
while cnt < limit:
ave_error = np.average(errors)
input_error = cnt/(cnt+1) * ave_error
input_error = input_error.reshape(-1, 1)
predict_steer = clf.predict(input_error)
if predict_steer > min_steer:
break
cnt += 1
str_prd_steer = str(predict_steer[0])
print('predict_steer = {}'.format(str_prd_steer))
conn.send(str_prd_steer.encode())
return predict_steer[0], min_steer, steers, errors
else:
cur_steer = cur_steer*2/3
print('next_steer = {}'.format(cur_steer))
conn.send(str(cur_steer).encode())
return cur_steer, min_steer, steers, errors
variable initialize
gyro = np.zeros(0)
element_cnt = 1 # element count
plot_interval = 30 # plot point interval
X = np.zeros(0)
Y = np.zeros(0)
steers = np.zeros(0)
errors = np.zeros(0)
Lap = 0
ROUND_CHECK = 355 # confirm one round
ini_steer = 70
cur_steer = ini_steer
generate
if os.path.exists('datafile.csv') == False:
writedata = ['steer', 'error']
f = open ('datafile.csv', 'w', newline = '') # Datei öffnen
writer = csv.writer(f)
writer.writerow (writedata) # Daten schreiben
f.close()
data = pd.read_csv ("datafile.csv", sep = ",") # CSV-Datei lesen
steer_data = data.loc [:, 'steer']. values # Setzt den Schwenkwert in der Zielvariablen (ersetzen Sie die Daten in der Steer-Spalte).
error_data = data ['error'] .values # Setzt einen Fehler in der erklärenden Variablen (ersetzen Sie die Daten in der Fehlerspalte)
min_steer = 0 # Mindestwert
for cnt, data in np.ndenumerate(steer_data):
if error_data[cnt] < 900:
steers = np.append (steuert, Daten) # Ersetzen Sie andere Werte als den Kursausgang
elif data > min_steer:
min_steer = Daten # Update Minimum
for data in error_data:
if data < 900:
error = np.append (Fehler, Daten) # Ersetzen Sie einen anderen Wert als "Kursausgang"
Main program
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('169.254.207.161', 50010)) # your PC's Bluetooth IP & PORT
s.listen(1)
print('Start program...')
while True:
conn, addr = s.accept()
with conn:
if len(errors) > 1:
values = feedback(element_cnt,
plot_interval,
min_steer,
X,
cur_steer,
steers,
errors)
cur_steer = values[0]
min_steer = values[1]
steers = values[2]
errors = values[3]
else:
conn.send(str(cur_steer).encode())
while True:
gyro_data = conn.recv(1024).decode()
if not gyro_data:
break
gyro_data_float = float(gyro_data) # change type
gyro = np.append(gyro, gyro_data_float) # gyro angle
np.set_printoptions(suppress=True)
cosgy = plot_interval * np.cos(np.radians(gyro)) * -1
singy = plot_interval * np.sin(np.radians(gyro))
X = np.append(X, np.sum(cosgy[0:element_cnt]))
Y = np.append(Y, np.sum(singy[0:element_cnt]))
if ROUND_CHECK < gyro_data_float:
plt.figure()
plt.plot(X, Y, '-', color='blue')
print('Plot file output')
print(str(Lap) + '-plot.png')
plt.savefig(str(Lap) + '-plot.png')
values = feedback(element_cnt,
plot_interval,
min_steer,
X,
cur_steer,
steers,
errors)
cur_steer = values[0]
min_steer = values[1]
steers = values[2]
errors = values[3]
# reset phase
element_cnt = 0
X = np.zeros(0)
Y = np.zeros(0)
gyro = []
plt.clf() # figure clear
plot_interval = 30
Lap += 1
element_cnt = element_cnt + 1 # Element count
# Schreiben Sie nach dem Kursabbruch mit einem Fehler von 1000 an csv
if element_cnt > 1:
writedata = [cur_steer, 1000]
f = open('datafile.csv', 'a', newline='')
writer = csv.writer(f)
writer.writerow(writedata)
f.close()
print('End program')
sys.exit()
Die in der zweiten Hälfte beschriebene s.bind (('169.254.207.161', 50010))
wird durch das folgende Verfahren je nach Umgebung wie das EV3-Nebenprogramm geändert.
Über die Socket-Kommunikation werden Daten zwischen dem EV3-seitigen Programm und dem PC-seitigen Programm ausgetauscht, um eine Rückmeldung über den Wert des Kreiselsensors und den Wendepunkt zu erhalten. Die im Programm beschriebene IP-Adresse wird jedoch je nach Umgebung geändert. Es besteht die Notwendigkeit.
Wenn der PC und der EV3 über Bluetooth verbunden sind, wird die lokale Verbindungsadresse "169.254.XXX.YYY" zugewiesen. Führen Sie die folgenden Schritte aus, um die IP-Adresse zu ermitteln.
ipconfig
ausEs ist notwendig, die folgende Beschreibung in der zweiten Hälfte von data_get_gyro.py
zu ändern.
s.connect(('169.254.207.161', 50010)) # your PC's Bluetooth IP & PORT
Die eigentliche Programmbearbeitung ist wie folgt.
Übertragen Sie nach dem Ändern der Beschreibung den Arbeitsbereich im VS-Code auf EV3.
Es ist notwendig, die folgende Beschreibung in der zweiten Hälfte von "LinearRegression.py" zu ändern.
s.bind(('169.254.207.161', 50010)) # your PC's Bluetooth IP & PORT
Die eigentliche Bearbeitung des Programms ist wie folgt.
Führen Sie das Programm aus, nachdem Sie drei Programme erstellt und die Beschreibung der IP-Adressen an zwei Stellen geändert haben. Das Folgende ist das Ausführungsverfahren.
cd Desktop \ program
an der Eingabeaufforderung aus (\ ist gleichbedeutend mit \ mark)
python LinearRegression.py
ausInstallieren Sie EV3 zu Beginn des Kurses
Öffnen Sie das SSH-Terminal des mit VS Code verbundenen EV3 und führen Sie cd ev3 workspace /
aus
Führen Sie "python3 data_get_gyro.py" im SSH-Terminal aus
In jeder Runde stoppt EV3 in der Nähe des Startpunkts. Drücken Sie daher die obere Taste des intelligenten Blocks, um die nächste Runde zu starten.
Nach 6 Runden ist das Ergebnis wie folgt. Die Eingabeaufforderung zeigt den Wendewert und -fehler, die Anzahl der Daten (Anzahl der Runden) und den nächsten Wendewert für jede Runde an. Der Wert des Kreiselsensors, der auf der PC-Seite an das Programm gesendet wird, wird auf dem SSH-Terminal des VSCode angezeigt.
Wie im folgenden Video gezeigt, können Sie sehen, dass die Linienverfolgung glatter wird und die Rundengeschwindigkeit mit jeder Runde schneller wird.
Wenn Sie die CSV-Datei öffnen, in der die Daten in Excel gespeichert sind, werden die Daten wie folgt zusammengefasst.
Es ist möglich, es im Programm grafisch darzustellen, aber dieses Mal habe ich ein lineares Regressionsdiagramm in Excel erstellt. Es ist ersichtlich, dass der Fehler bei einem bestimmten Drehwert bis zu einem gewissen Grad vorhergesagt werden kann.
Dieses Mal haben wir eine Linienverfolgung und eine lineare Regression durchgeführt, um den Wendewert aus dem Fahrweg zu ermitteln. Wenn Sie zwei verwandte Daten erhalten können, können Sie auf diese Weise eine Vorhersage treffen. Es ist eine Methode, die angewendet werden kann, obwohl überlegt werden muss, welche Art von Daten verwendet werden sollen, um das zu verbessernde Problem zu bewerten und anzupassen.
Das nächste Mal werden wir anhand der vom Farbsensor erfassten numerischen RGB-Daten verschiedene Arten von Farben beurteilen.
Recommended Posts