LiDAR-Based SLAM with Pose Graph Optimization

UC San Diego, ECE 276A: Sensing & Estimation in Robotics · Winter Quarter 2026

Code  |  Report


Overview

This project implements a complete LiDAR SLAM pipeline on a differential-drive robot equipped with wheel encoders, an IMU, a Hokuyo UTM-30LX LiDAR, and a Kinect RGBD camera. The pipeline has four stages: encoder/IMU odometry, ICP scan matching, probabilistic occupancy and texture mapping, and GTSAM pose graph optimization with loop closure. Results are presented for two indoor datasets collected in the same building.

Dataset provided by Prof. Nikolay Atanasov as part of ECE 276A. Encoder/IMU/LiDAR data is included in the project repository; Kinect RGBD data is available separately at the course SharePoint.


Pipeline

Part 1: Encoder/IMU Odometry

Linear velocity is estimated from four wheel encoder counts using the differential-drive model. Yaw rate is taken from the IMU z-axis and interpolated to encoder timestamps. Exact discrete-time integration (sinc-based) propagates poses forward, avoiding linearization error for large heading changes.

Dataset 20 odometry

Dataset 20 odometry trajectory

Dataset 21 odometry

Dataset 21 odometry trajectory

Part 2: ICP Scan Matching

2D ICP aligns consecutive Hokuyo scans to refine the odometry trajectory. Each ICP step is initialized from the odometry-predicted relative transform. A key detail is the source/target convention: the newer scan is passed as source and the older as target, yielding the correctly signed relative transform for pose chaining. Steps where ICP MSE exceeds 0.05 fall back to the odometry estimate.

ICP trajectories are consistently more compact than odometry, removing wheel-slip accumulation over long straight corridors. Both datasets show the same L-shaped floor plan topology preserved through scan matching.

Dataset 20 odometry vs ICP

Dataset 20: odometry (blue) vs. ICP (red)

Dataset 21 odometry vs ICP

Dataset 21: odometry (blue) vs. ICP (red)

ICP Warm-Up: 3D Point Cloud Registration

Before applying ICP to 2D scans, the algorithm was validated on depth images of two objects. Each canonical model (blue) was aligned to four observed point clouds (red) using yaw-discretized initialization over 36 angles. The asymmetric drill aligned accurately across all viewpoints (mean MSE 1.9 x 10-4 m2). The near-cylindrical liquid container was harder, with mean MSE 1.1 x 10-3 m2, due to rotational ambiguity in the yaw direction.

Drill PC0

Drill PC 0

Drill PC1

Drill PC 1

Drill PC2

Drill PC 2

Drill PC3

Drill PC 3

Liquid container PC0

Container PC 0

Liquid container PC1

Container PC 1

Liquid container PC2

Container PC 2

Liquid container PC3

Container PC 3

Part 3: Occupancy and Texture Mapping

A probabilistic log-odds occupancy grid is built from the ICP trajectory and LiDAR scans using Bresenham ray casting. Asymmetric updates (occupied +2.0, free -0.5) reflect the high reliability of valid Hokuyo hits. No-return beams carve free space without marking occupied endpoints, improving corridor clarity. Kinect RGBD images are projected to world frame and floor points (|z| < 0.1 m) are painted onto a co-registered texture grid.

First-scan sanity check

Dataset 20 first scan

Dataset 20: first-scan occupancy map

Dataset 21 first scan

Dataset 21: first-scan occupancy map

Dataset 20: ICP map progression and final maps

DS20 ICP 24%

24%

DS20 ICP 50%

50%

DS20 ICP 74%

74%

DS20 ICP 100%

100%

Dataset 20 ICP occupancy

Dataset 20: ICP occupancy map

Dataset 20 texture

Dataset 20: ICP texture map

Dataset 21: ICP map progression and final maps

DS21 ICP 24%

24%

DS21 ICP 49%

49%

DS21 ICP 74%

74%

DS21 ICP 100%

100%

Dataset 21 ICP occupancy

Dataset 21: ICP occupancy map

Dataset 21 texture

Dataset 21: ICP texture map

Part 4: GTSAM Pose Graph Optimization

A GTSAM factor graph is built over ICP poses with odometry (between) factors, fixed-interval loop closure factors every 10 poses, and proximity-based loop closures detected via KD-tree. All loop closure candidates pass an MSE consistency check before being added. Loop closure factors use a Huber robust kernel to limit outlier influence.

Dataset 20 had one proximity-based loop closure (MSE 0.013), providing a global anchoring constraint and yielding a 63.7% error reduction. Dataset 21 had no proximity loop closures; optimization relied entirely on fixed-interval smoothing, achieving a 54.3% reduction.

ParameterDataset 20Dataset 21
Total poses4,9624,785
Odometry factors4,9614,784
Fixed-interval closures496478
Proximity-based closures10
Initial graph error5.7249.149
Final graph error2.0804.176
Error reduction63.7%54.3%

Dataset 20: GTSAM trajectory and maps

Dataset 20 GTSAM trajectory

ICP (blue) vs. GTSAM (red) trajectory

Dataset 20 GTSAM occupancy

GTSAM occupancy map with trajectory

DS20 GTSAM 24%

24%

DS20 GTSAM 50%

50%

DS20 GTSAM 74%

74%

DS20 GTSAM 100%

100%

Dataset 20 GTSAM final occupancy

Dataset 20: GTSAM occupancy map

Dataset 20 GTSAM texture

Dataset 20: GTSAM texture map

Dataset 21: GTSAM trajectory and maps

Dataset 21 GTSAM trajectory

ICP (blue) vs. GTSAM (red) trajectory

Dataset 21 GTSAM occupancy

GTSAM occupancy map with trajectory

DS21 GTSAM 24%

24%

DS21 GTSAM 49%

49%

DS21 GTSAM 74%

74%

DS21 GTSAM 100%

100%

Dataset 21 GTSAM final occupancy

Dataset 21: GTSAM occupancy map

Dataset 21 GTSAM texture

Dataset 21: GTSAM texture map


← Back to Portfolio