Time-Series Classification: 1D CNN on Sensor Data
Design and size a 1D CNN for sensor time-series classification on Cortex-M: convolution math, MAC/memory budget, quantization, and windowing pitfalls.
Contents & prerequisites
Time-series classification is the workhorse task behind activity recognition, fall detection, predictive maintenance (bearing/vibration fault classes), gesture recognition, and ECG/PPG arrhythmia flagging — all running on Cortex-M class MCUs with a few hundred kB of RAM. A 1D CNN is usually the right architecture for this: it exploits local temporal correlation the way a 2D CNN exploits spatial correlation in images, it is cheap in MACs compared to LSTMs/GRUs, and it maps directly onto CMSIS-NN/TFLite Micro kernels that are already optimized for Cortex-M SIMD and NPUs like Ethos-U. This article covers the math of 1D convolution over sensor windows, how to size a network for an MCU budget, and a worked example classifying IMU windows into activity classes.
Why 1D CNN Over RNN/LSTM for Embedded Time Series
| Property | 1D CNN | LSTM/GRU |
|---|---|---|
| Compute per inference | O(kernel·channels·length), fully parallel | Sequential, O(4·hidden²) per step, recurrent |
| Latency on MCU | Deterministic, vectorizable (CMSIS-NN arm_convolve) | Poor SIMD utilization, gate nonlinearities per step |
| Memory | Weights only; no persistent hidden state across long sequences | Hidden/cell state must persist; harder to quantize well |
| Receptive field | Grows with depth/dilation — must be engineered | Naturally unbounded (in theory) |
| Typical accuracy on fixed-length windows (HAR, KWS-like) | Matches or beats RNNs when window ≤ a few seconds | Better for variable-length / long-range dependencies |
For fixed-length sensor windows (e.g., 1–4 s of accelerometer/gyro data, or a vibration FFT frame), a 1D CNN is almost always the better MCU choice: it quantizes cleanly to INT8, runs on CMSIS-NN or an NPU accelerator, and has no sequential data dependency that stalls a pipeline.
The 1D Convolution Operation
Given an input sequence x[n] with C_in channels (e.g., 6 channels for a 3-axis accel + 3-axis gyro), a 1D conv layer with kernel size K, C_out filters, stride S, applies:
y[c_out, n] = Σ_{c_in=0}^{C_in-1} Σ_{k=0}^{K-1} w[c_out, c_in, k] · x[c_in, n·S + k] + b[c_out]
This differs from a 2D image conv only in that the sliding window moves along one axis (time) instead of two (height/width). Each filter learns a temporal pattern — e.g., the shape of a single footstep acceleration spike, or a fault-induced vibration transient — and slides it across the whole window, producing a feature map whose length is:
L_out = floor((L_in − K) / S) + 1 (no padding)
L_out = floor((L_in + 2·P − K) / S) + 1 (with padding P)
MAC count per conv layer:
MACs = C_in · C_out · K · L_out
This is the number you multiply against your MCU's MACs/cycle (e.g., 1 for plain Cortex-M4 DSP extension, 4–16 for Cortex-M55 + Ethos-U55 offload) to estimate layer latency.
Typical Architecture for Sensor Windows
A practical MCU-sized 1D CNN for activity/vibration classification:
Input: [C_in=6, L=128] (e.g., 6-axis IMU, 128 samples @ 64 Hz ≈ 2 s)
Conv1D: 16 filters, K=5, stride=1, ReLU -> [16, 124]
MaxPool1D: pool=2 -> [16, 62]
Conv1D: 32 filters, K=5, stride=1, ReLU -> [32, 58]
MaxPool1D: pool=2 -> [32, 29]
Conv1D: 32 filters, K=3, stride=1, ReLU -> [32, 27]
GlobalAveragePool1D -> [32]
Dense: 32 -> num_classes, Softmax
Design choices that matter for embedded deployment:
- Global average pooling instead of Flatten+Dense on the last feature map — removes a large, position-sensitive dense layer and makes the network robust to small time shifts in the window (the sensor event doesn't always start at sample 0).
- Stride/pooling instead of deep stacks — MCU SRAM is the binding constraint, not depth; two or three conv layers with pooling usually saturate accuracy for windowed sensor classification.
- Small kernels (K=3–7) — sensor transients (footsteps, gear mesh impacts) are short; large receptive fields are better built by stacking layers or using dilation than by using huge kernels.
- Depthwise-separable variants (depthwise conv along time + pointwise 1×1 across channels) cut MACs substantially when C_in is large (e.g., 8+ sensor channels), at a small accuracy cost — same trick as MobileNet, applied along the time axis.
Sizing the Network to an MCU Budget
Worked example: STM32-class Cortex-M4 @ 80 MHz, 128 kB RAM budget for tensors/scratch, no NPU offload, running INT8 CMSIS-NN kernels at roughly 1 MAC/cycle after DSP extension utilization.
Using the architecture above, C_in=6, L=128:
Layer 1: MACs = 6 · 16 · 5 · 124 = 59,520 Layer 2: MACs = 16 · 32 · 5 · 58 = 148,480 Layer 3: MACs = 32 · 32 · 3 · 27 = 82,944 Dense: MACs = 32 · num_classes (say 6 classes) = 192
Total ≈ 291,136 MACs/inference
At ~1 MAC/cycle: 291,136 cycles ÷ 80 MHz ≈ 3.6 ms per inference. For a classifier running once per 2 s window, that's <0.2% CPU duty cycle — comfortably real-time with large headroom for the rest of the application (sensor fusion, BLE stack, etc.).
Memory check:
- Weights: (6·16·5) + (16·32·5) + (32·32·3) + (32·6) ≈ 480 + 2560 + 3072 + 192 = 6,304 params → ~6.3 kB at INT8, negligible.
- Largest activation buffer: layer-1 output [16, 124] at INT8 = 1,984 bytes; input buffer [6,128] = 768 bytes. Peak scratch, including double-buffering for in-place-friendly ops, stays under ~8 kB.
Both compute and memory fit comfortably inside a 128 kB RAM / 80 MHz M4 budget — this workload is dominated by conv layer 2, so if a further speed cut is needed, reducing filters there (32→24) or switching to depthwise-separable convs there gives the biggest return.
Windowing and Labeling — the Part That Breaks in Practice
The CNN math is straightforward; most real-world accuracy loss comes from windowing choices upstream:
- Window length vs. class dynamics: must be long enough to contain at least one full cycle of the pattern (e.g., one gait cycle ≈ 1–1.2 s at typical walking cadence) but short enough to bound latency and keep the class boundary crisp during transitions.
- Overlap (sliding window with 50% overlap is typical): increases training data and smooths decision boundaries across window edges, at the cost of ~2× inference rate.
- Normalization stats computed on-device: if per-window mean/std normalization is used at inference, it must match exactly what training used (same axis reduction, same epsilon), or accuracy silently degrades — this is a common silent bug in TFLite Micro deployment pipelines.
- Sensor sampling jitter: MCU ADC/IMU sampling isn't perfectly periodic; training data collected on a PC-tethered logger with different jitter characteristics than the final firmware's DMA-driven sampling can create a train/deploy distribution shift that no amount of model tuning fixes.
Quantization and Deployment Path
The standard pipeline (see the TFLite Micro Workflow and Post-Training Quantization articles for depth) applies directly:
- Train in float32, verify accuracy on held-out windows.
- Post-training INT8 quantization (or QAT if the float→INT8 accuracy drop exceeds ~1–2 points, which is more common in time-series nets than vision nets because activation dynamic range per channel can vary sharply between quiet and event windows).
- Convert to a
.tfliteflatbuffer, deploy via TFLite Micro or compile straight to CMSIS-NN calls. - Validate that the interpreter's chosen kernel (e.g.,
arm_convolve_s8) matches the expected MACs/cycle assumption used in the sizing above — im2col-based fallbacks on unsupported shapes can be 2–4× slower than the optimized direct-conv path.
Key Takeaways
- 1D CNNs slide a temporal kernel across sensor channels; MACs per layer =
C_in · C_out · K · L_out, and this number drives both latency and memory sizing before any code is written. - They beat LSTM/GRU on MCUs for fixed-length windows because they're fully parallel, SIMD/NPU-friendly, and quantize cleanly to INT8.
- Global average pooling before the final dense layer improves shift-robustness and shrinks parameter count versus Flatten+Dense.
- A 3-layer, ~6k-parameter network classifying 2 s / 6-channel IMU windows fits comfortably in ~3.6 ms at 80 MHz and ~8 kB scratch RAM on a plain Cortex-M4 — no NPU required for this class of problem.
- Most accuracy loss in deployed systems comes from windowing/normalization mismatches between training and firmware, not from the CNN architecture itself — verify these first when accuracy drops after deployment.
Learning
Sign in to track your progress.
Evidence
Public projects engineers linked to Time-Series Classification: 1D CNN on Sensor Data.
No engineer has linked a project to this topic yet. Built something that proves it? Add the project and tag it with embedded-systems-time-series-classification-1d-cnn-on-sensor-data — it then shows here and on your public profile.
