
Rep Counting From Accelerometer Data

Accelerometers are everywhere. Every smartphone, smartwatch, and fitness band ships with one. Turning that raw signal into a meaningful rep count sounds simple. It rarely is.
What the raw signal actually looks like
A triaxial accelerometer gives you three time-series: acceleration along X, Y, and Z axes, sampled at anywhere from 25 Hz to 200 Hz depending on the device and power mode. During a bicep curl, the dominant motion is roughly sinusoidal along one axis, but you also get noise from grip shifts, breathing, and sensor orientation drift.
The first thing most engineers do is compute the magnitude vector:
magnitude = sqrt(ax² + ay² + az²)
This collapses three channels into one and makes the signal orientation-agnostic. Gravity contributes a constant ~9.81 m/s² offset. You subtract that out with a high-pass filter before doing anything useful.
Filtering choices matter more than the algorithm
A 4th-order Butterworth low-pass filter with a 5 Hz cutoff is a reasonable starting point for most gym movements. Human repetitive motion sits between 0.3 Hz and 3 Hz. Anything above that is noise. If you set the cutoff too low, you round off the peaks and your counter under-counts. Too high, and noise creates phantom peaks.
For real-time on-device inference, IIR filters are cheaper than FIR. A biquad cascade of two second-order sections gives you the 4th-order response without accumulating floating-point error the way a direct-form implementation does.
How do you actually detect a single rep?
Peak detection is the obvious answer. Find local maxima above a threshold and count them.
It works. It also fails constantly in practice.
The threshold problem is the main one. A fatigued user's rep amplitude drops 20-30% by the end of a set. A fixed threshold misses those reps. An adaptive threshold that tracks a rolling maximum is better, but it has latency. You're always chasing the signal.
Zero-crossing detection is the alternative. Count how many times the filtered signal crosses its mean. Divide by two. That gives you reps. It handles amplitude variation better than peak detection, but it's sensitive to signal drift and to partial reps where the user pauses mid-movement.
Autocorrelation for period estimation
If you buffer a few seconds of signal, autocorrelation tells you the dominant period of the motion. This is more stable than per-peak detection and works well for exercises with a consistent cadence. The lag at the first significant autocorrelation peak gives you the rep duration. You can then use that estimate to gate your peak or crossing detector so it ignores events that arrive too close together.
The downside is latency. You need enough signal to form a reliable autocorrelation estimate, typically 2-4 seconds. For a user doing 10-second sets, that's a meaningful delay before the count starts.
Template matching
Record one clean rep, cross-correlate it against the incoming signal in a sliding window. Every time correlation exceeds a threshold, increment the counter. This is exercise-specific and requires per-user calibration, but it's precise. It also handles asymmetric movements (like a pull-up, where the up-phase and down-phase have different durations) better than zero-crossing.
The memory and compute cost is low enough to run on a microcontroller. A 200-sample template at 50 Hz is 4 KB of RAM. That's fine.
/// Not sure where to start?
Get the architecture before you commit
Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.
When should you use a machine learning model instead?
When the exercise library grows beyond a handful of movements, rule-based detectors become hard to maintain. Each exercise needs its own filter cutoffs, thresholds, and axis mappings. That's manageable for five exercises. For fifty, it's a maintenance problem.
A 1D CNN trained on labelled accelerometer windows handles multi-exercise classification and rep counting in one pass. The architecture is straightforward: three or four convolutional layers with ReLU activations, global average pooling, and a softmax head for exercise classification plus a regression head for rep count. Input window size is typically 2-4 seconds with 50% overlap.
Training data is the bottleneck. You need hundreds of labelled sessions per exercise, with variation in sensor placement, body size, and movement quality. Collecting that cleanly (with a reference count you trust) is harder than it sounds. Video annotation plus manual review is the standard approach, and it's slow.
| Approach | Latency | Accuracy on seen exercises | New exercise cost |
|---|---|---|---|
| Peak detection | Real-time | Moderate | Low (tune threshold) |
| Autocorrelation | 2-4 s | Good | Low |
| Template matching | Real-time | High | Medium (one calibration rep) |
| 1D CNN | 0.5-1 s | High | High (relabel and retrain) |
The CNN wins on accuracy at scale. The rule-based methods win when you're shipping quickly, have limited training data, or are targeting a narrow exercise set.
What breaks in production?
Sensor placement variability is the biggest one. A wrist-worn device held in a pronated grip behaves differently from the same device in a supinated grip. Normalisation helps, but it doesn't fully solve it.
Exercise confusion is real. A dumbbell row and a bent-over lateral raise have similar magnitude profiles. Without an explicit exercise classifier upstream (user-selected or inferred), your rep counter will misfire.
Transition detection is underrated. The user picks up the weight, adjusts their grip, takes a breath. All of that generates signal. Your detector needs a way to identify "this is a rep" vs. "this is preamble." A minimum duration gate (ignore anything shorter than 0.5 s) handles most cases.
Battery and sampling rate are in tension. At 200 Hz you get a clean signal. At 25 Hz (more common in low-power wearables) you lose resolution on fast movements like box jumps. If you're building for a wearable, test at the actual hardware sampling rate, not on a phone dataset.
How do you validate the counter before shipping?
You need a ground truth. Video is the most common reference. Record a set, count manually, compare. For internal testing, a simple Python script using OpenCV's background subtraction or MediaPipe Pose can automate that count if lighting is controlled.
Target metrics worth tracking: mean absolute error (MAE) per set, percentage of sets within ±1 rep, and false positive rate (reps counted during non-exercise periods). An MAE below 1.0 rep per set across your exercise library is a reasonable bar for a consumer product.
Test edge cases explicitly: partial reps, paused reps, very slow reps (>4 s), very fast reps (<0.8 s), and sets of fewer than 5 reps.
Conclusion
Rep counting from an accelerometer is a solved problem at the simple end and an ongoing one at the production end. Peak detection or autocorrelation will get you to a demo. Getting below 1-rep MAE across a wide exercise library, on real hardware, with real users, takes careful filter design, honest edge-case testing, and usually some form of learned component.
If you're building a fitness feature and unsure whether to start with rule-based or ML-first, start with autocorrelation and template matching for your first five exercises. Add a CNN when you hit ten or more exercises and have enough labelled data to train it properly. The incremental complexity is only worth it at that point.
FAQ
Can you count reps without knowing which exercise the user is doing? You can, but accuracy drops significantly. Exercises with similar movement frequencies and magnitudes (like a shoulder press and a bicep curl) are easy to confuse at the signal level. A user-selected exercise mode, or a classifier running in parallel, makes the rep counter substantially more reliable.
What sampling rate is sufficient for rep counting? 25 Hz is workable for most strength exercises where peak rep duration is above 1 second. For fast plyometric movements or box jumps, 50 Hz gives you more headroom. 200 Hz is overkill for counting; it's useful for biomechanical analysis but wastes power on a wearable.
How much training data does a CNN-based rep counter need? As a rough baseline, 500 labelled sets per exercise, spread across at least 30 different users, gives you enough variation to generalise reasonably. Less than that and the model will overfit to your data collection protocol. More is always better, but returns diminish past a few thousand sessions.
Why does the rep counter over-count at the end of a set? Usually an adaptive threshold problem. As the user fatigues, rep amplitude decreases. If your threshold adapts too aggressively downward, noise during the rest phase at the end of the set looks like reps. Adding a minimum inter-rep interval check (at least 0.5 s between detected reps) reduces this significantly.
Is on-device processing better than sending data to a server? For rep counting, yes. The latency of sending raw accelerometer data to a server and receiving a count back is typically 100-400 ms on a mobile network, which feels wrong to users. The algorithms are light enough to run on an ARM Cortex-M4 at 64 MHz with power to spare. Keep it on-device.
Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.
/// Work with us
Talk to the engineers who'd build it
You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.
