Project 7: a grasp that survives randomization

Level 10 (after Lessons 10.1 and 9.3; Research mode for the statistics) · Time 3 hours · Difficulty 3 of 5

Objective

On models/gantry_gripper.xml, write a grasp-and-lift routine that works whatever cube it is given. The cube’s position, yaw, mass and friction are randomized. The routine is judged on 400 initial states drawn from a seed you did not tune on, and must succeed on at least 95% of them with a 95% Wilson lower bound above 90%.

Prerequisites

Starter code

The randomization is uniform and independent over these ranges:

Quantity Range
cube centre x, y ±0.2 m
yaw ±π/4 rad (a cube repeats every π/2)
mass 0.05 to 0.5 kg, inertia scaled with it
sliding friction 0.4 to 1.2

Success means three things. Through the whole final second of the 5 s episode, the cube is at least 0.10 m above where it started. At the end it moves slower than 0.02 m/s. And it touches both pads.

A randomization that does nothing. The gripper’s pads have geom priority 1. When two geoms of different priority touch, MuJoCo uses the higher-priority geom’s friction, so a pad-cube contact uses the pad’s 1.5 whatever the cube’s friction is set to. A randomizer that only edits the cube’s geom_friction leaves the grasp unchanged. Measured with the cube’s friction set to 0.3, the pad-cube contacts still reported 1.5. grasp_scene.py therefore raises the cube’s priority to 2, so the sampled friction governs both its contacts.

Expected behaviour

The reference reads the cube’s position once, moves above it, descends with the gripper open, closes for 0.6 s, and lifts 0.15 m along a 1.5 s minimum-jerk profile. It never rotates: the gantry cannot, and closing the jaws turns a yawed cube square to them.

Tests

MJC_IMPL=starter pytest projects/p07_robust_grasp
pytest projects/p07_robust_grasp

Three tests: commands inside ctrlrange; the 32 corner states; and the held-out rate on 400 states from seed 7 (about 12 s). The common failures that a routine can make are mutants in mutants.json; python tools/mutate_project.py p07_robust_grasp checks that each one fails.

Common failures

Extension challenges

  1. Detect grasp failure from the pad contact forces before lifting (sum of normal forces against $mg/2\mu$), and retry. Measure the gain under a 0.15 s lift.
  2. Widen the friction range down to 0.2 and find the fastest lift that still meets the bound.
  3. Make the routine closed loop: correct the gantry’s position from the cube’s pose during the descent, and test with a cube that is nudged sideways after $t = 0$.

Solution

solution.py, about 35 lines.