TLS Risley Prism Scanner

Notebook: Hannah Weiser, 2026

NOTE: This notebook is a work in progress and is still missing functionality that has to be ported from main to alpha-dev. The respective notebook cells are thus disabled.

This demo notebook demonstrates a terrestial laser scanning (TLS) survey with a risley beam deflector. This scanning technology results a non-repetitive, retina-like scan pattern, which means the point density depends on the integration time.

[1]:
# ToDO: Re-run notebook once the maximum duration parameter is in alpha-dev - also add the mid-100 as tls scanner and demonstrate here
import helios
import numpy as np
import matplotlib.pyplot as plt

Virtual scene

We use the box scene, which is perfect for demonstrating the non-repetitive scan pattern of a risley beam deflector on a vertical wall.

[2]:
box = helios.ScenePart.from_obj("../data/sceneparts/basic/box/box100.obj")
scene = helios.StaticScene([box])

Scanner and platform

We are simulation a Livox Mid-40 scanner mounted on a tripod. For this deflector type, the scan pattern is controlled by the rotation speeds (rotorFreq1_Hz and rotorFreq2_Hz) of two rotating risley prisms. This design on which the low-cost Livox scanners are based is described in detail in Liu et al. (2022). More information on the Livox sensors and their point cloud characteristics can be obtained from the Livox Wiki. For further reading on rotational risley prisms, see Duma & Schitea (2018).

[3]:
scanner = helios.scanner_from_name("livox_mid40")
platform = helios.platform_from_name("tripod")

Scan positions

To demonstrate influence of integration times on point density, we simulate three scan positions with integration times of 0.2 s, 1 s and 2 s.

survey = helios.Survey(scene=scene, scanner=scanner, platform=platform) x = y = z = 0.0 leg_1 = survey.add_leg(x, y, z, max_duration=0.2 * helios.units.s) leg_1 = survey.add_leg(x, y, z, max_duration=1.0 * helios.units.s) leg_1 = survey.add_leg(x, y, z, max_duration=2.0 * helios.units.s)

Running the survey

points, trajectories = survey.run( verbosity=helios.LogVerbosity.VERBOSE, format=helios.OutputFormat.NPY )

Visualizing the results

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 8)) coords = points["position"] leg1 = points["point_source_id"] == 0 leg2 = points["point_source_id"] == 1 leg3 = points["point_source_id"] == 2 ax1.scatter(coords[leg1, 0], coords[leg1, 2], s=0.01, c=points["gps_time"][leg1]) ax2.scatter(coords[leg2, 0], coords[leg2, 2], s=0.01, c=points["gps_time"][leg2]) ax3.scatter(coords[leg3, 0], coords[leg3, 2], s=0.01, c=points["gps_time"][leg3]) for ax in (ax1, ax2, ax3): ax.set_xlabel("X") ax.set_ylabel("Z") ax.set_aspect("equal", "box") ax1.set_title("Integration time: 0.2 s") ax2.set_title("Integration time: 1 s") ax3.set_title("Integration time: 2 s")

We can see a nonrepeating pattern, where with increasing integration time, the point density increases. This is different for other deflector types, such as rotating mirrors, where the scanner itself needs to move or rotate in order to result in a nonrepeating pattern. As stated in Liu et al. (2022), the scanning mechanisms results in a retina-like scanning pattern, with the highest point density in the centre of the field of view.