Skip to main content

LIDAR, Cameras, IMUs: Fusion and Integration for Robust Perception

Learning Objectives​

By the end of this chapter, you will be able to:

  • Explain the principles of sensor fusion for improving perception accuracy
  • Implement basic sensor fusion algorithms combining LIDAR, camera, and IMU data
  • Describe the advantages and limitations of different sensor modalities
  • Design a sensor fusion system that leverages complementary sensor characteristics

Prerequisites​

  • Understanding of individual sensor principles (covered in Week 1)
  • Basic probability and statistics knowledge
  • Familiarity with coordinate transformations

Theory​

Sensor fusion combines data from multiple sensors to achieve better perception performance than could be achieved by using any single sensor alone. Different sensors have complementary strengths and weaknesses:

  • LIDAR: Excellent for accurate distance measurements and creating geometric maps, but limited in semantic understanding
  • Cameras: Rich in semantic information and texture, but sensitive to lighting conditions and unable to directly measure depth
  • IMUs: Provide high-frequency motion and orientation data, but suffer from drift over time

The goal of sensor fusion is to combine these complementary sources of information to create a more complete, accurate, and robust perception system.

Key Fusion Approaches​

Early Fusion: Raw sensor data is combined before processing. This approach preserves the most information but requires careful calibration and synchronization.

Late Fusion: Individual sensor measurements are processed separately and then combined. This approach is computationally efficient but may lose some information.

Deep Fusion: Learned representations from different sensors are combined in a neural network architecture. This approach can capture complex relationships between sensors but requires training data.

Code Example 1: Kalman Filter for IMU-LIDAR Fusion​

# Purpose: Fuse IMU and LIDAR data using a Kalman filter for improved position estimation
# Setup Instructions: Install numpy
# Run: python kalman_fusion.py

import numpy as np

class IMULidarKalmanFilter:
def __init__(self):
# State vector: [x, y, vx, vy]
self.state = np.zeros(4) # [x, y, vx, vy]

# Covariance matrix (uncertainty in state estimate)
self.P = np.eye(4) * 1000 # High initial uncertainty

# Process noise covariance
self.Q = np.eye(4) * 0.1 # Motion model uncertainty

# Measurement noise covariance
self.R_imu = np.eye(2) * 0.01 # IMU measurement noise
self.R_lidar = np.eye(2) * 0.5 # LIDAR measurement noise

# State transition model (assuming constant velocity)
self.F = np.array([
[1, 0, 0.1, 0], # x = x + dt*vx
[0, 1, 0, 0.1], # y = y + dt*vy
[0, 0, 1, 0], # vx = vx
[0, 0, 0, 1] # vy = vy
])

# Measurement model for position (from LIDAR)
self.H_pos = np.array([
[1, 0, 0, 0], # Measure x
[0, 1, 0, 0] # Measure y
])

# Measurement model for velocity (from IMU)
self.H_vel = np.array([
[0, 0, 1, 0], # Measure vx
[0, 0, 0, 1] # Measure vy
])

def predict(self, dt):
"""Predict the next state based on motion model"""
# Update state transition matrix with new dt
self.F[0, 2] = dt
self.F[1, 3] = dt

# Predict state
self.state = self.F @ self.state

# Predict covariance
self.P = self.F @ self.P @ self.F.T + self.Q

def update_position(self, measured_pos):
"""Update state estimate with LIDAR position measurement"""
# Innovation
innovation = measured_pos - self.H_pos @ self.state
# Innovation covariance
S = self.H_pos @ self.P @ self.H_pos.T + self.R_lidar
# Kalman gain
K = self.P @ self.H_pos.T @ np.linalg.inv(S)
# Update state
self.state = self.state + K @ innovation
# Update covariance
I = np.eye(len(self.state))
self.P = (I - K @ self.H_pos) @ self.P

def update_velocity(self, measured_vel):
"""Update state estimate with IMU velocity measurement"""
# Innovation
innovation = measured_vel - self.H_vel @ self.state
# Innovation covariance
S = self.H_vel @ self.P @ self.H_vel.T + self.R_imu
# Kalman gain
K = self.P @ self.H_vel.T @ np.linalg.inv(S)
# Update state
self.state = self.state + K @ innovation
# Update covariance
I = np.eye(len(self.state))
self.P = (I - K @ self.H_vel) @ self.P

# Example usage
kf = IMULidarKalmanFilter()

# Initial state
print(f"Initial state: x={kf.state[0]:.2f}, y={kf.state[1]:.2f}, vx={kf.state[2]:.2f}, vy={kf.state[3]:.2f}")

# Simulate measurements
lidar_pos = np.array([1.0, 0.5]) # LIDAR measured position
imu_vel = np.array([0.8, 0.2]) # IMU measured velocity

# Update with LIDAR
kf.update_position(lidar_pos)
print(f"After LIDAR update: x={kf.state[0]:.2f}, y={kf.state[1]:.2f}")

# Update with IMU
kf.update_velocity(imu_vel)
print(f"After IMU update: vx={kf.state[2]:.2f}, vy={kf.state[3]:.2f}")

# Predict forward
kf.predict(dt=0.1)
print(f"After prediction: x={kf.state[0]:.2f}, y={kf.state[1]:.2f}")

Expected Output:

Initial state: x=0.00, y=0.00, vx=0.00, vy=0.00
After LIDAR update: x=1.00, y=0.50
After IMU update: vx=0.80, vy=0.20
After prediction: x=1.08, y=0.52

Code Example 2: Camera-LIDAR Fusion for Object Detection​

# Purpose: Combine camera and LIDAR data for improved object detection
# Setup Instructions: Install numpy
# Run: python camera_lidar_fusion.py

import numpy as np

class CameraLidarFusion:
def __init__(self):
# Camera intrinsics (simplified)
self.fx = 500.0 # Focal length x
self.fy = 500.0 # Focal length y
self.cx = 320.0 # Principal point x
self.cy = 240.0 # Principal point y

def project_point_to_image(self, point_3d, extrinsic_matrix=None):
"""Project a 3D point from LIDAR coordinate frame to 2D image coordinates"""
if extrinsic_matrix is None:
# Assume LIDAR and camera share the same coordinate frame for simplicity
extrinsic_matrix = np.eye(4)

# Convert 3D point to homogeneous coordinates
point_homo = np.array([point_3d[0], point_3d[1], point_3d[2], 1.0])

# Transform to camera coordinate frame
point_cam = extrinsic_matrix @ point_homo

# Project to 2D image plane
if point_cam[2] > 0: # Point is in front of camera
u = self.fx * point_cam[0] / point_cam[2] + self.cx
v = self.fy * point_cam[1] / point_cam[2] + self.cy
return np.array([u, v, point_cam[2]]) # u, v, depth
else:
return None # Point is behind camera

def associate_detections(self, camera_boxes, lidar_points):
"""Associate camera detections with LIDAR points"""
associations = []

for cam_box in camera_boxes:
# Extract center of camera bounding box
box_center_x = (cam_box[0] + cam_box[2]) / 2
box_center_y = (cam_box[1] + cam_box[3]) / 2

# Find LIDAR points projected inside this bounding box
associated_points = []
for lidar_point in lidar_points:
proj = self.project_point_to_image(lidar_point)
if proj is not None:
u, v, depth = proj
# Check if LIDAR point projects inside camera box
if (cam_box[0] <= u <= cam_box[2] and
cam_box[1] <= v <= cam_box[3]):
associated_points.append((lidar_point, depth))

if associated_points:
associations.append({
'camera_box': cam_box,
'lidar_points': associated_points
})

return associations

# Example usage
fusion = CameraLidarFusion()

# Simulated camera detections (x1, y1, x2, y2 format)
camera_detections = [
np.array([100, 100, 200, 200]), # Box around object 1
np.array([300, 150, 400, 250]) # Box around object 2
]

# Simulated LIDAR points (x, y, z coordinates)
lidar_points = [
np.array([2.0, 1.0, 0.5]), # Point from object 1
np.array([2.1, 1.1, 0.5]),
np.array([4.0, 2.0, 0.5]), # Point from object 2
np.array([4.1, 2.1, 0.5]),
np.array([6.0, 3.0, 0.5]) # Background point
]

associations = fusion.associate_detections(camera_detections, lidar_points)

print(f"Found {len(associations)} associations between camera and LIDAR detections")
for i, assoc in enumerate(associations):
print(f"Association {i+1}: Camera box {assoc['camera_box']} -> {len(assoc['lidar_points'])} LIDAR points")

Expected Output:

Found 2 associations between camera and LIDAR detections
Association 1: Camera box [100. 100. 200. 200.] -> 2 LIDAR points
Association 2: Camera box [300. 150. 400. 250.] -> 2 LIDAR points

Hands-on Exercises​

  1. Implement an Extended Kalman Filter (EKF) for fusing GPS and IMU data for outdoor navigation
  2. Create a particle filter for fusing odometry and LIDAR data in a SLAM system
  3. Develop a sensor fusion system that combines thermal camera and LIDAR data for object detection in low-visibility conditions

Summary​

Sensor fusion is a critical component of robust robotic perception systems. By combining data from multiple sensors, robots can achieve greater accuracy, reliability, and robustness than would be possible with individual sensors. The choice of fusion approach depends on the specific sensors, computational constraints, and accuracy requirements of the application.

Hardware Requirements​

This chapter can be completed using simulation environments. For physical implementation, the following hardware is recommended:

  • LIDAR sensor (e.g., RPLIDAR A1)
  • RGB camera (e.g., Intel Realsense D435)
  • IMU (e.g., Bosch BNO055)
  • Calibration board for sensor extrinsic calibration
  • Robot platform with synchronized sensor interfaces