Skip to main content

Backend Selection

Choose a simulation backend based on your use case:
BackendPerformanceDependenciesBest For
GenesisGPU-parallelgenesis-world, torchRL training, visualization
MuJoCo MJXJAX JITmujoco, mjx, jax[cuda]Research, custom gradients
MockCPU single-threadtorch onlyTesting, CI/CD
All backends implement the same SimRobot interface, so switching is as simple as changing the backend parameter.

Genesis Backend

Genesis provides GPU-accelerated physics with thousands of parallel environments and built-in visualization.

Installation

uv pip install --python 3.13 torch
uv pip install --python 3.13 genesis-world
Python 3.13 Required: Genesis dependencies do not currently publish wheels for CPython 3.14. Use Python 3.13 or earlier.

Basic Usage

From the rfx/examples/genesis_viewer.py example:
import torch
import rfx
from rfx.config import SO101_CONFIG

# Create Genesis-backed robot with viewer
robot = rfx.SimRobot.from_config(
    SO101_CONFIG.to_dict(),
    num_envs=1,
    backend="genesis",
    device="cuda" if torch.cuda.is_available() else "cpu",
    substeps=4,
    viewer=True,
    auto_install=True,  # Auto-install genesis-world if missing
)

print(f"Robot: {robot}")
# Output: SimRobot(backend='genesis', num_envs=1, state_dim=12, device='cuda')

# Reset and run simulation
obs = robot.reset()
action_dim = robot.max_action_dim
control_dt = 1.0 / robot.config.control_freq_hz

for step in range(2000):
    t = step * control_dt
    action = torch.zeros(1, action_dim, device="cuda")
    
    # Simple sinusoidal motion
    action[:, 0] = 0.25 * torch.sin(torch.tensor(t, device="cuda"))
    action[:, 1] = 0.25 * torch.cos(torch.tensor(t, device="cuda"))
    
    robot.act(action)
    obs = robot.observe()
    
    if step % 100 == 0:
        norm = obs["state"].norm(dim=-1).mean().item()
        print(f"step={step:5d}  state_norm={norm:.4f}")

robot.close()

Auto-Install Behavior

Genesis can auto-install if missing:
robot = SimRobot.from_config(
    config_path,
    backend="genesis",
    auto_install=True,  # Automatically install genesis-world
)
Or enable globally via environment variable:
export RFX_AUTO_INSTALL_GENESIS=1
The auto-installer tries:
  1. uv pip install --python <interpreter> genesis-world (if uv available)
  2. <interpreter> -m pip install genesis-world (fallback)

Parallel Environments

Genesis excels at massively parallel simulation:
robot = SimRobot.from_config(
    "rfx/configs/so101.yaml",
    backend="genesis",
    num_envs=4096,  # 4096 parallel environments on GPU
    device="cuda",
)

obs = robot.reset()  # (4096, 64)
action = torch.randn(4096, robot.max_action_dim, device="cuda")
robot.act(action)    # Step all 4096 envs in parallel

Configuration Options

Genesis backend supports these parameters:
robot = SimRobot.from_config(
    config_path,
    backend="genesis",
    viewer=True,           # Enable 3D viewer
    dt=0.002,              # Timestep in seconds
    substeps=4,            # Physics substeps per step
    gravity=(0, 0, -9.81), # Gravity vector
    robot_pos=(0, 0, 0.5), # Initial robot position
    max_steps=1000,        # Episode length
    auto_install=True,     # Auto-install if missing
)

Running Examples

uv run --python 3.13 rfx/examples/genesis_viewer.py

Headless Mode

For training without visualization:
robot = SimRobot.from_config(
    config_path,
    backend="genesis",
    viewer=False,  # No viewer window
    device="cuda",
)

MuJoCo MJX Backend

MuJoCo MJX provides JAX-accelerated physics with JIT compilation and automatic differentiation.

Installation

pip install torch
pip install mujoco mujoco-mjx jax[cuda13] warp-lang
Or install optional dependencies:
uv pip install "rfx-sdk[sim-mjx]"

Basic Usage

From rfx/python/rfx/sim/mjx.py:
import torch
from rfx.sim import SimRobot

# Create MuJoCo MJX-backed robot
robot = SimRobot.from_config(
    "rfx/configs/so101.yaml",
    backend="mjx",
    num_envs=512,
    device="cuda",
)

obs = robot.reset()
action = torch.randn(512, robot.max_action_dim, device="cuda")
robot.act(action)

Implementation Details

The MJX backend (rfx/python/rfx/sim/mjx.py:20-138) converts between PyTorch and JAX:
class MjxBackend:
    """MuJoCo MJX backend for JAX-accelerated simulation."""

    def __init__(self, config, num_envs=1, device="cuda", **kwargs):
        import jax
        import mujoco
        from mujoco import mjx
        
        # Load MuJoCo model from XML/URDF
        if config.urdf_path:
            xml_path = config.urdf_path.replace(".urdf", ".xml")
            self._model = mujoco.MjModel.from_xml_path(xml_path)
        else:
            self._model = self._create_default_model()
        
        # Create batched JAX simulation
        self._mjx_model = mjx.put_model(self._model)
        self._batched_data = jax.vmap(lambda _: self._mjx_data)(jnp.arange(num_envs))
        self._step_fn = jax.jit(jax.vmap(mjx.step, in_axes=(None, 0)))
    
    def act(self, action: torch.Tensor) -> None:
        # Convert PyTorch action to JAX
        ctrl = self._torch_to_jax(action)
        self._batched_data = self._batched_data.replace(ctrl=ctrl)
        
        # JIT-compiled parallel step
        self._batched_data = self._step_fn(self._mjx_model, self._batched_data)

URDF/MJCF Requirements

MJX requires MJCF (MuJoCo XML) format. If you have URDF:
# Convert URDF to MJCF
rfx/assets/robots/go2/
├── urdf/
   ├── go2.urdf
   └── meshes/...
└── mjcf/
    └── go2.xml  # Converted for MJX

Mock Backend

The Mock backend provides a lightweight CPU-based simulator with zero external dependencies (beyond PyTorch).

Installation

No installation required - Mock is built-in:
from rfx.sim import SimRobot

robot = SimRobot.from_config(
    "rfx/configs/so101.yaml",
    backend="mock",  # No extra dependencies!
    device="cpu",
)

Implementation

From rfx/python/rfx/sim/mock.py:19-81, the Mock backend uses a simple spring-damper model:
class MockBackend:
    """Mock physics backend - pure PyTorch, no dependencies."""
    
    def __init__(self, config, num_envs=1, device="cpu", **kwargs):
        self.config = config
        self.num_envs = num_envs
        self.device = device
        
        self._positions = torch.zeros(num_envs, config.action_dim, device=device)
        self._velocities = torch.zeros(num_envs, config.action_dim, device=device)
        self._dt = 1.0 / config.control_freq_hz
    
    def act(self, action: torch.Tensor) -> None:
        action = action[:, :self.config.action_dim]
        
        # Simple PD controller physics
        k, d = 100.0, 10.0
        error = action - self._positions
        acceleration = k * error - d * self._velocities
        
        # Integrate
        self._velocities = self._velocities + acceleration * self._dt
        self._positions = self._positions + self._velocities * self._dt
        
        # Clamp to joint limits
        self._positions = torch.clamp(self._positions, -3.14159, 3.14159)
        self._velocities = torch.clamp(self._velocities, -10.0, 10.0)

Use Cases

Mock is ideal for:

Unit Testing

Test robot interfaces without installing simulation dependencies

CI/CD

Run automated tests in GitHub Actions or other CI systems

Prototyping

Quickly test control logic on CPU

Documentation

Generate examples without heavy dependencies

Example Usage

import torch
from rfx.sim import SimRobot

# Create mock robot (CPU, no dependencies)
robot = SimRobot.from_config(
    "rfx/configs/so101.yaml",
    backend="mock",
    num_envs=8,
    device="cpu",
)

obs = robot.reset()  # (8, 64)
action = torch.randn(8, robot.max_action_dim)
robot.act(action)
obs = robot.observe()

reward = robot.get_reward()  # (8,)
done = robot.get_done()      # (8,)

Switching Backends

All backends use identical APIs. Switch with a single parameter:
robot = SimRobot.from_config(
    config_path,
    backend="genesis",
    num_envs=4096,
    device="cuda",
)

Troubleshooting

Solution: Use Python 3.13 or earlier until upstream Genesis publishes cp314 wheels:
uv run --python 3.13 rfx/examples/genesis_viewer.py
Solutions:
  • Check graphics support: --device cpu
  • Try headless mode: viewer=False
  • Verify Genesis install: uv pip install genesis-world
Solution: Install all MJX dependencies:
pip install mujoco mujoco-mjx jax[cuda13] warp-lang
Solution: Use Mock backend for rapid testing, then switch to Genesis/MJX for validation:
backend = "mock" if args.fast else "genesis"
robot = SimRobot.from_config(config, backend=backend)

Next Steps

MockRobot for Testing

Learn about the standalone MockRobot class for unit testing

Build docs developers (and LLMs) love