跳转至

11 - LiDAR 点云模拟

本章介绍如何使用 LESS 模拟机载 LiDAR (ALS) 和地面 LiDAR (TLS) 点云数据。

LiDAR 模拟原理

LiDAR(光探测与测距)通过发射激光脉冲并记录回波信号来获取三维空间信息。LESS 模拟的过程是:

  1. 根据扫描仪参数和平台轨迹,计算每个激光脉冲的发射位置和方向
  2. 对 Mesh 计算表面交点,对 TurbidBoundary 计算激光在介质内的传播区间
  3. 计算脉冲展宽、波束发散、体散射、路径消光和多次回波
  4. 输出三维点云,也可以保留完整波形

Mesh 与统计植被

LiDAR 使用场景中已有的几何和光学属性,不需要创建另一套树冠。显式叶片使用 Mesh;统计树冠使用 TurbidBoundaryOpticalVegetation

tree.add_component(
    "crown",
    less.TurbidBoundary("tree.obj", group="crown"),
)
tree.set_property(
    "crown",
    less.OpticalVegetation(
        leaf=less.Lambertian(
            reflectance=0.35,
            transmittance=0.15,
        ),
        leaf_area_density=1.5,
        leaf_angle_distribution="spherical",
    ),
)

统计树冠的回波由沿激光路径连续分布的叶片后向散射形成。地面和 Mesh 回波在 穿过树冠时也会发生衰减。单站 LiDAR 的发射路径与接收路径重合,因此模型使用 同一空隙的相关传播概率,而不是把上下行遮挡当成两个独立事件。这一处理对应 零相位角处的遮挡隐藏效应,不需要用户设置经验热点因子。

Mesh 和 TurbidBoundary 可以出现在同一个场景或同一个 Object 中。三个后端 使用相同的统计树冠波形定义。

LiDAR 系统的三个组件

LESS 的 LiDAR 模拟由三个组件构成:

LiDARSurvey = Scanner + Platform + Legs/Positions

Scanner —— 扫描仪参数

import less

scanner = less.Scanner(
    beam_divergence=0.5,     # 波束发散角 (mrad)
    pulse_rate=200_000,      # 脉冲重复频率 (Hz)
    scan_freq=80.0,          # 扫描频率 (Hz)
    scan_angle=27.0,         # 半扫描角 (°)
    wavelengths=[1064],      # 激光波长 (nm)
    beam_samples=8,          # 波束采样数(模拟波束发散)
)
参数 含义 ALS 典型值 TLS 典型值
beam_divergence 波束发散角 (mrad) 0.3-0.5 0.1-0.3
pulse_rate 脉冲频率 (Hz) 100k-400k -
scan_freq 扫描频率 (Hz) 50-200 -
scan_angle 半扫描角 (°) 15-30 -
wavelengths 激光波长 (nm) 1064 1550
beam_samples 波束采样点数 4-16 1-4
angular_resolution TLS 角分辨率 (°) - 0.05-0.2

Platform —— 平台类型

# 无人机
platform = less.UAV(altitude=50.0, speed=5.0)

# 有人飞机
platform = less.Aircraft(altitude=1000.0, speed=60.0)

# 地面三脚架
platform = less.Tripod(height=1.5)

# 车载
platform = less.Vehicle(height=2.5, speed=5.0)

Legs/Positions —— 飞行路径或扫描位置

# ALS:定义飞行航线(起点 → 终点)
legs = [
    less.Waypoint(-10, 25, active=True),       # 起点
    less.Waypoint(60, 25, active=True),         # 终点
]

# TLS:定义扫描站点
legs = [
    less.ScanPosition(
        x=25, y=25,                             # 站点位置
        azimuth_range=(0, 360),                  # 水平扫描范围
        zenith_range=(5, 80),                    # 垂直扫描范围
    ),
]

完整示例:机载 LiDAR (ALS)

import less
import numpy as np

# ── 构建森林场景 ─────────────────────────────────────────────
scene = less.Scene()
scene.size = 50.0
scene.repetitive = False  # 周期与 LiDAR 工作流当前均未公共发布

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.examples.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))

rng = np.random.RandomState(42)
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))
sensor_als = less.LiDARSurvey(
    scanner=less.Scanner(
        beam_divergence=0.5,
        pulse_rate=200_000,
        scan_freq=80.0,
        scan_angle=27.0,
        wavelengths=[1064],
        beam_samples=8,
    ),
    platform=less.UAV(altitude=50.0, speed=5.0),
    legs=[
        less.Waypoint(-10, 25, active=True),
        less.Waypoint(60, 25, active=True),
    ],
    range_resolution=0.15,     # 距离分辨率 (m)
    name="ALS LiDAR",
)

# ── 模拟 ─────────────────────────────────────────────────────
print("开始 ALS 模拟...")
pointcloud = scene.simulate(sensor_als)

# ── 保存点云 ─────────────────────────────────────────────────
pointcloud.save("11_als_pointcloud.ply")
print(f"点云大小: {pointcloud.num_points} 个点")
print(f"X 范围: {pointcloud.points[:, 0].min():.1f} ~ {pointcloud.points[:, 0].max():.1f} m")
print(f"Y 范围: {pointcloud.points[:, 1].min():.1f} ~ {pointcloud.points[:, 1].max():.1f} m")
print(f"Z 范围: {pointcloud.points[:, 2].min():.1f} ~ {pointcloud.points[:, 2].max():.1f} m")

配套脚本:scripts/11_lidar.py

地面 LiDAR (TLS)

TLS 从固定站点进行半球扫描,获得高密度的近距离点云:

sensor_tls = less.LiDARSurvey(
    scanner=less.Scanner(
        beam_divergence=0.2,
        wavelengths=[1550],
        beam_samples=1,
        angular_resolution=0.1,     # 角分辨率 0.1°
    ),
    platform=less.Tripod(height=1.5),
    legs=[
        less.ScanPosition(
            x=25, y=25,
            azimuth_range=(0, 360),    # 全方位扫描
            zenith_range=(5, 80),      # 5° ~ 80°(避开天顶和地面)
        ),
    ],
    range_resolution=0.02,     # 2 cm 距离分辨率
    name="TLS",
)

pointcloud_tls = scene.simulate(sensor_tls)
pointcloud_tls.save("11_tls_pointcloud.ply")

TLS vs ALS 对比

特性 ALS (机载) TLS (地面)
视角 自上而下 自下而上/水平
覆盖面积 大(航线长度) 小(站点周围)
点密度 中等(1-20 pt/m²) 高(>100 pt/m²)
树冠穿透 较好 遮挡严重
典型应用 大面积测图、CHM 树干测量、结构分析

点云数据结构

PointCloud 对象包含以下属性:

pc = scene.simulate(sensor_als)

# 坐标 (N × 3)
print(pc.points.shape)       # (N, 3)
print(pc.points[:5])         # 前 5 个点的 xyz

# 点数
print(pc.num_points)         # 返回点总数

# 强度
print(pc.intensity.shape)    # (N,)

# 回波次序
print(pc.return_number)      # 第几次回波
print(pc.num_returns)        # 该脉冲总回波数

获取完整波形

pc = scene.simulate(sensor_als, product="waveform")

waveform = pc.waveform["data"]
print(waveform.shape)                    # pulse × range bin × wavelength
print(pc.waveform["min_range"])          # 第一个距离窗口的起点,m
print(pc.waveform["range_resolution"])   # 距离采样间隔,m
print(pc.waveform["wavelengths"])        # 激光波长,nm

point_cloudwaveform 使用同一套传播计算。离散回波是从合并后的表面与 体散射波形中提取的,因此点云中的树冠回波、林下回波和地面回波与保存的波形 保持一致。

多航线扫描

真实的机载 LiDAR 任务通常包含多条平行航线:

# 三条平行航线,间距 20m
legs = []
for y_offset in [15, 25, 35]:
    legs.extend([
        less.Waypoint(-10, y_offset, active=True),
        less.Waypoint(60, y_offset, active=True),
    ])

sensor_multi = less.LiDARSurvey(
    scanner=less.Scanner(
        beam_divergence=0.5, pulse_rate=200_000,
        scan_freq=80.0, scan_angle=20.0,
        wavelengths=[1064], beam_samples=8,
    ),
    platform=less.UAV(altitude=50.0, speed=5.0),
    legs=legs,
    range_resolution=0.15,
    name="Multi-strip ALS",
)

应用场景

应用 所需数据 方法
冠层高度模型 (CHM) ALS 点云 最高点 - 地面点
LAI 反演 ALS 点云 Gap fraction 方法
单木分割 ALS/TLS 分水岭、深度学习
胸径测量 (DBH) TLS 圆拟合
生物量估算 ALS 高度-面积统计

下一步

相关 API

  • less.Scannerless.LiDARSurvey
  • less.Aircraftless.UAVless.Tripodless.Vehicle
  • less.Waypointless.ScanPosition
  • less.LiDARProduct.save()less.LiDARProduct.waveforms