Skip to content

25 - LiDAR point clouds and waveforms

This chapter simulates airborne (UAV) and terrestrial LiDAR and produces discrete point clouds and full waveforms.

What is simulated

  1. The emission position and direction of every laser pulse, from the scanner parameters and the platform trajectory;
  2. intersections with triangle meshes and the path of the beam through statistical vegetation;
  3. pulse width, beam divergence, volume scattering, attenuation along the path and multiple returns;
  4. the point cloud as output, and optionally the full waveform of every pulse.

LiDAR uses the geometry and optical properties already in the scene. Returns from a statistical crown come from the backscatter of leaves distributed continuously along the beam path. In a monostatic LiDAR the emitted and received paths coincide, and the return calculation takes this into account, so no extra hotspot parameter is needed. Triangle meshes and statistical vegetation can be in the same scene.

Parts of a LiDAR survey

A LiDAR survey consists of a scanner, a platform and flight lines (or scan positions):

import less

scanner = less.Scanner(
    beam_divergence=0.5,     # beam divergence (mrad)
    pulse_rate=20_000,       # pulse rate (Hz)
    scan_freq=80.0,          # scan frequency (Hz)
    scan_angle=27.0,         # half scan angle (degrees)
    wavelengths=[1064],      # laser wavelength (nm)
    beam_samples=4,          # samples per beam, representing the footprint size
)
Parameter Meaning Typical airborne Typical terrestrial
beam_divergence Beam divergence (mrad) 0.3–0.5 0.1–0.3
pulse_rate Pulse rate (Hz) 10⁵–4×10⁵ —
scan_freq Scan frequency (Hz) 50–200 —
scan_angle Half scan angle (degrees) 15–30 —
wavelengths Wavelengths (nm) 1064 1550
beam_samples Samples per beam 4–16 1–4
angular_resolution Angular resolution of terrestrial scans (degrees) — 0.05–0.2
max_returns Maximum number of returns per pulse — —

Platforms:

uav = less.UAV(altitude=50.0, speed=5.0)
aircraft = less.Aircraft(altitude=1000.0, speed=60.0)
tripod = less.Tripod(height=1.5)
vehicle = less.Vehicle(height=2.5, speed=5.0)

Airborne surveys use waypoints to define flight lines; terrestrial surveys use scan positions to define the scan range:

flight_line = [less.Waypoint(-10, 25, active=True), less.Waypoint(60, 25, active=True)]
station = [less.ScanPosition(x=25, y=25, azimuth_range=(0, 360), zenith_range=(5, 80))]

UAV LiDAR

scene = less.Scene()
scene.size = 50.0
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.illumination = less.Illumination(
    source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())

tree = less.Object("ash", mesh=less.asset_path("FREX.obj"))
tree.set_property("leaves", less.Prospect(cab=40, car=10, cw=0.012, cm=0.008, N=1.5))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.08))
positions = less.place.random(scene, n=60, min_dist=3.0, seed=42)
n = len(positions)
scene.add(tree, positions=positions,
          scales=less.place.uniform_scale(n, 0.6, 1.5, seed=42),
          rotations=less.place.random_rotation(n, seed=42))

als = less.LiDARSurvey(
    scanner=scanner,
    platform=uav,
    legs=flight_line,
    range_resolution=0.15,     # range resolution (m)
    name="UAV LiDAR",
)
cloud = scene.simulate(als)
cloud.save("uav_pointcloud.ply")

print(cloud.num_points, "points")
print("height range:", cloud.points[:, 2].min(), "to", cloud.points[:, 2].max(), "m")

Terrestrial LiDAR

Terrestrial LiDAR scans a hemisphere from a fixed position, with very high point density close by:

tls = less.LiDARSurvey(
    scanner=less.Scanner(beam_divergence=0.2, wavelengths=[1550], beam_samples=1,
                         angular_resolution=0.5),      # 0.5° angular resolution
    platform=tripod,
    legs=station,
    range_resolution=0.02,
    name="TLS",
)
tls_cloud = scene.simulate(tls)
tls_cloud.save("tls_pointcloud.ply")
Airborne Terrestrial
View From above From below and sideways
Coverage Large areas along flight lines Around the scan position
Point density 1–20 points per m² Over 100 points per m²
Strengths Canopy height, mapping large areas Stems, inner canopy structure

Point cloud contents

print(cloud.points.shape)       # (points, 3) coordinates
print(cloud.intensity.shape)    # intensity
print(cloud.return_number[:10]) # return number
print(cloud.num_returns[:10])   # number of returns of the pulse

Discrete returns are extracted from each pulse's waveform: the strongest peaks are kept (at most Scanner.max_returns) and numbered from near to far; intensity is summed over the wavelength channels. cloud.return_timeline gives the time span of pulse emission and return arrival.

Full waveforms

waveform_product = scene.simulate(als, product="waveform")
waveform = waveform_product.waveform["data"]
print(waveform.shape)                                   # (pulses, range bins, wavelengths)
print(waveform_product.waveform["min_range"])           # start of the first range bin (m)
print(waveform_product.waveform["range_resolution"])    # length of a range bin (m)

Point clouds and waveforms come from the same propagation, so canopy, understorey and ground returns in the point cloud agree with the waveform.

Several flight lines

strips = []
for y in (15, 25, 35):
    strips += [less.Waypoint(-10, y, active=True), less.Waypoint(60, y, active=True)]

multi_strip = less.LiDARSurvey(
    scanner=less.Scanner(beam_divergence=0.5, pulse_rate=20_000, scan_freq=80.0,
                         scan_angle=20.0, wavelengths=[1064], beam_samples=4),
    platform=uav, legs=strips, range_resolution=0.15, name="Multi-strip")

Common applications

Application Data Method
Canopy height model Airborne point cloud Highest point minus ground elevation
Leaf area index Airborne point cloud Gap fraction
Individual tree segmentation Airborne or terrestrial Watershed, deep learning
Diameter at breast height Terrestrial Circle fitting

Reconstructing scenes from point clouds is covered in chapter 17.

Companion script: scripts/25_lidar.py

See A5 - LiDAR ranging and waveforms for independent zero-divergence and finite-beam validation, with rotatable point clouds, waveform comparisons and sampling-convergence results.

Next