Motion Artifacts#

Motion during a scan can distort a lidar point cloud. Motion BVH enables tracing against changing sensor and scene transforms. The Lidar generic core model can then compensate the output for sensor motion using omni:sensor:Core:outputMotionCompensationState.

The Lidar generic wpm model carries the same control as a bool, omni:sensor:LidarWpm:outputMotionCompensationState (USD Attributes), and needs the motion BVH in the same way. The Radar generic wpmDmat model has no compensation attribute (Outputs).

Before you start, you should have a lidar rendering points (Your First Lidar Sensor), know the renderer options a sensor scene depends on (Output Configuration) and know the attributes that shape the lidar’s output (Configure Lidar Output).

Motion settings#

The following example retains motion distortion with the default NONCOMPENSATED setting:

#usda 1.0

def OmniLidar "Lidar" (
    prepend apiSchemas = ["OmniSensorGenericLidarCoreAPI"]
)
{
    token omni:sensor:Core:outputMotionCompensationState = "NONCOMPENSATED"
}
from pxr import Gf, Sdf

lidar = stage.DefinePrim("/Lidar", "OmniLidar")
lidar.AddAppliedSchema("OmniSensorGenericLidarCoreAPI")
lidar.CreateAttribute(
    "omni:sensor:Core:outputMotionCompensationState", Sdf.ValueTypeNames.Token, custom=False
).Set("NONCOMPENSATED")

Motion BVH is selected when creating the renderer. It enables tracing at the sensor and scene poses corresponding to each firing time. Motion compensation transforms the resulting coordinates into the requested output frame. These are separate operations, illustrated below.

Three rows share one time axis of a single scan while the sensor moves to the right. Top row, motion BVH off: every ray starts from the sensor's frame-end pose and meets a frozen scene, so the points show no motion. Middle row, motion BVH on and NONCOMPENSATED: each ray starts from the sensor's pose at its own tick and meets the scene where it is at that instant, and the points keep that spread as a distortion. Bottom row, motion BVH on and COMPENSATED: the same points are re-expressed in one frame and the distortion is gone.

Effect of motion BVH and motion compensation on a scan. From top to bottom: motion BVH disabled; enabled with NONCOMPENSATED output; enabled with COMPENSATED output. Schematic, not to scale.#

Without motion BVH, rays use the sensor’s frame-end pose and a fixed scene. The model still produces points but omits motion during the scan and logs a warning once.

instantLidar fires the entire scan at one instant, leaving no within-scan motion to compensate. Without motion BVH, it also uses the frame-end pose. See Configure Lidar Output for instant-lidar timing.

Setting COMPENSATED does not enable motion BVH. With motion BVH off, compensation still runs, but the trace contains no within-scan motion. Output metadata does not identify this mismatch. Configure both settings when validating a moving sensor.

Enable motion BVH when you create the renderer#

Select ENABLE when creating the renderer. See Motion BVH for Python and C examples, GPU support, available modes and requirements for other sensors, including rolling-shutter cameras.

Motion compensation#

NONCOMPENSATED (default) reports detections without correcting sensor motion, retaining the distortion of a physical scanning sensor. COMPENSATED uses each point’s timestamp and sensor motion to transform detections into the requested output frame. This can improve alignment for mapping and localization. For application-side deskewing, request firing times through TimeOffsetNs.

See USD Attributes for the controls and Outputs for motionCompensationState, which identifies the state used for each frame.

Two examples show what the states do to a scene:

  • A rotary lidar moving forward and scanning a circle on the ground. Non-compensated, the circle comes back as a complete closed circle, every point at the same range, the sensor-to-ground distance. Compensated, the circle has a tear in it, because the sensor has moved by the time the scan closes.

  • A moving solid-state line-scanning lidar seeing a vertical pole. Non-compensated, the pole looks slanted. Compensated, the pole looks vertical.

Motion compensation example (orange: compensated, gray: non-compensated).

Lidar points from the circle and pole, with motion compensation (orange) and without (gray).#

Two top-down renders of one sweep of the panel scene from a lidar moving forward at 8 m/s, points colored by TimeOffsetNs from dark at the start of the scan to light at its end. Left, NONCOMPENSATED: closed rings on the ground, and the glass pane ahead doubled, its early and late points 0.8 m apart. Right, COMPENSATED: the rings torn where the scan wraps, the pane whole.

One sweep of the panel scene from a lidar moving forward at 8 m/s, delivered NONCOMPENSATED (left) and COMPENSATED (right), seen from above, points colored by TimeOffsetNs. With the motion kept in, the ground rings close and the pane ahead, scanned at the start of the sweep and again at its end, comes back twice, 0.8 m apart; with the motion taken out, the rings tear where the scan wraps and the pane is whole.#

Compensation supports runtime updates. Frames already in flight retain their previous state. With partialOutputs = false, conversion happens on the completing step, so a mid-scan edit applies to the entire delivered scan. See Runtime Parameter Updates for update handling, Configure Lidar Output for output configuration and Outputs for state metadata.

Verification#

Test motion BVH and compensation separately.

For motion BVH, animate the sensor or a target during a scan and compare runs with BVH enabled and disabled. Without it, within-step motion does not distort the trace. A nonzero point count alone cannot verify motion BVH: the model produces points in both cases; see behavior without motion BVH.

The runnable lidar scene is static. Add time-sampled sensor or target transforms and advance USD time each step, as the radar example does for its approaching target. See the attached update loop and clock descriptions in ovstage Integration.

For compensation, compare Coordinates with NONCOMPENSATED and COMPENSATED in the same moving-sensor scene. Check each frame’s motionCompensationState on Outputs to confirm the requested state applied. If coordinates are identical, check whether the sensor moved during the scan and whether the edit reached the model. Compensation removes sensor motion only; moving targets retain their distortion. instantLidar has no within-scan motion to compensate.

Potential issues#

See the Symptom Index for additional causes and checks.

  • A moving sensor or target leaves the points of each step undistorted: the motion BVH is off, because it was not requested or because the GPU cannot trace against motion. Refer to Motion BVH and to what the lidar does without it. Symptom Index: points in the wrong place.

  • The point cloud is smeared or sheared while the platform moves: that is NONCOMPENSATED behaving as designed. Author COMPENSATED, or deskew downstream from TimeOffsetNs. Symptom Index: points in the wrong place.

  • COMPENSATED is authored and the smearing is unchanged: read the motionCompensationState param. 0 means the authored state is not the one in force: the attribute never reached the model. 1 means compensation ran: either the motion BVH is off and there was no sensor motion in the trace to compensate (refer to both settings), or the smear belongs to a moving target, which compensation leaves in place. Symptom Index: points in the wrong place.

  • Scan-to-scan alignment degrades in a mapping or localization stage: the scans are motion-distorted. Compensate at the sensor, or deskew before the mapper. Symptom Index: points in the wrong place.

  • The attribute was changed while the sensor was running and nothing happened: read the motionCompensationState param of a later frame. If it still reports the old state, the edit did not reach the model; Runtime Parameter Updates says how an edit reaches a running sensor and which outcomes it can have. Symptom Index: Runtime edits have no effect.