Skip to content

Randomizing gravity

76 · Domain randomization

Five pendulums released from 30 degrees, each in a world with its own gravity, set with model.set_gravity(g, world=w). The period shrinks as 1 / sqrt(g), matching the point-mass estimate. Steel reads model.gravity every step; the script notifies MODEL_PROPERTIES, which matters when the solver reuses carried kinematics (reuse_kinematics), where a gravity change otherwise lags by one step.

0.00 s

What to look at

  • The weakest gravity swings slowest.
  • Every world swings with the same amplitude.

Key parameter

model.set_gravity(g, world=w)

Sets one world's gravity vector; model.gravity has one entry per world plus one for the global world.

Try changing

  • Tilt the gravity vector a few degrees per world instead of scaling it.
  • If your observations use the gravity vector, normalize it: it is an acceleration, not a unit vector.

Concept

When Steel sees a randomized value

What it is

Steel reads this input from the model or state every step, so a write takes effect on the next step. The tutorial still notifies where a flag exists, which keeps sleeping islands and carried kinematics consistent.

In this scene

Every world swings with the same amplitude.

Considerations

Cost
Call only, default solver, six-link chain and four boxes per world, 1024 worlds (1.12 ms per collide + step): MODEL_PROPERTIES 5 µs (3 µs captured). Flags measured one at a time; combined calls and feature validation can cost more. This notification does not request a mass-matrix refresh.
Graph capture
These flags can be captured in a reset graph. Kinematic-flag changes, sleeping, and shape flags with sparse factors plus restitution run eagerly; captured calls skip some value checks.
Use it when
Slopes, payload tilts or other-planet tests, per world.

The script

cd tutorials && python dr_gravity.py prints each variant's final metric; --record out.json also writes the replay. Every tutorial imports replay.py from the same folder.

tutorials/dr_gravity.py
# SPDX-FileCopyrightText: Copyright (c) 2026 Skild AI and the Steel contributors
# SPDX-License-Identifier: Apache-2.0
"""Gravity randomization: per-world vectors with model.set_gravity, then notify MODEL_PROPERTIES."""
import math
import newton
import numpy as np
import warp as wp
from newton.solvers import SolverSteel
from replay import Recorder
DT = 1.0 / 240.0
SUBSTEPS = 8
WORLDS = 5
LENGTH = 0.4
def tf(x, y, z):
return wp.transform((x, y, z), wp.quat_identity())
def build():
pendulum = newton.ModelBuilder()
link = pendulum.add_link(xform=tf(0.0, 0.0, 0.8))
pendulum.add_shape_capsule(
link,
xform=tf(0.0, 0.0, -0.2),
radius=0.01,
half_height=0.19,
cfg=newton.ModelBuilder.ShapeConfig(density=100.0),
)
pendulum.add_shape_sphere(link, xform=tf(0.0, 0.0, -LENGTH), radius=0.04)
j = pendulum.add_joint_revolute(-1, link, axis=newton.Axis.Y, parent_xform=tf(0.0, 0.0, 0.8))
pendulum.add_articulation([j])
pendulum.joint_q[-1] = math.radians(30.0)
builder = newton.ModelBuilder()
builder.replicate(pendulum, WORLDS, spacing=(0.0, 0.3, 0.0))
return builder.finalize()
def simulate(rec, label, g):
model = build()
solver = SolverSteel(model)
for w in range(WORLDS):
model.set_gravity((0.0, 0.0, -g[w]), world=w)
solver.notify_model_changed(newton.ModelFlags.MODEL_PROPERTIES)
state_0, state_1, control = model.state(), model.state(), model.control()
newton.eval_fk(model, model.joint_q, model.joint_qd, state_0)
run = rec.variant(label, model)
for _ in range(rec.frames(5.0)):
q = np.degrees(state_0.joint_q.numpy())
run.frame(state_0, **{f"g {v:.2f}": a for v, a in zip(g, q)})
for _ in range(SUBSTEPS):
solver.step(state_0, state_1, control, None, DT)
state_0, state_1 = state_1, state_0
run.check(solver)
if __name__ == "__main__":
rng = np.random.default_rng(10)
g = np.sort(rng.uniform(2.0, 15.0, WORLDS)) # m/s^2
rec = Recorder()
simulate(rec, "gravity sampled per world", g)
rec.save()
print(
"small-angle period of a point mass, s:", np.round(2.0 * math.pi * np.sqrt(LENGTH / g), 2)
)

Recorded with SolverSteel on Newton 1.9.0.dev0, Warp 1.19.0, NVIDIA RTX PRO 6000 Blackwell Server Edition. Replays read the state back every frame, so they are not a speed benchmark. Script · page text