ApplicationsHow-to

One vision result, two robot tasks: share one work object

Trigger the camera once, write the vision result into one work object and drive two robot tasks from it: steps, code, numbers and checks that catch drift.

LumaSpec Vision10 min read Share on LinkedIn

On this page 12 sections
  1. 01Why one capture serves both arms
  2. 02What you need
  3. 03Step 1: Fix the camera in the base frame
  4. 04Step 2: Declare one work object in every task
  5. 05Step 3: Capture once and write oframe once
  6. 06Step 4: Teach targets against the shared frame
  7. 07Worked example: field of view, scale and cycle time
  8. 08Common mistakes and the test for each
  9. 09Checks before you run parts
  10. 10When two captures is the right answer
  11. 11Next step
  12. 12Questions

Both arms of a dual-arm robot can work from one photograph. The common workaround — one capture per arm — doubles the vision time in the cycle and leaves each arm moving to a pose detected in a different image, so the two grippers disagree by a small margin that neither task reports.

The fix is structural, not a faster camera. Fix the camera in the robot base frame once, declare one vision work object in every task, capture once, write the detected pose into that object's oframe, and point both arms' targets at it. This guide gives the four steps, the code pattern, a worked example in pixels and milliseconds, and the checks that catch the classic failures — a stale frame, a private copy of the work object, a target still taught against the wrong frame. The camera can ride on one arm or sit fixed in the cell; the pattern is the same.

Why one capture serves both arms

One image, one work object, two tasks: that is the whole pattern. A capture writes the detected part pose into the oframe of a persistent work object, and both tasks reference their targets against that frame. A new image then moves both arms at once, and neither task calls the camera again.

Two captures cannot beat it. Detection on a stable part repeats to about ±0.5 px, and at the 0.1 mm/px scale a 6 MP camera gives over a 300 mm field, that is ±0.05 mm of noise per capture. The second capture draws that noise again, so the arms can disagree by up to 0.1 mm before either robot moves. With one capture the error is common to both arms, and the relative disagreement drops to the robots' own repeatability.

The saving is on the clock too: the worked example below puts a two-capture cycle near 2.2 s and a one-capture cycle near 1.2 s. The pattern in four steps:

  1. Fix the camera in the robot base frame with a calibration.
  2. Declare one persistent work object, under the same name, in every task.
  3. Capture once and write the detected pose into that object's oframe.
  4. Teach and run every arm's targets against that work object.

What you need

  • A camera running the vision job. A smart camera does the matching on the device and leaves the controller to run both robot programs.
  • A vision controller or PC with one Ethernet port for the camera and one per robot task, plus a port for the HMI if the cell has one.
  • A calibration target and the routine that fixes the camera in the base frame (step 1).
  • Edit access to both task programs and the pendant's data viewer, for the readback check in step 2.

One concrete pairing starts with the camera: the MV-SC5060XM-08M-WBN carries a 6 MP mono sensor on a GigE interface, with an M12 mount for a fixed bracket over the station.

Smart cameras & vision sensorsHikrobot MV-SC5060XM-08M-WBN SC5000X series Smart Camera, 6 MP, Mono, 8 mm focal length, M12-mountMV-SC5060XM-08M-WBN
Interface
GigE
Resolution
6-10 MP
Sensor format
1/2.4"
Shutter
Rolling
$1,629.00View part

The controller needs port count more than CPU here: the MV-VC3301H-128G66 lists 5+ LAN ports, 4 built-in channels and 8 GB of memory. The LAN ports on this model are not PoE, so plan a separate power lead for the camera.

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

Step 1: Fix the camera in the base frame

When the camera rides on an arm — the usual dual-arm layout, since one arm photographs while both work — an eye-in-hand calibration writes its pose into the base frame. The pose-selection guide for an eye-in-hand calibration covers robot positions that converge, and how nine-point alignment works covers the fixed-camera case. Run it once: the result is one fixed transform from camera pixels to base-frame millimetres, and both tasks will lean on it.

An error here is copied into every pick both arms make, so verify the camera-to-base result before teaching any targets against it. Recalibrate after moving the camera bracket, changing the lens or altering the lighting geometry — none of those moves the base frame, but all of them move the camera.

Step 2: Declare one work object in every task

Declare the work object as persistent data, under the same name, in both tasks:

! both tasks, module scope
PERS wobjdata wobjVision := [FALSE, TRUE, "", [[0,0,0],[1,0,0,0]],[[0,0,0],[1,0,0,0]]];
PERS bool bFrameValid := FALSE;

The fields are short to explain. FALSE keeps the frame in the cell rather than on a wrist; TRUE says the user frame is programmed by hand; the empty string is the unused mechanical-unit field; then come the hand-taught uframe and the oframe your vision job overwrites. Your targets are taught relative to oframe, so overwriting it relocates every target that references the object.

Declared persistent in both tasks, one logical copy serves both: the controller keeps persistent data synchronized across tasks, so a value written by the capture task is what the second task reads. A plain variable stays private to its task, which is the most common way this pattern fails. Confirm the behavior once at commissioning: write a test value from the capture task, open the data viewer in the other task's scope, and check the value arrived.

Step 3: Capture once and write oframe once

The arm that holds the camera — or the fixed camera's own task — runs the only capture in the cycle:

VAR cameratarget cT;   ! the pose the vision job returns

PROC Capture()
    ! camVis is the camera handle configured in the vision option
    CamReqImage camVis;
    CamGetResult camVis, cT;
    wobjVision.oframe := cT.cframe;
    bFrameValid := TRUE;
ENDPROC

Use the timeout variant your camera package offers, and leave bFrameValid false when the read fails. Both arm tasks check that flag before any motion, so a lost frame stops the cell instead of aiming both arms at last cycle's pose. The same guard covers a camera that drops off the network mid-shift, and the disconnect-detection pattern keeps the programs themselves from faulting while you handle it.

Step 4: Teach targets against the shared frame

Teach each arm's targets once, in wobjVision, with that arm's own tooldata:

PERS tooldata tGripper := [TRUE,[[0,0,0],[1,0,0,0]],[0,[0,0,0],[1,0,0,0],0,0,0]];
PERS robtarget pPick := [[0,0,0],[1,0,0,0],[0,0,0,0],[9E9,9E9,9E9,9E9,9E9,9E9]];

PROC PickFromVision()
    Capture;
    IF bFrameValid THEN
        MoveL Offs(pPick,0,0,100), v1000, z10, tGripper\WObj:=wobjVision;
        MoveL pPick, v200, fine, tGripper\WObj:=wobjVision;
        ! grip, lift, place - then clear bFrameValid for the next cycle
    ENDIF;
ENDPROC

The second task has the same shape — its own tooldata, its own taught targets, and no Capture call at all — because the frame it references is already current. Two details carry the pattern. Every motion instruction passes \WObj:=wobjVision explicitly; leave it off and the move silently targets the default frame, with no error to warn you. And tooldata stays per task: the shared frame carries position and orientation, while each gripper's TCP and approach offsets live in its own tooldata and Offs values. If your vision job returns poses computed into the base frame, as in eye-to-hand calibration results computed into robot-base picks, the same single write into oframe applies.

Worked example: field of view, scale and cycle time

FOV = sensor width × WD / f scale = FOV / pixels across t_cycle = t_capture + max(t_right, t_left)

Put the camera looking straight down at the part from 420 mm working distance. A 1/2.4-inch sensor is about 5.7 mm wide, so an 8 mm lens gives a field of 5.7 mm × 420 mm / 8 mm ≈ 300 mm across. A 6 MP sensor reads about 3000 px over that width, and the scale formula sets the budget: 300 mm / 3000 px = 0.1 mm/px. Detection repeatable to ±0.5 px lands within ±0.05 mm, two independent captures can differ by up to 0.1 mm arm to arm, and one shared capture makes that error common to both arms.

Cycle time, with example values for the two-capture cell: each capture costs 220 ms (40 ms exposure, 30 ms transfer, 150 ms matching), the right arm needs 800 ms to reach its pick and the left arm 950 ms. Run serialized, the cycle is 220 + 800 + 220 + 950 = 2,190 ms. Sharing one capture, it is 220 + max(800, 950) = 1,170 ms, because both arms start once the frame is written. The saving is 2,190 - 1,170 = 1,020 ms, about 47% of the cycle — 17 minutes per shift at 1,000 cycles. Substitute your own exposure, transfer and motion times; the shape of the answer holds.

Every value in this section is an example input; the three formulas are the part you keep.

Common mistakes and the test for each

  • The work object exists in one task only, or as a plain variable. The second task keeps its first frame forever. Test: run a cycle, open the data viewer in the second task, and confirm oframe changed.
  • The tasks spell the name differently — wobjVision in one, wobj_Vision in the other. There is no link and no error. Test: search both programs for the exact name before commissioning.
  • Targets still taught against the default frame from an earlier program version. The vision write then shifts them by the full camera offset. Test: with vision disabled, place the part at the taught position and run the approach — the tool should stop 100 mm above it, the Offs value you taught.
  • A timeout leaves last cycle's oframe in place and the arms move anyway. Test: block the camera's view and confirm the cell stops instead of repeating.
  • One tooldata shared by two grippers with different TCPs. Both arms approach at the wrong height by the TCP difference. Test: check each task's tooldata against its gripper drawing.

Checks before you run parts

  1. Run Capture alone and read oframe in the data viewer. Compare it with the part's taught reference: the difference should equal the part's real placement offset, within about ±0.1 mm at a 0.1 mm/px scale.
  2. Command both arms to one taught reference — a pin in the fixture — through wobjVision, each with its own gripper. The spread between the two approaches is robot repeatability alone, because both arms consumed the same detection result.
  3. Cut the camera's view mid-test and confirm bFrameValid stays false and no arm starts its approach.
  4. Log oframe across a full shift and compare its drift with the fixture's known variation; the two should track.

When two captures is the right answer

Three cases still want a capture per arm: the part moves between the first capture and the second arm's approach, on a belt or indexing table; the single viewpoint cannot see both grip features because one arm occludes them; or the arms serve two stations that need different scenes. Even then, keep one calibration to the base frame and give each capture its own work object in that frame, so the calibration work is still done once. And when one pose can see both grip features, one capture stays the pattern to prefer — the drop-target program structure shows how to keep a single vision result feeding several downstream moves.

Next step

Size the controller by ports before CPU: count the camera link, one per robot task and one for the HMI. If the cell also needs a display for the operator, an HMI or an industrial panel PC mounts alongside the controller. For a question that turns on your cell's layout, /contact/ reaches the applications team, and the applications topic page collects the robot-side patterns around this one.

Questions

Does each robot task need its own camera calibration?

No. The calibration writes one fixed camera pose into the base frame, and both tasks reference targets through a work object built on that frame — two calibrations would just give the two arms slightly different opinions of the same camera. Recalibrate whenever the camera-to-base geometry changes: a moved bracket, a new lens, a lighting or focus change. Verify the result once before teaching targets, because every pick both arms make copies its error.

Can the two arms use different tools and speeds with the shared work object?

Yes. The work object carries only the part frame's position and orientation. Tooldata, speeds, zones and approach offsets stay per task: set each gripper's TCP correctly and hold grip-point differences in the tooldata or the Offs values, never by nudging the shared object. Pass the work object explicitly on every motion instruction so no move falls back to the default frame.

What happens if the camera result is late or the camera drops off the network?

Gate every vision-guided move behind a valid flag and decide the fallback before commissioning: hold and retry the capture, route the part to a reject station, or stop with a clear operator message. Never move on a stale frame — last cycle's oframe points at where the part used to be. A network drop trips the same guard; handle it without faulting the robot programs.

When does one image stop being enough?

When the part can move after the capture. Budget it against pick tolerance: a tight 0.25 mm pick leaves almost no room for part motion between capture and grip, while a palletizing cell that tolerates ±5 mm absorbs belt creep easily. Occlusion is the other trigger — one arm hiding the other's grip feature. Even then keep one calibration and give each capture its own work object in the same base frame.