How LiDAR Works
LiDAR (Light Detection and Ranging) is like sonar but uses laser light instead of sound. It sends out rapid laser pulses and measures how long each pulse takes to bounce back. With the speed of light known, distance = (speed × time) / 2.
Think of it like throwing a ball at a wall and timing the bounce-back — except LiDAR does this millions of times per second with light.
ToF vs FMCW LiDAR
There are two main LiDAR technologies:
Time-of-Flight (ToF): Measures the round-trip time of a single laser pulse. Simple, reliable, used in most drone LiDARs (Velodyne, Livox, DJI L1).
Frequency-Modulated Continuous Wave (FMCW): Uses a continuously modulated laser. It can measure velocity simultaneously with distance — like how a police radar detects speed, not just distance.
LiDAR Scanning Patterns
Different LiDARs scan the environment in different ways. The choice affects coverage, resolution, and suited applications.
Point Cloud Data
A LiDAR produces a point cloud — a collection of 3D points (x, y, z) with optional intensity values. Each point represents one laser return.
# Simulated LiDAR point cloud (N points)
n_points = 10000
points = np.random.randn(n_points, 3) * 20 # x, y, z in meters
# Each point: [x, y, z, intensity]
intensities = np.random.uniform(0.1, 1.0, (n_points, 1))
point_cloud = np.hstack([points, intensities])
print(f"Point cloud shape: {point_cloud.shape}") # (10000, 4)
print(f"X range: {points[:,0].min():.1f} to {points[:,0].max():.1f} m")
print(f"Y range: {points[:,1].min():.1f} to {points[:,1].max():.1f} m")
print(f"Z range: {points[:,2].min():.1f} to {points[:,2].max():.1f} m")
Point Cloud Visualization Pipeline
LiDAR Specifications for Drones
| Spec | Hobby Grade | Survey Grade | Top-end |
|---|---|---|---|
| Range | 10-30m | 100-300m | 300m+ |
| Points/sec | 10K-100K | 100K-1M | 1M+ |
| Accuracy | ±3-5cm | ±1-2cm | ±1cm |
| Weight | 50-200g | 500g-2kg | 2kg+ |
| Examples | RPLidar A1 | DJI L1 | Velodyne VLP-16 |
Hands-On Project: LiDAR Point Cloud Viewer
Create a simple point cloud visualizer and ground-plane detector:
from scipy.spatial import KDTree
class SimpleGroundDetector:
"""Simple RANSAC-based ground plane detection."""
def __init__(self, distance_threshold=0.3, iterations=100):
self.threshold = distance_threshold
self.iterations = iterations
def detect_ground(self, points):
best_inliers = []
best_plane = None
for _ in range(self.iterations):
# Random 3 points
idx = np.random.choice(len(points), 3, replace=False)
p1, p2, p3 = points[idx]
# Plane equation: ax + by + cz + d = 0
v1 = p2 - p1
v2 = p3 - p1
normal = np.cross(v1, v2)
norm = np.linalg.norm(normal)
if norm < 1e-6:
continue
normal /= norm
d = -np.dot(normal, p1)
# Count inliers
distances = np.abs(points @ normal + d)
inliers = np.where(distances < self.threshold)[0]
if len(inliers) > len(best_inliers):
best_inliers = inliers
best_plane = (normal, d)
return best_inliers, best_plane
def remove_ground(self, points):
inliers, plane = self.detect_ground(points)
mask = np.ones(len(points), dtype=bool)
mask[inliers] = False
return points[mask], points[inliers]
# Demo usage
np.random.seed(42)
# Simulate: ground plane + objects
ground = np.column_stack([
np.random.uniform(-20, 20, 5000),
np.random.uniform(-20, 20, 5000),
np.random.normal(0, 0.05, 5000) # flat ground with noise
])
objects = np.random.randn(500, 3) * 2 + np.array([5, 5, 2])
point_cloud = np.vstack([ground, objects])
detector = SimpleGroundDetector()
objects_only, ground_pts = detector.remove_ground(point_cloud)
print(f"Total points: {len(point_cloud)}")
print(f"Ground points: {len(ground_pts)}")
print(f"Object points: {len(objects_only)}")
Key Takeaways
- LiDAR measures distance using laser light (ToF or FMCW)
- Scanning patterns determine coverage: mechanical (360°), MEMS (raster), Flash (full frame)
- Point clouds are collections of 3D points with optional intensity
- Processing pipeline: raw → filter → segment → register → mesh
- Popular drone LiDARs: RPLidar (hobby), DJI L1/L2 (survey), Livox (mid-range)