Hi, I’m running an EDU Go2 with the L1 LiDAR on ROS 2 Foxy and trying to do saved-map localization. Mapping works, localization does not, and I want a sanity check on whether the approach is even right for this sensor.
Phase 1: mapping
I preprocess /utlidar/cloud and /utlidar/imu (rotation into body frame, IMU bias, self-return removal) and feed Point-LIO (point_lio_unilidar). That gives me a global .pcd plus a 2D occupancy grid. Most of the time the map looks correct in RViz, but on some runs I get skewed walls like the attached screenshot. I save the intiatal pose as initial_pose.yaml (Point-LIO aft_mapped in camera_init) or 2D Pose Estimate" in rviz so I can seed localization later, mapping work, I get the .pcd map next, I just want to do localization against it so that I can run the robot for navigation and I do not need to build the map every time
Phase 2: localization (this is where it falls apart)
I run a small package go2_localization doing PCL NDT scan-to-saved-PCD. Inputs:
- cloud:
/utlidar/transformed_cloud(framebody) - odom predictor:
/utlidar/robot_odom(publisher reports_CREATED_BY_BARE_DDS_APP_, ~150 Hz, used only for twist integration between scans) - target: the saved
.pcd - seed pose: from
initial_pose.yamlor 2D Pose Estimate in rviz
It publishes /pcl_pose, /path, and map → body TF.
With the robot physically still and not moved since launch, I measured:
/utlidar/transformed_cloud: 15 Hz/utlidar/robot_odom: under 1 mm xy drift in 12 s, max linear 0.029 m/s, max angular 0.044 rad/s. Odom is good./pcl_pose: drifts and jumps jitter and the robot does not know where it is, the drift can be in meters.
What I tried
- Load saved PCD from disk and downsample with
map_voxel_leaf_size. From ~700,000 to ~33,000 points at 0.1 m leaf. - Crop the downsampled map around the predicted pose so NDT only sees a 5 m local window (about 4–20 k points depending on leaf size).
- Filter the live scan with
voxel_leaf_size = 0.1, range gate1.0 – 8.0 m. - Scan accumulation: buffer the last 5 scans (~333 ms), motion-compensate older scans into the latest body frame using the odom-integrated pose, merge, re-voxel, run NDT once at ~3 Hz. Fitness drops to about 0.002 (10x better), per-scan jitter halves, accepted-pose drift drops from ~6 cm in 30 s to ~2.5 cm in 30 s, and the robot localizes itself as you see in the screenshot but it still does not give a smooth path and sometimes it drifts a bit.
My current theory
I think the L1’s scan pattern is the root issue. It’s a non-repetitive “flower”/spirograph-style mirror pattern, ~21k points/s, so a single 100 ms frame is roughly 2,160 points. Frame A and Frame B (robot still) hit different parts of the same wall, so scan-to-map NDT sees a different point distribution every frame and drifts the optimum. This is the opposite of what NDT/ICP assume (consistent geometry from one scan to the next, like Velodyne VLP-16 at ~300k points/s).
That would explain why scan accumulation helped (more points per match averages out the spirograph aliasing) but didn’t fully fix it (the underlying scan-pattern variation is still there).
What I want to ask
- Is pure scan-to-saved-PCD localization actually can work and it si a solution on the Unitree L1, or is the supported workflow on Go2 always live LIO + a drift-correction layer (e.g. running Point-LIO during operation and adding something like
small_gicp_relocalizationor FAST-LIO localization mode to publishmap → odom)? - If pure scan-to-map on L1 is supposed to work, what registration method, NDT resolution, voxel sizes, and minimum scan accumulation window do you use? My current setup is
ndt_resolution 0.75,voxel_leaf_size 0.1,map_voxel_leaf_size 0.1, accumulation = 5 scans. - Is
/utlidar/robot_odomthe recommended source for predicting motion between scans for global localization, or is there a better internal source (e.g./utlidar/cloud_deskewed,/uslam/localization/odom)? The Bare DDS publisher name made me unsure how official this topic is. - The skewed-wall mapping result I sometimes get with Point-LIO. Is there a known cause on Go2 (IMU calibration, gait-induced vibration, motion type), and a recommended capture procedure?
Thank you

