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,252 @@
|
||||
"""Raycast sensor demo.
|
||||
|
||||
Run with:
|
||||
uv run mjpython scripts/demos/raycast_sensor.py [--viewer native|viser] # macOS
|
||||
uv run python scripts/demos/raycast_sensor.py [--viewer native|viser] # Linux
|
||||
|
||||
Examples:
|
||||
# Grid pattern (default)
|
||||
uv run python scripts/demos/raycast_sensor.py --pattern grid
|
||||
|
||||
# Pinhole camera pattern
|
||||
uv run python scripts/demos/raycast_sensor.py --pattern pinhole
|
||||
|
||||
# With yaw alignment (ignores pitch/roll)
|
||||
uv run python scripts/demos/raycast_sensor.py --alignment yaw
|
||||
|
||||
If using the native viewer, you can launch in interactive mode with:
|
||||
uv run mjpython scripts/demos/raycast_sensor.py --viewer native --interactive
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Literal
|
||||
|
||||
import mujoco
|
||||
import numpy as np
|
||||
import torch
|
||||
import tyro
|
||||
|
||||
import mjlab
|
||||
import mjlab.terrains as terrain_gen
|
||||
from mjlab.entity import EntityCfg
|
||||
from mjlab.envs import ManagerBasedRlEnv, ManagerBasedRlEnvCfg
|
||||
from mjlab.rl import RslRlVecEnvWrapper
|
||||
from mjlab.scene import SceneCfg
|
||||
from mjlab.sensor import (
|
||||
GridPatternCfg,
|
||||
ObjRef,
|
||||
PinholeCameraPatternCfg,
|
||||
RayCastSensorCfg,
|
||||
)
|
||||
from mjlab.terrains.terrain_entity import TerrainEntityCfg
|
||||
from mjlab.terrains.terrain_generator import TerrainGeneratorCfg
|
||||
from mjlab.utils.torch import configure_torch_backends
|
||||
from mjlab.viewer import NativeMujocoViewer, ViserPlayViewer
|
||||
|
||||
|
||||
def create_scanner_spec() -> mujoco.MjSpec:
|
||||
spec = mujoco.MjSpec()
|
||||
spec.modelname = "scanner"
|
||||
|
||||
mat = spec.add_material()
|
||||
mat.name = "scanner_mat"
|
||||
mat.rgba[:] = (1.0, 0.5, 0.0, 0.9)
|
||||
|
||||
scanner = spec.worldbody.add_body(mocap=True)
|
||||
scanner.name = "scanner"
|
||||
scanner.pos[:] = (0, 0, 2.0)
|
||||
|
||||
geom = scanner.add_geom()
|
||||
geom.name = "scanner_geom"
|
||||
geom.type = mujoco.mjtGeom.mjGEOM_BOX
|
||||
geom.size[:] = (0.15, 0.15, 0.05)
|
||||
geom.mass = 1.0
|
||||
geom.material = "scanner_mat"
|
||||
|
||||
scanner.add_camera(name="scanner", fovy=58.0, resolution=(16, 12))
|
||||
|
||||
record_cam = scanner.add_camera(name="record_cam")
|
||||
record_cam.pos[:] = (2, 0, 2)
|
||||
record_cam.fovy = 40.0
|
||||
record_cam.mode = mujoco.mjtCamLight.mjCAMLIGHT_TARGETBODY
|
||||
record_cam.targetbody = "scanner"
|
||||
|
||||
return spec
|
||||
|
||||
|
||||
def create_env_cfg(
|
||||
pattern: Literal["grid", "pinhole"],
|
||||
alignment: Literal["base", "yaw", "world"],
|
||||
) -> ManagerBasedRlEnvCfg:
|
||||
custom_terrain_cfg = TerrainGeneratorCfg(
|
||||
size=(4.0, 4.0),
|
||||
border_width=0.5,
|
||||
num_rows=1,
|
||||
num_cols=4,
|
||||
curriculum=True,
|
||||
sub_terrains={
|
||||
"pyramid_stairs_inv": terrain_gen.BoxInvertedPyramidStairsTerrainCfg(
|
||||
proportion=0.25,
|
||||
step_height_range=(0.1, 0.25),
|
||||
step_width=0.3,
|
||||
platform_width=1.5,
|
||||
border_width=0.25,
|
||||
),
|
||||
"hf_pyramid_slope_inv": terrain_gen.HfPyramidSlopedTerrainCfg(
|
||||
proportion=0.25,
|
||||
slope_range=(0.6, 1.5),
|
||||
platform_width=1.5,
|
||||
border_width=0.25,
|
||||
inverted=True,
|
||||
),
|
||||
"random_rough": terrain_gen.HfRandomUniformTerrainCfg(
|
||||
proportion=0.25,
|
||||
noise_range=(0.05, 0.15),
|
||||
noise_step=0.02,
|
||||
border_width=0.25,
|
||||
),
|
||||
"wave_terrain": terrain_gen.HfWaveTerrainCfg(
|
||||
proportion=0.25,
|
||||
amplitude_range=(0.15, 0.25),
|
||||
num_waves=3,
|
||||
border_width=0.25,
|
||||
),
|
||||
},
|
||||
add_lights=True,
|
||||
)
|
||||
|
||||
terrain_cfg = TerrainEntityCfg(
|
||||
terrain_type="generator",
|
||||
terrain_generator=custom_terrain_cfg,
|
||||
num_envs=1,
|
||||
)
|
||||
|
||||
scanner_entity_cfg = EntityCfg(
|
||||
spec_fn=create_scanner_spec,
|
||||
init_state=EntityCfg.InitialStateCfg(pos=(0.65, -0.4, 0.5)),
|
||||
)
|
||||
|
||||
if pattern == "grid":
|
||||
pattern_cfg = GridPatternCfg(
|
||||
size=(0.6, 0.6),
|
||||
resolution=0.1,
|
||||
direction=(0.0, 0.0, -1.0),
|
||||
)
|
||||
else:
|
||||
assert pattern == "pinhole"
|
||||
pattern_cfg = PinholeCameraPatternCfg.from_mujoco_camera("scanner/scanner")
|
||||
|
||||
raycast_cfg = RayCastSensorCfg(
|
||||
name="terrain_scan",
|
||||
frame=ObjRef(type="body", name="scanner", entity="scanner"),
|
||||
pattern=pattern_cfg,
|
||||
ray_alignment=alignment,
|
||||
max_distance=5.0,
|
||||
exclude_parent_body=True,
|
||||
debug_vis=True,
|
||||
viz=RayCastSensorCfg.VizCfg(
|
||||
hit_color=(0.0, 1.0, 0.0, 0.9),
|
||||
miss_color=(1.0, 0.0, 0.0, 0.5),
|
||||
show_rays=False,
|
||||
show_normals=True,
|
||||
),
|
||||
)
|
||||
|
||||
cfg = ManagerBasedRlEnvCfg(
|
||||
decimation=10,
|
||||
scene=SceneCfg(
|
||||
num_envs=1,
|
||||
env_spacing=0.0,
|
||||
extent=2.0,
|
||||
terrain=terrain_cfg,
|
||||
entities={"scanner": scanner_entity_cfg},
|
||||
sensors=(raycast_cfg,),
|
||||
),
|
||||
)
|
||||
|
||||
cfg.viewer.body_name = "scanner"
|
||||
cfg.viewer.distance = 12.0
|
||||
cfg.viewer.elevation = -25.0
|
||||
cfg.viewer.azimuth = 135.0
|
||||
|
||||
return cfg
|
||||
|
||||
|
||||
def main(
|
||||
viewer: str = "auto",
|
||||
interactive: bool = False,
|
||||
pattern: Literal["grid", "pinhole"] = "grid",
|
||||
alignment: Literal["base", "yaw", "world"] = "base",
|
||||
) -> None:
|
||||
configure_torch_backends()
|
||||
|
||||
device = "cuda:0" if torch.cuda.is_available() else "cpu"
|
||||
|
||||
print("=" * 60)
|
||||
print("Raycast Sensor Demo - 4 Terrain Types")
|
||||
print(f" Pattern: {pattern}")
|
||||
print(f" Alignment: {alignment}")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
env_cfg = create_env_cfg(pattern, alignment)
|
||||
env = ManagerBasedRlEnv(cfg=env_cfg, device=device)
|
||||
env = RslRlVecEnvWrapper(env)
|
||||
|
||||
if viewer == "auto":
|
||||
has_display = bool(os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
|
||||
resolved_viewer = "native" if has_display else "viser"
|
||||
else:
|
||||
resolved_viewer = viewer
|
||||
|
||||
use_auto_scan = (resolved_viewer == "viser") or (not interactive)
|
||||
|
||||
if use_auto_scan:
|
||||
|
||||
class AutoScanPolicy:
|
||||
def __init__(self):
|
||||
self.step_count = 0
|
||||
|
||||
def __call__(self, obs) -> torch.Tensor:
|
||||
del obs
|
||||
t = self.step_count * 0.005
|
||||
y_period = 1000
|
||||
y_normalized = (self.step_count % y_period) / y_period
|
||||
y = -8.0 + 16.0 * y_normalized
|
||||
x = 1.5 * np.sin(2 * np.pi * t * 0.3)
|
||||
z = 1.0
|
||||
env.unwrapped.sim.data.mocap_pos[0, 0, :] = torch.tensor(
|
||||
[x, y, z], device=device, dtype=torch.float32
|
||||
)
|
||||
env.unwrapped.sim.data.mocap_quat[0, 0, :] = torch.tensor(
|
||||
[1, 0, 0, 0], device=device, dtype=torch.float32
|
||||
)
|
||||
self.step_count += 1
|
||||
return torch.zeros(env.unwrapped.action_space.shape, device=device)
|
||||
|
||||
policy = AutoScanPolicy()
|
||||
else:
|
||||
|
||||
class PolicyZero:
|
||||
def __call__(self, obs) -> torch.Tensor:
|
||||
del obs
|
||||
return torch.zeros(env.unwrapped.action_space.shape, device=device)
|
||||
|
||||
policy = PolicyZero()
|
||||
|
||||
if resolved_viewer == "native":
|
||||
print("Launching native viewer...")
|
||||
NativeMujocoViewer(env, policy).run()
|
||||
elif resolved_viewer == "viser":
|
||||
print("Launching viser viewer...")
|
||||
ViserPlayViewer(env, policy).run()
|
||||
else:
|
||||
raise ValueError(f"Unknown viewer: {viewer}")
|
||||
|
||||
env.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
tyro.cli(main, config=mjlab.TYRO_FLAGS)
|
||||
Reference in New Issue
Block a user