I’ve spent a lot of time working on high-speed line tracking (iRPickTool) applications. When I first started working on them back in 2008, throughputs around 100 cycles per minute were really pushing the envelope (but this was when FANUC’s flagship high-speed picking robot was the M-430iA).
Since then FANUC has introduced a number of Delta robots: the M-1iA in 2009, the M-3iA in 2010, the M-2iA in 2013(?) and the DR-3iB in 2019. These robots are much better suited for high-speed picking applications than the articulated M-430iA arm: lighter, faster, better work envelope– great robots.
Applications requiring throughputs in excess of 100 cycles per minute are pretty common now, but fast cycle times bring new challenges.
Here’s what I normally like to do during a pick cycle:
- Make sure the gripper is empty
- Pick the part with some delay, if any
- Check part-presence sensors to make sure we have the part
- Retreat from the pick position
- Double-check part-presence sensors to make sure we still have the part
I had an application recently where the part-presence sensors were too slow. I put a
WAIT-statement on the part-presence signals after the pick retreat move, and the system slowed to a grinding halt.
To be fair, these M-2iAs are incredibly fast. At 1kg, an M-2iA/3S takes ~40ms to move through a 50mm linear retreat on a 100mm segment from a dead stop. (i.e.
L PR[x] max_speed CNT100 Tool_Offset,PR[y:-100mm] RT_LD50)
We were using vacuum sensors that had nowhere near the response time I wanted. Of course there were things we could have done to improve that response time a bit but not enough to get to the point where the robots were never held up. (Maybe vacuum switches weren’t the right choice here, but I digress…)
It turns out that I didn’t really care whether the robot picked successfully or not at the pick retreat position. It was ok to check before placing.
Great! So just move the part-presence checking
CALL and we’re done, right? I wish. This job had a lot going on.
There were three different grippers: single-, double- and triple-pick with a tool changer. There were also accumulating buffers the robots would fill or pull from depending on the status of the infeed and outfeed. Oh, one more wrinkle: for the triple-pick gripper, it was ok to not be completely full… sometimes.
We were picking blind on the infeed (coming away with 1 or 2 was ok), but we knew exactly what was coming out of the accumulators (mispick = bad). We’re also never quite sure (on the place side) where the parts are coming from. The dynamics of the system made any of these paths possible:
- Infeed to Outfeed
- Infeed to Accumulator
- Accumulator to Outfeed
- Accumulator to Accumulator
How do we check part-presence correctly on the place side if we’re not sure where we picked from?
I suppose you could do this by setting a bit for the pick location and then calling the correct part-presence check routine on the place side, but that seems brittle to me. I also prefer to figure out what I have as soon as possible – before I get to the place side, ideally.
I ended up handling this part-presence checking asynchronously. The robot would fire off a task during the pick retreat (one version for a pick from the infeed and another for the accumulator), and the place routines would happily wait for either task to return, blissfully unaware of anything complicated going on.
In the infeed pick program:
F[1:PartPresenceDone]=(OFF) ; RUN PPCHK_INFEED ;
In the accumulator pick program:
F[1:PartPresenceDone]=(OFF) ; RUN PPCHK_ACCUMULATOR ;
In the place programs:
WAIT (F[1:PartPresenceDone]) ; CALL CHECK_PART_PRESENCE(1) ; IF R[2:Status]<>0,JMP LBL ;
Obviously the contract here is that both
F[1:PartPresenceDone] when they’re done. To avoid repeating myself, the general-purpose
CHECK_PART_PRESENCE routine can be called with an argument of
1 to indicate whether or not it should “double-check” against what it should have from the pick side; it returns a
R[2:Status] if things look ok.
CHECK_PART_PRESENCE looked something like this:
! default to OK ; R[2:Status]=0 ; ! R is the current gripper ; JMP LBL[R] ; ; LBL ; ! single gripper ; WAIT (RI[1:PartPresent1]) TIMEOUT,LBL ; END ; ; LBL ; ! double gripper ; WAIT (RI[1:PartPresent1] AND RI[2:PartPresent2]) TIMEOUT,LBL ; END ; ; LBL ; ! triple gripper ; IF (AR=0) THEN ; WAIT (RI[1:PartPresent1] AND RI[2:PartPresent2] AND RI[3:PartPresent3]) TIMEOUT,LBL ; ELSE ; ! double-check ; WAIT ((RI[1:PartPresent1] OR R[31:Grip1]=0) AND (RI[2:PartPresent1] OR R[32:Grip2]=0) AND (RI[3:PartPresent3] OR R[33:Grip3]=0)) TIMEOUT,LBL ; ENDIF ; END ; ; LBL ; ! fail ; R[2:Status]=1 ; END ;
The asynchronous part-presence tasks use this same checking program.
CALL CHECK_PART_PRESENCE(0) ; ! we don’t care about mispicks here ; CALL RECORD_PART_PRESENCE ; F[1:PartPresenceDone]=(ON) ;
CALL CHECK_PART_PRESENCE(0) ; IF R[2:Status]<>0,JMP LBL ; ! gripper is full ; CALL RECORD_PART_PRESENCE ; F[1:PartPresenceDone]=(ON) ; END ; ; LBL ; ! gripper not full ; ! definitely bad if not triple-gripper ; IF R[4:GripperID]<>3,JMP LBL ; ! possibly ok… ; CALL VALIDATE_TRIPLE_GRIPPER ; IF R[2:Status]<>0,JMP LBL ; ! we have what we’re supposed to have ; CALL RECORD_PART_PRESENCE ; F[4:PartPresenceDone]=(ON) ; END ; ; LBL ; ! mispick detected ; CALL SIGNAL_MISPICK ; F[4:PartPresenceDone]=(ON) ; END ;
I’ll leave the implementation of
SIGNAL_MISPICK to the imagination, but you get the idea.
There are several takeaways here:
Use Arguments to Reduce Duplication
Part-presence needs to be checked by the asynchronous routines and the placement routines; the only difference is whether or not we double-check the result. This is a prime example of when to use an argument (
We Can Return Values (sort of)
TP does not support functions and return values, but we can kinda do this by setting up a convention for a return parameter (e.g.
R[2:Status] here) or explicitly provide it in an argument (e.g.
CALL SOMETHING(x) where
SOMETHING returns to
Don’t Let Async Tasks Run Wild
When multitasking, you probably need to synchronize your tasks somehow. Otherwise you run the risk of a hanging task or running into
INTP-267 Run stmt failed/
PROG-007 Program is already running.
Here we explicitly turn
F off before starting the task and ensure that all asynchronous code paths turn
F on just before returning. The robot always waits for
F before continuing in the main task.
Hide the Details
The main task doesn’t care about the details of our part-presence checking. It just cares about the result once it’s done. I find that separating details behind a simple pass/fail interface tends to work really well.
Hopefully this post provides a little bit of insight into how I break somewhat complex problems down into easy-to-chew bites, sometimes concurrently.