Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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...*
Expand Down
10 changes: 10 additions & 0 deletions docs/getting_started/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
10 changes: 7 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -74,6 +76,8 @@ dev = [
build_deps = [
"mujoco==3.2.6",
"pin==3.7.0",
"cmeel-urdfdom<5",
"cmeel-tinyxml2<11",
]

[tool.cibuildwheel]
Expand Down
2 changes: 2 additions & 0 deletions python/rcs/camera/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
29 changes: 26 additions & 3 deletions python/rcs/sim/egl_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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():
Expand Down
8 changes: 8 additions & 0 deletions src/rcs/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
Loading