Implementation for a differentiable particle filter for SE(3) robot localization using a 3D LIDAR. Multiple modules are available for the observation model, motion model and resampling strategies.
Motion models - dynamics.py:
- Odometry only model with
OdometrySE3Config - RealNVP from TODO: add citation with
RealNVPConfig - Anchored-Ensembles RealNVP model with
EnsembleConfig - Anchored-Ensembles MLP model with
MLPConfig
Observation models - obs.py:
- Pillar-Encoder based with
PPObserverConfig(both with and without pooling) - Neural Signed Distance Function based with
SDFOBSModuleConfig
Resampling strategies - resample.py:
- Contraction-based with
ContractResampleConfig - Gumbel Softmax with
GSResampleConfig - Optimal-Transport based with
OTResampleConfig
This project also contains an ICP-based SLAM implementation to generate reference trajectories that is used to generate dataset labels from raw ROS2 recordings.
- Install uv
- Create a virtual environment with
uv venv - Install dependencies with
uv sync - Download the training data TODO: remove data as it is probably not double blind
mkdir -p data
# Original Jackal Data
curl -L "https://drive.usercontent.google.com/download?id=1Qjf40SJPy6MPzpXJtT0DS3HKSPe45upO&confirm=t" | tar -xf - -C data
# TUM RoboRacer/F1TENTH Data
curl -L "https://drive.usercontent.google.com/download?id=10_hjd2Vy9NJ_pMslh-IomGywuB03HzpH&confirm=t" | tar -xf - -C data
# ETF Jackal Data
curl -L "https://drive.usercontent.google.com/download?id=1L84A4_gUZ6srrGiLp1a5TU74fSg3WYXQ&confirm=t" | tar -xzf - -C data
# ETH Mocap Environment internal Jackal Rosbags and Mocap GT Data
curl -L "https://drive.usercontent.google.com/download?id=1i8AJ7bGfrunJ_BA5_6G7-PX1CuRp3E66&confirm=t" | tar -xzf - -C data
# ETH ETF Floor F Jackal Data, split by north/south/central parts of the Hallway
curl -L "https://drive.usercontent.google.com/download?id=1cDQCbpdNvW4I7W7FuSAbRUjC6YXfC_CZ&confirm=t" | tar -xzf - -C data
# ETH ETZ Loading Dock Data
curl -L "https://drive.usercontent.google.com/download?id=1CXaehPlUjAxOP4HAczBgeNHD3FBdAG4w&confirm=t" | tar -xzf - -C data
# TUM RoboRacer/F1TENTH Data; Prepared and Split for Training, Eval and Testing
curl -L "https://drive.usercontent.google.com/download?id=1b9irVhwQQwqLVRQTrr_YfmU_B0Ko_ZWj&confirm=t" | tar -xzf - -C data
- Run a script with
uv run python main.py
- Record data with ROS 2
ros2 bag record /livox/lidar /odom
-
Create a folder if not there yet and place ROSbag inside ~/Diff-PF/data
-
You can also create already a map
cd ~/diff-slam-main
uv run python -m diff_slam.build_map --bag data/<ROSbag name> --output map.pt --lidar-topic /livox/lidar --odometry-topic /odom
Train the observation model on your dataset (obs_sdf for the SDF model, obs_pp for the pillar model). Datasets use the data/<name>,<bag1>,<bag2>,... format:
uv run python -m diff_slam.obs obs_sdf --train.train-ds "data/<dataset>,<bag1>,<bag2>"
The checkpoint is written to models/<save-tag>/model.pt.
To train a motion model on you dataset, similarly ues the following:
uv run python -m diff_slam.dynamics --train.train-ds "data/<dataset>,<bag1>,<bag2>" model:mlp-config
Train the end-to-end DPF, wiring in the pretrained observation model and a motion-model architecture (odometry-se3, realnvp-se3, ensemble-se3, or mlp-se3):
uv run python -m diff_slam.dpf --train.obs-model models/<save-tag>/model.pt --train.motion-arch mlp-se3 --train.train-ds "data/<dataset>,<bag1>"
You can specifiy the motion model checkpoint as follows:
uv run python -m diff_slam.dpf --train.obs-model models/<save-tag>/model.pt --train.motion models/<save-tag>/model.pt --train.train-ds "data/<dataset>,<bag1>"
The checkpoint is written to models/dpf-<obs>-<motion>/model.pt.
- Create a ros2_ws and link the existing folder to its src folder
mkdir -p ~/diffslam_ws/src
ln -s ~/diff-slam-main ~/diffslam_ws/src/diff_slam
- Run the code in live inference mode
colcon build
source install/setup.bash
ros2 launch diff_slam localizer.launch.py map_path:=~/diff-slam-main/map.pt model_path:=~/diff-slam-main/models/<dpf-run>/model.pt pointcloud_topic:=/livox/lidar
Run offline evaluation on a recorded bag with a specific model to generate and record evaluation metrics to csv, md and wandb.
uv run python -m diff_slam.eval --dataset data/<bag> --model models/<model>/model.pt --mode <mode>Output is written to eval_runs/metrics.csv, eval_runs/summary.md and wandb.
Optionally, KITTI-style output paths can be written to data/<bag>/eval/ with --out:
pred.txt— estimated trajectory (KITTI format)gt.txt— ground truth trajectory (KITTI format)
Ground truth sources
| Source | When used | Notes |
|---|---|---|
pose_graph.g2o |
default | SLAM-derived pseudo-GT, produced by build_map. |
gt/ bag |
--mocap-gt flag |
Real mocap poses; requires a gt/ sub-bag. Synced to lidar frames by bag recording timestamp. |
| On-the-fly SLAM | fallback | If no pose_graph.g2o is found, reference poses are recomputed via SLAM. |
Flags
| Flag | Description |
|---|---|
--dataset |
Path to the ROS2 bag directory (default: data/jackal/flat). |
--model |
Full self-contained DPF checkpoint (cannot combine with --obs/--motion). |
--obs |
Observation-model-only checkpoint; assemble a DPF around it. |
--motion |
Motion-model-only checkpoint (requires --obs). |
--mode |
Observation model type: ppe/ppp (pillar) or sdf (SDF-based). |
--mocap-gt |
Use mocap GT from data/<bag>/gt/ instead of g2o pseudo-GT. |
--out |
Enable KITTI output and optionally specify path (default: data/<bag>/eval/). |
--results-dir |
Directory for metrics CSV and MD summary (default: eval_runs/). |
--downsample |
Point cloud downsample count (default: 8000). |
--steps |
Steps per eval sequence (default: 6). |
--no-log |
Disable CSV, MD, and WandB logging. |
--no-wandb |
Disable WandB logging. |
--lidar-topic |
ROS2 topic for PointCloud2 data (default: /livox/lidar). |
--odometry-topic |
ROS2 topic for Odometry data (default: /odometry/filtered). |
bag_parser.pyROS2 bag parser to extract LIDAR & odometry datadef extract_syncextracts raw data and computes SLAM reference poses (disk-cached)def extract_mocap_gtsyncs mocap poses from agt/sub-bag to lidar frame timestampsclass LocalisationDatasetbase pytorchDatasetwrapper for ROS2 recordings
build_map.pybuilds a localization map from a ROS2 bag or PCD file; also savespose_graph.g2oeval.pyoffline evaluation: runs DPF inference on a recorded bag and saves pred/GT trajectories in KITTI formatmodule.pyModuleConfig/ConfigRegistry: per-model config dataclasses and checkpoint (config + weights) bundlingdpf.pydifferentiable particle filter implementation & e2e trainingclass SequenceDatasetdataset to derive short sequences for e2e trainingclass DPFv2differentiable particle filter implementationclass DPFConfigconfig bundling the dynamics, observation and resampling configsdef train_dpftraining routine for the DPF (wires in a pretrained observation model)
obs.pyobservation models & pre-trainingclass AlignmentDatasetdataset with Gaussian-derived pseudo labels as alignment scoreclass FixedPillarEncoderpoint cloud voxel encoder with fixed feature extractionclass StructuralComparatorpillar/voxel-based observation modelclass MultiSDF/class SDFObservationModelneural SDF observation modeldef train_sdf,def main_sdf,def main_ppobservation-model training entry points
resample.pydifferentiable resampling strategies (contraction, Gumbel-Softmax, optimal transport)slam.pyreference point cloud registration using ICPutil.pyhelper functionsvisualize.pyvisualization helpers/dataROS2 bag storage/cacheexecution cache, generated automatically/modelsmodel checkpoints & training stats, generated automatically
For all steps the training progress is displayed in the console and checkpoints will be written to /models. The necessary SLAM computations and bag extraction will happen automatically. The results are cached, deleting /cache and /models will force recomputation.
- Pre-train the observation model, e.g.
uv run python -m diff_slam.obs obs_sdf --train.train-ds "data/<dataset>,<bag>"; checkpoints are written tomodels/<save-tag>/. - Run e2e training for the entire DPF, e.g.
uv run python -m diff_slam.dpf --train.obs-model models/<save-tag>/model.pt --train.motion-arch mlp-se3; checkpoints are written tomodels/dpf-<obs>-<motion>/.