Skip to content

MCAP ROS2BAG import export - #496

Merged
JanuszBedkowski merged 2 commits into
MapsHD:mainfrom
michalpelka:mp/mcap_export
Aug 20, 2026
Merged

MCAP ROS2BAG import export#496
JanuszBedkowski merged 2 commits into
MapsHD:mainfrom
michalpelka:mp/mcap_export

Conversation

@michalpelka

@michalpelka michalpelka commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Non-ROS, self contained importer/exporter to ROS 2 bag.

Tested imports:

  • Hesai (Oxford Spires)
  • Livox mid-360 (IndoorOffice1_dataset, TIERS)
  • Ouster (IndoorOffice1_dataset, TIERS)

Works with Python tool to convert : rosbags convert.

Data from TIERS imports after step1:
image
Data from Oxfod Spires (Ouster):
image

There is no compression available on Windows - we can circle navigate it with rosbags convert.

@JanuszBedkowski

Copy link
Copy Markdown
Member

It looks good!

Adds a laz_to_mcap console tool (apps/lidar_odometry_step_1) that reads a
LAZ scan and optional IMU csv and writes a self-contained .mcap file
(embedded ros2msg schemas, no ROS install needed to produce or inspect it)
with /lidar_points (sensor_msgs/PointCloud2), /imu (sensor_msgs/Imu) and
/lidar_sn (std_msgs/String) topics. Reuses load_point_cloud()/load_imu()
from lidar_odometry_utils.cpp rather than load_data(), which additionally
drops the first LAZ file's points as part of the odometry pipeline's own
preprocessing -- wrong for a lossless exporter.

The PointCloud2 field layout is selectable via --lidar-type
(generic|velodyne|ouster|hesai) to match what different downstream LIO
consumers expect, and topic names/frame_id are configurable.

rosbags/McapWriter.{h,cpp} and cdr_serializer.hpp started from files
copied out of the mandeye firmware/recorder project; rewrote them against
plain McapPoint/McapImuSample structs instead of that project's
LidarPoint/LidarIMU/lidars-BaseLidarClient.h types, which don't exist here.

Also fixes 3rdparty/mcap's CMakeLists.txt: the INTERFACE target never
linked liblz4/libzstd, which mcap's reader/writer .inl files need
unconditionally even when compression is never used.

Tests: rosbags/tests/test_mcap_writer.cpp (doctest, wired into
BUILD_TESTING) round-trips PointCloud2 (all four layouts) and IMU
messages through the real mcap::McapReader. Also validated end-to-end
against a real mandeye recording with ros2 bag info/play (ROS2 jazzy).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

laz_to_mcap: support exporting a whole mandeye session directory

The single-file <input.laz> positional arg now also accepts a directory:
laz_to_mcap scans it for lidarNNNN.laz/.las + imuNNNN.csv chunk pairs
(matched by the filename's last 4 digits, same convention load_data()
uses), writes one /lidar_points message per chunk (in filename order),
and merges + sorts every matched chunk's IMU samples into a single
stream. Chunks without a matching csv still get their points exported,
with a warning. --imu stays single-file-only since IMU now comes from
the directory itself.

Sequential, no threading, to keep the loop straightforward.

Also gives McapFileWriter real per-channel sequence numbers instead of
always writing 0 -- previously harmless (one message per channel), but
directory mode now regularly writes many /lidar_points messages per file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Move laz_to_mcap from lidar_odometry_step_1 into console_tools

It's a standalone LAZ/IMU-csv -> MCAP converter with no GUI and no real
tie to the odometry app beyond reusing its load_point_cloud()/load_imu()
parsing -- apps/console_tools is where every other converter like this
(laz_to_pcd, laz_to_ply, pcd_to_laz, laz_to_txt) already lives, and
matching that pattern means it's now gated by BUILD_WITH_CLI_TOOLS like
its siblings instead of always building.

Renamed mcap_export_main.cpp -> laz_to_mcap.cpp to match every sibling
file in that directory being named after its target. It still pulls
lidar_odometry_step_1/lidar_odometry_utils.{h,cpp} in by relative path
for load_point_cloud()/load_imu() rather than duplicating them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Fix macOS-safe text and label formatting

Co-authored-by: michalpelka <3209244+michalpelka@users.noreply.github.com>

laz_to_mcap: add MLvxCalib calibration support, drop single-file mode

Session directories now auto-detect calibration.json/.mjc + .sn (or
accept --calibration/--sn overrides) and apply per-sensor extrinsics
plus the configured IMU serial when exporting, matching the convention
used by lidar_odometry.cpp and concatenate_multi_livox. Single-file
(.laz + --imu) input is removed since multi-lidar calibration only
makes sense for a full session directory.
cmake/cpu_optimizations.cmake forces -D_HAS_STD_BYTE=0 for all MSVC
targets (a leftover pybind11/Windows SDK workaround), which silently
disables std::byte project-wide. mcap's headers are the first thing in
this codebase to actually need it, so Windows CI failed with "'byte' is
not a member of 'std'" in McapWriter.cpp and test_mcap_writer.cpp.

Re-enable std::byte scoped to just those two translation units, ahead
of any standard header that would otherwise lock the disabled value in
for the rest of the TU, rather than touching the global define.
@JanuszBedkowski
JanuszBedkowski merged commit e5f2cc6 into MapsHD:main Aug 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants