Skip to content

Bolt sweep

Many contacts

A robot-mounted scraper sweeps 96 loose nuts, bolts and washers into a bin.

0.00 s
The steel preset (the default), recordedon an NVIDIA RTX PRO 6000 Blackwell.

Run it

Terminal window
cd examples
# the default preset (steel), then the others
uv run shelf play bolt_sweep
# change solver settings live
uv run shelf tune bolt_sweep

Source: examples/shelf/02_many_contacts/bolt_sweep/ · ported from newton/examples/robot/example_steel_bolt_sweep.py @ skild-ai/newton 7565a26b1

Presets

PresetSolverStatusChecked

Solvers with no row are untested on this example. The full settings are inpresets.toml.

Code

examples/shelf/02_many_contacts/bolt_sweep/example.py
# 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 np
import warp as wp
import newton.examples
from common import presets
from newton.examples.robot import _bolt_sweep
from 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)