Senior design capstone · MAE 151A/B, UC Irvine · five-person team · Oct 2025 – Mar 2026

ArmY — Grasp-Pose Selection

ArmY was our capstone team's attempt at a grocery-shelf picking robot. Mine was the logic that picks one usable grasp out of a hundred candidates, the bridge that puts the camera's point cloud into the arm's own frame, and a GUI an operator can actually drive it from.

Target detection
15/15
Grasp pose within ±1 cm
7/10
Pick cycle time
10–60 s

Step 1

Choosing one grasp out of a hundred

To grasp an object, what an arm needs is a pose in space — where to put the jaws, from which direction, and how far to open them.

Producing those is the open-source GPD library's job: it samples the point cloud and returns a ranked set of candidate gripper poses — a great many on a cluttered scene, plenty of them unusable, driving the gripper through the surface the item rests on or sitting on the ragged fringe of the cloud.

What I added around it

  • A reachability box — the stock all sits inside it, and candidates falling outside that conservative box in the arm’s base frame are dropped before anything else is considered; this is what removes most of them
  • Ranking — what survives is ordered by the classifier’s own score, with distance to a preferred point as the tie-break
RViz on the robot's laptop: a white point cloud of the scanned scene with blue candidate gripper poses generated on it, the GPDPlotGrasps display layer enabled, and a terminal reporting that gpd_grasp has subscribed to /detect_grasps/clustered_grasps.
The white surface is the scanned point cloud; the blue jaws are candidate grasps generated on it.

On hardware

Three picks, one pipeline

different objects, different benches

Step 2

Making three pieces into one system

a grasp generator, a moving camera and an arm that have to agree on where things are
Detection, candidate selection and the pick, end to end with no intervention.

I made three pieces that were never built for each other behave as one system: an open-source grasp generator, a depth camera on the wrist, and the arm's own control interface.

The point-cloud bridge is where that is settled. GPD is written for a camera on a fixed mount, watching the scene from outside while the arm moves through it. Ours is the other arrangement — the depth camera is bolted to the wrist, directly above the jaws — so there is no fixed camera-to-base extrinsic to write down, and the frame the cloud arrives in is a different one after every joint move.

That breaks an assumption GPD never states, because with a fixed camera it never has to. It crops with a plain box of metres — −0.05 to 0.36 m out from the base, ±0.30 m across, −0.05 to 0.40 m up — which describes the space above the table only if the cloud is in the arm's base frame. Ours arrives wrist-relative, so the box cuts a volume out of a different wrong place after every joint move. Every cloud is therefore pulled through the live TF tree into base_link first, downsampled on the way. Get it wrong and nothing errors: the arm reaches confidently for a place that is not there.

We could have run GPD first and transformed afterwards, throwing away whatever landed outside the crop box — moving the pose GPD hands back is far cheaper than moving the cloud, a handful of poses against a few hundred thousand points. We still transform the whole cloud into base_link first. The crop box takes effect before the data reaches GPD, so on an untransformed cloud GPD generates a great many candidates outside the box, and those can score high enough to crowd out the ones actually on the item. That is what we hit in testing.

Around it sits the node that takes the chosen candidate to the team's IK and MoveIt 2 service, and a PyQt5 panel keeping hand-eye calibration, exposure and grasp offsets adjustable while the arm runs — so an experiment costs a slider instead of a rebuild.

Verification I owned

The perception and grasp requirements were mine to test. What passed:

  • Object detection15/15 across the baseline and cluttered sets, against distractors and partial occlusion
  • Grasp sequence automation — detection through to extraction; the operator only clicks the item they want

And what did not

Position accuracy is the one that matters, and the one that failed: the estimate had to stay within ± 1.0 cm for 95% of samples, and did so for 7 of 10. Pick cycle time landed at 10–60 s against a < 45 s budget.

15/15 detection is not 15/15 grasping: the system almost always sees the item, and still does not always know where it is well enough to grasp it cleanly. The limit is the depth camera — a grasp pose is only as accurate as the surface it was fitted to, and the camera's own precision leaves this cloud ragged at its edges.


Scope

What I actually did

ArmY was a five-person team. The mechanical design, the motion-planning line and the electrical work were teammates’. Mine was the grasp pipeline: the grasp-selection node, the point-cloud bridge, the operator interface, and the verification above.

The grasp-candidate generator is not mine either — it is the open-source GPD library, sitting in the tree unchanged. What I wrote is everything around it that makes it run on this arm.

And what the project did not get to: shelf-aware obstacle avoidance never reached a working state. The picks here are in open scenes and on a rack, not reaching past neighbouring stock inside a confined bay — the grasp pipeline is the part that works.

Sponsor: Prof. Solmaz Kia. Advisors: Prof. Mark Walter and Prof. David Copp. Submitted as the UC Irvine degree capstone for MAE 151A/B.

The project’s structural decomposition: six subsystems covering simulation, perception, low-level driver and ROS, kinematics and motion planning, hardware modification and calibration, and visual servoing and integration.
The project as the team divided it. My work sat in the perception column and the integration column on the right.