Dexterous manipulation ◆ Imitation learning ◆ Teleoperation ◆ New York University ◆ 2026
Get a grip: a geometric ACT policy for contact-rich, long-horizon dexterous pick-and-place. A human wearing an Apple Vision Pro drives a 23-DoF arm-and-hand in MuJoCo; the demonstrations train a chunked transformer that clears a table of fruit. The hard parts were not the network — they were getting a human hand onto a robot hand at all, and stopping the policy from freezing when it has to choose which apple to pick next.
The learned policy clearing the table: locate a fruit, reach, close the 16-DoF hand around it, place it on the plate, return to the home posture, and go again. No resets between objects.
A human index finger and an ORCA index finger do not have the same link lengths, the same joint axes, or the same number of joints. Mapping joint angle to joint angle is not just inaccurate, it is invalid — the resulting robot pose is not the pose the operator made.
So the retargeter does not match joint angles, and it does not really match absolute fingertip positions either. It matches geometry, in the palm frame, with three terms that pull in different directions:
The arm is solved separately — 7 DoF tracking the operator's wrist transform by SLSQP with λp = 500, λR = 200, λs = 2 — and its palm pose is then treated as a fixed base for the finger problem. Solving all 23 jointly at 30 Hz is both too slow and too full of local minima.
Left: the operator's hand as the Apple Vision Pro recorded it, 21 landmarks in the palm frame, nine seconds of a real pinch-and-release sequence. Right: the ORCA hand, its real MuJoCo meshes posed by the exported kinematics, tracking that hand through the released cost function, solved live in your browser. Every term of the cost is a slider; fingertips share a colour on both hands so you can see which one is off and by how much.
Finger size factors, human to robot (the calibration the pipeline applies to each finger's landmarks):
Drag either view to orbit. The solver is coordinate descent on the released cost with the hand's real joint limits; the offline harness uses SLSQP on the same objective, so the numbers differ by solver, not by cost.
The released checkpoint (the sequential ACT model, joint-space actions from the wrist and overhead RGB-D cameras) rolled out on the xArm Lite 6 with the ORCA hand in the MuJoCo scene, with one to five fruits dropped at random inside the training spawn zone. Fifty-two episodes, one seed per episode, nothing cherry-picked out of the tally. These are my numbers, not the report's — the report quotes 96 % on a single fruit over 50 rollouts, and re-running the checkpoint here, in the multi-fruit scene and with the fruit required to come to rest on the plate, I get half that on one fruit and much less as fruits are added. The clips are the successes and, where there were none, the runs that got furthest; clips longer than 19 seconds are sped up.
Loading the recorded runs…
The protocol, the thresholds, the figures and the caveats — including why my single-fruit number and the report's differ — are written up in the rollout study (PDF).
One fruit on the table: half the episodes end with it on the plate. Two: one in ten. Three or more: none of the thirty episodes cleared the table, though they placed 26 individual fruits between them. The break is after the first success. I logged the hand and every fruit at 10 Hz: in the 25 multi-fruit episodes that placed something, the palm afterwards sits a median 14 cm from the nearest remaining fruit and comes within 4 cm of one — it does go back and engage the next fruit, and 24 of the 70 remaining fruits get visibly nudged. But only 15 of those 70 (21 %) are ever lifted clear of the table again, and only 7 (10 %) reach the plate. It approaches and paws at the next fruit without closing on it. That is the freezing the report describes and treats with the sequential sub-task encoding, and it is not fixed here — a single fruit just hides it.
Right after the hand lifts a fruit, something happens to it: it is knocked out sideways, taken out of the hand and put back elsewhere on the table, pressed down while held, or removed for good. The policy never saw any of this in training and it has no memory beyond its observation window, so this is a fair test of whether the visuomotor loop is closing on what is actually in front of it. Twenty-four attempts, fifteen of which got far enough to have a grasp to disturb (one of those took away the only fruit on the table, so there was nothing left to recover with). Of the fourteen real tests, three still finished the task, and the two clean re-grasps came back within about a second. Every disturbed run is below, recovery or not.
Loading the disturbance runs…
The policy is a 40-million-parameter transformer over two RGB-D cameras; it does not run in a browser. What runs here is the library of recorded episodes: drop fruits on the map of the table and the page finds the recorded episode whose starting layout is closest to yours, shows that layout over your own, and plays what the policy did. Click a fruit again to remove it.
Drop one to five fruits on the table.
The report asserts that the pairwise term is what preserves precision-pinch geometry. To check it I re-ran the released cost against the real ORCA MuJoCo model on 268 frames of recorded Vision Pro hand data, once per ablation, with the operator-to-robot palm frame solved by Kabsch alignment the way a teleoperation rig anchors at start-up.
| Cost configuration | fingertip direction [deg] | keypoint cos sim | pinch error, all frames [mm] | pinch error while pinching [mm] | aperture error [mm] | solve [ms] |
|---|---|---|---|---|---|---|
| full (released) | 18.94 | 0.871 | 13.54 | 11.30 | 14.6 | 10.3 |
| no pairwise (γrel=0) | 13.27 | 0.881 | 25.13 | 42.20 | 19.1 | 5.5 |
| no cosine (γcos=0) | 40.36 | 0.765 | 13.89 | 4.24 | 16.8 | 9.2 |
| no smoothness | 18.37 | 0.873 | 13.28 | 9.06 | 15.2 | 13.6 |
| naive keypoint L2 | 11.77 | 0.878 | 25.58 | 47.40 | 21.5 | 9.1 |
← swipe the table →
Dropping γrel takes the thumb–index gap error from 11.3 mm to 42.2 mm on the frames where the operator is actually pinching — nearly four times worse — and from 13.5 mm to 25.1 mm overall. Naive absolute-keypoint retargeting is worse still at 47.4 mm, despite having the best fingertip direction of any configuration, 11.8° against the full cost's 18.9°. That is the whole point: getting each fingertip pointing the right way says nothing about whether the fingers can meet.
This one I did not expect. Removing the cosine term makes pinch tracking better — 4.2 mm instead of 11.3 mm while pinching — at the cost of more than doubling the fingertip direction error, 18.9° to 40.4°, and dropping keypoint cosine similarity from 0.871 to 0.765. The two terms are optimising different things over the same 16 joints, and γcos = 50 against γrel = 100 is the compromise between them. A pinch-gated cosine weight, mirroring the boost that already exists on the pairwise term, would probably get both.
Solve times here (5–14 ms median) are slower than the 2–4 ms the report quotes, because this harness runs SLSQP with Python-side MuJoCo forward kinematics per cost evaluation rather than the tuned in-process planner loop. The ratios between configurations are the comparable quantity, not the absolute times. The solver is fully converged: raising the iteration cap from 5 to 40 changes the direction error by less than 0.01°.
Until September 2026 this section, and the 3-D playground above it, were built from teleop/xml/hand_camera_scene.xml — a Kyber hand that happens to live in the same repository, with 20 joints and its own link lengths. Everything downstream inherited it: the meshes, the forward kinematics, the joint limits, and every number in the table. The hand on this page was not the hand in the robot.
Re-exported from the model the project actually runs, ORCA/combined_lite6_orca/task2_scene.xml — 16 joints, bodies left_palm, left_thumb_mp…dp, left_index_mp…ip — the qualitative conclusions survive intact: the pairwise term is what makes pinching work, the cosine term is what holds posture, and the two trade against each other. The magnitudes do not. Fingertip direction error is 18.9° rather than 4.7°, because the real ORCA finger has three joints and no distal one, so it simply cannot point where a human fingertip points.
The per-finger size factors were wrong in the same way, and for the same reason: they were 1.15–1.40, implying a robot hand larger than the operator's. Measured against the ORCA model they are 0.76–0.87 — it is smaller. Correcting them halved the overall pinch error, from 30.0 mm to 13.5 mm, before any of the comparisons above were made.
Loading the right model then exposed a second bug that the wrong one had been hiding. MuJoCo turns a hinge by qpos − qpos0: body_pos and body_quat describe the body at the model's reference pose, not at zero. The browser's forward kinematics rotated by qpos alone, which is correct only when every reference angle is zero — as it happened to be on the Kyber hand. On the ORCA hand the thumb abduction reference is −0.733 rad, index −0.40, pinky +0.523, so those fingers were posed wrong by up to 93 mm at the fingertip while the middle finger, whose three reference angles are all zero, was pixel-perfect. Exporting the reference angle and subtracting it brings the browser's kinematics to agreement with MuJoCo to 0.00000 mm at every keypoint. The offline ablation was never affected — it calls MuJoCo directly.
With demonstrations in hand the policy is an Action Chunking Transformer: two ResNet-18 towers over the wrist and overhead RGB-D views, an MLP over the 23-DoF proprioceptive state, a CVAE latent, and a decoder that emits a 60-step (2.0 s) action chunk. At deployment the chunks overlap and are combined by temporal ensembling with τ = 10, so every executed action is an exponentially weighted average over every prediction still in flight.
Two implementation details mattered more than the architecture:
Clear one fruit and the policy is near-perfect. Clear four and it freezes. The reason is not capacity — it is that continuous teleoperation makes the dataset genuinely ambiguous. After a placement, two apples at near-equal distance produce identical images mapping to diverging action distributions, and a CVAE trained on that averages the modes instead of committing to one. The robot hovers between the two.
The fix is a protocol, not a loss. After securing an object the operator must return to a high-clearance home posture before reaching again. That makes the visual state at the start of every reach deterministic and unoccluded, which disentangles the action distribution the policy has to fit. It costs the operator a second per sub-task and it is the difference between a policy that clears a table and one that stalls on the second object.
| Training dataset | SR-1 | SR-2 | SR-3 | SR-4 |
|---|---|---|---|---|
| Unstructured (uncut) | 90 % | 22 % | 0 % | 0 % |
| State-Bottleneck | 96 % | 88 % | 23 % | 3 % |
| Δ | +6 pp | +66 pp | +23 pp | +3 pp |
Success rate over 50 independent rollouts per condition. SR-k is clearing k objects in sequence without a reset. The bottleneck protocol rescues SR-2 almost entirely; SR-3 and SR-4 stay hard, which is where residual RL on top of the frozen policy comes in.
The full stack — the Vision Pro streaming client, the three-process ZeroMQ teleoperation loop, the ACT and diffusion training, and the MuJoCo scenes — lives in a separate private repository. The public one is the part that reproduces from a clean checkout: the released cost function, the ORCA hand model, the recorded hand data, and the study above.
The browser playground loads assets/data/geodex_hand.json — the ORCA finger kinematics and 134 calibrated frames of the recorded trajectory, both produced by the exporters above.
GeoDEX was the final project for DS-GA 3001 at NYU, joint work with Yipeng Wang. The ORCA anthropomorphic hand is open-sourced by ETH Zürich; the arm is an xArm Lite 6. Everything is evaluated in MuJoCo — the sim-to-real gap is explicitly unaddressed, and is the first thing the next version has to fix.
The teleoperation stack, the policies and the report are from the project. The retargeting ablation, the frame calibration, the exporters and the browser playground on this page were built afterwards, on the released cost function and the recorded hand data.