Convert KITTI raw drives to self-contained ROS2 MCAP files (no ROS install required)
Project description
kitti2mcap
Convert a KITTI raw drive into a single, self-contained ROS2 MCAP file — no ROS installation required.
The output carries all the standard sensor topics (camera, LiDAR, IMU, TF) and embeds every ROS2 message schema inline, so it opens directly in any MCAP-based tool — Foxglove, the mcap CLI, and most calibration/visualization pipelines — without a sourced ROS workspace.
Install
Available on PyPI:
# as a standalone CLI tool (recommended)
uv tool install kitti2mcap # or: pipx install kitti2mcap
# or into the current environment
pip install kitti2mcap
You can also run it without installing:
uvx kitti2mcap --help
From source
Uses uv:
git clone https://github.com/AdityaSrinivasManohar/kitti2mcap
cd kitti2mcap
uv sync
This installs the pinned dependencies from pyproject.toml:
pykitti, mcap, mcap-ros2-support, numpy, Pillow, and
opencv-python-headless (pulled in by pykitti's package import).
Usage
kitti2mcap --date 2011_09_26 --drive 0001 --kitti-dir ./data --output out.mcap
(From a source checkout, prefix with uv run, e.g. uv run kitti2mcap ....)
| Flag | Description |
|---|---|
--date |
KITTI date folder (e.g. 2011_09_26) |
--drive |
Drive sequence number (e.g. 0001) |
--kitti-dir |
Root directory containing the date folder |
--output |
Output .mcap path |
--frames |
Number of frames to convert (default: all) |
--cameras |
Cameras to include, e.g. 0,1 (default: 2 — left color) |
--no-lidar |
Skip velodyne data |
--no-imu |
Skip IMU/OXTS data |
Examples
# First 5 frames only, quick smoke test
kitti2mcap --date 2011_09_26 --drive 0001 --kitti-dir ./data --output test.mcap --frames 5
# Both grayscale + color left cameras, no IMU
kitti2mcap --date 2011_09_26 --drive 0001 --kitti-dir ./data --output out.mcap --cameras 0,2 --no-imu
You can also run it as a module: python -m kitti2mcap ....
Camera indices
| Index | Frame | Topic basename | Encoding |
|---|---|---|---|
0 |
camera_gray_left |
camera_gray_left |
mono8 |
1 |
camera_gray_right |
camera_gray_right |
mono8 |
2 |
camera_color_left |
camera_color_left |
rgb8 |
3 |
camera_color_right |
camera_color_right |
rgb8 |
Expected input layout
--kitti-dir must follow pykitti's canonical layout — calibration files live
inside the date folder, alongside the drive:
<kitti-dir>/
└── 2011_09_26/
├── calib_cam_to_cam.txt
├── calib_imu_to_velo.txt
├── calib_velo_to_cam.txt
└── 2011_09_26_drive_0001_sync/
├── image_00/ image_01/ image_02/ image_03/
├── oxts/
└── velodyne_points/
Downloading a drive + its calibration from the KITTI raw archive and unzipping both into the same directory produces exactly this structure.
Topics written
| Topic | Type | Notes |
|---|---|---|
/kitti/camera_color_left/image_raw |
sensor_msgs/Image |
Camera 2 (left color), rgb8. Other cameras via --cameras |
/kitti/camera_color_left/camera_info |
sensor_msgs/CameraInfo |
Rectified intrinsics (P_rect / R_rect / K) |
/kitti/velodyne_points |
sensor_msgs/PointCloud2 |
x, y, z, intensity (float32) |
/tf_static |
tf2_msgs/TFMessage |
Sensor extrinsics, latched |
/tf |
tf2_msgs/TFMessage |
Ego pose map → base_link from OXTS (optional) |
/kitti/imu |
sensor_msgs/Imu |
Orientation, angular velocity, linear acceleration (optional) |
Transform tree
map ──(/tf, per frame)──▶ base_link ──(/tf_static)──┬──▶ velodyne
└──▶ camera_*
base_link is coincident with the OXTS/IMU unit. Static extrinsics are
expressed relative to base_link so the dynamic ego pose has a single,
conflict-free child to drive — a valid single-parent tree.
How it works
- No ROS dependency.
pykittireads the data;mcap+mcap-ros2-supportwrite the file. All ROS2 message schemas are emitted from inline.msgdefinitions (ros2_defs.py), so the MCAP is fully self-describing. - Schemas registered up front, so the file is readable without a ROS environment.
- Lazy, prefix frame loading — one frame at a time, so long drives don't exhaust RAM.
- Faithful timestamps. Each sensor's
timestamps.txtis parsed directly to nanosecond precision (pykitti truncates to microseconds), so per-sensor timing and playback rate are preserved. - Rectified camera model. KITTI sync images are already rectified, so
CameraInfousesP_rect/R_rect/Kwith zero distortion. /tf_staticlatched at the first frame's timestamp so the transform tree is available immediately on load.
Project structure
kitti2mcap/
├── cli.py # CLI entrypoint, arg parsing, orchestration
├── reader.py # Wraps pykitti; per-sensor nanosecond timestamps
├── calibration.py # pykitti calib → CameraInfo + static transform tree
├── ros2_defs.py # Concatenated ROS2 .msg schema text (self-contained)
├── mcap_writer.py # Wraps mcap-ros2-support; schema registration + writing
└── writers/
├── image.py # PIL image → sensor_msgs/Image
├── pointcloud.py # velodyne .bin → sensor_msgs/PointCloud2
├── imu.py # OXTS packet → sensor_msgs/Imu
└── tf.py # quaternion/transform helpers, TFMessage builders
Inspecting the output
With the mcap CLI:
mcap info out.mcap
mcap doctor out.mcap
Or trim a slice for a quick test:
mcap filter out.mcap tiny.mcap --start-secs 0 --end-secs 1
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kitti2mcap-0.1.1.tar.gz.
File metadata
- Download URL: kitti2mcap-0.1.1.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c640950dfb338505d4fa1ca8a3436bfcb9578e92db3d0fc34ea068873115cac
|
|
| MD5 |
f71a02edbb7698b2a40c3f292a950b65
|
|
| BLAKE2b-256 |
3895e71245b46ebd82697540324bb20974b76ea7bb4cbccf63cb6d772c1afe1a
|
File details
Details for the file kitti2mcap-0.1.1-py3-none-any.whl.
File metadata
- Download URL: kitti2mcap-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b8898849e41e34f57e2fff33c6d73c2eb68801c3d2b72ca3dd3a936efa93f495
|
|
| MD5 |
e5aacb37fdfff30bb7bd2535526ad87f
|
|
| BLAKE2b-256 |
02295d1b06e5786105996d21eec24792c23ccc396013e84951d6e994a288c3c4
|