Differentiable Simulation#

Genesis supports end-to-end differentiable physics simulation, enabling gradient-based optimization through the physics engine.

Overview#

Differentiable simulation allows you to:

  • Compute gradients of simulation outputs with respect to inputs

  • Optimize control policies through physics

  • Learn physical parameters from observations

  • Backpropagate through multi-step trajectories

Quick Start#

import genesis as gs
import torch

gs.init()

# Enable gradient tracking
scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=0.01,
        requires_grad=True,  # Enable differentiability
    ),
)

robot = scene.add_entity(gs.morphs.URDF(file="robot.urdf"))
scene.build()

# Forward simulation
initial_pos = torch.tensor([0.0, 0.0, 0.0], requires_grad=True, device=gs.device)
robot.set_dofs_position(initial_pos)

for i in range(100):
    robot.control_dofs_force(forces)
    scene.step()

# Compute loss
final_pos = robot.get_pos()
target = torch.tensor([1.0, 0.0, 0.5], device=gs.device)
loss = torch.nn.functional.mse_loss(final_pos, target)

# Backward pass
loss.backward()

# Access gradients
print(initial_pos.grad)

Key Concepts#

Enabling Gradients#

Set requires_grad=True in SimOptions:

scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        requires_grad=True,
    ),
)

Genesis Tensors#

Genesis uses custom tensors that extend PyTorch tensors:

# State outputs are genesis tensors
pos = robot.get_pos()  # genesis.grad.Tensor

# Supports standard PyTorch operations
loss = (pos - target).pow(2).sum()

# Backward automatically flows through simulation
loss.backward()

Gradient Flow#

Input (controls, initial state)
    ↓
Forward simulation (physics steps)
    ↓
Output (final state, observations)
    ↓
Loss function
    ↓
backward() propagates through simulation
    ↓
Input gradients available

Components#

Example: Trajectory Optimization#

import genesis as gs
import torch

gs.init()

scene = gs.Scene(
    sim_options=gs.options.SimOptions(
        dt=0.01,
        requires_grad=True,
    ),
)

robot = scene.add_entity(gs.morphs.URDF(file="robot.urdf"))
scene.build()

# Optimize control sequence
n_steps = 100
n_dofs = robot.n_dofs
controls = torch.zeros(n_steps, n_dofs, requires_grad=True, device=gs.device)
optimizer = torch.optim.Adam([controls], lr=0.01)

target = torch.tensor([1.0, 0.0, 0.5], device=gs.device)

for epoch in range(100):
    scene.reset()

    # Forward simulation
    for t in range(n_steps):
        robot.control_dofs_force(controls[t])
        scene.step()

    # Compute loss
    final_pos = robot.get_pos()
    loss = torch.nn.functional.mse_loss(final_pos, target)

    # Optimize
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    print(f"Epoch {epoch}: loss = {loss.item():.4f}")

Limitations#

  1. Memory: Multi-step trajectories require storing intermediate states

  2. Not all operations differentiable: Some collision operations may not have gradients

  3. Numerical stability: Long horizons may have gradient stability issues

See Also#