Gesture Classification
Classify accelerometer's data with blazing fast spectral features extraction

In this project we will use Machine Learning to detect which gesture the user is performing by means of the accelerometer and gyroscope data coming from an IMU (Inertia Measurement Unit) sensor.
This task differs from the previous ones since it involves time as a feature. We are no more working with static data: we'll work with values that change over time.
We will learn that, when dealing with time series data, we need to use a windowing scheme, to groups sensor readings in chunks to be processed. From each chunk we can later extract a set of spectral features to use as inputs for classification.
This project is a replica of the one from Edge Impulse. Instead of using Neural Networks, though, we will use a "traditional" classifier.
Hardware requirements
To follow this project you need an IMU sensor.
It can either be built-in your board (e.g Arduino BLE Sense) or an external sensor (e.g. MPU9250).
Software requirements
To implement the Machine Learning part in Python you will need the everywhereml
library.
# !pip install everywhereml>=0.2.9
Data collection
We will collect training data via the Serial port of your PC.
If you followed the Fruit classification project, the next piece of code will look familiar.
First of all, create a sketch that is able to print accelerometer and gyroscope data. This will change based on your board and sensor; here is an example for the Arduino BLE Sense.
// file IMUCollect.ino
#include <Arduino_LSM9DS1.h>
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("Started");
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println("Hz");
}
void loop() {
float ax, ay, az, gx, gy, gz;
if (!IMU.accelerationAvailable() || !IMU.gyroscopeAvailable())
return;
IMU.readAcceleration(ax, ay, az);
IMU.readGyroscope(gx, gy, gz);
Serial.print("IMU: ");
Serial.print(ax);
Serial.print(",");
Serial.print(ay);
Serial.print(",");
Serial.print(az);
Serial.print(",");
Serial.print(gx);
Serial.print(",");
Serial.print(gy);
Serial.print(",");
Serial.print(gz);
Serial.print("\n");
}
Then run the following Python code. It will connect to the Serial port of your board and start reading whatever gets printed. Everytime it finds a well-formed line of data, the program will add it to a list that you can eventually save to a file for later use.
from everywhereml.data import Dataset
from everywhereml.data.collect import SerialCollector
"""
Create a SerialCollector object.
Each data line is marked by the 'IMU:' string
Collect 30 seconds of data for each gesture
Replace the port with your own!
If a imu.csv file already exists, skip collection
"""
try:
imu_dataset = Dataset.from_csv(
'imu.csv',
name='ContinuousMotion',
target_name_column='target_name'
)
except FileNotFoundError:
imu_collector = SerialCollector(
port='/dev/cu.usbmodem141401',
baud=115200,
start_of_frame='IMU:',
feature_names=['ax', 'ay', 'az', 'gx', 'gy', 'gz']
)
imu_dataset = imu_collector.collect_many_classes(
dataset_name='ContinuousMotion',
duration=30
)
# save dataset to file for later use
imu_dataset.df.to_csv('imu.csv', index=False)
"""
Print summary of dataset
"""
imu_dataset.describe()
ax | ay | az | gx | gy | gz | target | |
---|---|---|---|---|---|---|---|
count | 13902.000000 | 13902.000000 | 13902.000000 | 13902.000000 | 13902.000000 | 13902.00000 | 13902.000000 |
mean | -0.062706 | 0.126784 | 0.874660 | 3.964229 | 1.939992 | -1.01502 | 1.068264 |
std | 0.186281 | 0.559018 | 0.583573 | 48.614454 | 63.302283 | 64.79808 | 0.819701 |
min | -1.530000 | -1.300000 | -1.010000 | -850.890000 | -302.120000 | -247.99000 | 0.000000 |
25% | -0.180000 | -0.180000 | 0.400000 | -7.510000 | -23.130000 | -31.56000 | 0.000000 |
50% | -0.100000 | 0.030000 | 0.930000 | 0.490000 | 0.920000 | -0.61000 | 1.000000 |
75% | 0.060000 | 0.590000 | 1.160000 | 14.280000 | 35.190000 | 34.55000 | 2.000000 |
max | 1.330000 | 4.000000 | 4.000000 | 1265.500000 | 252.930000 | 328.31000 | 2.000000 |
"""
Plot features pairplot
Since this is a time series dataset, the pairplot won't be very informative
We will come back to the pairplot after feature pre-processing to see great improvements!
"""
imu_dataset.plot.features_pairplot(n=300)

Feature extraction
Now that we have collect our dataset, it is time to extract features from it.
When working with time series data, our go-to feature extractor is a combination of Window
+ Spectral Features
.
Window
packs a given number of consecutive readings into a single, large array. You can configure it to overlap each window with the previous by a given amount, so as to capture different time slices of the same event.
SpectralFeatures
takes in input the windowed data and extracts a number of statistics from it, for example:
from everywhereml.preprocessing import Pipeline, MinMaxScaler, Window, SpectralFeatures
# this is the frequency of your sensor
# change according to your hardware
sampling_frequency = 104
mean_gesture_duration_in_millis = 1000
window_length = sampling_frequency * mean_gesture_duration_in_millis // 1000
imu_pipeline = Pipeline(name='ContinousMotionPipeline', steps=[
MinMaxScaler(),
# shift can be an integer (number of samples) or a float (percent)
Window(length=window_length, shift=0.3),
# order can either be 1 (first-order features) or 2 (add second-order features)
SpectralFeatures(order=2)
])
"""
Enumerate features extracted from the SpectralFeatures step
"""
from pprint import pprint
pprint(imu_pipeline['SpectralFeatures'][0].feature_names)
['maximum', 'minimum', 'abs_maximum', 'abs_minimum', 'mean', 'abs_energy', 'mean_abs_change', 'cid_ce', 'std', 'var', 'count_above_mean', 'count_below_mean', 'first_position_of_max', 'first_position_of_min', 'max_count', 'min_count', 'has_large_std', 'skew', 'kurtosis', 'variation_coefficient']
"""
Apply feature pre-processing
"""
imu_dataset.apply(imu_pipeline)
<everywhereml.data.Dataset.Dataset at 0x12c5ffeb0>
imu_dataset.describe()
ax_maximum | ax_minimum | ax_abs_maximum | ax_abs_minimum | ax_mean | ax_abs_energy | ax_mean_abs_change | ax_cid_ce | ax_std | ax_var | ... | gz_count_below_mean | gz_first_position_of_max | gz_first_position_of_min | gz_max_count | gz_min_count | gz_has_large_std | gz_skew | gz_kurtosis | gz_variation_coefficient | target | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 4.460000e+02 | 4.460000e+02 | ... | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 | 446.000000 |
mean | 0.586942 | 0.429874 | 0.586942 | 0.429874 | 0.513022 | 0.267436 | 0.007395 | 0.000443 | 3.461373e-02 | 1.695827e-03 | ... | 46.908072 | 50.943946 | 52.553812 | 14.502242 | 9.029148 | 0.695067 | -0.132979 | 1.600321 | 0.216918 | 1.069507 |
std | 0.091861 | 0.084704 | 0.091861 | 0.084704 | 0.050534 | 0.059306 | 0.006471 | 0.001529 | 2.233463e-02 | 1.869955e-03 | ... | 13.233362 | 29.509762 | 28.801693 | 22.908881 | 23.152092 | 0.460896 | 0.395895 | 1.692191 | 0.141367 | 0.819489 |
min | 0.493007 | 0.000000 | 0.493007 | 0.000000 | 0.444964 | 0.198737 | 0.000000 | 0.000000 | 2.220446e-16 | 4.930381e-32 | ... | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | -2.757265 | 0.000000 | 0.000308 | 0.000000 |
25% | 0.527972 | 0.405594 | 0.527972 | 0.405594 | 0.481517 | 0.232281 | 0.003267 | 0.000022 | 1.621054e-02 | 2.627815e-04 | ... | 45.000000 | 28.000000 | 30.000000 | 5.000000 | 1.000000 | 0.000000 | -0.331838 | 1.418639 | 0.074097 | 0.000000 |
50% | 0.562937 | 0.430070 | 0.562937 | 0.430070 | 0.505026 | 0.258414 | 0.006552 | 0.000096 | 2.883145e-02 | 8.312540e-04 | ... | 49.000000 | 50.000000 | 51.000000 | 9.000000 | 3.000000 | 1.000000 | -0.046329 | 1.589309 | 0.234187 | 1.000000 |
75% | 0.615385 | 0.461538 | 0.615385 | 0.461538 | 0.530788 | 0.284050 | 0.008385 | 0.000173 | 5.461791e-02 | 2.983117e-03 | ... | 54.000000 | 75.000000 | 76.000000 | 13.000000 | 6.000000 | 1.000000 | 0.000000 | 1.844672 | 0.356645 | 2.000000 |
max | 1.000000 | 0.681818 | 1.000000 | 0.681818 | 0.822250 | 0.677735 | 0.053873 | 0.016381 | 1.171193e-01 | 1.371692e-02 | ... | 94.000000 | 103.000000 | 103.000000 | 104.000000 | 104.000000 | 1.000000 | 3.467912 | 21.711908 | 0.534780 | 2.000000 |
8 rows × 121 columns
"""
Plot features pairplot after feature extraction
Now it will start to make sense
Since SpectralFeatures generates 8 or 20 features (depending on the order)
for each axis, we limit the visualization to a more reasonable number
"""
imu_dataset.plot.features_pairplot(n=300, k=6)

"""
Perform classification with a RandomForest
"""
from everywhereml.sklearn.ensemble import RandomForestClassifier
imu_classifier = RandomForestClassifier(n_estimators=20, max_depth=20)
imu_train, imu_test = imu_dataset.split(test_size=0.3)
imu_classifier.fit(imu_train)
print('Score on test set: %.2f' % imu_classifier.score(imu_test))
Score on test set: 0.99
As you can see from the plot, the SpectralFeatures
feature extractor can convert the raw data windows to a meaningful set of features that a classifier can use to accurately predict the gesture.
Port to Arduino
We need to port two pieces of code to Arduino:
- the pre-processing pipeline
- the classifier
Both of them implement the to_arduino_file()
function to perform this task.
"""
Port pipeline to C++
"""
print(imu_pipeline.to_arduino_file(
'sketches/IMUClassify/Pipeline.h',
instance_name='pipeline'
))
#ifndef UUID5055166448 #define UUID5055166448 #include <cstring> namespace ContinousMotionPipeline { #ifndef UUID5055494704 #define UUID5055494704 /** * MinMaxScaler(low=0, high=1) */ class Step0 { public: /** * Transform input vector */ bool transform(float *x) { for (uint16_t i = 0; i < 6; i++) { x[i] = (x[i] - offset[i]) * scale[i] + 0; if (x[i] < 0) x[i] = 0; else if (x[i] > 1) x[i] = 1; } return true; return true; } protected: float offset[6] = {-1.53000000000f, -1.30000000000f, -1.01000000000f, -850.89000000000f, -302.12000000000f, -247.99000000000f}; float scale[6] = {0.34965034965f, 0.18867924528f, 0.19960079840f, 0.00047250271f, 0.00180163949f, 0.00173520736f}; }; #endif #ifndef UUID5055494656 #define UUID5055494656 /** * Window(length=104, shift=31) */ class Step1 { public: /** * Transform input vector */ bool transform(float *x) { // append x to queue memcpy(queue + head, x, sizeof(float) * 6); head += 6; if (head != 624) return false; // copy queue to x and shift memcpy(x, queue, sizeof(float) * 624); memcpy(queue, queue + 186, sizeof(float) * 438); head -= 186; return true; } protected: uint16_t head = 0; float queue[624] = {0}; }; #endif #ifndef UUID5055494800 #define UUID5055494800 /** * SpectralFeatures(num_inputs=6, window_length=104, order=2, num_features=20) */ class Step2 { public: /** * Transform input vector */ bool transform(float *x) { // spectral features uint16_t idx = 0; for (uint16_t k = 0; k < 6; k++) { float minimum = 9999999; float maximum = -9999999; float abs_minimum = 999999; float abs_maximum = 0; float mean = 0; float abs_energy = 0; float mean_abs_change = 0; float cid_ce = 0; float xi_prev = x[k]; // first-order features for (uint16_t i = k; i < 624; i += 6) { float xi = x[i]; float abs_xi = abs(xi); float xi_prev2 = (i >= k + 12) ? x[i - 12] : xi; float xi2 = xi * xi; float diff = xi - xi_prev; // start of features minimum = xi < minimum ? xi : minimum; maximum = xi > maximum ? xi : maximum; abs_minimum = abs_xi < abs_minimum ? abs_xi : abs_minimum; abs_maximum = abs_xi > abs_maximum ? abs_xi : abs_maximum; mean += xi; abs_energy += xi2; mean_abs_change += abs(diff); cid_ce += diff * diff; xi_prev = xi; } mean /= 104; abs_energy /= 104; mean_abs_change /= 104; cid_ce /= 104; buffer[idx++] = maximum; buffer[idx++] = minimum; buffer[idx++] = abs_maximum; buffer[idx++] = abs_minimum; buffer[idx++] = mean; buffer[idx++] = abs_energy; buffer[idx++] = mean_abs_change; buffer[idx++] = cid_ce; // second-order features float xi_mean_prev = x[k] - mean; float count_above_mean = 0; float count_below_mean = 0; float first_position_of_max = 624; float first_position_of_min = 624; float max_count = 0; float min_count = 0; float max_thresh = maximum - abs(maximum) * 0.02; float min_thresh = minimum + abs(minimum) * 0.02; float var = 0; float std = 0; float kurtosis = 0; float skew = 0; float has_large_std = 0; float variation_coefficient = 0; for (uint16_t i = k; i < 624; i += 6) { float xi = x[i]; float xi_mean = xi - mean; float var_i = xi_mean * xi_mean; // start of features var += var_i; count_above_mean += xi_mean > 0.001 ? 1 : 0; count_below_mean += xi_mean < -0.001 ? 1 : 0; first_position_of_max = (i < first_position_of_max && abs(xi - maximum) < 0.001) ? i : first_position_of_max; first_position_of_min = (i < first_position_of_min && abs(xi - minimum) < 0.001) ? i : first_position_of_min; if (var_i > 0.001) { skew += var_i * xi_mean; kurtosis += var_i * var_i; } if (xi > max_thresh) { max_count += 1; } if (xi < min_thresh) { min_count += 1; } xi_mean_prev = xi_mean; } var /= 104; std = sqrt(var); first_position_of_max = int((first_position_of_max - k) / 104); first_position_of_min = int((first_position_of_min - k) / 104); skew = var > 0.001 ? skew / pow(var, 1.5) : 0; kurtosis = var > 0.001 ? kurtosis / (var * var) : 0; has_large_std = std > 0.25 * (maximum - minimum); variation_coefficient = (mean > 0.001) ? var / mean : 0; buffer[idx++] = std; buffer[idx++] = var; buffer[idx++] = count_above_mean; buffer[idx++] = count_below_mean; buffer[idx++] = first_position_of_max; buffer[idx++] = first_position_of_min; buffer[idx++] = max_count; buffer[idx++] = min_count; buffer[idx++] = has_large_std; buffer[idx++] = skew; buffer[idx++] = kurtosis; buffer[idx++] = variation_coefficient; } memcpy(x, buffer, 120 * sizeof(float)); return true; } protected: float buffer[120]; }; #endif /** * Pipeline: * --------- * > MinMaxScaler(low=0, high=1) * > Window(length=104, shift=31) * > SpectralFeatures(num_inputs=6, window_length=104, order=2, num_features=20) */ class Pipeline { public: static const uint16_t NUM_INPUTS = 6; static const uint16_t NUM_OUTPUTS = 120; static const uint16_t WORKING_SIZE = 624; float X[624]; /** * Apply pipeline to input vector */ template<typename T> bool transform(T *x) { for (uint16_t i = 0; i < 6; i++) this->X[i] = x[i]; size_t start = micros(); bool isOk = step0.transform(X) && step1.transform(X) && step2.transform(X) ; latency = micros() - start; return isOk; } /** * Debug output feature vector */ template<typename PrinterInterface> void debugTo(PrinterInterface &printer, uint8_t precision=5) { printer.print(X[0], precision); for (uint16_t i = 1; i < 120; i++) { printer.print(", "); printer.print(X[i], precision); } printer.print('\n'); } /** * Get latency in micros */ uint32_t latencyInMicros() { return latency; } /** * Get latency in millis */ uint16_t latencyInMillis() { return latency / 1000; } protected: float latency; ContinousMotionPipeline::Step0 step0; ContinousMotionPipeline::Step1 step1; ContinousMotionPipeline::Step2 step2; }; } static ContinousMotionPipeline::Pipeline pipeline; #endif
"""
Port classifier to C++
"""
print(imu_classifier.to_arduino_file(
'sketches/IMUClassify/Classifier.h',
instance_name='forest',
class_map=imu_dataset.class_map
))
#ifndef UUID5043206704 #define UUID5043206704 /** * RandomForestClassifier(base_estimator=DecisionTreeClassifier(), bootstrap=True, ccp_alpha=0.0, class_name=RandomForestClassifier, class_weight=None, criterion=gini, estimator_params=('criterion', 'max_depth', 'min_samples_split', 'min_samples_leaf', 'min_weight_fraction_leaf', 'max_features', 'max_leaf_nodes', 'min_impurity_decrease', 'random_state', 'ccp_alpha'), max_depth=20, max_features=auto, max_leaf_nodes=None, max_samples=None, min_impurity_decrease=0.0, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=20, n_jobs=None, num_outputs=3, oob_score=False, package_name=everywhereml.sklearn.ensemble, random_state=None, template_folder=everywhereml/sklearn/ensemble, verbose=0, warm_start=False) */ class RandomForestClassifier { public: /** * Predict class from features */ int predict(float *x) { int predictedValue = 0; size_t startedAt = micros(); uint16_t votes[3] = { 0 }; uint8_t classIdx = 0; float classScore = 0; tree0(x, &classIdx, &classScore); votes[classIdx] += classScore; tree1(x, &classIdx, &classScore); votes[classIdx] += classScore; tree2(x, &classIdx, &classScore); votes[classIdx] += classScore; tree3(x, &classIdx, &classScore); votes[classIdx] += classScore; tree4(x, &classIdx, &classScore); votes[classIdx] += classScore; tree5(x, &classIdx, &classScore); votes[classIdx] += classScore; tree6(x, &classIdx, &classScore); votes[classIdx] += classScore; tree7(x, &classIdx, &classScore); votes[classIdx] += classScore; tree8(x, &classIdx, &classScore); votes[classIdx] += classScore; tree9(x, &classIdx, &classScore); votes[classIdx] += classScore; tree10(x, &classIdx, &classScore); votes[classIdx] += classScore; tree11(x, &classIdx, &classScore); votes[classIdx] += classScore; tree12(x, &classIdx, &classScore); votes[classIdx] += classScore; tree13(x, &classIdx, &classScore); votes[classIdx] += classScore; tree14(x, &classIdx, &classScore); votes[classIdx] += classScore; tree15(x, &classIdx, &classScore); votes[classIdx] += classScore; tree16(x, &classIdx, &classScore); votes[classIdx] += classScore; tree17(x, &classIdx, &classScore); votes[classIdx] += classScore; tree18(x, &classIdx, &classScore); votes[classIdx] += classScore; tree19(x, &classIdx, &classScore); votes[classIdx] += classScore; // return argmax of votes uint8_t maxClassIdx = 0; float maxVote = votes[0]; for (uint8_t i = 1; i < 3; i++) { if (votes[i] > maxVote) { maxClassIdx = i; maxVote = votes[i]; } } predictedValue = maxClassIdx; latency = micros() - startedAt; return (lastPrediction = predictedValue); } /** * Predict class label */ String predictLabel(float *x) { return getLabelOf(predict(x)); } /** * Get label of last prediction */ String getLabel() { return getLabelOf(lastPrediction); } /** * Get label of given class */ String getLabelOf(int8_t idx) { switch (idx) { case -1: return "ERROR"; case 0: return "left-right"; case 1: return "up-down"; case 2: return "circle"; default: return "UNKNOWN"; } } /** * Get latency in micros */ uint32_t latencyInMicros() { return latency; } /** * Get latency in millis */ uint16_t latencyInMillis() { return latency / 1000; } protected: float latency = 0; int lastPrediction = 0; /** * Random forest's tree #0 */ void tree0(float *x, uint8_t *classIdx, float *classScore) { if (x[101] <= 0.20615998655557632) { if (x[97] <= -0.852979302406311) { if (x[11] <= 56.0) { *classIdx = 1; *classScore = 104.0; return; } else { *classIdx = 0; *classScore = 102.0; return; } } else { if (x[46] <= 0.013923609163612127) { *classIdx = 0; *classScore = 102.0; return; } else { if (x[7] <= 0.00544339045882225) { *classIdx = 2; *classScore = 106.0; return; } else { if (x[1] <= 0.21503496076911688) { *classIdx = 2; *classScore = 106.0; return; } else { *classIdx = 1; *classScore = 104.0; return; } } } } } else { if (x[81] <= 0.32597965002059937) { *classIdx = 1; *classScore = 104.0; return; } else { if (x[109] <= 0.007831086404621601) { if (x[84] <= 0.5459002256393433) { if (x[84] <= 0.5457448363304138) { if (x[92] <= 16.5) { if (x[9] <= 0.0015504520102282981) { *classIdx = 0; *classScore = 102.0; return; } else { *classIdx = 2; *classScore = 106.0; return; } } else { if (x[80] <= 0.5804882347583771) { *classIdx = 1; *classScore = 104.0; return; } else { *classIdx = 0; *classScore = 102.0; return; } } } else { *classIdx = 0; *classScore = 102.0; return; } } else { if (x[112] <= 14.5) { *classIdx = 1; *classScore = 104.0; return; } else { *classIdx = 2; *classScore = 106.0; return; } } } else { *classIdx = 0; *classScore = 102.0; return; } } } } /** * Random forest's tree #1 */ void tree1(float *x, uint8_t *classIdx, float *classScore) { if (x[65] <= 0.1646103635430336) { if (x[2] <= 0.5594405829906464) { *classIdx = 0; *classScore = 91.0; return; } else { if (x[61] <= 0.25923389196395874) { *classIdx = 2; *classScore = 114.0; return; } else { if (x[8] <= 0.0238403482362628) { if (x[59] <= 0.0036848323652520776) { *classIdx = 1; *classScore = 107.0; return; } else { if (x[60] <= 0.4137399047613144) { if (x[84] <= 0.5459002256393433) { if (x[95] <= 4.5) { if (x[21] <= 0.22452830523252487) { *classIdx = 0; *classScore = 91.0; return; } else { *classIdx = 1; *classScore = 107.0; return; } } else { *classIdx = 0; *classScore = 91.0; return; } } else { *classIdx = 2; *classScore = 114.0; return; } } else { *classIdx = 2; *classScore = 114.0; return; } } } else { if (x[24] <= 0.22170718014240265) { if (x[63] <= 0.36150000989437103) { *classIdx = 0; *classScore = 91.0; return; } else { *classIdx = 2; *classScore = 114.0; return; } } else { *classIdx = 1; *classScore = 107.0; return; } } } } } else { if (x[7] <= 0.0011794675956480205) { *classIdx = 2; *classScore = 114.0; return; } else { if (x[58] <= 1.8163162469863892) { *classIdx = 0; *classScore = 91.0; return; } else { *classIdx = 1; *classScore = 107.0; return; } } } } /** * Random forest's tree #2 */ void tree2(float *x, uint8_t *classIdx, float *classScore) { if (x[48] <= 0.0413026325404644) { if (x[8] <= 0.025791436433792114) { if (x[44] <= 0.37927989661693573) { if (x[45] <= 0.06746238097548485) { if (x[31] <= 27.5) { *classIdx = 0; *classScore = 103.0; return; } else { *classIdx = 1; *classScore = 90.0; return; } } else { *classIdx = 2; *classScore = 119.0; return; } } else { *classIdx = 0; *classScore = 103.0; return; } } else { if (x[21] <= 0.09905660524964333) { *classIdx = 0; *classScore = 103.0; return; } else { if (x[64] <= 0.4104910343885422) { *classIdx = 1; *classScore = 90.0; return; } else { *classIdx = 0; *classScore = 103.0; return; } } } } else { if (x[103] <= 0.2664237320423126) { if (x[17] <= 1.5523573160171509) { if (x[65] <= 0.19812984019517899) { *classIdx = 2; *classScore = 119.0; return; } else { if (x[23] <= 0.11226414889097214) { *classIdx = 0; *classScore = 103.0; return; } else { *classIdx = 2; *classScore = 119.0; return; } } } else { if (x[104] <= 0.4240120202302933) { *classIdx = 1; *classScore = 90.0; return; } else { *classIdx = 0; *classScore = 103.0; return; } } } else { if (x[40] <= 0.5818363130092621) { *classIdx = 2; *classScore = 119.0; return; } else { if (x[5] <= 0.2993701845407486) { if (x[23] <= 0.11226414889097214) { *classIdx = 0; *classScore = 103.0; return; } else { *classIdx = 1; *classScore = 90.0; return; } } else { *classIdx = 2; *classScore = 119.0; return; } } } } } /** * Random forest's tree #3 */ void tree3(float *x, uint8_t *classIdx, float *classScore) { if (x[88] <= 0.042928967624902725) { if (x[63] <= 0.40007276833057404) { if (x[64] <= 0.39961662888526917) { if (x[64] <= 0.3994302749633789) { if (x[59] <= 0.0511703547090292) { *classIdx = 2; *classScore = 110.0; return; } else { *classIdx = 0; *classScore = 106.0; return; } } else { *classIdx = 1; *classScore = 96.0; return; } } else { if (x[4] <= 0.5648870170116425) { if (x[61] <= 0.3273829519748688) { *classIdx = 2; *classScore = 110.0; return; } else { *classIdx = 0; *classScore = 106.0; return; } } else { *classIdx = 2; *classScore = 110.0; return; } } } else { *classIdx = 1; *classScore = 96.0; return; } } else { if (x[86] <= 0.01472831517457962) { if (x[108] <= 0.09371737390756607) { *classIdx = 1; *classScore = 96.0; return; } else { if (x[89] <= 0.01160075340885669) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } } else { if (x[106] <= 0.011076182126998901) { if (x[8] <= 0.056467942893505096) { if (x[98] <= 1.7449243068695068) { *classIdx = 1; *classScore = 96.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } else { *classIdx = 1; *classScore = 96.0; return; } } else { if (x[44] <= 0.3131141662597656) { if (x[81] <= 0.11650301516056061) { *classIdx = 0; *classScore = 106.0; return; } else { if (x[108] <= 0.07197083160281181) { *classIdx = 1; *classScore = 96.0; return; } else { if (x[71] <= 37.0) { *classIdx = 1; *classScore = 96.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } } } else { if (x[2] <= 0.9807692170143127) { if (x[21] <= 0.0028301887214183807) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } else { *classIdx = 1; *classScore = 96.0; return; } } } } } } /** * Random forest's tree #4 */ void tree4(float *x, uint8_t *classIdx, float *classScore) { if (x[64] <= 0.4054749011993408) { if (x[29] <= 0.006414404837414622) { if (x[31] <= 39.0) { if (x[21] <= 0.41886791586875916) { if (x[109] <= 0.00035645199386635795) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } else { *classIdx = 0; *classScore = 96.0; return; } } else { if (x[63] <= 0.25923389196395874) { *classIdx = 2; *classScore = 108.0; return; } else { if (x[97] <= 0.3700479716062546) { if (x[5] <= 0.3184020519256592) { if (x[61] <= 0.3268631845712662) { if (x[56] <= 0.5) { *classIdx = 1; *classScore = 108.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } else { *classIdx = 1; *classScore = 108.0; return; } } else { *classIdx = 2; *classScore = 108.0; return; } } else { *classIdx = 2; *classScore = 108.0; return; } } } } else { if (x[111] <= 43.5) { if (x[91] <= 53.0) { *classIdx = 2; *classScore = 108.0; return; } else { if (x[106] <= 0.010455803479999304) { *classIdx = 1; *classScore = 108.0; return; } else { *classIdx = 0; *classScore = 96.0; return; } } } else { *classIdx = 0; *classScore = 96.0; return; } } } else { if (x[7] <= 0.0011794675956480205) { if (x[77] <= 1.6548011302947998) { *classIdx = 2; *classScore = 108.0; return; } else { if (x[1] <= 0.4685314744710922) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 0; *classScore = 96.0; return; } } } else { if (x[58] <= 1.8163162469863892) { *classIdx = 0; *classScore = 96.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } } } /** * Random forest's tree #5 */ void tree5(float *x, uint8_t *classIdx, float *classScore) { if (x[20] <= 0.3518867939710617) { if (x[27] <= 4.735126822197344e-05) { *classIdx = 2; *classScore = 113.0; return; } else { *classIdx = 1; *classScore = 99.0; return; } } else { if (x[88] <= 0.06187122315168381) { if (x[2] <= 0.5576923191547394) { *classIdx = 0; *classScore = 100.0; return; } else { if (x[64] <= 0.39941608905792236) { if (x[73] <= 53.0) { *classIdx = 2; *classScore = 113.0; return; } else { if (x[84] <= 0.5381522178649902) { *classIdx = 1; *classScore = 99.0; return; } else { if (x[37] <= 0.18935085833072662) { *classIdx = 2; *classScore = 113.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } } } else { if (x[59] <= 0.0037844966864213347) { *classIdx = 1; *classScore = 99.0; return; } else { if (x[72] <= 14.0) { *classIdx = 2; *classScore = 113.0; return; } else { if (x[70] <= 5.5) { *classIdx = 1; *classScore = 99.0; return; } else { if (x[0] <= 0.561188817024231) { *classIdx = 1; *classScore = 99.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } } } } } } else { if (x[39] <= 0.31973332166671753) { if (x[89] <= 0.022863940335810184) { if (x[117] <= -1.17048841714859) { *classIdx = 0; *classScore = 100.0; return; } else { if (x[91] <= 41.0) { *classIdx = 1; *classScore = 99.0; return; } else { *classIdx = 2; *classScore = 113.0; return; } } } else { *classIdx = 1; *classScore = 99.0; return; } } else { if (x[106] <= 0.025685111992061138) { *classIdx = 2; *classScore = 113.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } } } } /** * Random forest's tree #6 */ void tree6(float *x, uint8_t *classIdx, float *classScore) { if (x[48] <= 0.05949857644736767) { if (x[65] <= 0.1578432321548462) { if (x[53] <= 73.0) { *classIdx = 2; *classScore = 93.0; return; } else { if (x[49] <= 0.0003333433633088134) { *classIdx = 2; *classScore = 93.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } } else { if (x[66] <= 0.00014964114961912856) { *classIdx = 1; *classScore = 108.0; return; } else { if (x[1] <= 0.5646853148937225) { if (x[97] <= -1.3094336986541748) { *classIdx = 1; *classScore = 108.0; return; } else { if (x[33] <= 1.0) { if (x[82] <= 0.5903882384300232) { *classIdx = 0; *classScore = 111.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } else { *classIdx = 0; *classScore = 111.0; return; } } } else { *classIdx = 2; *classScore = 93.0; return; } } } } else { if (x[29] <= 0.0012912569800391793) { if (x[59] <= 0.2586899697780609) { *classIdx = 2; *classScore = 93.0; return; } else { if (x[71] <= 35.5) { if (x[26] <= 0.007629602449014783) { *classIdx = 2; *classScore = 93.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } else { *classIdx = 1; *classScore = 108.0; return; } } } else { if (x[114] <= 2.5) { if (x[88] <= 0.1578180193901062) { if (x[115] <= 0.5) { *classIdx = 0; *classScore = 111.0; return; } else { if (x[21] <= 0.28962263464927673) { *classIdx = 2; *classScore = 93.0; return; } else { *classIdx = 1; *classScore = 108.0; return; } } } else { *classIdx = 1; *classScore = 108.0; return; } } else { *classIdx = 2; *classScore = 93.0; return; } } } } /** * Random forest's tree #7 */ void tree7(float *x, uint8_t *classIdx, float *classScore) { if (x[59] <= 0.16472728550434113) { if (x[28] <= 0.06554679200053215) { if (x[44] <= 0.22538576275110245) { *classIdx = 0; *classScore = 100.0; return; } else { if (x[43] <= 0.25449101626873016) { *classIdx = 1; *classScore = 107.0; return; } else { if (x[20] <= 0.35943396389484406) { *classIdx = 1; *classScore = 107.0; return; } else { *classIdx = 2; *classScore = 105.0; return; } } } } else { if (x[108] <= 0.08099111914634705) { *classIdx = 1; *classScore = 107.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } } else { if (x[39] <= 0.12323303520679474) { if (x[4] <= 0.5445131957530975) { if (x[90] <= 44.0) { *classIdx = 2; *classScore = 105.0; return; } else { *classIdx = 1; *classScore = 107.0; return; } } else { *classIdx = 2; *classScore = 105.0; return; } } else { if (x[103] <= 0.19619989395141602) { if (x[65] <= 0.19812984019517899) { *classIdx = 2; *classScore = 105.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } else { if (x[4] <= 0.592169851064682) { *classIdx = 1; *classScore = 107.0; return; } else { *classIdx = 2; *classScore = 105.0; return; } } } } } /** * Random forest's tree #8 */ void tree8(float *x, uint8_t *classIdx, float *classScore) { if (x[19] <= 0.03867301903665066) { if (x[100] <= 0.51827172935009) { if (x[5] <= 0.316843181848526) { if (x[24] <= 0.41403302550315857) { *classIdx = 2; *classScore = 87.0; return; } else { *classIdx = 1; *classScore = 106.0; return; } } else { if (x[23] <= 0.41886791586875916) { *classIdx = 2; *classScore = 87.0; return; } else { *classIdx = 0; *classScore = 119.0; return; } } } else { if (x[99] <= 0.1854959949851036) { *classIdx = 0; *classScore = 119.0; return; } else { *classIdx = 2; *classScore = 87.0; return; } } } else { if (x[37] <= 0.0008813084568828344) { if (x[108] <= 0.07701681554317474) { if (x[71] <= 35.5) { if (x[34] <= 3.5) { *classIdx = 1; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 87.0; return; } } else { *classIdx = 1; *classScore = 106.0; return; } } else { if (x[83] <= 0.4791550189256668) { *classIdx = 2; *classScore = 87.0; return; } else { *classIdx = 0; *classScore = 119.0; return; } } } else { if (x[109] <= 0.0033812231849879026) { *classIdx = 1; *classScore = 106.0; return; } else { if (x[46] <= 0.015338255558162928) { *classIdx = 0; *classScore = 119.0; return; } else { *classIdx = 2; *classScore = 87.0; return; } } } } } /** * Random forest's tree #9 */ void tree9(float *x, uint8_t *classIdx, float *classScore) { if (x[42] <= 0.5339321196079254) { if (x[61] <= 0.33802370727062225) { if (x[23] <= 0.3500000089406967) { if (x[103] <= 0.40124066174030304) { *classIdx = 2; *classScore = 93.0; return; } else { *classIdx = 1; *classScore = 113.0; return; } } else { *classIdx = 1; *classScore = 113.0; return; } } else { if (x[62] <= 0.4022202789783478) { *classIdx = 1; *classScore = 113.0; return; } else { if (x[102] <= 0.550312340259552) { if (x[43] <= 0.2405189573764801) { *classIdx = 0; *classScore = 106.0; return; } else { if (x[46] <= 0.0032846928224898875) { *classIdx = 2; *classScore = 93.0; return; } else { *classIdx = 1; *classScore = 113.0; return; } } } else { *classIdx = 0; *classScore = 106.0; return; } } } } else { if (x[19] <= 0.08684312924742699) { if (x[9] <= 0.00034576476900838315) { if (x[109] <= 0.02077473560348153) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 93.0; return; } } else { if (x[88] <= 0.06902964413166046) { *classIdx = 1; *classScore = 113.0; return; } else { *classIdx = 2; *classScore = 93.0; return; } } } else { if (x[100] <= 0.6207357048988342) { if (x[22] <= 0.26886792480945587) { if (x[117] <= -0.1728222295641899) { *classIdx = 1; *classScore = 113.0; return; } else { *classIdx = 2; *classScore = 93.0; return; } } else { if (x[32] <= 102.5) { *classIdx = 1; *classScore = 113.0; return; } else { *classIdx = 2; *classScore = 93.0; return; } } } else { *classIdx = 2; *classScore = 93.0; return; } } } } /** * Random forest's tree #10 */ void tree10(float *x, uint8_t *classIdx, float *classScore) { if (x[43] <= 0.2824351340532303) { if (x[100] <= 0.6090317368507385) { if (x[1] <= 0.440559446811676) { *classIdx = 1; *classScore = 97.0; return; } else { if (x[60] <= 0.4022202789783478) { *classIdx = 1; *classScore = 97.0; return; } else { if (x[75] <= 26.5) { if (x[28] <= 0.039970505982637405) { if (x[73] <= 43.5) { *classIdx = 2; *classScore = 109.0; return; } else { *classIdx = 1; *classScore = 97.0; return; } } else { *classIdx = 2; *classScore = 109.0; return; } } else { if (x[93] <= 89.0) { *classIdx = 0; *classScore = 106.0; return; } else { if (x[93] <= 95.0) { *classIdx = 2; *classScore = 109.0; return; } else { *classIdx = 0; *classScore = 106.0; return; } } } } } } else { if (x[97] <= -0.6617821156978607) { *classIdx = 0; *classScore = 106.0; return; } else { if (x[6] <= 0.004430036060512066) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 109.0; return; } } } } else { if (x[29] <= 0.0034833433164749295) { *classIdx = 2; *classScore = 109.0; return; } else { *classIdx = 0; *classScore = 106.0; return; } } } /** * Random forest's tree #11 */ void tree11(float *x, uint8_t *classIdx, float *classScore) { if (x[102] <= 0.6217508316040039) { if (x[48] <= 0.058697016909718513) { if (x[44] <= 0.3917645364999771) { if (x[39] <= 0.0047744776820763946) { if (x[43] <= 0.21756486594676971) { *classIdx = 0; *classScore = 94.0; return; } else { if (x[71] <= 7.5) { *classIdx = 1; *classScore = 110.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } } else { if (x[95] <= 3.0) { *classIdx = 1; *classScore = 110.0; return; } else { if (x[102] <= 0.5386083573102951) { if (x[35] <= 40.5) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 1; *classScore = 110.0; return; } } else { *classIdx = 0; *classScore = 94.0; return; } } } } else { if (x[109] <= 0.006840005749836564) { *classIdx = 1; *classScore = 110.0; return; } else { *classIdx = 0; *classScore = 94.0; return; } } } else { if (x[23] <= 0.05754717066884041) { *classIdx = 2; *classScore = 108.0; return; } else { if (x[5] <= 0.4598323106765747) { if (x[107] <= 3.0264975066529587e-05) { *classIdx = 2; *classScore = 108.0; return; } else { if (x[91] <= 59.5) { if (x[84] <= 0.6049992144107819) { *classIdx = 1; *classScore = 110.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } else { *classIdx = 2; *classScore = 108.0; return; } } } else { *classIdx = 2; *classScore = 108.0; return; } } } } else { if (x[23] <= 0.0028301887214183807) { *classIdx = 0; *classScore = 94.0; return; } else { if (x[54] <= 3.5) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 0; *classScore = 94.0; return; } } } } /** * Random forest's tree #12 */ void tree12(float *x, uint8_t *classIdx, float *classScore) { if (x[43] <= 0.2684630751609802) { if (x[100] <= 0.5845219492912292) { if (x[90] <= 20.0) { if (x[86] <= 0.00019162097305525094) { *classIdx = 1; *classScore = 96.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } else { if (x[72] <= 1.5) { *classIdx = 2; *classScore = 116.0; return; } else { if (x[43] <= 0.03692614659667015) { *classIdx = 2; *classScore = 116.0; return; } else { *classIdx = 1; *classScore = 96.0; return; } } } } else { if (x[115] <= 0.5) { *classIdx = 0; *classScore = 100.0; return; } else { if (x[103] <= 0.33462606370449066) { if (x[88] <= 0.047023409977555275) { *classIdx = 0; *classScore = 100.0; return; } else { if (x[65] <= 0.19691621512174606) { *classIdx = 2; *classScore = 116.0; return; } else { if (x[81] <= 0.1938023567199707) { *classIdx = 1; *classScore = 96.0; return; } else { *classIdx = 2; *classScore = 116.0; return; } } } } else { *classIdx = 1; *classScore = 96.0; return; } } } } else { if (x[39] <= 0.2591274231672287) { if (x[38] <= 1.4711158275604248) { *classIdx = 2; *classScore = 116.0; return; } else { *classIdx = 1; *classScore = 96.0; return; } } else { if (x[100] <= 0.5527503192424774) { *classIdx = 1; *classScore = 96.0; return; } else { *classIdx = 0; *classScore = 100.0; return; } } } } /** * Random forest's tree #13 */ void tree13(float *x, uint8_t *classIdx, float *classScore) { if (x[89] <= 0.005752284778282046) { if (x[108] <= 0.07743877917528152) { if (x[45] <= 0.051511574536561966) { *classIdx = 0; *classScore = 117.0; return; } else { if (x[46] <= 0.0005038466624682769) { *classIdx = 1; *classScore = 92.0; return; } else { if (x[44] <= 0.3884538561105728) { *classIdx = 2; *classScore = 103.0; return; } else { *classIdx = 1; *classScore = 92.0; return; } } } } else { *classIdx = 0; *classScore = 117.0; return; } } else { if (x[119] <= 0.21985995769500732) { if (x[112] <= 12.0) { if (x[101] <= 0.3862050920724869) { *classIdx = 2; *classScore = 103.0; return; } else { *classIdx = 1; *classScore = 92.0; return; } } else { if (x[83] <= 0.16476894170045853) { *classIdx = 2; *classScore = 103.0; return; } else { *classIdx = 1; *classScore = 92.0; return; } } } else { if (x[53] <= 3.0) { *classIdx = 0; *classScore = 117.0; return; } else { *classIdx = 2; *classScore = 103.0; return; } } } } /** * Random forest's tree #14 */ void tree14(float *x, uint8_t *classIdx, float *classScore) { if (x[103] <= 0.19619989395141602) { if (x[97] <= -0.852979302406311) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 101.0; return; } } else { if (x[108] <= 0.08755338937044144) { if (x[106] <= 0.003870102111250162) { if (x[106] <= 0.0007526251429226249) { if (x[4] <= 0.562886655330658) { *classIdx = 1; *classScore = 105.0; return; } else { if (x[69] <= 9.350435992150352e-08) { *classIdx = 1; *classScore = 105.0; return; } else { if (x[3] <= 0.5646853148937225) { *classIdx = 0; *classScore = 106.0; return; } else { *classIdx = 2; *classScore = 101.0; return; } } } } else { *classIdx = 2; *classScore = 101.0; return; } } else { if (x[43] <= 0.04491017945110798) { *classIdx = 2; *classScore = 101.0; return; } else { if (x[3] <= 0.45279720425605774) { *classIdx = 1; *classScore = 105.0; return; } else { if (x[105] <= 0.18123524636030197) { *classIdx = 2; *classScore = 101.0; return; } else { if (x[32] <= 89.0) { *classIdx = 1; *classScore = 105.0; return; } else { *classIdx = 2; *classScore = 101.0; return; } } } } } } else { if (x[8] <= 0.04136615991592407) { if (x[5] <= 0.22317729145288467) { *classIdx = 2; *classScore = 101.0; return; } else { *classIdx = 0; *classScore = 106.0; return; } } else { if (x[87] <= 0.0018561588949523866) { *classIdx = 2; *classScore = 101.0; return; } else { *classIdx = 1; *classScore = 105.0; return; } } } } } /** * Random forest's tree #15 */ void tree15(float *x, uint8_t *classIdx, float *classScore) { if (x[68] <= 0.005983148235827684) { if (x[3] <= 0.5646853148937225) { if (x[68] <= 0.0004055327517562546) { *classIdx = 1; *classScore = 105.0; return; } else { *classIdx = 0; *classScore = 99.0; return; } } else { *classIdx = 2; *classScore = 108.0; return; } } else { if (x[80] <= 0.7659400105476379) { if (x[22] <= 0.3707547187805176) { *classIdx = 1; *classScore = 105.0; return; } else { if (x[69] <= 0.000789271667599678) { if (x[39] <= 0.46699461340904236) { if (x[42] <= 0.29241517186164856) { *classIdx = 1; *classScore = 105.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } else { if (x[39] <= 0.5124154537916183) { *classIdx = 0; *classScore = 99.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } } else { if (x[17] <= -0.04603559896349907) { if (x[102] <= 0.5881659090518951) { *classIdx = 1; *classScore = 105.0; return; } else { *classIdx = 2; *classScore = 108.0; return; } } else { *classIdx = 1; *classScore = 105.0; return; } } } } else { if (x[49] <= 0.01844543870538473) { if (x[58] <= 8.55187201499939) { if (x[87] <= 0.00350712810177356) { if (x[0] <= 0.9615384638309479) { if (x[7] <= 0.001176737598143518) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 0; *classScore = 99.0; return; } } else { *classIdx = 1; *classScore = 105.0; return; } } else { *classIdx = 1; *classScore = 105.0; return; } } else { *classIdx = 0; *classScore = 99.0; return; } } else { if (x[0] <= 0.5629370510578156) { *classIdx = 2; *classScore = 108.0; return; } else { *classIdx = 1; *classScore = 105.0; return; } } } } } /** * Random forest's tree #16 */ void tree16(float *x, uint8_t *classIdx, float *classScore) { if (x[68] <= 0.0053869562689214945) { if (x[83] <= 0.5450860261917114) { if (x[0] <= 0.5751748383045197) { if (x[82] <= 0.547608345746994) { if (x[73] <= 4.0) { *classIdx = 2; *classScore = 110.0; return; } else { *classIdx = 0; *classScore = 101.0; return; } } else { *classIdx = 0; *classScore = 101.0; return; } } else { *classIdx = 1; *classScore = 101.0; return; } } else { *classIdx = 1; *classScore = 101.0; return; } } else { if (x[106] <= 0.010447211563587189) { if (x[119] <= 0.21147403866052628) { if (x[34] <= 4.5) { if (x[71] <= 63.0) { *classIdx = 1; *classScore = 101.0; return; } else { if (x[30] <= 32.5) { *classIdx = 0; *classScore = 101.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } } else { if (x[48] <= 0.12451346963644028) { *classIdx = 2; *classScore = 110.0; return; } else { *classIdx = 1; *classScore = 101.0; return; } } } else { *classIdx = 0; *classScore = 101.0; return; } } else { if (x[77] <= 0.3749406188726425) { if (x[81] <= 0.3816773295402527) { if (x[105] <= 0.25021321326494217) { *classIdx = 2; *classScore = 110.0; return; } else { *classIdx = 0; *classScore = 101.0; return; } } else { if (x[68] <= 0.027558149304240942) { *classIdx = 0; *classScore = 101.0; return; } else { *classIdx = 1; *classScore = 101.0; return; } } } else { if (x[83] <= 0.12298891693353653) { *classIdx = 0; *classScore = 101.0; return; } else { if (x[43] <= 0.15568862855434418) { *classIdx = 1; *classScore = 101.0; return; } else { *classIdx = 2; *classScore = 110.0; return; } } } } } } /** * Random forest's tree #17 */ void tree17(float *x, uint8_t *classIdx, float *classScore) { if (x[41] <= 0.26646706461906433) { if (x[20] <= 0.43113207817077637) { if (x[97] <= 0.3700479716062546) { if (x[40] <= 0.22055888175964355) { *classIdx = 0; *classScore = 110.0; return; } else { if (x[22] <= 0.2669811397790909) { *classIdx = 2; *classScore = 107.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } } else { *classIdx = 2; *classScore = 107.0; return; } } else { if (x[102] <= 0.5845219492912292) { if (x[55] <= 6.5) { if (x[24] <= 0.3628174960613251) { *classIdx = 2; *classScore = 107.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } else { *classIdx = 0; *classScore = 110.0; return; } } else { if (x[110] <= 44.5) { *classIdx = 0; *classScore = 110.0; return; } else { if (x[110] <= 49.5) { if (x[86] <= 0.012960522901266813) { *classIdx = 0; *classScore = 110.0; return; } else { *classIdx = 2; *classScore = 107.0; return; } } else { *classIdx = 2; *classScore = 107.0; return; } } } } } else { if (x[23] <= 0.12735848873853683) { *classIdx = 0; *classScore = 110.0; return; } else { if (x[108] <= 0.02299004327505827) { *classIdx = 2; *classScore = 107.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } } } /** * Random forest's tree #18 */ void tree18(float *x, uint8_t *classIdx, float *classScore) { if (x[69] <= 4.023433393740561e-05) { if (x[82] <= 0.546509325504303) { *classIdx = 1; *classScore = 102.0; return; } else { if (x[73] <= 6.5) { if (x[34] <= 49.5) { *classIdx = 0; *classScore = 101.0; return; } else { *classIdx = 2; *classScore = 109.0; return; } } else { *classIdx = 0; *classScore = 101.0; return; } } } else { if (x[119] <= 0.22386814653873444) { if (x[41] <= 0.24850299209356308) { if (x[5] <= 0.34927724301815033) { *classIdx = 1; *classScore = 102.0; return; } else { *classIdx = 2; *classScore = 109.0; return; } } else { if (x[114] <= 3.0) { *classIdx = 2; *classScore = 109.0; return; } else { if (x[113] <= 46.5) { *classIdx = 2; *classScore = 109.0; return; } else { *classIdx = 0; *classScore = 101.0; return; } } } } else { if (x[49] <= 0.003500548773445189) { if (x[32] <= 85.5) { *classIdx = 0; *classScore = 101.0; return; } else { *classIdx = 2; *classScore = 109.0; return; } } else { if (x[54] <= 4.5) { *classIdx = 2; *classScore = 109.0; return; } else { *classIdx = 0; *classScore = 101.0; return; } } } } } /** * Random forest's tree #19 */ void tree19(float *x, uint8_t *classIdx, float *classScore) { if (x[28] <= 0.06747397407889366) { if (x[23] <= 0.25660377740859985) { if (x[45] <= 0.14753440767526627) { if (x[68] <= 0.04735240712761879) { *classIdx = 2; *classScore = 114.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } else { *classIdx = 1; *classScore = 95.0; return; } } else { if (x[40] <= 0.22055888175964355) { *classIdx = 0; *classScore = 103.0; return; } else { if (x[44] <= 0.23972247540950775) { *classIdx = 1; *classScore = 95.0; return; } else { if (x[67] <= 0.00034951127599924803) { *classIdx = 2; *classScore = 114.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } } } } else { if (x[64] <= 0.4049363285303116) { if (x[104] <= 0.4816547781229019) { *classIdx = 0; *classScore = 103.0; return; } else { *classIdx = 2; *classScore = 114.0; return; } } else { if (x[103] <= 0.01710914447903633) { *classIdx = 0; *classScore = 103.0; return; } else { if (x[17] <= 2.290368974208832) { *classIdx = 2; *classScore = 114.0; return; } else { *classIdx = 1; *classScore = 95.0; return; } } } } } }; static RandomForestClassifier forest; #endif
With these two pieces in place, it's time to integrate them into a sketch.
// file IMUClassify.ino
#include <Arduino_LSM9DS1.h>
#include "Pipeline.h"
#include "Classifier.h"
void setup() {
Serial.begin(115200);
Serial.println("Started");
while (!IMU.begin())
Serial.println("Failed to initialize IMU!");
Serial.print("Accelerometer sample rate = ");
Serial.print(IMU.accelerationSampleRate());
Serial.println("Hz");
}
void loop() {
float ax, ay, az, gx, gy, gz;
// await for data
if (!IMU.accelerationAvailable() || !IMU.gyroscopeAvailable())
return;
IMU.readAcceleration(ax, ay, az);
IMU.readGyroscope(gx, gy, gz);
// perform feature extraction
float features[] = {ax, ay, az, gx, gy, gz};
if (!pipeline.transform(features))
return;
// perform classification
Serial.print("Predicted gesture: ");
Serial.print(forest.predictLabel(pipeline.X));
Serial.print(" (DSP: ");
Serial.print(pipeline.latencyInMicros());
Serial.print(" micros, Classifier: ");
Serial.print(forest.latencyInMicros());
Serial.println(" micros)");
}
Deploy the sketch to your board and start performing each gesture.
Based on the length of your gestures and the complexity of the classifier, you can expect sub-millisecond DSP time if using first-order features only and 100-200 microseconds for classification.