System designHow-to

Batch picking from a tray: loop vision locate until no parts found

Batch picking from a tray: set the detection cap and batch quantity, loop the vision locate on the detected-object count, and stop when the tray is empty.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01How many parts can one snapshot locate?
  2. 02Why does the robot keep picking from an empty tray?
  3. 03What should the loop condition read instead?
  4. 04How does the batch quantity setting change the cycle?
  5. 05How many snapshot-locate cycles does a 48-part tray need?
  6. 06Which camera runs a tray locate loop?
  7. 07How many pixels does the field of view need over the tray?
  8. 08Do you ever need a second locate node?
  9. 09What do you check before running the loop unattended?
  10. 10Questions

You have a tray of parts, a locate node that finds them and a robot that picks them. The program will not stop once the tray runs empty. The fix is almost never a second locate node. Loop one locate node instead: set a batch quantity so it re-images the tray after every pick batch. Read the detected-object count it returns each pass, and exit through the parts-not-found branch when that count reaches zero. The numbers below come from a 48-part tray cleared with a locator that detects at most 25 objects per snapshot and a batch quantity of 10: five productive passes, one confirming empty snapshot, 48 parts counted. By the end you can size the passes for any tray load and wire an exit that never sends the robot into an empty pocket.

How many parts can one snapshot locate?

Plan around the locator's per-snapshot detection cap before anything else. The wrist-mounted locate node this pattern comes from reports at most 25 detected objects per image. A 48-part tray therefore needs at least ceil(48 / 25) = 2 snapshot-locate passes to clear.

The cap lives in the locator, not the sensor. The camera may image all 48 parts cleanly, but the detector returns no more than 25 results, so part 26 is not mislocated — it is simply absent from the list until the next image. No branch logic can recover a part the locate never reported; only a fresh snapshot can. Read the number from the locate node's settings or its manual before sizing anything: 25 applies to the wrist node this pattern comes from, and other locators cap elsewhere. This is also why a packed tray looks half-found — 25 boxes drawn over the parts, the rest unmarked.

The snapshot-locate-grip chain around this loop is walked step by step in the guide to building a robot vision system for object location and gripping.

Why does the robot keep picking from an empty tray?

Because the program branches on a fixed number of locate nodes instead of the count each locate returns. After the last part leaves the tray, the parts-not-found branch never sees a genuine zero, and the robot drives its pick moves into empty pockets.

Trace the two-node version. Locate 1 finds its 25 and the robot picks them; locate 2 finds the remaining 23. The final snapshot should return a count of zero and jump to the parts-not-found branch. Two things break that ending. False detections come first: reflections on shiny parts, tray texture or debris can score above the threshold, so the count reads 1 or 2 instead of 0 and the cycle repeats. Branch order comes second: if the parts-not-found check sits after a pick move, the robot has already committed to the empty pocket before the test runs.

Watch a full tray empty once with the live camera view in the setup screen open. Every box the detector draws after the last real part is a false detection — fix it with a higher minimum score, better lighting or a cleaner background before trusting the loop. The conveyor version of this failure, keyed off a part-detected flag, has its own write-up: vision locate with a part detected flag variable: stop the empty belt.

What should the loop condition read instead?

Read the detected-object count from the locate result on every pass and keep looping while it is greater than zero. In the result list used below, that count is element 0, the part pose occupies elements 1 through 6, and the edge-detection score sits in element 7.

Call the returned list f for this example. It is a list of numbers the locate node refills on every pass: f[0] is the number of detected objects, f[1] through f[6] are the current part's X, Y, Z, Rx, Ry, Rz, and f[7] is its edge-detection score. Your integration may name the list differently — the order of count, pose, score is the common pattern — so check which variable your locate node writes before wiring the branch.

The explicit-loop implementation, in pseudocode:

n = 0                  # integer, parts picked from this tray
q = 10                 # integer, batch quantity from the locate settings
while true:
    f = locate()       # number list, refilled by the locate node each pass
    if f[0] == 0:      # element 0: detected-object count
        break          # tray empty: parts-not-found branch
    k = min(q, f[0])   # parts to take this pass
    pick k parts at the poses in elements 1 to 6
    n = n + k
# after the loop the tray is empty and n equals the parts picked

The test that proves the wiring: on the full 48-part tray, a print of f[0] per pass reads 25, 25, 25, 18, 8, 0 and n finishes at 48. Any other sequence means the cap, the quantity or the branch is not reading what you think. The same loop also runs with the quantity left inside the locate node — set it to 10 in the locate settings and the node re-snapshots after each batch on its own; the exit stays keyed to the zero count either way.

Commissioning steps:

  1. Teach the snapshot position over the tray and train the locator on one part.
  2. Read the per-snapshot detection cap from the locate node's settings — 25 in this example.
  3. Set the batch quantity below the cap: 10.
  4. Key the parts-not-found branch on the count element of the result list, placed before any pick move.
  5. Run one full tray with the count logged each pass and confirm the sequence sums to the tray load.
  6. Add the operator prompt or home move to the not-found branch, then drop the approach speed for the first unattended trays.

If your passes also change locator settings — a different score threshold per part type, say — keep those writes out of the grab path; the piece on setting and getting camera parameters safely in a capture loop shows the pattern that avoids a stalled camera.

How does the batch quantity setting change the cycle?

Set the batch quantity below the detection cap — 10 against a cap of 25 — and the locate node returns to the snapshot position and re-images after every batch. The parts the previous image could not report arrive on the next pass, so no second locate node is needed.

Productive passes work out to ceil(parts / quantity) as long as the quantity sits at or under the cap; set it above the cap and the cap wins. For the 48-part load:

Batch quantity Productive passes, ceil(48 / q) Picks per pass Snapshots incl. empty confirm
25 (the cap) 2 25, 23 3
16 3 16, 16, 16 4
12 4 12, 12, 12, 12 5
10 5 10, 10, 10, 10, 8 6

Each extra pass costs one more image and locate — a fraction of a robot move — but every pass starts from a fresh image, so parts the gripper nudged are found where they now sit. Start at 10 and keep it there if picks disturb their neighbours; raise it toward the cap when the tray settles quietly between picks and cycle time matters. Teaching that snapshot position per layer instead of per tray is the destacking variant — the guide on teaching one snapshot position per layer in tray destacking covers it.

How many snapshot-locate cycles does a 48-part tray need?

With the cap at 25 and the batch quantity at 10, a 48-part tray takes five productive passes — picks of 10, 10, 10, 10 and 8, which sum to 48 — plus one last snapshot that returns a count of zero: six locate cycles in all.

detected = min(cap, parts_left) picked = min(quantity, detected) productive passes = ceil(parts / quantity) (quantity <= cap) total snapshots = productive passes + 1
Pass Parts left Detected, min(25, left) Picked, min(10, detected) Left after
1 48 25 10 38
2 38 25 10 28
3 28 25 10 18
4 18 18 10 8
5 8 8 8 0
6 0 0 exit: parts not found —

Check the arithmetic: ceil(48 / 10) = 5 productive passes, and 10 + 10 + 10 + 10 + 8 = 48. Raise the quantity to the cap of 25 and the same tray needs ceil(48 / 25) = 2 productive passes — 25, then 23 — plus the empty confirm, three snapshots in total. Three is the floor for this load; six is the cautious setting that re-checks the tray every ten picks.

Which camera runs a tray locate loop?

Any locator that returns a count and a pose list can drive this loop. A smart camera runs it on the device with no PC, while an area-scan camera plus a vision controller runs it PC-side with more headroom for logging, several cameras or AI steps.

For a compact cell where the whole inspection lives in the camera:

Smart cameras & vision sensors1.6 MP smart camera, 60 fps, 16 mm lens, white light (polarized, transparent light) (LS-SMC-0063)LS-SMC-0063
Interface
GigE
Resolution
Up to 3 MP
$1,110.00View part

When the tray needs more pixels than a fixed-lens smart camera gives, a vision sensor with an M12 mount takes the lens that covers your field:

Smart cameras & vision sensorsHikrobot MV-SC3050XC-16M-ATN-H 5 MP color vision sensorMV-SC3050XC-16M-ATN-H
Interface
Fast Ethernet
Resolution
5 MP
Sensor format
1/1.45"
Shutter
Global
$1,809.00View part

The on-device options sit in the smart cameras category, the PC-side boxes in vision controllers. For one camera with standard logging, a base controller is enough:

Vision controllersHikrobot MV-VC3301H-128G66 I3-8100, 8GB+128G SSD, No PoE, PCIe expansion supported, Incl. VM6200Pro licenseMV-VC3301H-128G66
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it

Several cameras on one tray cell need PoE camera ports and more built-in light channels:

Vision controllersHikrobot MV-VC5525-256G70-NN VC5000 Vision Controller, 12th-I5, 32GB+256G SSD, Supported PCIEMV-VC5525-256G70-NN
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
PoE
PoE camera ports
Light outputs
8 built-in channels
$4,479.00 · quotedView and quoteBuild a system with it

How many pixels does the field of view need over the tray?

Cover the pocket area and keep the smallest feature the locator matches at 5 pixels wide as a floor. A 5 MP sensor of roughly 2500 × 2000 pixels over a 400 mm tray resolves 400 / 2500 ≈ 0.16 mm per pixel, so a 20 mm part spans about 125 pixels.

Those pixel counts are an estimate for a common 5 megapixel array, and the 5-pixel floor is a rule of thumb for template and blob matching, not a standard. Read it backwards too: at 0.16 mm per pixel, a 5-pixel feature is 0.8 mm across — the practical minimum texture your locator can hold onto. Run your own tray width and sensor through the calculator; it filters cameras down to the ones with enough pixels for your field of view:

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

Do you ever need a second locate node?

Only when two genuinely different views serve one tray — a coarse locate across the whole tray plus a fine locate over the pick zone. Two nodes over one identical snapshot merely split the same 48 parts into 25 and 23.

The two-node program exists because the cap tempts you to hand the remainder to another node: locate 1 takes 25, locate 2 takes the other 23, and the tray clears. It then has no clean ending, which is the empty-pocket symptom from earlier. If you inherit one, size it at ceil(48 / 25) = 2 nodes for this load and key the final exit on the count, not on reaching the last node. A genuinely second view — a tray wider than one field of view, or a wrist camera re-imaging the pick zone — earns its own snapshot, its own locator and the same count-driven loop. Choosing between a fixed overhead camera and robot-mounted imaging is an architecture call: the guide to selecting a 2D vision pick and place planar guidance system compares the options.

What do you check before running the loop unattended?

Empty a full tray once with the live camera view open, log the count of every pass, and confirm the sequence sums to the tray load — 25, 25, 25, 18, 8, 0 for the 48-part example — before the cell runs without an operator.

  • Detection cap and quantity — cap read from the locate settings (25 in this example), quantity set below it and planned against the tray load.
  • Count wiring — the parts-not-found branch reads the count element of the result list (f[0] in the example) and fires before any pick move.
  • Empty-tray proof — snapshot a known-empty tray: any detection drawn in the live view is a score, glare or background problem to fix before the loop can ever terminate.
  • Score floor — discard located parts whose edge-detection score (element 7 of the example list) falls below your minimum instead of sending the robot to them.
  • Counter reset — reset the picked-parts counter when the not-found branch swaps in a new tray, the way production counters reset per batch; the script pattern is in the guide to resetting pass fail counters per batch in a vision inspection script.
  • First unattended trays — reduced approach speed with the operator prompt still in place, trusted only once a tray has emptied and terminated cleanly on its own.

Next step: size the sensor in the camera finder, or start the build in the system configurator. More loop and termination patterns sit on the systems topic page.

Questions

What happens when the tray holds more parts than one snapshot can detect?

Parts beyond the cap stay invisible until the next snapshot: with a 25-detection cap, the locator reports 25 results and says nothing about part 26. The loop handles this by design, because every batch ends back at the snapshot position and the next pass finds what the previous image could not report. Size the loop on ceil(parts / cap) passes rather than bolting on extra locate nodes.

Why does the locate still report parts on a tray I just emptied?

False detections. Reflections on shiny parts, tray texture or debris can score above the threshold, so the count comes back 1 or 2 on a physically empty tray. Run the snapshot with the live camera view open: anything the detector draws on the empty tray is a false positive. Raise the minimum score, diffuse or polarize the light, and retrain the locator on a clean background.

Can I reuse the same loop for a deep bin instead of a shallow tray?

Only down to the depth where every part still appears un-occluded in the image. A shallow tray shows each pocket; a deep bin hides parts under parts, so a 2D locate under-counts and the count-of-zero exit fires while parts remain inside. Layer the bin so each level presents a full face, or move to a 3D sensor for unstructured bins.

How do I report how many parts the cell picked per shift?

Increment an integer counter by the number of parts taken in each pass — min(quantity, count) — and add the per-tray total to a shift total whenever the not-found branch swaps in a new tray. Because every increment comes from a locate that actually returned pickable poses, the reported quantity matches what the robot picked, with no separate counting sensor.

Should the camera be wrist-mounted or fixed above the tray?

Both run the same loop. A fixed camera over the tray needs one field of view covering the whole pocket area and a single taught snapshot position; a wrist-mounted camera re-images from above the current pick zone, so a smaller sensor covers the same tray, but every pass adds a robot move to the snapshot position. The count-driven exit logic is identical either way.