Franka plate rack
Mesh and SDF contacts
A Franka lifts a dinner plate and places it in a wire rack beside two resting plates.
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 franka_plate_rack# change solver settings liveuv run shelf tune franka_plate_rackSource: examples/shelf/03_mesh_sdf_contacts/franka_plate_rack/ · ported from newton/examples/robot/example_steel_plate_rack.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.
"""Lift a free dinner plate and place it in a wire rack beside two resting plates.
Command: python -m shelf run franka_plate_rack"""
import numpy as npimport warp as wp
import newton.examplesfrom common import presets
from newton.examples.robot import _plate_rackfrom newton.examples.robot._tabletop_demo import TabletopDemo
class Example(TabletopDemo): task = "plates"
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): _plate_rack.populate(builder, info, ee, fingers, wp.get_device(), 3)
def test_final(self): """Verify a real lift followed by a supported plate placement.""" self.check_capacity() body = self.info["tracked_body"] pose = self.state_0.body_q.numpy()[body] assert self.peak_height > 0.25, f"Plate was not lifted: {self.peak_height}" center = wp.transform_point(wp.transform(*pose), wp.vec3(*self.info["plate_center_local"])) assert abs(center[0] - self.info["plate_slot_x"]) < self.info["plate_slot_half_width"], ( f"Plate missed rack slot: center={center}, slot={self.info['plate_slot_x']}" ) assert abs(pose[1] - self.info["rack_origin"][1]) < 0.04, f"Plate missed rack: {pose}" assert 0.07 < pose[2] < 0.16, f"Plate is unsupported: {pose}" normal = np.asarray(wp.quat_rotate(wp.quat(*pose[3:]), wp.vec3(0, 0, 1))) goal_normal = np.asarray(wp.quat_rotate(wp.quat(*self.info["plate_goal_rotation"]), wp.vec3(0, 0, 1))) assert abs(np.dot(normal, goal_normal)) > np.cos(np.deg2rad(15)), f"Plate is not upright: {pose}" speed = np.linalg.norm(self.state_0.body_qd.numpy()[body]) assert speed < 0.25, f"Plate did not settle: {speed}"
if __name__ == "__main__": viewer, args = newton.examples.init(Example.create_parser()) example = Example(viewer, args) newton.examples.run(example, args)example = "franka_plate_rack"source = "newton/examples/robot/example_steel_plate_rack.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 Franka lifts a dinner plate and places it in a wire rack beside two resting plates."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 }# "propagation_colored" misses the rack slot in most runs (12 to 32 iterations); "immediate" places it.solver = { solve_mode = "serial", iterations = 12, articulated_contact_response = "immediate" }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.