English | 한국어 | 한국어 학습 가이드
Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into a unified PointCloud2 and/or LaserScan, with per-source and output filtering, IMU deskewing, and optional CUDA acceleration. One composable node replaces a relay / filter / transform / merge / downsample chain.
Each clip runs polka with a different config on the TIERS multi-LiDAR dataset (Ouster OS1 + Livox Avia + Mid-360), rendered headless with Open3D. See doc/media/ to regenerate.
Deskew: per-point SE(3) correction removes intra-scan motion smear (synthetic yaw, mechanism demo). This clip is synthetic, generated separately from the TIERS dataset clips above.
CUDA. The GPU merge engine wins on heavy pipelines by fusing transform, filter, voxel, and scan flatten into a single pass over the points. On a filterless merge the CPU stays competitive, because kernel dispatch and host to device transfer overhead dominate when there is little per-point work to hide them behind. Build with -DWITH_CUDA=ON (it falls back to CPU automatically); it is not universally faster.
Bandwidth. polka fans N sensor streams into one output topic, so every downstream node subscribes once instead of to each raw sensor. Voxel downsampling can thin that cloud further, a tradeoff the user sets through leaf_size: at the leaf size used in the demo clip it drops 69k points to 5k (about 14x), which is an example of the ratio, not a fixed figure or a 0.5.0 speedup.
See Performance notes for measurement context, the per number sources, and the CPU to CUDA crossover.
- Heterogeneous fusion: mix 3D PointCloud2 and 2D LaserScan sources freely
- Dual output: merged PointCloud2, LaserScan, or both at once
- Per-source and output filtering: range, angular, box, height cap, footprint (ego-body) exclusion, voxel downsample
- IMU deskewing: per-point SE(3) motion correction, with per-point timestamp auto-detect
- CUDA acceleration: optional GPU merge engine, falls back to CPU
- TF2 integration: automatic lookup with last-known-good fallback
- Full runtime reconfiguration: filters, outputs, deskewing, and even the source list can be changed live via
ros2 param set— no restart - Diagnostics, drift detection, and a terminal dashboard: per-source rate/bandwidth/lag on
/diagnostics, timing/rate drift flags, and an optionalpolka_monitorTUI - Composable node: runs standalone or loaded into a component container
| Capability | Supported | How |
|---|---|---|
| 3D PointCloud2 | yes | native |
| 2D LaserScan | yes | projected and merged |
| Single global IMU | yes | motion_compensation.imu_topic |
| Multiple IMUs (per source) | yes | sources.<name>.imu_topic |
| Decentralized IMUs (different mounts) | yes | TF rotates angular velocity and acceleration into each sensor frame |
| Articulated IMUs (moving joint or turret) | yes | dynamic TF from joint_states; config/example_articulated_imu.yaml |
Every source can carry its own IMU on its own mount. polka looks up the live TF from each IMU frame to its sensor frame and rotates that IMU's angular velocity and acceleration into the sensor frame before deskewing, so a fixed chassis LiDAR and a rotating turret LiDAR each deskew against their own motion:
graph LR
gimu[global IMU] -->|TF into sensor frame| chassis[chassis LiDAR]
timu[turret IMU] -->|TF into sensor frame| turret[turret LiDAR]
chassis --> polka
turret --> polka
polka --> merged[one merged cloud]
Each ROS 2 distro has its own code-identical branch:
| Distro | Ubuntu | Branch |
|---|---|---|
| Humble | 22.04 | humble |
| Iron | 22.04 | iron |
| Jazzy | 24.04 | jazzy |
| Kilted | 24.04 | kilted |
| Lyrical | 26.04 | lyrical |
git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka
# add --cmake-args -DWITH_CUDA=ON for the GPU merge enginecp config/example_params.yaml config/my_robot.yaml # edit topics + output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yamlSet output_frame_id to your base frame, list sensors under source_names, and make sure TF resolves each sensor frame_id to output_frame_id. Replaying a bag? Pass use_sim_time:=true and play with --clock (see Configuration).
- Korean learning guide / 한국어 학습 가이드: installation, concepts, hands-on examples, diagnostics, and advanced operation
- Configuration: every parameter, filters, IMU deskewing, rosbag playback
- Pipeline and architecture: what polka replaces, internal stages, file layout
- Performance: measured 0.5.0 speedups, the CPU to CUDA crossover, and the bandwidth angle
- Maintaining distro branches: single-source-of-truth sync across the five branches
Apache-2.0. The per-point deskewing motion model is inspired by rko_lio (Malladi et al., 2025, arXiv:2509.06593).