Import upstream snapshot c19f713c415a699a79d71cd96aa13c3104a05047
nightly / Test against latest dependencies (py3.10) (push) Has been cancelled
nightly / Test against latest dependencies (py3.13) (push) Has been cancelled
tests / tests (3.13, locked) (push) Has been cancelled
tests / tests (3.13, unlocked) (push) Has been cancelled
tests / pyright (3.10) (push) Has been cancelled
tests / lint-format (push) Has been cancelled
tests / tests (3.10, locked) (push) Has been cancelled
tests / tests (3.11, locked) (push) Has been cancelled
tests / tests (3.12, locked) (push) Has been cancelled
tests / pyright (3.11) (push) Has been cancelled
tests / pyright (3.12) (push) Has been cancelled
tests / pyright (3.13) (push) Has been cancelled
tests / ty-check (3.10) (push) Has been cancelled
tests / ty-check (3.11) (push) Has been cancelled
tests / ty-check (3.12) (push) Has been cancelled
tests / ty-check (3.13) (push) Has been cancelled
tests / stubs (push) Has been cancelled
tests / smoke-test (push) Has been cancelled
Docker / check_paths (push) Has been cancelled
Docker / build (push) Has been cancelled
docs / build (push) Has been cancelled
nightly / Test against latest dependencies (py3.10) (push) Has been cancelled
nightly / Test against latest dependencies (py3.13) (push) Has been cancelled
tests / tests (3.13, locked) (push) Has been cancelled
tests / tests (3.13, unlocked) (push) Has been cancelled
tests / pyright (3.10) (push) Has been cancelled
tests / lint-format (push) Has been cancelled
tests / tests (3.10, locked) (push) Has been cancelled
tests / tests (3.11, locked) (push) Has been cancelled
tests / tests (3.12, locked) (push) Has been cancelled
tests / pyright (3.11) (push) Has been cancelled
tests / pyright (3.12) (push) Has been cancelled
tests / pyright (3.13) (push) Has been cancelled
tests / ty-check (3.10) (push) Has been cancelled
tests / ty-check (3.11) (push) Has been cancelled
tests / ty-check (3.12) (push) Has been cancelled
tests / ty-check (3.13) (push) Has been cancelled
tests / stubs (push) Has been cancelled
tests / smoke-test (push) Has been cancelled
Docker / check_paths (push) Has been cancelled
Docker / build (push) Has been cancelled
docs / build (push) Has been cancelled
Upstream: https://github.com/michaelgillett/mjlab Upstream-Commit: c19f713c415a699a79d71cd96aa13c3104a05047 Upstream-Branch: main
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
.. _raycast_sensor:
|
||||
|
||||
RayCast Sensor
|
||||
==============
|
||||
|
||||
``RayCastSensor`` provides GPU-accelerated raycasting for terrain
|
||||
scanning, obstacle detection, and depth sensing. Rays are emitted from
|
||||
a frame attached to a body, site, or geom in the scene, and the sensor
|
||||
reports hit distances, world-space hit positions, and surface normals.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video controls style="display: block; margin: 0 auto; max-width: 100%; height: auto;">
|
||||
<source src="../../_static/raycast_demo.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
|
||||
Quick start
|
||||
-----------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from mjlab.sensor import RayCastSensorCfg, GridPatternCfg, ObjRef
|
||||
|
||||
# Downward-facing grid for terrain height scanning.
|
||||
raycast_cfg = RayCastSensorCfg(
|
||||
name="terrain_scan",
|
||||
frame=ObjRef(type="body", name="base", entity="robot"),
|
||||
pattern=GridPatternCfg(size=(1.0, 1.0), resolution=0.1),
|
||||
max_distance=5.0,
|
||||
)
|
||||
|
||||
scene_cfg = SceneCfg(
|
||||
entities={"robot": robot_cfg},
|
||||
sensors=(raycast_cfg,),
|
||||
)
|
||||
|
||||
# Access at runtime.
|
||||
data = env.scene["terrain_scan"].data
|
||||
data.distances # [B, N] distance to hit, -1 if miss
|
||||
data.hit_pos_w # [B, N, 3] world-space hit positions
|
||||
data.normals_w # [B, N, 3] surface normals
|
||||
|
||||
|
||||
Ray patterns
|
||||
------------
|
||||
|
||||
Ray patterns define the spatial distribution and direction of rays
|
||||
emitted from the sensor frame.
|
||||
|
||||
.. grid:: 2
|
||||
|
||||
.. grid-item-card:: Grid pattern
|
||||
|
||||
Parallel rays in a 2D grid with fixed spatial resolution. The
|
||||
ground footprint does not change with sensor height because ray
|
||||
spacing is defined in world units (meters). The natural choice for
|
||||
height maps and terrain scanning.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video autoplay loop muted playsinline style="width: 100%; height: auto;">
|
||||
<source src="../../_static/pattern_grid.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. grid-item-card:: Pinhole camera pattern
|
||||
|
||||
Diverging rays emitted from a single origin, analogous to a depth
|
||||
camera. The ground coverage increases with sensor
|
||||
height because the field of view is fixed in angular units.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video autoplay loop muted playsinline style="width: 100%; height: auto;">
|
||||
<source src="../../_static/pattern_pinhole.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from mjlab.sensor import GridPatternCfg, PinholeCameraPatternCfg
|
||||
|
||||
# Parallel grid: fixed footprint, height-invariant.
|
||||
grid = GridPatternCfg(
|
||||
size=(1.0, 1.0), # Grid dimensions in meters
|
||||
resolution=0.1, # Spacing between rays
|
||||
direction=(0.0, 0.0, -1.0), # Ray direction (down)
|
||||
)
|
||||
|
||||
# Pinhole: perspective projection, diverging rays.
|
||||
pinhole = PinholeCameraPatternCfg(
|
||||
width=16,
|
||||
height=12,
|
||||
fovy=45.0, # Vertical FOV in degrees
|
||||
)
|
||||
|
||||
# Pinhole from a MuJoCo camera definition.
|
||||
pinhole = PinholeCameraPatternCfg.from_mujoco_camera("robot/depth_cam")
|
||||
|
||||
# Pinhole from an intrinsic matrix.
|
||||
pinhole = PinholeCameraPatternCfg.from_intrinsic_matrix(
|
||||
intrinsic_matrix=[500, 0, 320, 0, 500, 240, 0, 0, 1],
|
||||
width=640,
|
||||
height=480,
|
||||
)
|
||||
|
||||
|
||||
Pattern comparison
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 40 40
|
||||
|
||||
* - Aspect
|
||||
- Grid
|
||||
- Pinhole
|
||||
* - Ray direction
|
||||
- Parallel
|
||||
- Diverging
|
||||
* - Spacing unit
|
||||
- Meters
|
||||
- Degrees (FOV)
|
||||
* - Height affects coverage
|
||||
- No
|
||||
- Yes
|
||||
* - Projection model
|
||||
- Orthographic
|
||||
- Perspective
|
||||
|
||||
|
||||
Frame attachment
|
||||
----------------
|
||||
|
||||
Rays are emitted from a frame in the scene specified via ``ObjRef``.
|
||||
The frame can be a body, site, or geom on any entity.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
frame = ObjRef(type="body", name="base", entity="robot")
|
||||
frame = ObjRef(type="site", name="scan_site", entity="robot")
|
||||
frame = ObjRef(type="geom", name="sensor_mount", entity="robot")
|
||||
|
||||
``exclude_parent_body`` (default ``True``) prevents rays from hitting
|
||||
the body to which the sensor is attached.
|
||||
|
||||
|
||||
Ray alignment
|
||||
-------------
|
||||
|
||||
The ``ray_alignment`` setting controls how rays orient relative to the
|
||||
attached frame when the body rotates.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<video autoplay loop muted playsinline
|
||||
style="display: block; margin: 0 auto; max-width: 100%; height: auto;">
|
||||
<source src="../../_static/ray_alignment_comparison.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 45 40
|
||||
|
||||
* - Mode
|
||||
- Description
|
||||
- Use case
|
||||
* - ``"base"``
|
||||
- Full position and rotation tracking
|
||||
- Body-mounted sensors
|
||||
* - ``"yaw"``
|
||||
- Follows yaw, ignores pitch and roll
|
||||
- Terrain height maps
|
||||
* - ``"world"``
|
||||
- Fixed world-frame direction
|
||||
- Gravity-aligned sensing
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
RayCastSensorCfg(
|
||||
name="height_scan",
|
||||
frame=ObjRef(type="body", name="base", entity="robot"),
|
||||
pattern=GridPatternCfg(size=(1.0, 1.0), resolution=0.1),
|
||||
ray_alignment="yaw",
|
||||
)
|
||||
|
||||
|
||||
Geom group filtering
|
||||
--------------------
|
||||
|
||||
MuJoCo assigns geoms to groups 0 through 5. Use
|
||||
``include_geom_groups`` to restrict which geoms rays can hit. This is
|
||||
useful for ignoring visual-only geoms or isolating terrain geometry.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
RayCastSensorCfg(
|
||||
name="terrain_only",
|
||||
frame=ObjRef(type="body", name="base", entity="robot"),
|
||||
pattern=GridPatternCfg(),
|
||||
include_geom_groups=(0, 1),
|
||||
)
|
||||
|
||||
|
||||
Output
|
||||
------
|
||||
|
||||
``RayCastData`` is a dataclass with shape annotations relative to
|
||||
``B`` (number of environments) and ``N`` (number of rays).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@dataclass
|
||||
class RayCastData:
|
||||
distances: Tensor # [B, N] distance to hit, -1 if miss
|
||||
hit_pos_w: Tensor # [B, N, 3] world-space hit positions
|
||||
normals_w: Tensor # [B, N, 3] surface normals
|
||||
pos_w: Tensor # [B, 3] sensor frame position
|
||||
quat_w: Tensor # [B, 4] sensor frame orientation (w, x, y, z)
|
||||
|
||||
.. note::
|
||||
|
||||
Set ``debug_vis=True`` on the config to visualize ray hits at
|
||||
runtime.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from mjlab.sensor import (
|
||||
RayCastSensorCfg, GridPatternCfg, PinholeCameraPatternCfg, ObjRef,
|
||||
)
|
||||
|
||||
# Dense height map for terrain-aware locomotion.
|
||||
height_scan = RayCastSensorCfg(
|
||||
name="height_scan",
|
||||
frame=ObjRef(type="body", name="base", entity="robot"),
|
||||
pattern=GridPatternCfg(
|
||||
size=(1.6, 1.0),
|
||||
resolution=0.1,
|
||||
direction=(0.0, 0.0, -1.0),
|
||||
),
|
||||
ray_alignment="yaw",
|
||||
max_distance=2.0,
|
||||
)
|
||||
|
||||
# Simulated depth camera using pinhole projection.
|
||||
depth_cam = RayCastSensorCfg(
|
||||
name="depth",
|
||||
frame=ObjRef(type="site", name="camera_site", entity="robot"),
|
||||
pattern=PinholeCameraPatternCfg.from_mujoco_camera("robot/depth_cam"),
|
||||
max_distance=10.0,
|
||||
)
|
||||
|
||||
# Forward-facing obstacle scan.
|
||||
obstacle_scan = RayCastSensorCfg(
|
||||
name="obstacle",
|
||||
frame=ObjRef(type="body", name="head", entity="robot"),
|
||||
pattern=GridPatternCfg(
|
||||
size=(0.5, 0.3),
|
||||
resolution=0.1,
|
||||
direction=(-1.0, 0.0, 0.0),
|
||||
),
|
||||
max_distance=3.0,
|
||||
include_geom_groups=(0,),
|
||||
)
|
||||
|
||||
|
||||
TerrainHeightSensor
|
||||
-------------------
|
||||
|
||||
``TerrainHeightSensor`` is a thin ``RayCastSensor`` subclass that adds
|
||||
per-frame vertical clearance to the sensor data. It computes
|
||||
``frame_z - hit_z`` for each ray, replaces misses with ``max_distance``,
|
||||
and reduces across rays per frame.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from mjlab.sensor import TerrainHeightSensorCfg, RingPatternCfg, ObjRef
|
||||
|
||||
cfg = TerrainHeightSensorCfg(
|
||||
name="foot_height",
|
||||
frame=(
|
||||
ObjRef(type="site", name="left_foot", entity="robot"),
|
||||
ObjRef(type="site", name="right_foot", entity="robot"),
|
||||
),
|
||||
pattern=RingPatternCfg.single_ring(radius=0.04, num_samples=4),
|
||||
max_distance=1.0,
|
||||
include_geom_groups=(0,),
|
||||
)
|
||||
|
||||
# At runtime:
|
||||
sensor = env.scene["foot_height"]
|
||||
sensor.data.heights # [B, F] vertical clearance per foot
|
||||
sensor.data.distances # [B, N] raw ray distances (inherited)
|
||||
|
||||
The ``reduction`` config field controls how rays are aggregated within
|
||||
each frame: ``"min"`` (default), ``"max"``, or ``"mean"``.
|
||||
Reference in New Issue
Block a user