Learning Goals 5 min
Before you can train a model, you need labelled data. Today you stream accelerometer readings from the Nano 33 BLE Sense to your laptop, record N seconds of each gesture, and save labelled CSV files ready for tomorrow's training. By the end of this lesson you will:
- Write an Arduino sketch that streams IMU readings over Serial in CSV format.
- Use a laptop-side script (Python or a serial recorder app) to capture into files named by gesture label.
- Collect ~30 samples each of three gestures, balanced and varied.
Warm-Up 10 min
Hardware:
- Nano 33 BLE Sense (or any board with an MPU6050).
- USB cable to laptop.
- Python 3 on laptop, or Edge Impulse Studio (browser-based — easier).
Choose your three gestures
For the magic wand we'll build in L04-36: pick something distinct. Suggested:
- "wave" — back-and-forth swing.
- "punch" — quick straight thrust.
- "circle" — round motion.
New Concept · Streaming + recording 25 min
The capture sketch
// Streams IMU readings while a button is held.
// On press: print "BEGIN <label>" + 1.5 s of samples + "END".
#include <Arduino_LSM9DS1.h> // Nano 33 BLE Sense IMU
const int BTN_PIN = 2;
const int SAMPLE_HZ = 100; // 100 Hz
const int CAPTURE_MS = 1500; // 1.5 s
const char* LABEL = "wave"; // set this per session
bool wasButton = false;
void setup() {
Serial.begin(115200);
pinMode(BTN_PIN, INPUT_PULLUP);
while (!Serial);
if (!IMU.begin()) {
Serial.println("# IMU init failed");
while (true);
}
Serial.println("# Ready. Press button to capture a gesture.");
}
void loop() {
bool pressed = digitalRead(BTN_PIN) == LOW;
if (pressed && !wasButton) {
Serial.print("BEGIN "); Serial.println(LABEL);
unsigned long start = millis();
while (millis() - start < CAPTURE_MS) {
float ax, ay, az;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
Serial.print(ax); Serial.print(",");
Serial.print(ay); Serial.print(",");
Serial.println(az);
}
delay(1000 / SAMPLE_HZ);
}
Serial.println("END");
}
wasButton = pressed;
}The serial output for one gesture looks like:
BEGIN wave 0.12,0.05,9.78 0.20,0.10,9.75 ... 0.15,0.08,9.79 END
Recording to files (Python)
# capture.py (laptop)
import serial, sys
ser = serial.Serial("/dev/tty.usbmodem142101", 115200) # update port
label = sys.argv[1] if len(sys.argv) > 1 else "unknown"
trial = 0
while True:
line = ser.readline().decode().strip()
if line.startswith("BEGIN"):
trial += 1
filename = f"{label}_{trial:03d}.csv"
f = open(filename, "w")
f.write("ax,ay,az\n")
elif line == "END":
f.close()
print(f"saved {filename}")
elif line and not line.startswith("#"):
f.write(line + "\n")
Run: python capture.py wave. Press the button on the Nano 30 times, performing the gesture each time. You'll get wave_001.csv, wave_002.csv, ..., wave_030.csv.
Then change LABEL in the sketch (or pass via Serial), re-upload, and capture "punch_001..030" and "circle_001..030".
Quicker: Edge Impulse
Edge Impulse Studio (free for hobbyists) is a web app that handles the whole pipeline: capture from your Nano over USB, label, train, deploy. Use this if you want a polished workflow without writing Python. We'll use the manual workflow here to understand what's happening.
Worked Example · Record 30 of each 25 min
Step 1 — capture wave samples
- Set LABEL = "wave". Upload.
- Run capture.py with "wave" on the laptop.
- Hold the Nano. Press the button. Wave.
- Repeat 30 times with small variations.
Step 2 — punch samples
Update LABEL to "punch". Re-upload. Run capture.py with "punch". 30 punches.
Step 3 — circle samples
Same flow. 30 circles.
Step 4 — verify in Python
import pandas as pd
import glob, matplotlib.pyplot as plt
for f in glob.glob("wave_001.csv")[:1]:
df = pd.read_csv(f)
df.plot()
plt.savefig(f.replace(".csv", ".png"))Look at the plots. Wave should have an oscillating pattern; punch a single sharp peak; circle a smooth rotation. If they all look the same, your labels are wrong — re-capture.
Step 5 — augmentation (optional)
Take each captured gesture and shift it by ±50 ms, add small Gaussian noise, scale slightly. Generates 5× more training data from the same 30 real captures. Helps generalisation.
Step 6 — bundle for training
Tomorrow we'll feed all 90 CSVs into a neural network. Make sure files are organised in folders: data/wave/, data/punch/, data/circle/.
Try It Yourself 15 min
Goal: Add gyro readings (3 more channels) to each capture. More features → potentially more accuracy.
Goal: Have a classmate capture 30 of each gesture using YOUR Nano. Now you have 60 of each, from 2 people. Compare model performance with combined vs single-person training.
Goal: Sign up for Edge Impulse. Use their browser-based capture instead of Python. Same data, easier flow. Use whichever you prefer for the rest of Cluster F.
Mini-Challenge · 90 clean samples 10 min
- 30 × wave, 30 × punch, 30 × circle.
- All labelled, all readable as CSV.
- Plot one of each — confirm the shapes are different.
- Save as a zip / folder for tomorrow.
Recap 5 min
Training data = many labelled examples. Capture 1.5 s windows of accelerometer at 100 Hz. 30+ examples per gesture, varied across speed / angle / user. CSV per capture; folder per label. Tomorrow we feed this into TensorFlow.
- Window length
- Duration of one captured example. Should be slightly longer than the longest gesture you want to detect.
- Sample rate
- How many readings per second. 100 Hz is plenty for hand gestures.
- Data augmentation
- Synthetically expanding the dataset by transforming examples (noise, time-shift, scale). Cheaper than collecting more.
- Label folder structure
- Common convention:
data/<label>/<sample>.csv. Many ML libraries auto-load this. - Edge Impulse
- Browser-based no-code pipeline for capturing, training, deploying edge ML. Free for hobby use.
- CSV (Comma-Separated Values)
- Simplest data format. One row per sample, columns for each channel.
Homework 5 min
- Capture 90 clean samples (30 × 3 gestures).
- Plot one of each in matplotlib / Excel. Verify they look different.
- Read ahead to ARD-L04-34 (TensorFlow Lite Micro). Tomorrow we train.