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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
Dependant Packages
Launch files
Messages
Services
Plugins
Recent questions tagged polka at Robotics Stack Exchange
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
Additional Links
Maintainers
- Panav Arpit Raaj
Authors
- Panav Arpit Raaj
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
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 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 anddoc/PERFORMANCE.mdgain 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.svgis 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.
odometrydeskew now adds the lidar's velocity relative to the odometry frame, sampled from TF over the scan: on the real front-lidar scan intest/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) andmotion_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);odometrytakesv * dtfrom anav_msgs/Odometrytwist, moved to each lidar's origin with its lever arm.nonedeskews rotation only.imu_accelstays 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_ageandimu_framenever 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.yamlnow shows the real defaults foroutputs.scan(enabled,z_min,z_max,range_max) andoutputs.cloud.voxel.leaf_size; its oldleaf_size: 0.05silently 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^2is 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
UINT32time field is now accepted and read as a nanosecond offset fromheader.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
Package Dependencies
System Dependencies
| Name |
|---|
| libpcl-all-dev |
| eigen |
