TARAF: GPS-Free Pedestrian Navigation
A foot-mounted embedded sensing platform that estimates pedestrian position without GPS using IMU data, state estimation, and computer vision ground truth.
Problem
Outdoor positioning systems depend heavily on GPS, which fails or degrades in dense urban environments, indoor spaces, and difficult terrain. For pedestrian navigation in these conditions, an alternative that works purely from on-body sensors is needed.
The core difficulty is that inertial measurement units — accelerometers and gyroscopes — accumulate error rapidly. Raw IMU integration produces position estimates that drift by meters per minute. Any useful system needs to detect and suppress this drift without access to external reference signals.
System overview
TARAF is a foot-mounted pedestrian navigation system. Placing the sensor at the foot is a deliberate choice: during each walking stride, the foot passes through a brief stationary phase (the Zero Velocity Phase) when it is flat on the ground. This predictable near-zero-velocity moment provides a correction opportunity that hip- or wrist-mounted sensors do not have.
The system has two main components: an embedded data collection and estimation platform, and an offline validation and ML-assisted tuning pipeline.
What I built
Embedded platform (ESP32 + BNO055)
The foot unit uses an ESP32 microcontroller paired with a BNO055 IMU. The BNO055 outputs calibrated accelerometer, gyroscope, and magnetometer data over I²C. The ESP32 handles sensor polling, timestamping, onboard filtering, real-time estimator logic, and data logging to SD card or serial transmission for offline analysis.
Calibration
Raw BNO055 data requires careful calibration before use. The system includes a calibration routine that characterizes accelerometer bias and scale, gyroscope bias at rest, and magnetometer interference from nearby electronics. Without accurate bias removal, ZUPT corrections cannot suppress drift effectively.
Filtering
A low-pass filter is applied to raw accelerometer data before integration to suppress high-frequency noise. Filter cutoff frequency was selected empirically based on the frequency content of walking accelerations versus noise floor.
Stride detection and ZUPT/ZARU
Zero Velocity Update (ZUPT) and Zero Angular Rate Update (ZARU) are the primary drift-suppression mechanisms. The system detects stride boundaries using a combination of acceleration magnitude threshold and gyroscope norm. During the detected zero-velocity phase, the estimator injects a velocity measurement of zero — forcing the filter to correct accumulated velocity error at each step.
State estimation (ESKF)
The core estimator is an Error-State Kalman Filter (ESKF). This filter maintains the nominal state (position, velocity, orientation) and models the error state separately. This structure is well-suited to orientation estimation because it allows the filter to operate on small error quantities rather than tracking the full nonlinear dynamics directly.
The ESKF integrates IMU measurements to propagate state forward, then applies ZUPT/ZARU corrections during zero-velocity phases. Quaternion orientation representation is used throughout to avoid gimbal lock.
Computer vision ground truth
To validate estimated trajectories, I built an external ground truth system using a calibrated overhead camera and ArUco marker tracking via OpenCV. The foot unit carries a printed ArUco marker. Camera-derived position is compared to IMU-estimated position across the same trajectory to measure drift and estimator accuracy.
This validation approach avoids the need for a motion capture system and can be deployed in any room with a ceiling camera mounting point.
Offline ML-assisted tuning
Different movement patterns (slow walking, fast walking, turning, stair climbing) produce different IMU signal characteristics. A single fixed set of estimator parameters does not generalize well across all conditions.
The offline component trains Random Forest and SVM classifiers on labeled IMU segments to predict movement type, then selects estimator configurations tuned for each movement class. This allows the system to adapt parameter sets without real-time feedback.
Technical details
| Parameter | Value |
|---|---|
| Microcontroller | ESP32 (240 MHz, dual-core) |
| IMU | BNO055 (9-DoF fusion capable) |
| Communication | I²C at 400 kHz |
| Sampling rate | 100 Hz |
| Estimator | Error-State Kalman Filter |
| Drift correction | ZUPT + ZARU |
| Orientation | Quaternion (no gimbal lock) |
| Ground truth | ArUco + OpenCV overhead camera |
| Offline ML | Random Forest, SVM (scikit-learn) |
| Languages | C/C++ (firmware), Python (analysis) |
Validation results
Results section — to be updated as testing progresses.
Current status: ZUPT-corrected trajectories show significantly reduced velocity error compared to raw integration. Quantitative drift measurements against ArUco ground truth are in progress.
What I learned
Calibration matters more than the filter. Spending time properly characterizing IMU biases before implementing any estimation logic made a larger difference than filter parameter tuning. An uncalibrated sensor running an ESKF is worse than a calibrated sensor running a basic complementary filter.
ZUPT stride detection is sensitive to threshold parameters, and walking speed changes the signal profile enough that a fixed threshold works poorly at the extremes of pace. The ML-assisted configuration selection is a practical response to this.
The ArUco ground truth system is simpler to set up than expected but requires careful camera calibration and marker rigidity — a flexible marker mount introduces its own orientation error.
What I would do differently
Start with a stationary calibration test before writing any motion code. I lost time early on debugging what turned out to be gyroscope bias, not filter logic.
Build a data replay system from the beginning. Being able to re-run the estimator on logged data without re-recording accelerates iteration considerably.
Links
- Codeberg repository (update when public)
- Design document (placeholder — add PDF to public/docs/)