Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

Package symbol

polka package from polka repo

polka

ROS Distro
jazzy

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version jazzy
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

Package symbol

polka package from polka repo

polka

ROS Distro
kilted

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version kilted
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

Package symbol

polka package from polka repo

polka

ROS Distro
lyrical

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version lyrical
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

Package symbol

polka package from polka repo

polka

ROS Distro
rolling

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version rolling
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

No version for distro github showing humble. Known supported distros are highlighted in the buttons above.
Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

No version for distro galactic showing humble. Known supported distros are highlighted in the buttons above.
Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

No version for distro iron showing humble. Known supported distros are highlighted in the buttons above.
Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

No version for distro melodic showing humble. Known supported distros are highlighted in the buttons above.
Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange

No version for distro noetic showing humble. Known supported distros are highlighted in the buttons above.
Package symbol

polka package from polka repo

polka

ROS Distro
humble

Package Summary

Version 0.6.1
License Apache-2.0
Build type AMENT_CMAKE
Use RECOMMENDED

Repository Summary

Description
Checkout URI https://github.com/Pana1v/polka.git
VCS Type git
VCS Version humble
Last Updated 2026-10-23
Dev Status DEVELOPED
Released UNRELEASED
Contributing Help Wanted (-)
Good First Issues (-)
Pull Requests to Review (-)

Package Description

ROS 2 composable node for multi-LiDAR fusion with optional CUDA acceleration. Packages from https://polka-ros.github.io include the CUDA merge engine and fall back to CPU without a GPU; ROS build farm packages are CPU-only.

Additional Links

Maintainers

  • Panav Arpit Raaj

Authors

  • Panav Arpit Raaj

POLKA

Documentation ROS 2 distros CUDA optional License: Apache-2.0

Polka

Multi-LiDAR fusion node for ROS 2. Merges any mix of PointCloud2 and LaserScan sources into one cloud, one scan, or both, with per-source and output filters, IMU/odometry deskew, and CUDA when an NVIDIA GPU is present. It replaces a relay, filter, transform, merge and downsample chain.

Documentation: polka-ros.github.io

box filter deskew

Install

From apt, with CUDA built in (amd64; needs only the NVIDIA driver, falls back to CPU without a GPU):

sudo curl -fsSL https://polka-ros.github.io/polka.gpg -o /usr/share/keyrings/polka.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/polka.gpg] https://polka-ros.github.io $(. /etc/os-release && echo $VERSION_CODENAME) main" \
  | sudo tee /etc/apt/sources.list.d/polka.list
sudo apt update && sudo apt install ros-${ROS_DISTRO:?source your ROS setup.bash first}-polka

From source, using the branch named after your distro:

git clone -b humble https://github.com/Pana1v/polka.git ~/ros2_ws/src/polka
cd ~/ros2_ws && colcon build --packages-select polka   # CUDA engine is built when nvcc is found

Driver versions, distro branches and details: Install.

Quick start

cp config/example_params.yaml config/my_robot.yaml   # set your topics and output_frame_id
ros2 launch polka polka.launch.py config_file:=config/my_robot.yaml

Then see Configuration.

License

Apache-2.0. The per-point deskew model is inspired by rko_lio (arXiv:2509.06593). Maintainers: MAINTAINING.md.

CHANGELOG

Changelog for package polka

0.6.1 (2026-10-17)

  • Install from apt: CUDA-enabled packages for Humble, Jazzy, Kilted, Lyrical and Rolling at polka-ros.github.io. See the README.
  • Documentation moved to https://polka-ros.github.io, along with doc/ (pages, images, demo GIFs and the GIF toolchain). The README is now a short entry point. The source package is 21 MB smaller.
  • The CUDA build links the CUDA runtime statically, so it needs only the NVIDIA driver at run time. Adds sm_89 and PTX, so GPUs newer than the listed architectures still run.
  • Fall back to the CPU engine when a GPU is found but can't be set up (seen after suspend/resume: "CUDA-capable device(s) is/are busy or unavailable"). The node used to keep the CUDA engine on failed allocations and log an error on every call. (#62)
  • Fix the CUDA engine clipping every source to filters.range.min/max (default 0.1 to 100 m) even with the range filter disabled. The CPU engine kept those points. (#65)

0.6.0 (2026-10-04)

  • Add doc/DESKEW.md: how big skew gets, and what deskew fixes on public KITTI data. README and doc/PERFORMANCE.md gain figures for translation modes, the articulated rig from #2, and deskew cost against GLIM, rko_lio and LIO-SAM. Performance numbers are re-measured on public clouds. perf_summary.svg is gone; it showed a 0.5.0 deskew gain that never applied.
  • Deskew and merge follow lidars on moving joints. On an articulated (center-steered) vehicle, odometry reports one body while front lidars ride the steering joint. odometry deskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan in test/data, 0.61 to 0.46 m/s, and 6 cm of error removed. Each cloud is now placed with its mount pose at its own stamp rather than the newest one: a joint turning 0.5 rad in between swung a point 2 m ahead by 1 m. Both fall back to the newest pose when TF has not reached the scan yet.
  • Learn each IMU's gyro bias whenever it stands still, and subtract it. Deskew applied the bias as a turn: on the polka#2 rig the front lidar's IMU reads 24.6 mrad/s parked, 4.3 cm off at 20 m on a test scan, now under 0.04 mm. A second of gyro spreading under 2 mrad/s per axis, with a mean under 35 mrad/s, counts as still; on the rig's 99 s recording that fired only while parked.
  • Fix the first deskewed scan of each source using the gyro in the IMU's own frame instead of the lidar's. It reversed the turn for upside-down (NED) IMUs, as built into many lidars: 13 cm off on a real scan.
  • Add a real-data deskew test on 0.3 s of the articulated-rig bag from #2 (test/data/airy_turn.mcap, 8.4 MB). New test dependencies: rosbag2_cpp, rosbag2_storage_mcap, tf2_msgs.
  • Deskew cost no longer depends on point order. Exact rotations are built once per scan at evenly spaced angles, and each point steps from the nearest one. Vertically sweeping lidars stored column by column, and unordered clouds, now cost the same as spinning ones: on public 85k-point Airy clouds, shuffled order went from 9.9 to 4.4 ns per point (SE(3)) and 11.9 to 3.3 (rotation). Accuracy is unchanged.
  • Add motion_compensation.translation (none | imu_accel | odometry) and motion_compensation.odom_topic. Deskew had no velocity term, so steady motion went uncorrected (1.5 m/s: 15 cm of skew per 0.1 s scan); odometry takes v * dt from a nav_msgs/Odometry twist, moved to each lidar's origin with its lever arm. none deskews rotation only. imu_accel stays the default. New dependency: nav_msgs.
  • Behavior change: deskew and inter-source alignment average the IMU over the scan's own time span instead of using the newest sample, so one shock sample after a scan no longer skews it.
  • The SE(3) deskew path re-anchors often enough to stay under 0.1 mm at road speed, where translation reaches metres per scan. 4.3 to 4.4 ns per point.
  • motion_compensation.max_imu_age and imu_frame never had an effect. They still load, with a startup warning, and are gone from the example configs.
  • Example configs list only non-default values and load to the same parameters as before. detailed_params.yaml now shows the real defaults for outputs.scan (enabled, z_min, z_max, range_max) and outputs.cloud.voxel.leaf_size; its old leaf_size: 0.05 silently turned voxelization on.
  • Interpolate the SE(3) deskew path between exact anchors, as the rotation-only path already does: 6.9 to 4.3 ns per point on public 85k-point Airy clouds. Accuracy stays under 1 mm, including ring-major clouds whose point times jump back at every ring.
  • Fix per-point deskew direction. Points were moved by the inverse of the sensor motion, which doubled intra-scan skew instead of removing it (0.6 rad/s yaw: 25 cm RMS raw, 50 cm after deskew, under 0.01 cm now).
  • Make the rotation-only deskew path reachable. It required IMU acceleration of exactly zero, which no real IMU produces since the EMA gravity estimate, so every scan took the slow SE(3) path. It now runs whenever the scan-wide translation from 0.5 * a * dt^2 is under 1 mm, and re-anchors across time jumps such as ring boundaries.
  • Closed-form SE(3) deskew path. Per-point math now runs in float. Per point on public 85k-point Airy clouds: 41.2 to 3.4 ns on the rotation path, 41.2 to 6.9 ns on SE(3).
  • Support Ouster-style per-point timestamps: a UINT32 time field is now accepted and read as a nanosecond offset from header.stamp. Previously the datatype was rejected outright, so Ouster sources ran with per-point deskewing and timestamp passthrough silently disabled.
  • Resolve per-point time units and epoch from the field's declared

File truncated at 100 lines see the full file

Launch files

No launch files found

Messages

No message files found.

Services

No service files found

Plugins

No plugins found.

Recent questions tagged polka at Robotics Stack Exchange