Ragdoll
Articulations
A 10-part ragdoll tumbles down a staircase.
steel preset (the default), recordedon an NVIDIA RTX PRO 6000 Blackwell.Run it
cd examples# the default preset (steel), then the othersuv run shelf play ragdoll# change solver settings liveuv run shelf tune ragdollSource: examples/shelf/07_articulations/ragdoll/ · ported from steel-webgpu tools/scenes.py ragdoll() @ ad03e49
Presets
| Preset | Solver | Status | Checked |
|---|---|---|---|
steeldefault | steel | works | 2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition |
Solvers with no row are untested on this example. The full settings are inpresets.toml.
Code
# SPDX-FileCopyrightText: Copyright (c) 2026 Skild AI and the Steel contributors# SPDX-License-Identifier: Apache-2.0# Direct source: steel-webgpu; see presets.toml and THIRD_PARTY.md.# Modified for Steel Shelf: preset-driven execution and shared runner integration.
############################################################################ Example Ragdoll## Floating base: a jointed figure with a free-floating torso tumbling down stairs.## Ported from steel-webgpu's tools/scenes.py (ragdoll); the scene builds# as it does there and the shelf's preset drives the solver.## Command: python -m shelf run ragdoll############################################################################
import math
import numpy as npimport warp as wp
import newtonimport newton.examplesfrom common.steel_scenes import BLUE, GOLD, GREY, RED, TEAL, SceneExample, cfg, tf
class Example(SceneExample): file = __file__ camera = {'eye': (0.6, -2.6, 1.4), 'target': (0.6, 0.0, 0.35)}
def build(self): """Floating base: a jointed figure with a free-floating torso tumbling down stairs.""" b = newton.ModelBuilder() self.preset.apply_model(b) # the preset's builder settings (the contact gap), before any shape b.add_ground_plane() for k in range(6): h = 0.08 * (5 - k) + 0.04 b.add_shape_box(-1, xform=tf((0.22 * k, 0, h / 2)), hx=0.11, hy=0.4, hz=h / 2, color=GREY) body_cfg = cfg(mu=0.8, density=1000) start = np.array((-0.05, 0.0, 0.95)) torso = b.add_link(xform=tf(start, wp.quat_rpy(0.4, 0.5, 0.0))) b.add_shape_capsule(torso, radius=0.07, half_height=0.12, cfg=body_cfg, color=BLUE) joints = [b.add_joint_free(torso)] head = b.add_link(xform=tf(start + (0, 0, 0.29))) b.add_shape_sphere(head, radius=0.08, cfg=body_cfg, color=GOLD) joints.append(b.add_joint_ball(torso, head, parent_xform=tf((0, 0, 0.2)), child_xform=tf((0, 0, -0.09)))) limbs = [((0.0, 0.16, 0.17), (1, 0, 0)), ((0.0, -0.16, 0.17), (1, 0, 0)), ((0.0, 0.06, -0.2), (0, 1, 0)), ((0.0, -0.06, -0.2), (0, 1, 0))] for anchor, axis in limbs: down = -0.12 upper = b.add_link(xform=tf(start + anchor + (0, 0, down))) b.add_shape_capsule(upper, radius=0.035, half_height=0.08, cfg=body_cfg, color=TEAL) joints.append(b.add_joint_ball(torso, upper, parent_xform=tf(anchor), child_xform=tf((0, 0, 0.12)))) lower = b.add_link(xform=tf(start + anchor + (0, 0, 2 * down))) b.add_shape_capsule(lower, radius=0.03, half_height=0.08, cfg=body_cfg, color=TEAL) joints.append( b.add_joint_revolute(upper, lower, parent_xform=tf((0, 0, -0.12)), child_xform=tf((0, 0, 0.12)), axis=axis, limit_lower=-2.2, limit_upper=0.0) ) b.add_articulation(joints) return b, {torso: (2.0, 0, 0.3, 0, 4.0, 0)}, "Floating base: a 10-body ragdoll with a free-floating torso is thrown onto a staircase."
def check(self): """The ragdoll tumbles to the foot of the stairs and lies still (every body under 2 cm/s, torso 0.9-1.25 m out).""" qd = self.state_0.body_qd.numpy() q = self.state_0.body_q.numpy() speed = np.linalg.norm(qd[:, :3], axis=1) assert speed.max() < 0.02, f"body {speed.argmax()} still moves at {speed.max():.3f} m/s" assert 0.9 < q[0, 0] < 1.25, f"the torso ended at x={q[0, 0]:.2f} m, not at the foot of the stairs" assert q[:, 2].min() > 0.0, "a body is below the floor"
if __name__ == "__main__": parser = Example.create_parser() viewer, args = newton.examples.init(parser) newton.examples.run(Example(viewer, args), args)example = "ragdoll"source = "steel-webgpu tools/scenes.py ragdoll() @ ad03e49"default = "steel"
[check]# Arguments `python -m shelf check` passes in test mode: 300 frames, 5 s, at rest at the foot of the stairs; the example's check() asserts the outcome.args = ["--num-frames", "300"]
[gallery]short = "A 10-part ragdoll tumbles down a staircase."title = "Ragdoll"frame = 90
[xpbd]status = "untested"
[vbd]status = "untested"
[mujoco]status = "untested"
[featherstone]status = "untested"
[semi_implicit]status = "untested"
[feather_pgs]status = "untested"
[steel]status = "works"# 2 substeps x 12 iterations with the immediate response (1.20 ms/frame on an RTX PRO 6000, was 12.1 at the port's# 4 x 12 propagation_colored): thrown at 2 m/s, the ragdoll lands on the stairs, tumbles to their foot and is at rest# (every body < 1 cm/s) by frame ~115, as a 16 x 32 run (~100); penetration peak 2.4-3.1 mm, mean 0.04 mm.# 2 x 8 sinks a limb 17 mm on landing. propagation_colored is 4-5x slower here (its self-contacts of the one# articulation go to dense rows either way). At 4 substeps the head, on its frictionless ball joint, can keep rolling# on the floor for seconds. Joint limits on: the elbows and knees stop at [-2.2, 0] rad.# A 2 cm contact gap (Newton's default 10 cm): fewer speculative rows between the limbs; 128 dense rows hold the# peak (72 here, up to 95 at 4 substeps; the port's 32 dropped up to 24 rows a step and let limbs sink 39 mm).sim = { fps = 60, substeps = 2 }solver = { solve_mode = "matrix_free", iterations = 12, articulated_contact_response = "immediate", enable_joint_limits = true, articulation_constraint_max = 128 }model = { rigid_gap = 0.02 }checked = "2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition"
[kamino]status = "untested"
[implicit_mpm]status = "untested"
[style3d]status = "untested"Steel docs and tutorials are licensed under Apache-2.0. Third-party notices. "Skild", "Skild AI" and the Skild logo are trademarks of Skild AI and are not licensed under Apache-2.0.