Skip to content

Commit 971814d

Browse files
committed
Code explained
1 parent 12606db commit 971814d

File tree

1 file changed

+249
-0
lines changed
  • content/hardware/03.nano/boards/nano-matter/tutorials/08.ml-magic-wand

1 file changed

+249
-0
lines changed

content/hardware/03.nano/boards/nano-matter/tutorials/08.ml-magic-wand/content.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,254 @@ Download the `Modulino.h` library from the Arduino IDE Library Manager. This wil
9595

9696
![Modulino Library Installation](assets/modulino.png)
9797

98+
After the libraries import, several variables and structures are declared that has to be with the Machine Learning implementation:
99+
100+
```arduino
101+
#include "SilabsTFLiteMicro.h"
102+
#include "Modulino.h"
103+
104+
#define SEQUENCE_LENGTH 200
105+
#define SIGNAL_CHANNELS 3
106+
107+
#define GESTURE_COUNT 3
108+
#define WING_GESTURE 0
109+
#define RING_GESTURE 1
110+
#define NO_GESTURE 2
111+
112+
#define DETECTION_THRESHOLD 0.5f
113+
114+
static TfLiteTensor* model_input;
115+
static tflite::MicroInterpreter* interpreter;
116+
static int input_length;
117+
static TfLiteTensor *output;
118+
119+
typedef struct model_output {
120+
float gesture[GESTURE_COUNT];
121+
} model_output_t;
122+
123+
typedef float acc_data_t;
124+
```
125+
126+
We define some function prototypes to manage the Modulino Pixels and the Modulino Movement accelerometer data. Also, the classes for the Modulinos are declared.
127+
128+
```arduino
129+
ModulinoColor OFF(0, 0, 0);
130+
ModulinoColor YELLOW(255, 255, 0);
131+
132+
ModulinoMovement imu;
133+
ModulinoPixels leds;
134+
135+
void setPixel(int pixel, ModulinoColor color) {
136+
leds.set(pixel, color, 25);
137+
leds.show();
138+
}
139+
140+
bool accelerometer_setup();
141+
void accelerometer_read(acc_data_t* dst, int n);
142+
```
143+
In the `setup()` function, the **Tensorflow Lite model** is initialized alongside some modules support, including:
144+
145+
- Serial communication
146+
- Modulino Pixels LEDs
147+
- Modulino Movement IMU
148+
149+
At the beginning, relevant model parameters as the input tensor and its characteristics are printed in the Serial Monitor for debugging.
150+
151+
```arduino
152+
void setup() {
153+
Serial.begin(115200);
154+
Serial.println("Magic Wand - Silabs TensorFlowLite");
155+
Serial.println("init...");
156+
157+
// Init TFLite model
158+
sl_tflite_micro_init();
159+
160+
// Obtain pointer to the model's input tensor.
161+
model_input = sl_tflite_micro_get_input_tensor();
162+
interpreter = sl_tflite_micro_get_interpreter();
163+
output = sl_tflite_micro_get_output_tensor();
164+
165+
// Print model input parameters
166+
Serial.print("model_input->dims->size = ");
167+
Serial.println(model_input->dims->size);
168+
Serial.print("model_input->dims->data[0] = ");
169+
Serial.println(model_input->dims->data[0]);
170+
Serial.print("model_input->dims->data[1] = ");
171+
Serial.println(model_input->dims->data[1]);
172+
Serial.print("model_input->dims->data[2] = ");
173+
Serial.println(model_input->dims->data[2]);
174+
Serial.print("model_input->type = ");
175+
Serial.println(model_input->type);
176+
177+
// Check model input parameters
178+
if ((model_input->dims->size != 2) || (model_input->dims->data[0] != 1)
179+
|| (model_input->dims->data[1] != SEQUENCE_LENGTH * SIGNAL_CHANNELS)
180+
|| (model_input->type != kTfLiteFloat32)) {
181+
Serial.println("error: bad input tensor parameters in model");
182+
while(1) ;
183+
}
184+
185+
// Print model input lenght
186+
input_length = model_input->bytes / sizeof(float);
187+
Serial.print("input_length = ");
188+
Serial.println(input_length);
189+
190+
// Initialize accelerometer
191+
bool setup_status = accelerometer_setup();
192+
if (!setup_status) {
193+
Serial.println("error: accelerometer setup failed\n");
194+
while(1) ;
195+
}
196+
197+
// Init led to steady state (blue)
198+
leds.begin();
199+
for (int i = 0; i < 8; i++) {
200+
setPixel(i, BLUE);
201+
}
202+
203+
Serial.println("ready");
204+
}
205+
```
206+
207+
In the `loop()` function, a four steps process is executed as following:
208+
209+
1. **Significant movement detection:** The Nano Matter reads the accelerometer data searching for significant movement, if detected then the next step starts.
210+
2. **Accelerometer values reading:** The model input buffer is filled with new accelerometer values and normalized in the model format.
211+
3. **Inference run:** The inference is run, and its results are retrieved for visual feedback triggering.
212+
4. **Feedback results:** The inference results are analysed, printed in the Serial Monitor and reflected on the Modulino Pixels.
213+
214+
```arduino
215+
void loop() {
216+
acc_data_t *dst = (acc_data_t *) model_input->data.f;
217+
218+
// Wait until a significant movement is detected
219+
bool movementDetected = false;
220+
Serial.println("Waiting for significant movement...");
221+
222+
while (!movementDetected) {
223+
uint8_t acceleroStatus;
224+
acceleroStatus = imu.available();
225+
226+
// Proceed only if new data is available
227+
if (acceleroStatus == 1) {
228+
float acceleration[3];
229+
imu.update();
230+
231+
acceleration[0] = imu.getX();
232+
acceleration[1] = imu.getY();
233+
acceleration[2] = imu.getZ();
234+
235+
// Calculate the absolute sum of the acceleration components
236+
float absSum = fabs(acceleration[0]) + fabs(acceleration[1]) + fabs(acceleration[2]);
237+
238+
// If the movement exceeds the threshold, update the state
239+
if (absSum > 1.8) {
240+
movementDetected = true;
241+
Serial.println("Movement detected: start collecting data!");
242+
}
243+
}
244+
}
245+
246+
// Turn off leds when movement is detected
247+
for (int i = 0; i < 8; i++) {
248+
setPixel(i, OFF);
249+
}
250+
251+
// Get accelerometer values
252+
accelerometer_read(dst, input_length);
253+
254+
// Run inference
255+
TfLiteStatus invoke_status = interpreter->Invoke();
256+
257+
if (invoke_status == kTfLiteOk) {
258+
// Analyze the results to obtain a prediction
259+
const model_output_t *output = (const model_output_t *)interpreter->output(0)->data.f;
260+
261+
// Print inference results (Gesture, probability)
262+
int max_i = -1;
263+
float max_val = 0;
264+
for (int i = 0; i < GESTURE_COUNT; i++) {
265+
switch(i) {
266+
case WING_GESTURE: Serial.print("W"); break;
267+
case RING_GESTURE: Serial.print("O"); break;
268+
case NO_GESTURE: Serial.print("No gesture"); break;
269+
}
270+
Serial.print(": ");
271+
Serial.print(interpreter->output(0)->data.f[i]);
272+
Serial.println();
273+
if (output->gesture[i] > max_val) {
274+
max_val = output->gesture[i];
275+
max_i = i;
276+
}
277+
}
278+
279+
// Print the graphical representation of the recognized gesture
280+
if (max_val >= DETECTION_THRESHOLD) {
281+
switch(max_i) {
282+
case WING_GESTURE:
283+
Serial.println("detection = wing (W)");
284+
Serial.println("* *");
285+
Serial.println("* *");
286+
Serial.println("* * *");
287+
Serial.println(" * * * * ");
288+
Serial.println(" * * ");
289+
290+
// Green leds for W gesture
291+
for (int i = 0; i < 10; i++) {
292+
for (int j = 0; j < 8; j++) {
293+
setPixel(j, GREEN);
294+
}
295+
delay(200);
296+
297+
for (int j = 0; j < 8; j++) {
298+
setPixel(j, OFF);
299+
}
300+
delay(200);
301+
}
302+
break;
303+
case RING_GESTURE:
304+
Serial.println("detection = ring (O)");
305+
Serial.println(" ***** ");
306+
Serial.println(" * * ");
307+
Serial.println("* *");
308+
Serial.println(" * * ");
309+
Serial.println(" ***** ");
310+
311+
// Yellow leds for O gesture
312+
for (int i = 0; i < 10; i++) {
313+
for (int j = 0; j < 8; j++) {
314+
setPixel(j, YELLOW);
315+
}
316+
delay(200);
317+
318+
for (int j = 0; j < 8; j++) {
319+
setPixel(j, OFF);
320+
}
321+
delay(200);
322+
}
323+
break;
324+
case NO_GESTURE:
325+
Serial.println("No gesture");
326+
break;
327+
}
328+
}
329+
330+
// reset LEDs to steady state (blue)
331+
for (int i = 0; i < 8; i++) {
332+
setPixel(i, BLUE);
333+
}
334+
} else {
335+
printf("error: inference failed");
336+
}
337+
}
338+
```
339+
340+
In addition, some helper functions are used:
341+
342+
- `accelerometer_setup()`: This function initialize the Modulino Movement accelerometer and returns true if it did it successfully.
343+
- `accelerometer_read(acc_data_t* dst, int n)`: This function read the accelerometer data, normalize it and prepare the model input buffer.
344+
345+
346+
98347

99348

0 commit comments

Comments
 (0)