Bolt sweep
Many contacts
A robot-mounted scraper sweeps 96 loose nuts, bolts and washers into a bin.
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 bolt_sweep# change solver settings liveuv run shelf tune bolt_sweepSource: examples/shelf/02_many_contacts/bolt_sweep/ · ported from newton/examples/robot/example_steel_bolt_sweep.py @ skild-ai/newton 7565a26b1
Presets
| Preset | Solver | Status | Checked |
|---|---|---|---|
steeldefault | steel | works | 2026-11-09, newton c42985a6c, 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 The Newton Developers# SPDX-License-Identifier: Apache-2.0# Modified for Steel Shelf: preset-driven execution and shared runner integration.
"""Sweep 96 loose nuts, bolts and washers into a bin using a robot-mounted scraper.
Command: python -m shelf run bolt_sweep"""
import numpy as npimport warp as wp
import newton.examplesfrom common import presets
from newton.examples.robot import _bolt_sweepfrom newton.examples.robot._tabletop_demo import TabletopDemo
class Example(TabletopDemo): task = "sweep"
def __init__(self, viewer, args): self.preset = presets.load(__file__) # TabletopDemo (installed newton package) builds SolverSteel from these args, so fill them # from the preset before handing args over. Its frame rate is fixed at 60 Hz. args.substeps = self.preset.substeps args.iterations = self.preset.solver.need("iterations") args.solve_mode = self.preset.solver.need("solve_mode") args.contact_response = self.preset.solver.need("articulated_contact_response") super().__init__(viewer, args)
@staticmethod def create_parser(): # TabletopDemo.create_parser() without its solver-tuning arguments (--substeps, --iterations, # --solve-mode, --contact-response); those values now live in presets.toml. parser = newton.examples.create_parser() parser.set_defaults(num_frames=900) return parser
def populate(self, builder, info, ee, down, fingers): _bolt_sweep.populate(builder, info, ee, down, wp.get_device())
def test_final(self): """Verify that at least ``pile_required_count`` fasteners reach the collection bin.""" self.check_capacity() poses = self.state_0.body_q.numpy() lo, hi = np.asarray(self.info["pile_bin_bounds"]) centers = np.array( [ np.asarray(wp.transform_point(wp.transform(*poses[b]), wp.vec3(*c))) for b, c in zip(self.info["pile_bodies"], self.info["pile_piece_centers"], strict=True) ] ) count = np.count_nonzero(np.all((centers >= lo) & (centers <= hi), axis=1)) required, total = self.info["pile_required_count"], len(self.info["pile_bodies"]) assert count >= required, f"Only {count}/{total} fasteners collected"
if __name__ == "__main__": viewer, args = newton.examples.init(Example.create_parser()) example = Example(viewer, args) newton.examples.run(example, args)example = "bolt_sweep"source = "newton/examples/robot/example_steel_bolt_sweep.py @ skild-ai/newton 7565a26b1"default = "steel"
[check]# Arguments `python -m shelf check` passes in test mode (from Newton's test_examples.py).args = []
[gallery]short = "A robot-mounted scraper sweeps 96 loose nuts, bolts and washers into a bin."frame = 600
[xpbd]status = "untested"
[vbd]status = "untested"
[mujoco]status = "untested"
[featherstone]status = "untested"
[semi_implicit]status = "untested"
[feather_pgs]status = "untested"
[steel]status = "works"sim = { substeps = 4 }solver = { solve_mode = "colored", iterations = 12, articulated_contact_response = "propagation" }checked = "2026-11-09, newton c42985a6c, 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.