Skip to content

AcresLidarModel.h#

Acres/Source/Acres/AcresLidarModel.h Generated

Radiometric and multi-return LiDAR model shared by the CPU-proxy and GPU ray-tracing paths.

Pure maths: no Unreal objects. A beam is sampled by a small bundle of sub-rays inside its divergence footprint; each sub-ray reports the surfaces it met in range order (foliage is partially transmissive, solids stop the ray). ProcessBeam turns those surface hits into contributions (weight x two-way transmission x backscatter), clusters them into echoes by range, detects each echo against a shot-noise threshold, adds range noise and applies the configured return mode to pick which echoes become points.

Geometry comes from AcresSensors.cpp (collision traces and proxies) or AcresLidarGpu.cpp (inline ray tracing). FinishScan in AcresSensors.cpp calls ProcessBeam once per beam on the game thread, in ring-major beam order, so the random draws are reproducible.

Name Type Unit Default Description
MaterialNames extern const TCHAR* Lower-case material names, used as sensors.json "materials" keys and in episode.json.

SubRayPattern#

Sub-ray angular offsets and energy weights for one beam.

One sub-ray: the beam centre with weight 1. Seven: the centre (weight 0.25) plus six rays on a hexagon at 0.35 x DivergenceMrad (weight 0.125 each).

Argument Description
C Model settings (SubRays, DivergenceMrad).
Offsets Receives the offsets, radians, in the beam's perpendicular plane.
Weights Receives the weights (sum 1).
void SubRayPattern(const FAcresLidarModelConfig& C, TArray<FVector2D>& Offsets, TArray<double>& Weights);

Backscatter#

Lambertian-equivalent backscatter of a surface.

rho_bs = rho (1 - a w) cos(theta)^k + s exp(-(theta / width)^2), with rho the diffuse reflectance, a the wet darkening, w the wetness, theta the incidence angle, k the material's Minnaert exponent (1 = Lambertian) and s, width the specular peak and lobe width.

Argument Description
C Model settings (material table).
Material AcresLidar::EMaterial index (out of range falls back to MatOther).
Cos Cosine of the incidence angle; the absolute value is used.
Wetness Surface wetness 0-1.

Returns: Apparent reflectance (dimensionless, can exceed 1 for specular surfaces).

double Backscatter(const FAcresLidarModelConfig& C, uint8 Material, double Cos, double Wetness);

SignalCounts#

Mean received signal for an apparent reflectance at a range.

S = S10 (rho / 0.1) (R10 / R)^2 (1 - exp(-(R / Rov)^2)), where the last factor models the near-field overlap of transmitter and receiver.

Argument Description
C Model settings (SignalAt10PctCounts, RangeAt10PctM or RingRangeAt10PctM, OverlapRangeM).
Rho Apparent reflectance.
RangeM Range, m (floored at 1 mm).
Ring Ring index for a per-ring R10 (out of range or -1: RangeAt10PctM).

Returns: Mean signal, counts.

double SignalCounts(const FAcresLidarModelConfig& C, double Rho, double RangeM, int Ring = -1);

IntensityByte#

Calibrated 8-bit reflectivity, as Ouster sensors report it.

0-100 is linear in diffuse percent (round(100 rho)); 101-255 is logarithmic for specular or retro-reflective returns: 100 + 155 min(1, ln(rho) / ln(64)).

Argument Description
Rho Reflectivity estimate (1 = 100 % Lambertian).

Returns: Intensity byte 0-255 (0 for rho <= 0 or NaN).

uint8 IntensityByte(double Rho);

RainExtinction#

Rain extinction coefficient at the laser wavelength.

alpha = RainDbPerKm x R^RainExponent dB/km, converted to 1/m (divide by 10 log10(e) = 4.343 and by 1000).

Argument Description
C Model settings (RainDbPerKm, RainExponent).
RainMmH Rain rate, mm/h (<= 0 gives 0).

Returns: Extinction coefficient, 1/m.

double RainExtinction(const FAcresLidarModelConfig& C, double RainMmH);

FogExtinction#

Fog and haze extinction coefficient at the laser wavelength, from the meteorological visibility.

Koschmieder: sigma_550 = 3.912 / V. The environment's visibility already contains rain (AcresEnvironmentModel: V_rain = 11.1 km x R^-0.63), so that part is removed first to avoid counting rain twice. The remaining fog/haze extinction is scaled to the laser wavelength with Kim, McArthur & Korevaar (2001): alpha = sigma_550 (lambda / 550 nm)^-q, q = 1.6 (V > 50 km), 1.3 (6-50 km), 0.16 V + 0.34 (1-6 km), V - 0.5 (0.5-1 km), 0 (< 0.5 km: fog droplets are large, extinction is wavelength-neutral), V in km.

Argument Description
C Model settings (WavelengthNm).
VisibilityM Meteorological visibility including rain, m.
RainMmH Rain rate that visibility includes, mm/h.

Returns: Extinction coefficient, 1/m (0 for infinite visibility).

double FogExtinction(const FAcresLidarModelConfig& C, double VisibilityM, double RainMmH);

Atmosphere#

Builds the per-scan atmosphere from the weather.

Argument Description
C Model settings. With C.Weather false the result is clear air (Active() false).
RainMmH Rain rate, mm/h.
VisibilityM Meteorological visibility including rain, m.
ClutterClass Class id written for clutter points.

Returns: The extinction and backscatter coefficients and the drop density.

FAcresLidarAtmosphere Atmosphere(const FAcresLidarModelConfig& C, double RainMmH, double VisibilityM, uint8 ClutterClass);

ColumnTimeOffset#

Firing time of a column within the sweep, relative to the scan stamp (the end of the sweep).

The sensor spins at 1 / PeriodS turns per second starting at ScanStartAzimuthDeg; a column at azimuth Az has turned phi = (Az - start) (ccw) or (start - Az) (cw), wrapped to [0, 2 pi), and fires at phi / (2 pi) x PeriodS - PeriodS. All rings of a column fire together.

Argument Description
C Model settings (SpinDirection, ScanStartAzimuthDeg). MotionDistortion false returns 0 (instantaneous).
AzimuthRad Column azimuth, radians counter-clockwise from forward (FLU).
PeriodS Sweep period, s (1 / lidar.hz).

Returns: Time offset, s, in [-PeriodS, 0).

double ColumnTimeOffset(const FAcresLidarModelConfig& C, double AzimuthRad, double PeriodS);

ProcessBeam#

Echo formation, detection and return selection for one beam.

With an active atmosphere every contribution is scaled by the two-way transmission exp(-2 alpha R) (the calibrated reflectivity is not corrected for it, as on a real sensor), so weak or distant echoes drop below the threshold and their range noise grows with the lower SNR. Before echo formation, clutter contributions are added for slabs of length min_return_separation_m from min_range_m out to ClutterMaxRangeM (or the beam's nearest solid hit): fog is a continuous medium with equivalent reflectance pi beta_fog dR; rain is granular, with N ~ Poisson(n A(r) dR) drops in the slab (A = beam area from the exit diameter and divergence) whose brightness X = E^2 / 2 (E ~ Exp(1): a Marshall-Palmer diameter, backscatter ~ D^2, mean 1), giving pi beta_rain dR x sum(X) / (n A dR). Slabs whose mean signal is 5 sigma below the threshold are skipped. Clutter echoes carry Atm->ClutterClass and material "other".

Argument Description
C Model settings.
Weights Sub-ray weights from SubRayPattern.
Hits All sub-ray hits of the beam, any order.
StdFloorM Range-noise floor (lidar.std_m), m.
MaxRangeM Configured maximum range, m; echoes beyond it are not detected.
Rng The LiDAR noise stream. Exactly two Gaussian draws per echo, echoes visited in range order.
Out Detected, selected returns are appended here in range order.
Atm Atmosphere for this scan; null or inactive = clear air (identical to the model without weather).
ClutterRng Separate stream for the raindrop draws, so the noise stream is untouched by the rain sampling. Null disables clutter.
Ring The beam's ring, for a per-ring R10 (-1: RangeAt10PctM). With PulseLengthM > 0 each contribution's peak is scaled by its pulse-stretch factor (from BetaRad, its range and incidence); an echo's peak factor is the power-weighted mean of its contributions'. Detection and range noise use the peak signal; the reported reflectivity is rho_app x S' / S_peak (an energy measure: no loss from stretching).

Returns: The number of echoes formed (before detection).

int ProcessBeam(const FAcresLidarModelConfig& C, const TArray<double>& Weights, TArrayView<const FAcresLidarSubHit> Hits, double StdFloorM, double MaxRangeM, FAcresPcg32& Rng, TArray<FAcresLidarReturn>& Out, const FAcresLidarAtmosphere* Atm = nullptr, FAcresPcg32* ClutterRng = nullptr, int Ring = -1);

EMaterial#

enum EMaterial : uint8

Surface material at the laser wavelength. The value is the index into FAcresLidarModelConfig::Materials and the "material" byte of each PLY point.

Value Description
MatOther
MatSoil
MatGrass
MatGravel
MatAsphalt
MatConcrete
MatWater
MatCornLeaf
MatSoybeanLeaf
MatTreeFoliage
MatBark
MatShrub
MatBuilding
MatMetalBin
MatVehicle
MatMud
MatPotatoLeaf
MaterialCount

EReturnFlag#

enum EReturnFlag : uint8

Bits of the PLY "return_flags" byte: which role(s) a point plays among the beam's detected echoes.

Value Description
ReturnFirst
ReturnStrongest
ReturnLast
ReturnSecondStrongest

FAcresLidarMaterial#

struct FAcresLidarMaterial

Optical properties of one material at the laser wavelength (905 nm by default).

Name Type Unit Default Description
Reflectivity double .3 Diffuse (Lambertian) reflectance at the laser wavelength, 0-1.
Specular double 0 Peak specular backscatter at normal incidence, as a Lambertian-equivalent reflectance (0-100; can exceed 1).
SpecularWidthDeg double ° 8 Width of the Gaussian specular lobe in incidence angle, degrees.
Transmission double 0 Per-layer gap fraction when the surface is a pass-through (foliage) hit, 0-1. Solid hits ignore it.
WetDarkening double 0 Wet darkening: reflectance is multiplied by (1 - WetDarkening x wetness), wetness 0-1.
IncidenceExponent double 1 Minnaert exponent k of the diffuse term rho cos^k(theta) (AcresLidarPhysics::MinnaertBackscatter): 1 is Lambertian, 0 an apparent reflectance that does not change with incidence (the Polaris' Helios on asphalt, concrete, gravel and grass).
MicroSlopeDeg double ° 0 Standard deviation of the footprint-scale surface slope in the plane of incidence, deg (ground hits only). Each 0.25 m cell of ground has a fixed tilt (AcresLidarPhysics::PlaceNormal), which spreads the grazing-incidence loss of ground returns over range as real surface relief does. 0 = the mesh's own normal.
ReflectivitySd double 0 Footprint-scale albedo texture of ground: the reflectance of each 0.1 m cell is multiplied by exp(sd z - sd^2 / 2), z a fixed N(0, 1) per cell (lognormal, mean 1). Within a run the Helios reports asphalt within +/-7 %, grass +/-24 %, bare soil +/-29 % (sd of ln intensity at 4-8 m). 0 = uniform.
RoughnessM double m 0 Rms height of the ground's small-scale relief (stones, clods, grass blades) seen at normal incidence, m: a ground return's range moves by RoughnessM |cos theta|^-1.5 z (AcresLidarPhysics::RoughRangeSigmaM), z ~ N(0, 1) per beam and scan, so rough ground looks noisier at grazing incidence (gravel 0.7 mm, soil 1.7 mm, grass 2.6 mm for the Helios data). 0 = smooth.

FAcresLidarModelConfig#

struct FAcresLidarModelConfig

LiDAR beam, radiometry and return-selection settings (sensors.json "lidar" block, minus the scan geometry, which lives in FAcresSensorConfig).

Name Type Unit Default Description
WavelengthNm double N·m 905 Laser wavelength, nm. The material table encodes the wavelength for surfaces; the fog/haze extinction uses it for the Kim et al. (2001) wavelength scaling.
DivergenceMrad double 3 Full-angle 1/e^2 beam divergence, milliradians (0-20): the horizontal one when DivergenceVMrad is set.
DivergenceVMrad double 0 Vertical full-angle divergence, mrad (0 = DivergenceMrad, a round beam). The Helios' is 6.9 against 1.6 mrad horizontal. Only the pulse-stretching term uses the ellipse; the sub-ray pattern stays round (DivergenceMrad).
PulseLengthM double m 0 Pulse length in range, m (emitted pulse FWHM x c / 2). > 0 turns on echo stretching: a hit's peak signal is multiplied by L / sqrt(L^2 + dR^2), dR = R tan(theta) beta the footprint's range extent (AcresLidarPhysics), which loses grazing ground at range while the reported reflectivity (an energy measure) stays. 0 = off.
SubRays int 7 Sub-rays per beam: 1 (ideal pencil beam) or 7 (centre + hexagonal ring at 0.35 x divergence).
MaxHitsPerSubRay int 4 Surfaces recorded along one sub-ray (1-8); foliage lets the ray continue, solids stop it.
ContinueStepM double m .25 Skip distance after a foliage hit before the next surface is searched, m.
MinSeparationM double m .75 Echo separation, m (roughly the pulse length): contributions closer than this merge into one echo.
ReturnMode FString TEXT("dual_strongest_last") "strongest", "first", "last", "dual_strongest_last", "dual_first_last" or "triple_first_strongest_last".
MinRangeM double m .5 Echoes nearer than this are not detected, m.
RangeAt10PctM double m 60 Range at which a 10 % Lambertian target at normal incidence is detected with probability DetectionAtSpec, m.
RingRangeAt10PctM TArray&lt;double> m Per-ring RangeAt10PctM (radiometry.ring_range_at_10pct_m), in ring order; empty = RangeAt10PctM for all. The Helios' lower channels are specified at 40, 20 and 10 m against 90 m for the upper ones.
SignalAt10PctCounts double 100 Mean signal of that reference target at RangeAt10PctM, counts.
AmbientCounts double 20 Background counts added to the shot-noise variance.
DetectionAtSpec double .9 Detection probability at the spec point (0.5-0.9999).
OverlapRangeM double m 1.0 Near-field transmitter/receiver overlap scale, m.
SaturationCounts double 65535 Clip for the reported signal, counts.
RangeStdLowSnrM double m .03 Extra range noise at the spec SNR, m; scales with 1/SNR. The floor is lidar.std_m.
PondThresholdM double m .003 Standing water deeper than this turns a ground hit into a specular water surface, m.
Noise bool true False (-SensorNoNoise) removes signal and range noise; the Gaussian draws still happen so streams stay aligned.
MotionDistortion bool true Rolling scan ("motion_distortion"): each column is fired at its own time within the sweep and traced from the sensor pose interpolated at that time, so the cloud shows the skew of a spinning sensor on a moving vehicle. False (or -SensorLidarIdeal / -SensorLidarNoMotion) traces every beam from the pose at the scan stamp.
SpinDirection FString TEXT("cw") Spin sense seen from above: "cw" (clockwise, Velodyne-style) or "ccw".
ScanStartAzimuthDeg double ° 180 Azimuth where a sweep starts (and the previous one ends), degrees counter-clockwise from forward (FLU). 180 puts the seam behind the sensor.
Weather bool true Rain and fog along the beam ("weather.enabled"): two-way extinction, detection loss and backscatter clutter. False (or -SensorLidarIdeal / -SensorLidarNoWeather) gives clear-air scans whatever the weather.
RainDbPerKm, RainExponent double Rain extinction at the laser wavelength, alpha [dB/km] = RainDbPerKm x R^RainExponent with R in mm/h (Carbonneau 1998 / ITU-R free-space-optics rain law; raindrops are large, so near-IR ~ visible).
FogLidarRatioSr, HazeLidarRatioSr, RainLidarRatioSr double Extinction-to-backscatter (lidar) ratios, sr: fog droplets (~18.8 sr for liquid water droplets, O'Connor et al. 2004), haze aerosol (~50-70 sr for continental aerosol, Mueller et al. 2007) and rain (large drops backscatter weakly; engineering estimate, see sensors.json provenance). The fog/haze ratio is blended by the fog/haze visibility: droplets below 0.5 km, aerosol above 1 km, 1/ratio linear in between (the regimes of Kim et al.).
RainDropsPerM3, RainDropsExponent double Raindrop number density N = RainDropsPerM3 x R^RainDropsExponent, m^-3 (Marshall & Palmer 1948: N0 / Lambda = 8000 / (4.1 R^-0.21)).
ExitDiameterM double m .01 Beam diameter at the exit window, m (the footprint then grows with the divergence).
ClutterMaxRangeM double m 10 Backscatter clutter (false returns from droplets) is only simulated up to this range, m.
RainOverrideMmH, VisibilityOverrideM double Command-line weather overrides (-SensorLidarRain=<mm/h>, -SensorLidarVisibility=<m>); negative = use the environment.
Materials FAcresLidarMaterial Optical table indexed by AcresLidar::EMaterial.
DetectionThresholdCounts double 0 Cached DetectionThreshold(), counts; read by ProcessBeam for every beam.

FAcresLidarModelConfig::FAcresLidarModelConfig#

Fills the material table with approximate 905 nm defaults (sensors.json normally overrides them).

FAcresLidarModelConfig();

FAcresLidarModelConfig::Parse#

Reads the model keys of the sensors.json "lidar" block, including "radiometry" and "materials".

Argument Description
Lidar The "lidar" JSON object. Missing keys keep their defaults.
void Parse(const TSharedPtr<FJsonObject>& Lidar);

FAcresLidarModelConfig::ApplyCommandLine#

Applies -SensorLidarReturnMode=, -SensorLidarSubRays=, -SensorLidarDivergence=, -SensorNoNoise, -SensorLidarIdeal (instantaneous clear-air scan: no motion distortion, no weather), -SensorLidarNoMotion, -SensorLidarNoWeather, -SensorLidarRain= (mm/h) and -SensorLidarVisibility= (m).

Argument Description
CommandLine The command line to read (null: the process's; "" gives the file settings with the derived values updated, as the sensor profile's reference needs).
void ApplyCommandLine(const TCHAR* CommandLine = nullptr);

FAcresLidarModelConfig::Validate#

Checks every parameter against its supported range; stops the game with a checkf message if not.

void Validate() const;

FAcresLidarModelConfig::DetectionThreshold#

Computes the detection threshold T in counts from the current settings.

Chosen so that the reference target (S10 = SignalAt10PctCounts at RangeAt10PctM) is detected with probability DetectionAtSpec: T = S10 - z sqrt(S10 + AmbientCounts), z = inverse normal CDF of DetectionAtSpec (found by bisection). Use the cached DetectionThresholdCounts in hot code.

Returns: The threshold, counts (must be positive).

double DetectionThreshold() const;

FAcresLidarModelConfig::UpdateDerived#

Recomputes the cached derived values (DetectionThresholdCounts). Called by the constructor, Parse and ApplyCommandLine; call it again after changing fields by hand (Validate checks the cache is current).

void UpdateDerived();

FAcresLidarModelConfig::ToJson#

Serialises the settings (and the derived detection threshold) as compact JSON.

FString ToJson() const;

FAcresLidarModelConfig::Manifest#

ToJson plus plain-text descriptions of the beam, echo, signal, return-selection, intensity, wetness and material-provenance models, for episode.json "lidar.model".

TSharedPtr<FJsonObject> Manifest() const;

FAcresLidarAtmosphere#

struct FAcresLidarAtmosphere

Rain and fog along the beams of one scan, at the laser wavelength. Built by AcresLidar::Atmosphere.

Two-way Beer-Lambert extinction exp(-2 alpha R) scales every contribution; the volume backscatter coefficients (beta = alpha / lidar ratio) and the raindrop density drive the near-range clutter returns in ProcessBeam.

Name Type Unit Default Description
RainMmH, VisibilityM double Inputs: rain rate, mm/h, and meteorological (550 nm, 2 % contrast) visibility including rain, m.
AlphaRainPerM, AlphaFogPerM double Extinction coefficients at the laser wavelength, 1/m: rain, and fog + haze (visibility minus its rain part).
BetaRainPerMSr, BetaFogPerMSr double Volume backscatter coefficients, 1/(m sr).
DropsPerM3 double 0 Raindrop number density, m^-3.
ClutterClass uint8 0 Semantic class written for clutter points (the recorder's "precipitation" class).

FAcresLidarAtmosphere::Alpha#

Total extinction coefficient, 1/m.

double Alpha() const ;

FAcresLidarAtmosphere::Active#

True when there is anything to simulate (weather on and some rain or fog/haze).

bool Active() const ;

FAcresLidarSubHit#

struct FAcresLidarSubHit

One surface met by one sub-ray.

Name Type Unit Default Description
RangeM float m 0 Distance from the sensor, m.
Cos float 1 |cos| of the incidence angle (0.5 when the normal is unknown).
Wetness float 0 Surface wetness 0-1.
BetaRad float rad 0 Full-angle beam divergence in the plane of incidence, rad, for the pulse-stretching term (0 = none; foliage leaves none).
Albedo float 1 Reflectance multiplier of this spot (ground texture; 1 = the material's own).
SubRay uint8 0 Index into the sub-ray pattern.
Class uint8 0 Semantic class id.
Material uint8 0 AcresLidar::EMaterial.
bFoliage bool false True for partially transmissive surfaces (tree crowns, shrubs, crops, ground cover).

FAcresLidarReturn#

struct FAcresLidarReturn

One point produced from one echo; mirrors a PLY vertex.

Name Type Unit Default Description
RangeM float m 0 Measured range with noise, m.
Reflectivity float 0 Range-compensated reflectivity estimate (1 = 100 % Lambertian).
Signal uint16 0 Raw signal including 1/R^2 and near-field overlap, counts (clipped at SaturationCounts).
Intensity uint8 0 Calibrated reflectivity byte 0-255. Class,
Class uint8 0 See above.
Material uint8 0 From the echo's largest contributor.
Index uint8 1 1-based return index in range order.
Count uint8 1 Points emitted for this beam.
Flags uint8 0 EReturnFlag bits.