Grasping: friction, closure, and candidates
A grasp is a bet that friction beats gravity, placed using a friction coefficient you know to maybe fifty percent. Derive the friction cone, turn it into a point-cloud candidate generator, and wrap it in a state machine that tells you which subsystem lied when the bet fails.
- Derive the friction cone from the Coulomb coefficient and compute its half-angle for the actual finger-object pairs on your bench.
- Test any two contact points for the antipodal force-closure condition, and implement that test as dot products on point-cloud normals.
- Generate and rank parallel-jaw grasp candidates from a RealSense-class point cloud, honoring gripper aperture and approach-axis constraints.
- Design a grasp state machine whose verify step classifies failures into perception, planning, or execution using only gripper aperture and effort telemetry.
The previous lesson ended with an object pose in the robot base frame, good to a few millimeters. Everything before that — kinematics, Jacobians, IK, calibration — was infrastructure for this moment: choosing where two fingers touch an object and squeezing. Grasping is where your pipeline cashes out into a binary. There is no partial credit; the mug is either in the gripper or on the floor, and every budgeted error term — 0.6 mm depth noise, 2–5 mm registration error, 1–2 mm FK error — is now spent against one physical tolerance. The theory half of this lesson is elegant: one inequality about friction generates the entire geometry of what can be held. The practice half is where that elegance meets the WidowX AI's parallel-jaw gripper — 100 N of squeeze, silicone pads, and not a single tactile sensor — and you learn why the field quietly stopped trusting the elegant part.
Contact models: one inequality generates everything
Zoom into where a finger meets an object and you find a contact patch a few millimeters across. The classical idealization is a point contact with friction: a point with inward unit normal , through which the finger applies a force . Two physical facts constrain . Contact is unilateral — a finger pushes, never pulls, so the normal component is non-negative. And the tangential component is friction, which Coulomb's model bounds by the normal load:
Now derive the admissible set's shape. Write any candidate force by the angle it makes with the inward normal: normal component , tangential component . Substitute into the Coulomb bound and the magnitude cancels:
So the transmissible forces form a cone: axis along the inward normal, half-angle . Real numbers: bare aluminum on smooth ABS gives , a cone; the molded silicone pads on the WidowX AI's precision-grip fingers raise to – on most household surfaces — a – cone. That factor of two is the cheapest reliability upgrade in robotics: a wider cone keeps required forces admissible when contact placement is off by the several millimeters your perception stack guarantees.
Real fingertips also deform: a compliant pad flattens into a patch, and a patch — unlike a point — resists torsion about the contact normal. The soft-finger model adds one constraint: , a torsional budget proportional to squeeze force. It does quiet load-bearing work below, because two ideal point contacts cannot stop an object from pinwheeling about the line between the fingers. Your grasps work because your fingertips are soft, not despite it.
Foundations: Wrenches, Antipodal Geometry, and the Friction Cone
Before deriving the closure conditions, we must rigorously define the geometric and physical quantities that the subsequent logic relies upon. The primary abstraction is the wrench, a six-dimensional vector that unifies force and torque. While force describes linear acceleration, torque describes angular acceleration. For a rigid body, these are coupled: a force applied at a point away from the center of mass generates a moment. We define the wrench as the concatenation of the force vector and the torque vector about the object's center of mass (COM):
This definition is critical for the antipodal condition. In 2D, antipodal points are often described intuitively as 'opposite sides.' For the frictionless idealization in 3D, two smooth convex-surface contacts and are antipodal when the segment joining them is parallel to both inward surface normals, with opposite orientations. If points from to , the ideal condition is and . With friction, exact alignment relaxes to the friction-cone inequalities derived below. Equal and opposite collinear squeeze forces then have zero net force and zero net torque, leaving frictional margin to resist disturbances.
The friction cone derivation requires explicit vector decomposition to justify the geometric interpretation. Let be the total contact force. We decompose into a normal component and a tangential component . By the Pythagorean theorem in 3D, . Coulomb's law states . Substituting and dividing by yields the half-angle . This confirms that the admissible forces form a cone of constant half-angle, independent of the magnitude of the squeeze.
| Pad Material | Object Surface | Typical Range | Cone Half-Angle |
|---|---|---|---|
| Silicone (Soft) | ABS Plastic | 0.6 - 0.8 | 31° - 39° |
| Silicone (Soft) | Glass | 0.4 - 0.6 | 22° - 31° |
| Silicone (Soft) | Brushed Metal | 0.5 - 0.7 | 27° - 35° |
| Rubber (Hard) | Wood | 0.3 - 0.5 | 17° - 27° |
Worked Example: Sizing a Grasp for a 200g Object
Consider a parallel-jaw gripper with silicone pads () grasping a 200 g object ( kg). The object is to be lifted with an acceleration m/s². The gripper has a maximum force capacity of 100 N. We must determine the minimum squeeze force, apply a safety factor, and verify the aperture constraint.
To account for uncertainty in (which may drop to 0.4 due to wear or contamination), we calculate the worst-case force: N. Applying a safety factor , the commanded force is N. This is well within the 100 N limit. For the aperture, if the object width is mm and the pad thickness is mm, the required finger position is mm. Since the gripper's maximum aperture is 40 mm (finger position 20 mm), the grasp is feasible.
A grasp is planned with a cone half-angle of 30°. The estimated normal at one contact is off by 35° due to point cloud noise. What is the outcome?
Closure: force, form, and the antipodal test
A single contact constrains an object; a grasp immobilizes it. Immobilization is about the contact's effect on the whole rigid body — its wrench, force and torque stacked into a 6-vector: a contact at transmitting contributes — the same wrench objects from the force side of the Jacobian lesson, now sourced from fingers.
Two notions of “held” live here. Form closure is pure geometry: the contacts immobilize the object even with , like a peg caged in a matching hole. It is the stronger guarantee and the useless one for you — first-order form closure of a 3-D object needs at least seven frictionless contacts, and your gripper has two fingers. Force closure recruits friction: fewer contacts, conditional on and on actually supplying squeeze force. Everything a parallel-jaw gripper does is force closure — a deliberate trade that lets two actuated surfaces do the work of seven.
For two contacts, force closure collapses to a clean geometric test. Take contacts at , let be the unit vector from the first to the second, and squeeze: contact 1 pushes with , contact 2 with . Net force cancels by construction; net torque is , since is parallel to . A squeeze along the connecting line is always internally balanced — the only question is whether the friction cones permit it, a pair of angle conditions:
This is Nguyen's condition: a two-contact grasp with friction achieves force closure exactly when the segment joining the contacts lies inside both friction cones. When it holds, small tilts of each force within its cone — scaled by the squeeze — generate wrenches in every direction, promoting “balanced” to “resists anything.” One honest asterisk: ideal point contacts cannot resist torque about the connecting axis, so the 3-D result formally needs soft-finger contacts — the torsional term above. Perfectly antipodal contacts with pads enjoy of margin each; that margin is what pays for your combined perception-plus-calibration error.
Fingertips with mu = 0.4 (cone half-angle 21.8°) contact an object at two points. The line between the contacts makes an angle of 15° with the inward normal at contact 1, and 25° at contact 2. Is this grasp force closure under the antipodal test?
From point cloud to candidates
Now make the test operational. The registration lesson hands you a segmented object cloud — typically 30k–80k points after a tabletop crop at 0.4–0.6 m. The antipodal test needs normals, and the standard estimator is local PCA: for each point, take its nearest neighbors, form the covariance, and the eigenvector of the smallest eigenvalue is the normal — the direction the local patch does not extend. PCA leaves a sign ambiguity; flip each normal to face the camera, since every observed surface faced it. Keep the eigenvalue ratio too: a curvature score near zero on planes and large at edges, which becomes your data-quality filter.
Candidate generation is a sampling funnel with a familiar shape: retrieval-then-rerank. A cheap proposal stage (random point pairs) feeds a cascade of filters; only survivors reach the expensive verifier — the physical arm, at ~10 s per attempt. Sample ~5,000 pairs; the antipodal dot products kill most; the aperture constraint (the WidowX AI's gripper opens to 40 mm, so anything wider is unreachable by definition) and the approach constraint kill more. For a tabletop WidowX AI the latter is blunt: approach roughly top-down within a cone — steeper hits the table or the wrist limits your IK lesson predicted — with the grasp axis near-horizontal so the fingers straddle the object. A few dozen candidates survive to ranking.
import numpy as np
from scipy.spatial import cKDTree
def estimate_normals(points, k=30):
"""PCA normals for an N x 3 cloud in the camera frame.
Returns unit normals oriented toward the camera (at the origin)
and a curvature score for edge filtering."""
tree = cKDTree(points)
_, idx = tree.query(points, k=k)
normals = np.empty_like(points)
curvature = np.empty(len(points))
for i, nb in enumerate(idx):
q = points[nb] - points[nb].mean(axis=0)
w, v = np.linalg.eigh(q.T @ q) # ascending eigenvalues
n = v[:, 0] # smallest-eigenvalue direction
if n @ points[i] > 0.0: # flip to face the camera
n = -n
normals[i] = n
curvature[i] = w[0] / max(w.sum(), 1e-12)
return normals, curvature
def antipodal_pairs(points, normals, curvature, mu=0.5,
max_width=0.040, n_samples=5000,
curv_max=0.03, seed=0):
"""Sample point pairs; keep those passing Nguyen's condition
and the gripper aperture limit."""
rng = np.random.default_rng(seed)
cos_alpha = 1.0 / np.sqrt(1.0 + mu * mu)
clean = np.flatnonzero(curvature < curv_max)
i = rng.choice(clean, n_samples)
j = rng.choice(clean, n_samples)
d = points[j] - points[i]
width = np.linalg.norm(d, axis=1)
ok = (width > 5e-3) & (width < max_width)
u = d / np.maximum(width, 1e-9)[:, None]
inward_i = -normals[i] # camera-facing -> inward
inward_j = -normals[j]
ok &= np.einsum("nd,nd->n", u, inward_i) >= cos_alpha
ok &= np.einsum("nd,nd->n", -u, inward_j) >= cos_alpha
return i[ok], j[ok], width[ok]
def rank(points, i, j, width, normals, max_width=0.040):
"""Higher is better: cone margin and width margin reward,
gravity torque arm penalizes."""
axis = points[j] - points[i]
axis /= np.linalg.norm(axis, axis=1)[:, None]
c1 = np.einsum("nd,nd->n", axis, -normals[i])
c2 = np.einsum("nd,nd->n", -axis, -normals[j])
cone_margin = np.minimum(c1, c2) # cos of worst contact angle
mid = 0.5 * (points[i] + points[j])
torque_arm = np.linalg.norm(mid - points.mean(axis=0), axis=1)
width_margin = (max_width - width) / max_width
score = 2.0 * cone_margin + width_margin - 5.0 * torque_arm
return np.argsort(-score)Each ranking heuristic is a proxy for a distinct failure mode:
- Cone margin — how far inside both cones the grasp axis sits. Every degree absorbs a degree of combined pose, calibration, and normal error; below ~5° you are executing your error bars.
- Torque arm — centroid-to-grasp-axis distance. Gravity applies torque against the soft-finger torsional budget: a 200 g object gripped 30 mm off-centroid carries a standing 59 mN·m twist that grows under acceleration.
- Width margin — aperture minus object width. Fingers must descend around the object; a 40 mm gripper on a 36 mm box leaves 2 mm per side — less than your perception error, so the pre-grasp itself collides.
- Approach feasibility — approach axis inside the arm's comfortable IK region, checked with your solver; reject anything whose approach ray hits the table or the object's own cloud.
- Data quality at the contacts — curvature and neighbor density on both patches; prefer well-observed surface over the occluded far side, where normals are extrapolated fiction.
Note the systems profile: normals for a 50k-point cloud take ~5 ms vectorized (the loop above is the teaching version), and 5,000 antipodal tests are microseconds of einsum. The classical planner is effectively free next to a -class VLA forward pass at ~100 ms on your RTX box — which is why it makes a good oracle: run it every frame, log every candidate, and later diff its ranked list against whatever a learned policy chooses.
Grasp quality: where the theory meets its correlation problem
The classical literature went past binary closure into continuous quality metrics. The canonical one is the Ferrari–Canny epsilon metric: cap each contact force at unit magnitude, take the convex hull of all achievable wrenches, and report the radius of the largest origin-centered ball inside it — the worst-case disturbance resisted per unit squeeze. Principled minimax; it dominated grasp research for two decades. Benchmarked against physical trials at scale, the result was deflating: epsilon-style rankings correlate weakly with real success — better than random, nowhere near enough to trust. The Dex-Net 2.0 (opens in a new tab) line made the point operationally: what transferred to physical benchmarks was training on millions of simulated grasps labeled with robustness — success probability under sampled pose, friction, and gripper noise — not nominal analytic quality.
The failure is no mystery; each assumption breaks in a known way. The metric assumes exact contact locations — perception delivers millimeters of error. It assumes known — you have a guess. It assumes the object holds still while fingers arrive — the first finger to touch moves the object, sometimes helpfully (funneling it into the jaw), sometimes fatally (skating it off the table), and quasi-statics is silent on which. And it scores the final configuration while ignoring the approach that produces it. The metrics answer a precise question about an idealized snapshot the physical episode only loosely visits.
| Metric | What it measures | Cost per candidate | Correlation with real success |
|---|---|---|---|
| Form closure | Frictionless geometric caging of all 6 DOF | LP feasibility, ~ms | Nearly irrelevant for two jaws — needs 7+ contacts, so it rejects everything you can execute |
| Antipodal test + cone margin | Contact geometry vs the friction cones | Two dot products, ~µs | Useful as filter and ranking signal; blind to dynamics, occlusion, pose error |
| Ferrari–Canny epsilon | Worst-case resistible wrench per unit squeeze | 6-D convex hull, ~ms | Weak on hardware: assumes exact contacts, exact mu, rigid bodies, quasi-statics |
| Empirical success rate | What actually happens over N physical trials | ~10 s of robot time per trial | Ground truth by definition; expensive, binomially noisy, rig-specific |
Your gripper: current, compliance, and the grasp state machine
Time to land on the bench. The WidowX AI's gripper is two parallel fingers with molded silicone pads, driven by an integrated actuator — 40 mm of finger travel, up to 100 N of gripping force, and no dedicated force or tactile sensor. Readable from the driver's ~500 Hz state stream: position (which maps to finger aperture) and effort (roughly proportional to squeeze force, once past transmission friction). Writable: a position target or, since the driver exposes an effort mode, a squeeze effort directly. The standard configuration is effort-limited closing: command the fingers well past the object's width and let the effort limit — not the position target — set the squeeze. The actuator stalls against the object and holds: a crude but real force-controlled gripper. How hard to squeeze comes from the friction model, inverted: the pads must carry the weight plus the inertial load of the lift:
The effort trace is also your only tactile sensor, and it is a good one. A healthy hold is boring: aperture pinned at the object width, effort at the limit. Slip announces itself as aperture creep — the reported position drifts while the target has not changed — often with an effort transient as the object rotates through the pads. A lost object is unambiguous: effort collapses to near zero within ~100 ms as the suddenly-unloaded fingers reach their target. This is fingerprinting an unobservable phenomenon in the one telemetry channel you have — a move you know well. Log aperture and effort at 50 Hz or better (the driver streams ~500 Hz) for every grasp from day one; those traces are how you will debug every mysterious drop in this course.
Which brings us to the real deliverable: grasping is not an action, it is a state machine, and the verify states are where the phase goal — separating perception, planning, and execution failures — becomes executable code. A minimal recoverable pick:
- PERCEIVE — capture cloud, register pose, generate and rank candidates. Output: a ranked list, not one pose; the list is your retry budget.
- APPROACH — move to a pre-grasp waypoint 80–100 mm back along the approach axis, full speed, collision-checked.
- DESCEND — straight-line to the grasp pose at 0.05–0.10 m/s; slow, because this is where unmodeled contact happens.
- CLOSE — effort-limited close with the limit from your force calculation; wait for effort saturation or a 1 s timeout.
- VERIFY-GRASP — the predicate below. On failure, classify and branch: back to PERCEIVE or to the next candidate.
- LIFT — up 100 mm at modest acceleration (the term you sized the squeeze for).
- VERIFY-HOLD — re-check aperture and effort under the dynamic load; optionally re-observe the scene to confirm the object left the table.
The verify predicate needs only the two telemetry channels, and its power is that different failures produce separable signatures. Define an expected aperture band from the object's known width, say . After CLOSE: aperture in band, effort at the limit for 200 ms HOLDING. Aperture far below band, near-zero effort AIR — closed on nothing, indicting perception or calibration: re-perceive rather than retry the same pose. Aperture far above band, effort at the limit JAMMED — stalled on wrong geometry (a knuckle, a neighbor, the table), indicting the candidate: try the next one. HOLDING after CLOSE but AIR at VERIFY-HOLD the transport shook it loose — an execution failure: raise the effort limit or lower the lift acceleration. Four outcomes, three subsystems, one if-statement each — the debuggability no end-to-end policy gives you, and the oracle you will hold the learned stack accountable to.
After CLOSE on an object with expected width 32 mm, the gripper reports aperture 9 mm (near its mechanical minimum) and effort near zero. What happened?
Ship a candidate generator and a verify predicate
Two deliverables. (1) Run the antipodal pipeline on two clouds: a synthetic cylinder (25 mm diameter, 120 mm tall, ~20k points with 1 mm Gaussian noise — ground-truth normals let you score your estimator too) and one real RealSense capture of a tabletop object. Report the funnel: pairs sampled, survivors after the cone test, after the 40 mm aperture limit, after a top-down approach constraint, plus the top-10 candidates with scores. (2) Write your gripper's verify predicate as a pure function verify(aperture_mm, effort, window_ms) returning HOLDING, AIR, or JAMMED — and defend every threshold with data: close on air 10 times and on the object 10 times, logging both channels at 50 Hz.
Need a hint?
For the cylinder, ground truth is on your side: true antipodal pairs are diametrically opposite points whose grasp axis passes through the cylinder's axis, so you can measure precision directly — what fraction of survivors lie within 5 mm of a true diameter? Survivors clustering on the end-cap rims mean the curvature filter is too loose. For thresholds, overlay the 10 air and 10 hold traces; steady-state efforts should separate by an order of magnitude, and a threshold near their geometric mean is robust to both clusters' spread.
Where this goes next: Pose estimation and point-cloud registration told you where the object is; this lesson turned that pose into a ranked list of gripper placements and a state machine that knows when it has been lied to. But a grasp pose is a destination, not a route — Motion planning: RRT, optimization, and time generates the collision-free, time-parameterized path to the pre-grasp waypoint and through the descend, where the approach-axis constraints you imposed here stop being filters and become boundary conditions.