How We Solved Industrial Velocity Drift for ProxControl
ProxControl's spray-paint velocity sensor drifted 15% within 30 seconds. We got it under 2% with zero-velocity updates and adaptive filtering. Here's the full technical breakdown.

15% drift in 30 seconds. That's how far ProxControl's velocity measurement wandered from reality when they first approached us. Their industrial spray-painting system needed to measure the speed of a handheld spray gun in real-time, and the IMU (inertial measurement unit) they were using accumulated errors faster than the painters could finish a pass.
We stabilized the measurement to under 2% drift across full painting sessions. Two algorithm phases, 4 weeks of engineering, and a lot of time staring at accelerometer data. Here's the full technical story.
Key Takeaways > - IMU-based velocity measurement drifts because accelerometer bias and noise get integrated over time, compounding errors exponentially. > - Zero-Velocity Updates (ZUPT) detect stationary moments and reset drift, but industrial environments need adaptive thresholds. > - Our two-phase approach reduced drift from 15% to under 2% while maintaining real-time performance on embedded hardware.
The Problem: Why IMU Velocity Drifts
An IMU measures acceleration. To get velocity, you integrate acceleration over time. To get position, you integrate velocity. Every integration step compounds errors.
Here's the math that makes this painful. An accelerometer with a bias of 0.01 m/s^2 (a good consumer-grade IMU) produces a velocity error of 0.01 * t meters per second, where t is time in seconds. After 30 seconds, that's 0.3 m/s of drift. After 60 seconds, 0.6 m/s. The error grows linearly with time for velocity and quadratically for position.
ProxControl's application made this worse. The spray gun vibrates constantly during operation. High-frequency vibration looks like noise on top of the actual acceleration signal. Standard low-pass filtering removes the vibration but also smears the real signal, making the measurement sluggish. Too little filtering and you get noise. Too much and you lose responsiveness.
The gun also experiences sudden direction changes. A painter sweeps left, stops, sweeps right. Each direction change creates a sharp acceleration spike followed by deceleration. The IMU needs to track these transitions accurately, not smooth them away.
Phase 1: Zero-Velocity Update (ZUPT) Detection
ZUPT is the standard approach for correcting IMU drift. The concept is simple: when the sensor is stationary, velocity should be zero. If the integrated velocity says 0.3 m/s while the device is clearly not moving, that 0.3 m/s is drift, and you reset it to zero.
The hard part is detecting "stationary" in a noisy industrial environment.
Standard ZUPT detection uses a threshold on the accelerometer magnitude. If |a| is close to gravity (9.81 m/s^2) and gyroscope readings are near zero, the device is probably stationary. This works well in controlled environments - laboratory settings, indoor navigation. It fails in a spray-paint booth.
Why it failed for ProxControl: The spray gun transmits mechanical vibration through the housing to the IMU. Even when the painter holds the gun still, the accelerometer reads micro-vibrations from the compressor and paint flow. Standard ZUPT thresholds couldn't distinguish "stationary with vibration" from "slow movement."
Our approach: adaptive ZUPT thresholds. Instead of a fixed threshold, we computed a running variance of the accelerometer signal over a sliding window. Stationary periods show consistent variance (vibration is roughly constant). Movement shows changing variance as acceleration ramps up and down.
The detection algorithm:
1. Compute the variance of accelerometer magnitude over a 200ms sliding window. 2. Compute the variance of the gyroscope signal over the same window. 3. If both variances fall below adaptive thresholds (calibrated per device during a 5-second startup hold), classify the window as stationary. 4. Apply ZUPT correction: reset integrated velocity to zero, update the Kalman filter state.
The adaptive calibration step was critical. Every spray gun vibrates differently depending on compressor pressure, paint viscosity, and nozzle type. A 5-second calibration at startup captures the device-specific noise floor and sets thresholds accordingly.
Phase 1 alone reduced drift from 15% to about 6%. Better, but not good enough. The remaining drift accumulated during long sweeping motions where the gun was never truly stationary.
Phase 2: Complementary Filtering and Motion Model
ZUPT only corrects drift during stationary moments. During continuous motion, drift still accumulates. For ProxControl, a painting sweep could last 3-8 seconds without a pause. That's enough time for significant drift buildup.
The insight: spray-painting follows predictable motion patterns. Sweeps are roughly linear. Speed is roughly constant during a sweep. Direction changes happen at the ends of sweeps. We could use these priors to constrain the velocity estimate.
Motion model constraints:
1. Velocity smoothness prior. Real human motion doesn't have discontinuities. If velocity jumps from 0.5 m/s to 0.8 m/s in one sample, that's likely noise. We applied a rate-of-change constraint on the velocity estimate, penalizing sudden changes that exceeded physiologically possible acceleration.
2. Sweep direction detection. We used the gyroscope to detect when the gun was moving in a consistent direction. During a detected sweep, we constrained the velocity vector to the dominant axis, reducing cross-axis drift. A painter sweeping left-to-right shouldn't accumulate velocity in the up-down direction.
3. Adaptive Kalman filter tuning. Standard Kalman filters use fixed process noise and measurement noise parameters. We varied these based on the detected motion state. During detected sweeps: trust the accelerometer more (lower measurement noise). During transitions: trust the model more (lower process noise). During stationary periods: apply ZUPT corrections.
The implementation used a 7-state extended Kalman filter (3D velocity, 3 accelerometer biases, and 1 gyroscope scale factor). The bias states allowed the filter to track and compensate for accelerometer drift in real-time, not just during ZUPT corrections.
Here's the contrarian take on this project: the actual algorithm was less complex than most academic papers on the topic. We didn't use deep learning for motion classification. We didn't implement a particle filter. We used well-understood signal processing techniques - Kalman filtering, variance-based detection, kinematic constraints - and tuned them obsessively for the specific use case. Simplicity won because it ran in real-time on embedded hardware with deterministic latency.
Validation: How We Proved It Works
Algorithm development without validation is science fiction. Here's how we validated the solution.
Ground truth system: We built a test rig with a linear rail and an optical encoder. The spray gun moved on the rail at known velocities (0.2 m/s to 1.5 m/s, covering the range of real painting speeds). The encoder gave us ground-truth velocity at 1kHz. We compared IMU-derived velocity against encoder velocity in real-time.
Test scenarios:
- Constant-speed sweeps at 5 different velocities - Accelerating and decelerating sweeps - Direction changes (sweep left, pause, sweep right) - Extended operation (10 minutes continuous) - Different vibration levels (simulated with varying compressor settings)
Results:
| Metric | Before (raw IMU) | Phase 1 (ZUPT) | Phase 2 (full system) | |---|---|---|---| | Mean velocity error | 15.2% | 5.8% | 1.7% | | Max velocity error | 28.1% | 12.3% | 4.2% | | Drift after 60s | 0.62 m/s | 0.19 m/s | 0.04 m/s | | Latency | 1ms | 3ms | 8ms |
The 8ms latency in the full system came from the sliding-window variance computation and Kalman filter update. For real-time spray painting feedback, anything under 20ms is imperceptible. ProxControl's requirement was 15ms, so we had headroom.
Extended operation test: We ran the system for 10 continuous minutes of simulated painting (alternating sweeps and pauses). Without correction, drift exceeded 40% by minute 3. With the full system, drift stayed under 2% throughout, with periodic ZUPT resets pulling it back to near-zero during pauses.
Implementation Details
The final system ran on a STM32 microcontroller (ARM Cortex-M4, 168 MHz). Constraints that shaped the implementation:
Memory: 256 KB RAM. The Kalman filter state and covariance matrices fit comfortably, but we couldn't store long signal histories. The sliding window was limited to 200ms (200 samples at 1kHz) to stay within memory budget.
Processing time: The entire filter update (ZUPT detection + Kalman update + motion constraints) had to complete within 1ms to maintain the 1kHz sample rate. We unrolled matrix operations and used fixed-point arithmetic for the critical path. Floating-point was only used for the bias estimation, which updated at 100Hz instead of 1kHz.
Calibration: The 5-second startup calibration computed noise floor statistics and stored them in flash memory. This meant the device remembered its calibration across power cycles. Recalibration was only needed if the gun hardware changed (new compressor, different nozzle).
Output: Velocity magnitude and direction, streamed over UART at 100Hz to ProxControl's display system. The display showed the painter real-time speed feedback so they could maintain consistent velocity across the painting surface.
What We Learned
Domain knowledge matters more than algorithm sophistication. Understanding how spray painters move - sweep patterns, pause durations, speed ranges - was more valuable than any signal processing textbook. The motion model constraints came from watching painters work, not from literature review.
Calibration eliminates half the problem. A 5-second per-device calibration removed the device-specific noise floor from the equation. Without calibration, we would have needed a much more complex adaptive system to handle device-to-device variation.
Validation infrastructure is half the project. The test rig with the optical encoder took a week to build. The algorithm development took 3 weeks. But without the test rig, we would have been tuning parameters blindly. Invest in validation tooling first.
Embedded constraints drive better algorithms. The memory and latency limits forced us to keep the solution simple. A PC-based implementation might have tempted us toward heavier approaches (particle filters, neural networks). The embedded constraints kept us honest.
Frequently Asked Questions
What IMU hardware did ProxControl use?
We worked with an InvenSense ICM-20948 9-axis IMU (accelerometer, gyroscope, magnetometer). It's a mid-range industrial IMU - not the cheapest option, but not survey-grade either. The noise characteristics were sufficient for the application with our filtering approach. A cheaper IMU would have required more aggressive filtering and reduced accuracy.
Can this approach work for other industrial measurement applications?
The ZUPT + motion model framework is generalizable. We've discussed applying similar techniques to handheld welding guns (measuring torch speed), CNC tool path verification, and construction equipment monitoring. The motion model constraints would change per application, but the ZUPT detection and adaptive Kalman filter transfer directly.
Why not use GPS or optical tracking instead of an IMU?
Indoor industrial environments make GPS unusable (no signal). Optical tracking (cameras) would require infrastructure installation in the spray booth and is degraded by paint mist and lighting conditions. IMU-based measurement is self-contained - no external infrastructure needed. The sensor mounts directly on the gun and works in any environment.
How long did this project take and what did it cost?
Four weeks of engineering across two algorithm phases. This falls under our deep-tech engineering service tier starting at EUR 20k. The scope included algorithm development, embedded implementation, validation testing, and documentation of the calibration and tuning procedures for ProxControl's engineering team to maintain.
*Have a signal processing or deep-tech challenge? Book a 30-minute call and we'll assess whether it's a fit for our engineering team. Or see our Deep-Tech Engineering service for the types of problems we solve.*
Notes on building fast.
One short email a month from the RalphNex team. Projects we shipped, ideas we tested, and what worked.
No spam. Unsubscribe anytime.

Dash Santosh
Founding Engineer
Co-founder and engineer at RalphNex. Been coding since 14, shipping fast since.
More from the RalphNex Journal

How We Set Up CI/CD for Every Client Project
Every project we ship gets the same CI/CD pipeline. It takes 4 hours to set up and saves 200+ hours over the project lifetime.

SaaS Development for Edtech: Building for Schools and Students
Schools buy software in June, onboard in August, and complain in September. Your edtech product needs to survive all three.
