Skip to content

Repository files navigation

OnSLAM

OnSLAM demo showing a ROS1 LiDAR and IMU bag being processed into a 3D point-cloud map

Turn a ROS1 LiDAR + IMU bag into a navigable 3D point-cloud map on Windows.

No Linux, ROS, Python, Node.js, terminal commands, or cloud upload required.

Download OnSLAM for Windows Watch the OnSLAM demo

Latest GitHub release Windows 64-bit ROS1 bag input Local-only processing

Demo: the animated preview above shows the complete OnSLAM workflow.

OnSLAM is a local-first LiDAR-inertial odometry and mapping application for ROS1 bag files. It inspects compatible LiDAR and IMU streams, lets the user select topics and sensor settings, runs IMU-assisted scan-to-local-map odometry, visualizes progress live, and exports the resulting map as PLY or PCD.

The Windows installer bundles the Python/FastAPI processing engine, compiled React interface, scientific dependencies, and internal worker. Opening the desktop app starts a local service and opens the interface in the user's normal browser at 127.0.0.1; all processing remains on that computer.

Input ROS1 .bag containing compatible LiDAR and IMU topics
Processing Local IMU-assisted scan-to-map point-to-plane odometry
Live view Progress, trajectory, tracking quality, and point-cloud preview
Output Browser-viewable point cloud, PLY, PCD, and optional dense PLY
Privacy Bags, maps, trajectories, temporary files, and logs remain local

OnSLAM is an experimental and practical dataset-to-map pipeline. It is not presented as a complete replacement for FAST-LIO2, FAST-LIVO2, LIO-SAM, or a loop-closure and pose-graph SLAM system.


Download and run

Download the latest OnSLAM installer

  1. Download and run OnSLAM-Setup.exe.
  2. Open OnSLAM from the Desktop or Start Menu.
  3. Select a ROS1 .bag file and inspect its topics.
  4. Choose the LiDAR and IMU streams, processing quality, and export format.
  5. Run the mapping pipeline and download the generated PLY or PCD map.
  6. Exit the local engine through the OnSLAM system-tray icon when finished.

Normal users do not need to install Python, Node.js, ROS, or use a terminal.

Windows notice: the installer may trigger Microsoft Defender SmartScreen while the application is unsigned. The release page includes a SHA-256 checksum file so the downloaded installer can be verified.

Resource notice: large ROS bags can require substantial RAM, CPU time, processing time, and temporary disk space.

Optional checksum verification from PowerShell or Command Prompt:

certutil -hashfile OnSLAM-Setup.exe SHA256

Compare the result with OnSLAM-Setup.exe.sha256 on the latest release page.


Current status

Supported now:

ROS1 .bag files
Livox CustomMsg LiDAR topics
Generic sensor_msgs/PointCloud2 LiDAR topics
sensor_msgs/Imu topics
LiDAR topic selection
IMU topic selection
LiDAR-to-IMU extrinsics
LiDAR and IMU time offsets
N-frame or full-sequence runs
Fast, Balanced, and Detailed presets
Advanced processing controls
Gyroscope rotation prior
Optional rotation-only scan deskew
Cached local-map points, normals, and KD-tree
Robust point-to-plane ICP
Per-frame live progress and trajectory telemetry
Tracking-quality indicator
Incremental point-cloud preview
Browser point-cloud viewer
PLY download
PCD download
Optional dense full-resolution PLY export
Run cancellation
Temporary local run storage
Windows desktop installer

Not supported yet:

ROS2 bags
KITTI sequences
Loop closure
Pose graph optimization
Global relocalization
Visual odometry or camera fusion
Full FAST-LIO-style iterated error-state Kalman filtering
Automatic LiDAR/IMU extrinsic calibration
Automatic temporal calibration
Formal pose covariance

Public ROS bags used for testing

OnSLAM has been developed and tested with ROS1 bags from the TIERS Multi-Modal LiDAR Dataset. The dataset provides synchronized indoor and outdoor recordings from multiple LiDAR modalities, including Livox Avia, Livox Mid-360, and Ouster OS0-128 sensors.

Useful LiDAR/IMU topic pairs in those bags include:

Sensor LiDAR topic IMU topic LiDAR message
Livox Avia /avia/livox/lidar /avia/livox/imu sensor_msgs/PointCloud2
Livox Mid-360 /mid360/livox/lidar /mid360/livox/imu sensor_msgs/PointCloud2
Ouster OS0-128 /ouster/points /ouster/imu sensor_msgs/PointCloud2

The dataset is not bundled with OnSLAM. Download a sequence from the TIERS repository, open the .bag in OnSLAM, and select one matching LiDAR/IMU pair. Ground-truth topics are not required for generating a map.

Testing against this dataset does not guarantee identical results for every sequence or every custom ROS bag. Field layouts, timestamps, time offsets, extrinsics, motion, and scene geometry can all affect odometry. Compatibility reports and failing bags are especially useful; see Help test OnSLAM.


Core pipeline

ROS1 bag
→ inspect LiDAR/IMU topics
→ decode Livox CustomMsg or PointCloud2
→ convert both into the same LidarScan format
→ initialize IMU reference and gyro bias
→ optionally apply rotation-only deskew
→ preprocess and voxel-sample the scan
→ predict motion
→ run cached scan-to-local-map point-to-plane ICP
→ update the keyframe map and trajectory
→ accumulate a browser-safe point cloud
→ view and download the map as PLY/PCD

Livox CustomMsg and PointCloud2 are treated differently only during decoding.

Livox CustomMsg ─┐
                 ├──> LidarScan ───> shared odometry ───> viewer_map.ply
PointCloud2 ─────┘

The shared scan object contains:

timestamp_us
xyz
intensity
ring
point_time
frame_id

This keeps preprocessing, IMU handling, deskewing, registration, mapping, telemetry, and export generic.


Interface preview

Select LiDAR and IMU streams

OnSLAM LiDAR and IMU stream selection

Configure the mapping run

OnSLAM processing and export configuration

Follow processing in real time

OnSLAM live point-cloud construction, trajectory, tracking quality, and progress

Advanced sensor and processing settings

OnSLAM LiDAR-IMU extrinsics, time alignment, and processing settings


Application workflow

1. Open OnSLAM.
2. Select a ROS1 .bag file.
3. Inspect the bag.
4. Select the LiDAR topic.
5. Select the IMU topic.
6. Confirm the detected LiDAR type:
   - Auto
   - Livox CustomMsg
   - PointCloud2
7. Edit PointCloud2 fields if needed.
8. Keep the default extrinsics or enter T_imu_lidar.
9. Choose a frame count or the full sequence.
10. Select Fast, Balanced, or Detailed quality.
11. Choose the required export format.
12. Run SLAM.
13. Follow the live trajectory, tracking state, and point-cloud preview.
14. Navigate the final colored point cloud.
15. Download PLY or PCD.

Default extrinsics:

rotation = identity
translation = [0, 0, 0]

Extrinsics convention:

T_imu_lidar = transform from LiDAR frame into IMU frame

p_imu = R_imu_lidar @ p_lidar + t_imu_lidar

Incorrect extrinsics or timestamp offsets can substantially degrade odometry.


Processing presets

Fast

Processes every selected frame
Larger voxels
Lower point cap
Fewer ICP iterations
Smaller cached local map
Deskew disabled
Acceleration translation disabled
Translation deskew disabled

Fast is intended for quick previews and configuration checks.

Balanced

Processes every selected frame
Moderate voxel and point settings
Robust cached point-to-plane ICP
Optional automatic rotation-only deskew
Moderate local-map size
Acceleration translation disabled
Translation deskew disabled

Balanced is the recommended default.

Detailed

Processes every selected frame
Smaller voxels
Higher point cap
More ICP iterations
Larger cached local map
Optional automatic rotation-only deskew
Acceleration translation disabled
Translation deskew disabled

Detailed is intended for final map generation when additional processing time is acceptable.


IMU and deskew policy

The current production pipeline intentionally uses a conservative IMU policy:

Gyroscope rotation prior: supported
Rotation-only deskew: optional
Acceleration translation prior: disabled
Translation deskew: disabled

Acceleration-derived translation is disabled because reliable double integration requires a stronger estimator for velocity, gravity direction, accelerometer bias, gyro bias, timing, and uncertainty.

Rotation-only deskew may help when:

per-point timestamps are present
IMU data covers the scan interval
LiDAR/IMU time alignment is correct
LiDAR/IMU extrinsics are correct
the platform rotates enough to create visible scan distortion

The deskew implementation bins point timestamps rather than evaluating an independent IMU pose for every point. It also skips deskew when estimated angular motion is negligible.


Local desktop architecture

The installed application runs entirely on the user's computer:

OnSLAM.exe
├── starts the bundled FastAPI application
├── serves the compiled React/Vite interface
├── opens the interface in the default browser
├── launches OnSLAMWorker.exe when processing begins
└── remains available through a Windows system-tray icon

The browser connects only to a local loopback address:

http://127.0.0.1:<available-port>

The React interface and FastAPI engine share the same origin. The installed application does not require production CORS configuration, a remote processing backend, or a VITE_API_BASE environment variable.

Local data handling

During a desktop run:

ROS bag input        → local computer
LiDAR/IMU processing → local computer
Temporary files      → local temporary storage
Trajectory and maps  → local computer
PLY/PCD downloads    → local computer

ROS bags, maps, trajectories, and processing diagnostics are not transmitted to Vercel, Hugging Face, or another OnSLAM-operated server.

System-tray controls

While OnSLAM is running, its system-tray icon provides:

Open OnSLAM
Open logs folder
Exit

Closing the browser tab does not stop the local engine. Use Exit from the tray icon when finished.

Launcher diagnostics are stored under:

%LOCALAPPDATA%\OnSLAM\logs\launcher.log

Temporary run storage

Desktop runs use operating-system temporary storage rather than persistent job folders in the repository.

A typical run contains:

OS temporary directory/
├── onslam_upload_<random>.bag
└── onslam_run_<job_id>_<random>/
    ├── job_config.yaml
    └── outputs/
        ├── odometry/
        │   ├── estimated_trajectory.csv
        │   └── odometry_report.json
        ├── viewer_map/
        │   ├── viewer_map.ply
        │   └── viewer_map.pcd
        └── dense_map/
            └── dense_raw_full_sequence_map.ply

Run behavior:

Uploaded ROS bag copy:
deleted after completion or cancellation

Processing logs:
kept in a bounded in-memory buffer during the run

Run configuration:
stored only inside the temporary run directory

Completed results:
expire after the configured result lifetime

Cancelled runs:
removed shortly after cancellation

Repository data folders:
not used for desktop uploads or job logs

Supported topic types

LiDAR

livox_ros_driver/CustomMsg
livox_ros_driver2/CustomMsg
sensor_msgs/PointCloud2
sensor_msgs/msg/PointCloud2

IMU

sensor_msgs/Imu
sensor_msgs/msg/Imu

PointCloud2 field handling

Required fields:

x
y
z

Optional field names:

intensity
reflectivity
signal
remission
i
ring
line
channel
laser_id
time
t
timestamp
offset_time
time_offset

If no intensity field exists, map coloring falls back to another scalar such as height.

If no valid per-point time field exists, deskewing is skipped.


Odometry pipeline

Current ROS1 odometry flow:

1. Read LiDAR and IMU streams from the ROS1 bag.
2. Decode Livox CustomMsg or PointCloud2 into LidarScan.
3. Initialize the IMU reference and estimate early gyro bias.
4. Propagate a short-window rotation prior.
5. Optionally perform binned rotation-only deskew.
6. Filter invalid, out-of-range, height, and excess-density points.
7. Predict the next pose using IMU rotation and constant-velocity translation.
8. Reuse cached local-map points, normals, and KD-tree.
9. Query nearest neighbours.
10. Build point-to-plane residuals and Jacobians.
11. Apply correspondence trimming and robust weighting.
12. Solve the ICP correction.
13. Gate the correction by convergence, RMSE, translation, yaw, z, and degeneracy.
14. Accept the ICP pose or fall back to the predicted pose.
15. Add a keyframe when motion or frame-gap criteria are met.
16. Refresh the cached ICP target only when the keyframe map changes.
17. Accumulate a bounded browser-safe point cloud during the same pass.
18. Emit per-frame telemetry.
19. Write the trajectory and odometry report.
20. Write viewer_map.ply without a normal second pass through the bag.

Viewer map pipeline

The browser does not load the full raw dense map by default.

The normal application pipeline builds the viewer map during odometry:

filtered scan
+ accepted/predicted world pose
→ sampled world points
→ bounded online accumulator
→ intensity/height normalization
→ bright turbo-style coloring
→ browser-safe PLY
→ viewer_map.ply

Default viewer cap:

750,000 points

This removes a normal second pass through the ROS bag while keeping the browser responsive.


Live telemetry

Each processed frame can report:

processed frame index
selected total frame count
current pose
ICP RMSE
correspondence count
condition number
ICP accepted/rejected state
keyframe state
tracking state
downsampled point-cloud preview
processing rate

Trajectory state is emitted every processed frame. Point-cloud preview chunks are emitted less frequently to avoid unnecessary serialization and browser redraw overhead.


Tracking quality

The tracking indicator uses multiple consecutive frames rather than treating one weak frame as immediate tracking loss.

Possible states:

initializing
good
uncertain
lost

The state machine considers:

ICP acceptance
RMSE
condition number
correspondence count
consecutive weak frames
consecutive recovery frames

This indicator is a diagnostic aid and not a formal covariance estimate.


Help test OnSLAM

The most useful contribution right now is testing OnSLAM with real ROS1 LiDAR/IMU bags from different sensors and recording setups.

When reporting a successful or failed run, include:

  • LiDAR and IMU sensor models
  • selected topic names and message types
  • approximate bag duration and size
  • PointCloud2 field names, when applicable
  • selected quality preset and frame count
  • whether topic inspection, processing, visualization, and export completed
  • the error message or relevant log excerpt if something failed

Open a GitHub issue with the details. A bag that fails is often more useful for improving compatibility than another bag that already works.


Developer quick start

The installer is the recommended route for normal users. Developers can run the backend and frontend separately.

1. Install Python dependencies

py -3.11 -m pip install -r requirements.txt
py -3.11 -m pip install -r web\backend\requirements.txt

2. Install frontend dependencies

cd web\frontend
npm install
cd ..\..

3. Start the backend

py -3.11 -m uvicorn web.backend.app:app `
  --reload `
  --host 127.0.0.1 `
  --port 8000

4. Start the frontend

Open a second terminal:

cd web\frontend
npm run dev

Open:

http://127.0.0.1:5173

CLI usage

Inspect a ROS1 bag

py -3.11 scripts\inspect_rosbag.py `
  --bag data\datasets\your_bag.bag

Run odometry

py -3.11 scripts\run_rosbag_odometry.py `
  --config configs\ros1_lidar.yaml `
  --max-frames 120 `
  --out data\outputs\rosbag_odometry

Export a browser viewer map manually

The desktop/web pipeline normally builds the viewer map during odometry. This script remains available as a standalone fallback:

py -3.11 scripts\export_rosbag_viewer_map.py `
  --config configs\ros1_lidar.yaml `
  --trajectory data\outputs\rosbag_odometry\estimated_trajectory.csv `
  --out data\outputs\viewer_map `
  --max-points 750000

Optional dense map export

py -3.11 scripts\export_rosbag_dense_map.py `
  --config configs\ros1_lidar.yaml `
  --trajectory data\outputs\rosbag_odometry\estimated_trajectory.csv `
  --out data\outputs\dense_map

CLI outputs are written only to the locations explicitly supplied by the user.


Building the Windows application

Windows builds should be created from a local project checkout. Generated application and installer files are intentionally excluded from Git.

Prerequisites

Python 3.11
Node.js and npm
Inno Setup 6

Build the application

From the repository root:

powershell -ExecutionPolicy Bypass `
  -File .\tools\build_windows_app.ps1

The build script:

1. Creates an isolated Python build environment.
2. Installs Python runtime and packaging dependencies.
3. Installs frontend dependencies.
4. Builds the React/Vite production interface.
5. Packages the frontend, FastAPI backend, OnSLAM modules, scripts,
   configuration, scientific dependencies, and internal worker.
6. Produces the desktop application bundle.

Output:

dist\OnSLAM\OnSLAM.exe
dist\OnSLAM\OnSLAMWorker.exe
dist\OnSLAM\_internal\

After dependencies are already installed, use the faster rebuild:

powershell -ExecutionPolicy Bypass `
  -File .\tools\build_windows_app.ps1 `
  -SkipDependencyInstall

Build the installer

After testing dist\OnSLAM\OnSLAM.exe:

powershell -ExecutionPolicy Bypass `
  -File .\tools\build_windows_installer.ps1

Output:

release\OnSLAM-Setup.exe

The following generated paths must not be committed:

.build-venv/
build/
dist/
release/
web/frontend/dist/
web/frontend/node_modules/

Directory structure

OnSLAM/
├── README.md
├── requirements.txt
├── pyproject.toml
├── launcher.py
├── onslam.spec
│
├── configs/
│   └── ros1_lidar.yaml
│
├── docs/
│   └── images/
│       ├── demo.mp4
│       ├── onslam-results.jpeg
│       ├── onslam-streams.jpeg
│       ├── onslam-configure.jpeg
│       ├── onslam-processing.jpeg
│       └── onslam-advanced.jpeg
│
├── installer/
│   └── OnSLAM.iss
│
├── scripts/
│   ├── inspect_rosbag.py
│   ├── run_rosbag_odometry.py
│   ├── export_rosbag_viewer_map.py
│   └── export_rosbag_dense_map.py
│
├── src/
│   └── onslam/
│       ├── config/
│       ├── core/
│       ├── datasets/
│       │   └── ros1/
│       ├── io/
│       └── lio/
│
├── tests/
│   └── fixtures/
│
├── tools/
│   ├── cleanup_project.ps1
│   ├── build_windows_app.ps1
│   └── build_windows_installer.ps1
│
└── web/
    ├── backend/
    │   ├── app.py
    │   ├── desktop_app.py
    │   ├── job_manager.py
    │   ├── schemas.py
    │   ├── tracking_quality.py
    │   ├── pcd_export.py
    │   └── requirements.txt
    │
    └── frontend/
        ├── package.json
        ├── package-lock.json
        ├── index.html
        ├── vite.config.js
        └── src/
            ├── main.jsx
            ├── App.jsx
            ├── api.js
            ├── styles.css
            └── components/

Generated paths such as .build-venv, build, dist, release, node_modules, frontend dist, datasets, and run outputs are intentionally omitted from the repository.


Unified ROS1 config

Main config:

configs/ros1_lidar.yaml

Important sections:

input:
  format: ros1_bag
  path: data/datasets/your_bag.bag

topics:
  lidar: /livox/lidar
  imu: /livox/imu

lidar:
  type: auto
  fields:
    x: x
    y: y
    z: z
    intensity: intensity
    ring: ring
    time: time

extrinsics:
  convention: T_imu_lidar
  rotation_matrix:
    - [1, 0, 0]
    - [0, 1, 0]
    - [0, 0, 1]
  translation_m: [0, 0, 0]

rosbag_odometry:
  mode: imu_aided_scan_to_map_point_to_plane
  use_imu_accel_translation_prior: false
  deskew_use_translation: false

mapping:
  viewer_scan_voxel_m: 0.14
  viewer_scan_max_points: 30000

For an application run, the backend creates a temporary copy of this configuration and overrides the selected bag, topics, field mappings, extrinsics, frame count, quality preset, and processing options.


Development progression

v0.1  ROS1 bag inspection and topic detection
v0.2  Livox CustomMsg decoder
v0.3  First ROS1 LiDAR odometry smoke test
v0.4  IMU reading and motion propagation
v0.5  Livox offset_time deskewing
v0.6  Scan-to-local-map point-to-plane ICP
v0.7  Generic PointCloud2 decoder
v0.8  Shared LidarScan interface for Livox and PointCloud2
v0.9  FastAPI backend for upload/inspect/run/download
v0.10 React/Vite frontend
v0.11 Browser point-cloud viewer
v0.12 Direct per-job outputs
v0.13 Browser-friendly dense colored viewer_map.ply
v0.14 Lean ROS1-only cleanup and generic script/module names
v0.15 Full-screen staged workflow and live WebSocket telemetry
v0.16 Binned deskewing and conservative IMU policy
v0.17 Stable per-frame progress, tracking-state hysteresis, and cancellation
v0.18 Cached local-map normals/KD-tree and vectorized ICP
v0.19 One-pass browser map generation
v0.20 Local Windows application, bundled worker, and installer

Inspiration, acknowledgements, and related work

OnSLAM was independently implemented, but its design direction was informed by the FAST-LIO and FAST-LIVO research line from HKU MARS Lab.

High-level ideas that influenced the project include:

direct registration of LiDAR points against a map
maintaining a reusable local map rather than matching only consecutive scans
efficient scan-to-map processing
support for solid-state LiDAR scanning patterns
using inertial information for short-term motion prediction
building the map online while odometry is running

Relevant work:

OnSLAM does not currently implement:

FAST-LIVO2 visual fusion
FAST-LIVO2 direct photometric optimization
FAST-LIVO2 unified visual-LiDAR voxel map
FAST-LIVO2 exposure-time estimation
FAST-LIO2/FAST-LIVO2 iterated error-state Kalman filtering
FAST-LIO2 ikd-Tree
their complete tightly coupled state estimators

OnSLAM instead uses its own Python implementation of:

shared ROS1 decoding
short-window IMU rotation prediction
optional binned rotation-only deskew
cached local-map point-to-plane ICP
robust correction gates
browser telemetry
one-pass viewer-map accumulation
local temporary processing
Windows desktop packaging

No FAST-LIVO2 or FAST-LIO2 source code is included in this repository. Conceptual acknowledgement does not make OnSLAM an implementation of either system.

Suggested BibTeX

@article{zheng2024fastlivo2,
  title={FAST-LIVO2: Fast, Direct LiDAR-Inertial-Visual Odometry},
  author={Zheng, Chunran and Xu, Wei and Zou, Zuhao and Hua, Tong and Yuan, Chongjian and He, Dongjiao and Zhou, Bingyang and Liu, Zheng and Lin, Jiarong and Zhu, Fangcheng and Ren, Yunfan and Wang, Rong and Meng, Fanle and Zhang, Fu},
  journal={arXiv preprint arXiv:2408.14035},
  year={2024},
  doi={10.48550/arXiv.2408.14035}
}
@article{xu2021fastlio2,
  title={FAST-LIO2: Fast Direct LiDAR-Inertial Odometry},
  author={Xu, Wei and Cai, Yixi and He, Dongjiao and Lin, Jiarong and Zhang, Fu},
  journal={arXiv preprint arXiv:2107.06829},
  year={2021}
}

When describing OnSLAM, avoid implying that it reaches the real-time performance, estimator completeness, or benchmark accuracy reported by FAST-LIO2 or FAST-LIVO2 unless independently demonstrated.


Current limitations

No loop closure
No pose graph optimization
No global relocalization
No ROS2 adapter
No KITTI adapter
No automatic extrinsic calibration
No automatic temporal calibration
No complete IMU velocity/gravity/bias state estimator
No formal pose covariance
No visual odometry or camera fusion
No ikd-Tree or equivalent fully incremental spatial map
No benchmark claim against established LIO/LIVO systems
Windows-only packaged distribution

The current IMU logic is useful for rotation prediction and optional rotational deskewing, but it is not a complete inertial navigation stack.


Next milestones

1. Validate OnSLAM on more independent ROS1 Livox, Ouster, Velodyne, and Hesai bags.
2. Add automated regression tests for known compatible and incompatible bags.
3. Benchmark Fast, Balanced, and Detailed modes on public datasets.
4. Record runtime, accepted-ICP rate, trajectory consistency, and map quality.
5. Improve timestamp, PointCloud2 field, time-offset, and extrinsic validation.
6. Improve failure detection for sparse scans, poor geometry, and tracking loss.
7. Add optional map cropping and downsampling controls.
8. Add a portable Windows build alongside the installer.
9. Add loop closure after local odometry is stable.
10. Add ROS2 bag support.
11. Evaluate a proper error-state LiDAR-inertial estimator.

About

A Windows app for local LiDAR-inertial mapping of ROS1 bag files with live point-cloud visualization and PLY/PCD export.

Topics

Resources

Stars

Watchers

Forks

Releases

Contributors

Languages