diff --git a/README.md b/README.md index d88d7365..8579cb4d 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,16 @@ pip install -ve . ``` +### RCS Asset Cache + +RCS resolves its asset directory from the `RCS_PREFIX` environment variable. When it is unset, RCS defaults to `~/.rcs`. + +On import, RCS checks whether that path exists. If it does not, it downloads the matching asset archive from GitHub into that location automatically. + +```shell +export RCS_PREFIX=/path/to/rcs-assets +``` + ### Via PyPI/pip *Coming soon...* diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md index cdfac035..72ef4747 100644 --- a/docs/getting_started/index.md +++ b/docs/getting_started/index.md @@ -36,6 +36,16 @@ pip install -ve . For a docker deployment, see the `docker` folder in the repository. +## RCS Asset Cache + +RCS resolves its asset directory from the `RCS_PREFIX` environment variable. If `RCS_PREFIX` is not set, it defaults to `~/.rcs`. + +On import, RCS checks whether that path exists. If it does not, it downloads the matching asset archive from GitHub into that location automatically. + +```shell +export RCS_PREFIX=/path/to/rcs-assets +``` + ## Basic Usage The python package is called `rcs`. diff --git a/pyproject.toml b/pyproject.toml index ca7d0c3d..2ffc4658 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,9 +13,9 @@ requires = [ build-backend = "scikit_build_core.build" [project] -name = "rcs" +name = "rcs_core" version = "0.7.1" -description = "A Lean Ecosystem for Robot Learning at Scale" +description = "A lean, ROS-free sim-to-real framework for training and deploying Vision-Language-Action (VLA) models and RL agents. Native MuJoCo Gymnasium wrappers with synchronous execution for Franka, UR5e, xArm, and SO101." dependencies = [ "websockets>=11.0", "requests~=2.31", @@ -26,7 +26,6 @@ dependencies = [ "etils[epath]>=1.7.0", "glfw~=2.7", "pyopengl~=3.1.9", - "pynput>=1.7.7", "pillow>=10.3", "python-dotenv>=1.0.1", "opencv-python~=4.10.0.84", @@ -39,6 +38,9 @@ dependencies = [ "simplejpeg", "mujoco==3.2.6", "pin==3.7.0", + # pin 3.7.0 currently resolves against older urdfdom/tinyxml SONAMEs at runtime + "cmeel-urdfdom<5", + "cmeel-tinyxml2<11", "greenlet", "duckdb", "pandas", @@ -74,6 +76,8 @@ dev = [ build_deps = [ "mujoco==3.2.6", "pin==3.7.0", + "cmeel-urdfdom<5", + "cmeel-tinyxml2<11", ] [tool.cibuildwheel] diff --git a/python/rcs/camera/sim.py b/python/rcs/camera/sim.py index a374bc97..d508165a 100644 --- a/python/rcs/camera/sim.py +++ b/python/rcs/camera/sim.py @@ -11,6 +11,7 @@ from rcs._core.sim import SimCameraConfig from rcs._core.sim import SimCameraSet as _SimCameraSet from rcs.camera.interface import BaseCameraSet, CameraFrame, DataFrame, Frame, FrameSet +from rcs.sim import egl_bootstrap from rcs import sim @@ -31,6 +32,7 @@ def __init__( self.cameras = cameras self.physical_units = physical_units + egl_bootstrap.require("simulation camera rendering") super().__init__(simulation, cameras, render_on_demand=render_on_demand) self._sim: sim.Sim diff --git a/python/rcs/sim/egl_bootstrap.py b/python/rcs/sim/egl_bootstrap.py index 57884bde..9aa2d95a 100644 --- a/python/rcs/sim/egl_bootstrap.py +++ b/python/rcs/sim/egl_bootstrap.py @@ -10,12 +10,15 @@ import os _egl_available = False +_egl_error = None _addr_make_current = None _egl_display = None _egl_context = None name = ctypes.util.find_library("EGL") -if name is not None: +if name is None: + _egl_error = "Could not find libEGL via ctypes.util.find_library('EGL')." +else: try: import mujoco.egl from mujoco.egl import GLContext @@ -26,8 +29,28 @@ _egl_display = int(mujoco.egl.EGL_DISPLAY.address) _egl_context = int(_ctx._context.address) _egl_available = True - except Exception: - pass + except Exception as exc: + _egl_error = f"Failed to initialize MuJoCo EGL context: {exc!r}" + + +def is_available() -> bool: + return _egl_available + + +def failure_reason() -> str | None: + return _egl_error + + +def require(feature: str = "offscreen rendering"): + if _egl_available: + return + reason = _egl_error or "unknown EGL initialization failure" + message = ( + f"EGL is required for {feature}, but it is not available. {reason} " + "If you do not need rendering, run RCS without simulation cameras/viewers. " + "If you do need headless rendering, install the system EGL/OpenGL runtime libraries." + ) + raise RuntimeError(message) def bootstrap(): diff --git a/src/rcs/utils.cpp b/src/rcs/utils.cpp index d348375b..18a4e58b 100644 --- a/src/rcs/utils.cpp +++ b/src/rcs/utils.cpp @@ -18,6 +18,14 @@ void bootstrap_egl(uintptr_t fn_addr, uintptr_t dpy, uintptr_t ctx) { } void ensure_current() { + if (g_makeCurrent == nullptr || g_display == EGL_NO_DISPLAY || + g_context == EGL_NO_CONTEXT) { + throw std::runtime_error( + "EGL rendering was requested, but EGL was not bootstrapped. " + "This usually means libEGL or the MuJoCo EGL context is unavailable. " + "Run without cameras/viewers if you do not need rendering, or install " + "the required system EGL/OpenGL runtime libraries."); + } if (!g_makeCurrent(g_display, g_surface, g_surface, g_context)) throw std::runtime_error("eglMakeCurrent failed"); }