====== 8. Strojové učení ======
===== Cíle cvičení =====
* Seznámení výstupem z 6DOF IMU ICM-20648
* Základní zpracování
* [[courses:b0b37nsi:addons:01|Metoda nejmenších čtverců]]
* [[courses:b0b37nsi:addons:02|Kálmánův filtr]]
* [[courses:b0b37nsi:addons:03|IMU interpretace]]
* Rozpoznání náklonu akcelerometru
===== Další zdroje =====
* [[https://pythonnumericalmethods.berkeley.edu/notebooks/chapter16.00-Least-Squares-Regression.html|Least Squares Regression]]
* http://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/
* {{ :courses:b0b37nsi:tutorials:1704.06053.pdf |Using Inertial Sensors for Position and Orientation Estimation}}
===== Rozpoznání náklonu z akcelerometru =====
Cílem této úlohy je natrénovat SVM klasifikátor tak, aby rozpoznal přibližné naklonění akcelerometru. Abychom toho mohli dosáhnout, je třeba projít těmito kroky:
- Získání trénovacích dat
- Označení trénovacích dat (štítky)
- Natrénování modelu
- Predikce pomocí v reálném čase
==== Získání trénovacích dat ====
=== Thunderboard ===
Program pro Thunderboard čte data z IMU a posílá je do PC po rozhraní. Jedná se o data typu float, takže je nutné promyslet, jak data přenést. Dobrým nápadem je přenášet data ve struktuře, můžete se inspirovat tímto [[courses:b0b37nsi:tutorials:04#prenos_struktrovanych_dat|kódem]].
Zamyslete se nad význemem dat (a datových typech), potřebou přesností, ale také nad rychlostí, kterou budete data snímat a přenášet.
=== PC - příjem a uložení ===
Na straně PC je třeba datat dekódovat a uložit do souboru s jednoduše editovatelným formátem, případně zobrazit. Lze vycházet např. z kódu na [[https://gitlab.fel.cvut.cz/viteks/nsi-codes/-/blob/main/tut-08-processing/imu-receive.py|gitlabu]], nebo použít následující:
import struct
import numpy as np
#
points = []
# cteni ze serioveho portu
byte = ser.read(24)
points.append(struct.unpack('6f', byte))
Pro offline analýzu uložte data do souboru ve formátu CSV. To lze udělat např. pomocí modulu [[https://docs.python.org/3/library/csv.html|csv]].
import csv
header = ['acc_x', 'acc_y', 'acc_z', 'gyr_x', 'gyr_y', 'gyr_z']
data = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
with open('data.csv', 'w', encoding='UTF8', newline='') as f:
writer = csv.writer(f)
# write the header
writer.writerow(header)
# write the data
writer.writerow(data)
==== Přiřazení štíků ====
Rozpoznání náklonu akcelerometru je velmi jednoduchá klasifikační úloha. Hledané třídě (náklonu) odpovídá kombinace diskrétní hodnot, získaných z IMU. Nasbíraná data rozdělíme do tříd tak, že do jednotlivých řádků doplníme jednoznačný štítek, např. takto
* 0 - vodorovně
* 1 - náklon dopředu
* 2 - náklon dozadu
Štítky nemusí být číselné, může to být i textový řetězec. Příklad výsledného souboru:
acc_x,acc_y,acc_z,gyr_x,gyr_y,gyr_z,class
0.076416015625,-0.04150390625,1.00341796875,0.030517578125,6.591796875,0.9307861328125,0
0.1044921875,-0.04052734375,0.851318359375,-2.95257568359375,9.6435546875,-0.38909912109375,0
...
0.173583984375,0.426513671875,0.89892578125,2.99072265625,10.36834716796875,1.8768310546875,1
0.146728515625,0.327392578125,0.689697265625,3.692626953125,4.31060791015625,4.6844482421875,1
...
-0.076904296875,-0.660888671875,0.782958984375,5.767822265625,2.02178955078125,2.4566650390625,2
-0.1162109375,-0.67138671875,0.619384765625,6.44683837890625,-15.106201171875,-0.61798095703125,2
==== Trénování klasifikátoru ====
Uložená data použijeme jako trénovací (70% dat) a testovací (30% dat) množinu pro získání klasifikátoru.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn import svm
import pickle
dataset = pd.read_csv('data.csv')
X = dataset.iloc[:, [0, 1, 2, 3, 4, 5]].values
y = dataset.iloc[:, [6]].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.30, random_state = 0)
clf = svm.SVC(kernel='linear')
clf.fit(X_train, np.ravel(y_train))
Klasifikátor je možné uložit pro pozdější použití např. pomocí modulu [[https://docs.python.org/3/library/pickle.html|pickle]].
filename = 'model.sav'
pickle.dump(clf, open(filename, 'wb'))
==== Predikce ====
Pokud máme k dispozici model, je možné použít předikci z dat, které v reálném čase získáváme z Thunderboardu:
clf = pickle.load(open('model.sav', 'rb'))
# ziskani dat
D = [[data.acc_x, data.acc_y, data.acc_z, data.gyr_x, data.gyr_y, data.gyr_z]]
# predikce
print(clf.predict(D))
===== Rozpoznání gesta =====
Projekt je inspirován tímto [[https://eloquentarduino.github.io/2021/10/arduino-gesture-recognition-the-easy-way-with-machine-learning/|tutoriálem]].
K rozpoznání gest je třeba si nejprve připravit trénovací data. Zkusíme rozpoznat následující pohyby:
* bez pohybu (důležité)
* pohyb vodorovný
* pohyb svislý
* pohyb krouživý
* vlnovka
Pro každý z těchto pohybů nasbírejte data do textového souboru. Následující kód slouží pro přípravu trénovací množiny.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
if __name__ == '__main__':
# assume you saved your recordings into a "data" folder
rest = pd.read_csv("data/0-rest.csv")
vert = pd.read_csv('data/1-vertical.csv')
hori = pd.read_csv('data/2-horizontal.csv')
circ = pd.read_csv('data/3-circle.csv')
shak = pd.read_csv('data/4-shake.csv')
rest.plot(title='Rest')
vert.plot(title='Verticla')
hori.plot(title='Horizontal')
circ.plot(title='Circle')
shak.plot(title='Shake')
plt.show()
# X is the array of features to train the model on
# y is the array of labels
X = np.vstack([
rest.to_numpy(),
vert.to_numpy(),
hori.to_numpy(),
circ.to_numpy(),
shak.to_numpy()
])
y = np.concatenate([
0 * np.ones(len(rest)),
1 * np.ones(len(vert)),
2 * np.ones(len(hori)),
3 * np.ones(len(circ)),
4 * np.ones(len(shak)),
])
print("X.shape", X.shape)
print("y.shape", y.shape)