Skip to content

FAQ

Is Steel in Newton, or a separate project?

Section titled “Is Steel in Newton, or a separate project?”

It is newton.solvers.SolverSteel in Newton, Apache-2.0. New work lands on the skild-ai/newton integration branch first and reaches Newton through upstream pull requests; skild-ai/steel holds the project page and changelog.

Experimental support is coming soon: a differentiable Steel step is in progress. Today, construction raises for models with requires_grad=True.

SolverSteel raises for:

  • particles, cloth and soft bodies; couple Steel with VBD or XPBD instead (Coupling)
  • ROD joints, and disabled joints in an articulation tree
  • model.requires_grad=True
  • mimic relationships across articulations or on BALL, FREE and DISTANCE joints, loop-closing joints other than BALL, REVOLUTE and FIXED, and loop closures between two dynamic articulations
  • on CPU, the CUDA-only features listed under Does Steel run on CPU?

Steel does not simulate tendons, and it ignores DISTANCE joint limits (min_distance, max_distance), also with enable_joint_limits=True. The MJCF importer does not read a geom’s condim into steel:condim, which stays 3; set it after import.

enable_joint_limits defaults to False. Set it to True.

Why does my drive exceed its effort limit?

Section titled “Why does my drive exceed its effort limit?”

Implicit drives clamp only the explicit drive force; the implicit stiffness and damping response is unbounded. Use drive_mode="rows" to bound the complete reaction.

Can different worlds hold different robots?

Section titled “Can different worlds hold different robots?”

Yes. Heterogeneous worlds are supported and scheduled per world. Global bodies are shared with every world; see Mixed batches.

The core solve runs on CPU with world-serial portable kernels; "colored" sweeps in serial order there. Contact compliance, joint friction, Nesterov momentum and the non-default friction modes raise on CPU, as does sleeping with the colored sweep; contact torsion raises ValueError.

deterministic=wp.DeterministicMode.RUN_TO_RUN on the solver and CollisionPipeline(deterministic=True). See Determinism.

contact_warmstart=True starts each contact from the impulse it had last step, so the sweep begins near the answer instead of at zero. It helps where the same contacts persist and the iteration budget is the limit, and it does nothing, or harm, where contacts do not match from step to step.

Turn it on for resting stacks at a tight iteration budget, and for grasps held with point friction (friction_anchor_beta=0). Leave it off for heavy-on-light stacks (use nesterov_momentum instead), for settling piles where resting jitter matters, and for anything carried fast unless contact matching keeps up.

Measured with warmstart_experiment.py on one RTX PRO 6000 Blackwell, 60 fps. Both arms use the same contact matching, so only the warm start differs:

Scene (worlds) Setting Warm start off Warm start on
Ten equal 10 cm cubes (128) 2 substeps × 16 iterations 0 of 128 standing after 5 s 102 of 128 standing
2 × 24 127 standing; top sag 7.1 mm (median) 128 standing; 0.9 mm
2 × 48 top sag 1.9 mm 0.25 mm
Six cubes, each 3× the one below (128) 8 × 48 86 of 128 standing 36 standing
2 × 96, 4 × 24, 4 × 48, 8 × 24 0–1 standing 0–1 standing (collapses later)
Two-finger pinch, held 5 s, point friction (64) 3 × 8 63 of 64 dropped; twist 29° 0 dropped; twist 0.5°
Same, friction anchors (default) 3 × 8 0 dropped; slip 0.3 mm 0 dropped; slip 0.0 mm
Pinch carried at 0.05 m/s, point friction (64) 3 × 8 10 dropped; twist 8.4° 0 dropped; 0.04°
Carried at 0.2 m/s, point friction, world-frame matching 3 × 8 7 dropped; twist 8.6° 0 dropped; twist 25° (35% of contacts matched)
Same with contact_matching_body_frame=True 3 × 8 10 dropped; twist 8.8° 0 dropped; 1.0° (100% matched)
40 small boxes poured into a bin (64) 2 × 16, colored peak body speed after 1.5 s: 0.005 m/s 0.25 m/s

What to expect otherwise: in these runs warm start changed the frame time by −3% to +19% (median about +11%, eager stepping with contact matching in both arms), and resting bodies kept more residual motion (mean speed about 0.04 m/s against 0.004–0.006 m/s on the equal-cube stack at 32 and 48 iterations). The pile had no world-frames above the 2 m/s burst threshold after 1.5 s in either arm.

How to tell whether it helps. Unmatched contacts start cold, so check the share of contacts.rigid_contact_match_index >= 0 on the contacts that matter. A low match rate means fewer contacts reuse impulses; it does not by itself show incorrect matching or predict whether warm start helps (the equal-cube stack improved at 58–69% matched). Then A/B the task metric (sag, slip, twist, residual speed) at your world count, warm start off and on, with everything else identical. Body-frame matching (contact_matching_body_frame=True, integration branch) fixed the fast-carry case here; verify it on your scene.

Requirements and conflicts. The collision pipeline needs contact matching (contact_matching="latest" or "sticky"); a step without it raises. Warm start is rejected with enable_sleeping, contact torsion, contact_compliance, contact_friction_position_iterations other than -1, and inside coupled solvers, and it keeps the dense mass factors. contact_warmstart_decay scales the carried impulses, and contact_regularization=0.01 damps them if you see bursts.

See Porting from PhysX: attribute mapping, solver option equivalents, what differs, a step-by-step procedure and domain randomization.