SSIO trains inertial odometry from a robot's own unlabeled IMU stream, with no velocity or position labels. Trained from scratch on each of six embodiments (pedestrians, drones, quadrupeds, and a humanoid), SSIO (orange) is comparable to the supervised AirIO baseline (blue). A new embodiment needs only a calibrated IMU and recordings from normal operation.
- [Oct 2026] Initial code release: integration self-supervision, pose graph distillation, evaluation, and loaders for all six datasets in the paper.
SSIO trains in two steps, named as in the paper:
unlabeled IMU βββΊ 1. Integration Self-Supervision βββΊ 2. Pose Graph Distillation (optional)
train_ssio.py β "Ours" train_pgd.py β "Ours (w/ PGD)"
- Integration Self-Supervision (
train_ssio.py): the network learns body-frame velocity from an IMU integration teacher on short unlabeled windows, with optional Stationary Regularization on detected stops. - Pose Graph Distillation (
train_pgd.py, optional): a pose graph over each training sequence turns the network's predictions into sequence-consistent velocity targets, and the network is fine-tuned on them.
| Component | Requirement | Notes |
|---|---|---|
| Python | β₯ 3.10 | |
| PyTorch | < 2.14 | 2.14 breaks PyPose preintegration; tested with 2.5.1 |
| CUDA | Optional | Only for graph=sparse_imupgo (Linux, cuDSS); a GPU is recommended for full-scale training |
-
Create an environment
conda create -n ssio python=3.10 conda activate ssio
-
Install the remaining dependencies
pip install -r requirements.txt
Datasets are read from $DATA_ROOT; outputs go to $EXP_DIR (default: ./experiments).
export DATA_ROOT=/path/to/datasets| Dataset | Platform | Duration | Download | Path under $DATA_ROOT |
|---|---|---|---|---|
| TLIO | Pedestrian, head-mounted | 31.6 h | TLIO | TLIO/ |
| EuRoC | Micro aerial vehicle | 22.5 min | ETH ASL | EuRoC/<seq>/mav0/ |
| Blackbird | Racing drone | 5.0 h | Blackbird | BlackBird/ |
| MIPO | Unitree Go1 quadruped, 5 IMUs | 58.6 min | π€ mipo-go1-odometry | mipo_hdf5/indoor/ |
| GrandTour | ANYmal quadruped, outdoor | 5.65 h | GrandTour | grand_tour/ |
| Humanoid | Unitree G1 humanoid | 1.17 h | π€ humanoid-g1-odometry | humanoid_g1/ |
The Humanoid dataset is released with this work on Hugging Face, and so is MIPO, converted from the Cerberus 2.0 rosbags. Download them into the paths the configs read:
hf download Open-IO/humanoid-g1-odometry --repo-type dataset \
--local-dir $DATA_ROOT/humanoid_g1
hf download Open-IO/mipo-go1-odometry --repo-type dataset \
--local-dir $DATA_ROOT/mipo_hdf5The train, validation, and test splits in Config/dataset/ match the
Hugging Face checkpoints.
-
GrandTour configs read per-IMU IMU-to-GT time offsets from
$DATA_ROOT/grand_tour/anymal_imu_gt_offsets_per_imu.json, our calibration (not part of the GrandTour release) and the one the GrandTour checkpoints were trained with. Download it:hf download Open-IO/SSIO calibration/grand_tour/anymal_imu_gt_offsets_per_imu.json --local-dir /tmp/ssio cp /tmp/ssio/calibration/grand_tour/anymal_imu_gt_offsets_per_imu.json $DATA_ROOT/grand_tour/or regenerate it with
python Scripts/measure_anymal_imu_gt_offsets_per_imu.py --data-root $DATA_ROOT/grand_tour --out-json $DATA_ROOT/grand_tour/anymal_imu_gt_offsets_per_imu.json. -
MIPO is read from per-trial HDF5 files plus the
mipo_time_offsets.jsoncalibration file, both in the Hugging Face dataset (format:Doc/mipo_hdf5_format.md). The rosbag-to-HDF5 converter is not included in this repository.
The repository ships a 10 s slice of EuRoC MH_02_easy under tests/fixtures/. Run the
three steps below from the repository root.
python train_ssio.py network=airio_imu_only \
'hydra.searchpath=[file://tests/fixtures/config]' \
dataset@train_dataset=EuRoC_Smoke_Train dataset@eval_dataset=EuRoC_Smoke_Valid \
train.epoch=1 train.batch=4 train.max_train_steps=2 train.dv_imu_input=raw \
'meta.device=${as_device:cpu}' wandb.mode=disabled \
hydra.run.dir=./experiments/quickstart/ssiopython train_pgd.py network=airio_imu_only graph=dense_imupgo \
'hydra.searchpath=[file://tests/fixtures/config]' \
dataset@train_dataset=EuRoC_Smoke_Train dataset@eval_dataset=EuRoC_Smoke_Valid \
train.epoch=1 train.batch=4 train.max_train_steps=2 train.dv_imu_input=raw \
train.network_factor_sigma=1.0 \
train.resume_from=file:./experiments/quickstart/ssio/checkpoint_00002.pt \
'meta.device=${as_device:cpu}' wandb.mode=disabled \
hydra.run.dir=./experiments/quickstart/pgdpython evaluate.py ./experiments/quickstart/pgdMetrics and plots are written to experiments/quickstart/pgd/eval/checkpoint_00001/:
result.json (per sequence), summary.json (aggregate), and trajectory, velocity,
translation, and rotation plots for each sequence.
Config/experiment/<dataset>/ holds one recipe per paper experiment, with the
hyperparameters of the matching Hugging Face checkpoint. <dataset> is one of tlio,
euroc, blackbird, mipo, grandtour, or humanoid. Doc/recipes.md
lists every recipe with its command, Hugging Face run, and paper and checkpoint scores.
| Recipe | Paper row | Entry point |
|---|---|---|
airio |
AirIOβ (supervised); add network=airio for AirIO |
train_ssio.py |
ssio |
Ours | train_ssio.py |
ssio_pgd |
Ours (w/ PGD) | train_pgd.py |
airio_proprio, ssio_proprio |
AirIOβ + Proprio, Ours + Proprio (MIPO, GrandTour, Humanoid) | train_ssio.py |
python train_ssio.py +experiment=euroc/ssio
python train_pgd.py +experiment=euroc/ssio_pgd train.resume_from=file:<SSIO_RUN>/best_model.pt
python evaluate.py <RUN_DIR> --checkpoint best_model.pt --data Config/dataset/EuRoC_Test.yamlEach run writes a directory under $EXP_DIR with .hydra/config.yaml, checkpoints, and
best_model.pt; <SSIO_RUN> and <RUN_DIR> refer to it. Each recipe's header names its
Hugging Face run and that run's test AVE / ATE. Command-line overrides still take
precedence, e.g. +experiment=euroc/airio network=airio.
Note
The released "Ours (w/ PGD)" checkpoints do not match the paper's PGD row exactly; see
Doc/recipes.md for both numbers.
To train without a recipe, or to change the method, see Doc/training.md.
Every model in the paper's tables is on Hugging Face in
Open-IO/SSIO, one run directory per cell
(best_model.pt plus the training run's .hydra/ config). AirIO and AirIOβ are the
supervised baselines; the other columns are self-supervised.
| Dataset | AirIO | AirIOβ | Ours | Ours (w/ PGD) | AirIOβ + Proprio | Ours + Proprio |
|---|---|---|---|---|---|---|
| TLIO | q1k41m3r | 8pr646j5 | ev7hukzs | udfvlewc | β | β |
| EuRoC | n50vs978 | ncntl12t | 189uubgg | u0ej57z3 | β | β |
| Blackbird | 4ktbuoar | dvilntqz | fpza91ml | mks34gjg | β | β |
| MIPO | vqbya7lg | huamd5dg | lwr3ayev | 6059c2fr | 4ch9v4hx | zueppy3z |
| GrandTour | oyor793p | ghgafmbs | 3dqmg13r | gp86b7p6 | cdw3gb3x | cl7pny4l |
| Humanoid | c3qimksm | 0102al6c | rt7jor4e | r0vtbp2o | lo9hemti | 9gxajylc |
Download a run and evaluate it on its test split:
hf download Open-IO/SSIO --include "stage1/189uubgg/*" --local-dir ckpts
python evaluate.py ckpts/stage1/189uubgg --checkpoint best_model.pt --data Config/dataset/EuRoC_Test.yamlDoc/recipes.md lists each run's test split, its paper and checkpoint
scores, and the recipe that retrains it.
Evaluate a finished run by passing its Hydra output directory:
python evaluate.py <RUN_DIR>
python evaluate.py <RUN_DIR> --checkpoint best_model.pt --data Config/dataset/TLIO_Test.yamlBy default the evaluator loads .hydra/config.yaml and the highest-step
checkpoint_*.pt, and runs the run's validation split; --data evaluates on another
dataset configuration. The released Hugging Face runs store their splits under
val_dataset / test_dataset, so always pass --data for them, as above. It reports,
per sequence and in aggregate:
- AVE: average velocity error (m/s), the primary metric.
- ATE: absolute trajectory error, RMSE of the position error (m).
- Ξv error at 1, 2, and 10 s, and RVTE / nRVTE at 1, 2, and 5 s, which are invariant to a constant velocity offset.
Doc/recipes.md: every paper recipe with its command, Hugging Face run, and scoresDoc/training.md: training without a recipe, options, proprioceptive input, use of reference data, and code structureDoc/troubleshooting.md: environment, data errors, and testsDoc/debias.md: IMU bias calibrationDoc/mipo_hdf5_format.md: MIPO HDF5 format
SSIO builds on AirIO, AirIMU, and PyPose. We thank the TLIO, EuRoC, Blackbird, MIPO, and GrandTour datasets, and MAC-VO and SuperOdom, which generated ground truth for our data.
If you find this work useful, please cite:
@misc{qiu2026ssioselfsupervisedinertialodometry,
title={SSIO: Self-Supervised Inertial Odometry},
author={Yuheng Qiu and Yutian Chen and Carlton Cort and Weison Huang and Shibo Zhao and Pieter Abbeel and Rocky Duan and Koushil Sreenath and C. Karen Liu and Carmelo Sferrazza and Junyi Geng and Sebastian Scherer and Guanya Shi},
year={2026},
eprint={2610.25988},
archivePrefix={arXiv},
primaryClass={cs.RO},
url={https://arxiv.org/abs/2610.25988},
}
