# Synthetic Datasets for Predictive Localization Monitoring

## Overview

This repository contains a synthetic dataset designed for training and evaluating predictive localization monitoring models for autonomous mobile robots, specifically focusing on LiDAR-based particle filter localization (Adaptive Monte Carlo Localization, AMCL). The dataset is generated using NVIDIA Isaac Sim and includes 21 ROS 2 rosbags, capturing diverse scenarios with localization estimates, ground-truth poses, sensor data (LiDAR and odometry), and automatically labeled failure cases. The dataset is intended to support research in proactive fault detection for ground robots navigating in dynamic and challenging environments, as described in the paper "Synthetic Datasets for Data-Driven Localization Monitoring".

## Dataset Description

The dataset comprises 417,185 labeled samples across 21 experiment runs, with a failure rate of 23.1% (96,310 failure instances and 320,875 nominal instances). Experiments were conducted in seven distinct environments:

- **Warehouse**: A large, prebuilt environment with aisles, storage racks, and handling equipment, mimicking real-world warehouse settings.
- **Symmetric Maps (1–3)**: Three small-scale environments with symmetrical layouts to induce localization confusion due to repetitive structures.
- **Asymmetric Maps (1–3)**: Three small-scale environments with asymmetrical layouts for varied localization challenges.

Each environment was tested under three obstacle configurations:

- **Dynamic Only**: Spherical obstacles (representing humans, robots, or industrial trucks) with randomized trajectories. The number of spheres per run is given in the `info.txt` file inside each rosbag archive.
- **Static Only**: Manually placed static obstacles not included in the navigational map.
- **Combined (Dynamic + Static)**: Both dynamic and static obstacles.

Localization failures were induced through environmental challenges (e.g., dynamic obstacles, featureless zones, map ambiguities) and odometry drift, simulating real-world sensor inaccuracies (see [Odometry](#odometry)).

## File Structure

- **Rosbags** (`rec_*.zip`): 21 ROS 2 rosbags (SQLite3 storage, `.db3`), each corresponding to a unique experiment run (e.g., `rec_20250821_104113`). Each archive contains the raw experiment data, the rosbag `metadata.yaml`, and an `info.txt` with the map, obstacle configuration, and number of resets of the run.

- **info.csv**: A summary file detailing the experiment configurations and key statistics for each rosbag.

- **preprocessed_data.zip**: Processed versions of the dataset stored in Apache Parquet format, located in the `parquets/` folder.
  - For each rosbag, there are **two raw** (`parquets/raw/<run>_id_01.parquet`, `..._id_02.parquet`) and **two processed** (`parquets/processed/<run>_id_01.processed.parquet`, `..._id_02.processed.parquet`) parquet files.
  - The files are split in half to avoid memory issues during loading.
  - Raw parquets contain the unsynchronized messages of the topics `/amcl_pose`, `/momo/pose`, `/scan`, `/map`, `/delocalizations`, and `/particle_cloud`, extracted from the rosbags. In the second half (`_id_02`), latched topics (`/amcl_pose`, `/map`) start with a replay of the earlier messages, all stamped with the first timestamp of that half; `load_raw_run` in `scripts/load_parquets.py` removes these copies.
  - Processed parquets contain the labeled samples used for machine learning: the topics above aligned on a common time base (zero-order hold), together with `position_error`, `heading_error`, and the label `is_delocalized`. Each row of a processed parquet is one segment of the run between two localization resets; the samples of a segment are stored as a list in the column `measurements`, and the occupancy map in the column `/map`.
  - **The parquet files do not contain odometry.** Odometry is only available in the rosbags (see [Odometry](#odometry)).

- **scripts/load_parquets.py**: A Python script (only dependency: `polars`) to load the parquets as data frames (see [Loading the Parquets](#loading-the-parquets)).

- **momo_navigation.zip**, **USD_files.zip**: Simulation code, robot model, and environment USD files.

### info.csv Description

The `info.csv` file provides an overview of the 21 experiment runs, with the following columns:

- **Name**: The rosbag name (e.g., `rec_20250821_104113`).
- **Map**: The environment used (`warehouse`, `symmetric_exp_1`, `symmetric_exp_2`, `symmetric_exp_3`, `unsymmetric_exp_1`, `unsymmetric_exp_2`, `unsymmetric_exp_3`).
- **Dynamic**: Indicates if dynamic obstacles were present (`✓` for yes, `x` for no).
- **Static**: Indicates if static obstacles were present (`✓` for yes, `x` for no).
- **Resets**: Number of localization resets triggered when position error exceeded 0.4 m or orientation error exceeded 0.4 rad. This equals the number of segments of the run in the processed parquets.
- **Samples**: Total number of labeled samples in the run.
- **Nominal**: Number of samples labeled as nominal (`y=0`, no failure).
- **Failure**: Number of samples labeled as failure (`y=1`, localization error beyond thresholds).
- **Percentage**: Failure rate as a percentage (`Failure / Samples * 100`).

The file also includes a total row summarizing the dataset: 417,185 samples, 320,875 nominal, 96,310 failures, and a 23.1% failure rate.

The counts in `info.csv` are computed from the label `is_delocalized` in the processed parquets.

## Odometry

Odometry is recorded in the rosbags only:

| Topic | Type | Content | Runs |
|---|---|---|---|
| `/momo/odom` | `nav_msgs/msg/Odometry` | Ground-truth odometry from the simulator | all 21 runs |
| `/imperfect_odom` | `nav_msgs/msg/Odometry` | Artificially drifted odometry with noise, used by AMCL | 14 runs, from `rec_20250825_100748` onward |
| `/tf` | `tf2_msgs/msg/TFMessage` | Transform tree, including `odom → base_link` | all 21 runs |

`/imperfect_odom` is derived from the ground-truth odometry by applying a systematic drift proportional to the distance travelled (0.25 m per m in x and y, 0.1 rad per m in heading) plus Gaussian noise (standard deviation 0.001 m and 0.001 rad per step). The same pose is broadcast as the `odom → base_link` transform.

The 7 runs recorded on 2025-08-21 and 2025-08-22 (`rec_20250821_104113`, `rec_20250821_132354`, `rec_20250821_141846`, `rec_20250821_151429`, `rec_20250822_123916`, `rec_20250822_132819`, `rec_20250822_135308`) do not contain the `/imperfect_odom` topic.

The rosbags additionally contain, among others, `/imu`, `/cmd_vel`, `/point_cloud`, `/amcl_pose`, `/particle_cloud`, `/momo/pose`, `/map`, and `/delocalizations`. The full topic list of each run is given in its `metadata.yaml`.

## LiDAR Data

The simulated robot uses a VLP-16 Velodyne LiDAR model; the 2D `LaserScan` on `/scan` is used by AMCL.

- **Invalid beams**: A range of `-1.0` (below `range_min`) marks a beam without a valid return. About 10% of the beams of a typical scan are `-1.0`. Treat these beams as invalid / out of range.
- **Empty scans in 6 runs**: In the runs `rec_20250822_132819`, `rec_20250822_135308`, `rec_20250825_133605`, `rec_20250825_135829`, `rec_20250825_153216`, and `rec_20250825_161940`, the LiDAR was configured with a higher resolution (28,800 beams, range 0.2–100 m instead of 3,600 beams, range 1–200 m) and `/scan` was published at 70 Hz. Only every 7th message holds a full scan; the messages in between contain only `-1.0`. This is a simulation artifact, **not a sensor dropout**: valid scans arrive at a steady 10 Hz in these runs, as in the other runs, and AMCL ignores the empty messages. In the processed parquets, 112,085 of the 417,185 samples (all from these 6 runs) carry such an empty scan. Drop these samples or replace them with the last valid scan before training (see `empty_scans` in `scripts/load_parquets.py`).

| LiDAR configuration | Beams | Range [m] | `/scan` rate | Valid scan rate | Runs |
|---|---|---|---|---|---|
| A | 3,600 | 1–200 | 60 Hz | 60 Hz | `rec_20250821_104113`, `rec_20250821_132354`, `rec_20250822_123916` |
| B | 3,600 | 1–200 | 10 Hz | 10 Hz | remaining 12 runs |
| C | 28,800 | 0.2–100 | 70 Hz | 10 Hz | the 6 runs listed above |

## Usage

The dataset is designed for training supervised machine learning models to predict localization failures. Use the labeled samples (`is_delocalized = False` / `y=0` for nominal, `is_delocalized = True` / `y=1` for failure) to train classification models, and refer to `info.csv` to select runs based on map type, obstacle configuration, or failure rate.

### Loading the Parquets

Extract `preprocessed_data.zip`, install `polars` (`pip install polars`), and use `scripts/load_parquets.py`:

```python
from load_parquets import load_dataset, load_map, load_raw_run, load_run

# All processed samples of one run, one row per sample.
# Empty scans (see "LiDAR Data") are dropped by default.
df = load_run("rec_20250821_104113", root="parquets")

# All runs, with map and obstacle configuration from info.csv.
data = load_dataset(root="parquets", info_csv="info.csv", empty_scans="drop")

# Occupancy map of a run.
occupancy_map = load_map("rec_20250821_104113", root="parquets")

# Raw, unsynchronized topics of a run (one table per topic).
topics = load_raw_run("rec_20250821_104113", root="parquets")
```

Columns of the processed samples: `run`, `segment` (time is continuous only within a segment), `time` (simulation time in ns), `/scan/*`, `/particle_cloud/particles`, `/momo/pose/*` (ground truth), `/amcl_pose/*` (estimate), `position_error` [m], `heading_error` [rad], and `is_delocalized` (label).

`python load_parquets.py --root parquets --info info.csv` prints a summary of all runs.

### Loading the Rosbags

To access topics not contained in the parquets (e.g., odometry), use the rosbags:

1. Install ROS 2 and required dependencies.
2. Use the Flowcean framework to load rosbags and extract data into a tabular format (see Flowcean documentation: https://flowcean.me/examples/robot_localization_failure/), or read them with `rosbag2_py`.

## Simulation Setup

The dataset was generated using NVIDIA Isaac Sim with the MoMo omnidirectional robot model, equipped with a VLP-16 Velodyne LiDAR. The simulation includes:

- **Robot Model**: A digital model of the MoMo robot with holonomic kinematics.
- **Environments**: Seven maps (one warehouse, three symmetric, three asymmetric).
- **Challenges**: Dynamic obstacles, static obstacles, and odometry drift to induce failures.
- **Labeling**: Automatic labeling based on position error or orientation error.

The simulation code, robot model, and environment USD files are provided in this folder. Newer versions may be linked at https://flowcean.me/examples/robot_localization_failure/.

## Changelog

- **Version 2**: Corrected the nominal and failure counts of 8 runs in `info.csv` to match the processed parquets (total failures 96,315 → 96,310; deviations of at most 5 samples per run). Documented the odometry topics and the LiDAR data (invalid beams, empty scans in 6 runs), the structure of the parquet files, and added `scripts/load_parquets.py`.

## Citation

If you use this dataset in your research, please cite:

```txt
Markus Knitt, Sean Maroofi, Manav Thakkar, Hendrik Rose, Philipp Braun. "Synthetic Datasets for Data-Driven Localization Monitoring." Logistics Journal: Proceedings, 2025. DOI: 10.2195/lj_proc_knitt_en_202503_01.
```

```bibtex
@inproceedings{knitt2025synthetic,
    title={Synthetic Datasets for Data-Driven Localization Monitoring},
    author={Knitt, Markus and Maroofi, Sean and Thakkar, Manav and Rose, Hendrik and Braun, Philipp},
    booktitle={Logistics Journal: Proceedings},
    year={2025},
    doi={10.2195/lj_proc_knitt_en_202503_01}
}
```

## Contact

For questions or support, contact:

- Markus Knitt (markus.knitt@tuhh.de)
- Institute of Logistics Engineering, Hamburg University of Technology, Theodor-Yorck-Straße 8, 21079 Hamburg, Germany
