Motphys

Multi-beam LiDAR

MotrixSim 0.10.0 introduces multi-beam LiDAR based on physical collision geometry. The sensor outputs point clouds in the world coordinate frame for robot obstacle avoidance, terrain perception, localization and mapping, and reinforcement-learning observations. Real-time point-cloud visualization helps verify the mounting pose and scan results.

Video 1: Robot locomotion example with an Ouster OS1 LiDAR mounted on a Go2

Run from the motrixsim-docs directory:

uv run examples/control/robot_locomotion.py --robot go2 --scene playground --lidar ouster_os1_rev6_32ch_10hz_512res

MJCF Configuration

LiDAR configuration in MJCF consists of three parts: body/site defines the mounting position and orientation, asset/lidar defines a reusable model and scan geometry, and sensor/lidar attaches the sensor to a specified site.

<asset>
  <lidar name="grid_64" cutoff="25" pattern="grid"
         hscan="1024" vscan="64" hrange="-180 180" vrange="-15 15"/>
</asset>
<worldbody>
  <body name="robot">
    <site name="lidar_site" pos="0.25 0 0.20" quat="0.5 0.5 0.5 0.5"/>
  </body>
</worldbody>
<sensor>
  <lidar name="roof_lidar" asset="grid_64" site="lidar_site"
         exclude="parentsubtree"/>
</sensor>
site.pos / site.quatSet the LiDAR mounting position and orientation on the robot.
asset/lidar.nameDefine a reusable LiDAR profile name for sensor instances.
cutoffSet the maximum ray distance in metres.
patternSelect the scan pattern: grid for a uniform grid or rings for a rotating multi-line scan.
hscan / vscanSet the horizontal and vertical samples per frame, which determine Grid point-cloud resolution.
hrange / vrangeSet horizontal and vertical scan-angle ranges in degrees.
sensor/lidar.assetReference the LiDAR profile defined under asset.
sensor/lidar.siteSpecify the mounting site that defines ray origins and directions.
excludeConfigure filtering for the robot's own colliders; parentsubtree can be used for multi-link robots.
hzOptional full-scan frequency; when omitted, a complete scan is generated at every simulation step.

Reading Sensor Data

After each step(), read the point cloud with get_sensor_value(). The example below reshapes a Grid point cloud to (1024, 64, 3); missed rays are represented by zero vectors:

model.step(data)
points = model.get_sensor_value("roof_lidar", data).reshape(1024, 64, 3)
hit_points = points[(points != 0).any(axis=-1)]

Performance

The results below come from an end-to-end Python benchmark in the playground scene. Each LiDAR uses a 1024×64 Grid with hz omitted, producing 65,536 rays per simulation step. Tests ran on an AMD Ryzen 9 9950X (16 cores, 32 threads), with the median of three rounds reported. Measurements cover the full physics pipeline within model.step() and exclude rendering.

MotrixSim 0.10.0 end-to-end LiDAR raycast throughput results

With one LiDAR in each of 1,024 parallel environments, aggregate throughput reached approximately 80.656 million raycasts per second. This is a measured result for the software and hardware configuration described above, not the peak throughput of an isolated raycast kernel.

cd motrixsim-python/motrixsim-docs
uv run python examples/bench/lidar.py --case all