====== 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)