I’ve been working on several machine-tending applications lately. These projects tend to get complicated quickly as the robot decides where to go next based on the state of the peripheral equipment. The decision-making logic is hard enough, but getting the robot to and from each station safely can be tricky. This is how I handle it.
I start off by defining a list of stations:
- Home
- Inbound parts
- Regrip station
- Machine 1
- Machine 2
- Reject station
- Inspection station
- Outbound parts
I then define a register for saving the robot’s current location. I’ll typically also add another register for saving the previous station, just in case I need it.
R[50:current station]
R[51:previous station]
Let’s create a simple macro for saving the robot’s current station and previous station:
! SAVE_STATION.LS ;
! =============== ;
! Saves the robot's current location and ;
! previous station based on AR[1]. ;
! ;
! 1. Home ;
! 2. Inbound parts ;
! 3. Regrip station ;
! 4. Machine 1 ;
! 5. Machine 2 ;
! 6. Reject station ;
! 7. Inspect station ;
! 8. Outbound parts ;
;
! shift current station into previous station ;
R[51:previous station]=R[50:current station] ;
;
! save current station ;
R[50:current station]=AR[1] ;
It would be nice to be able to use String Registers for this, but until FANUC beefs up String Register comparison and assignment, simple integer constants are going to have to do.
I use this macro whenever the robot actually gets to a given station.
! HOME.LS ;
! ======= ;
! Move home ;
;
UFRAME_NUM=0 ;
UTOOL_NUM=0 ;
;
J PR[1:HOME] 10% CNT0 ;
;
! save current station as home ;
CALL SAVE_STATION(1) ;
Saving your current location is only half the battle. The next step is creating a routine that gets the robot from A to B, or C, or D, etc.
Let’s create a new routine called TRAVERSE
. It takes a single
argument, the target station, and simply acts as a man-in-the-middle,
inserting any necessary points between stations.
! TRAVERSE.LS ;
! =========== ;
! Inserts points as necessary to get ;
! the robot from its current station ;
! to the target station in AR[1]. ;
! ;
! 1. Home ;
! 2. Inbound parts ;
! 3. Regrip station ;
! 4. Machine 1 ;
! 5. Machine 2 ;
! 6. Reject station ;
! 7. Inspect station ;
! 8. Outbound parts ;
;
! prevent all traversals for now ;
UALM[1] ;
The TRAVERSE
program doesn’t do anything right now. It just prevents
us from accidentally crashing the robot into something when moving from
station to station. This is how I might use this routine:
! UNLOAD_MACHINE_1.LS ;
! =================== ;
;
CALL TRAVERSE(4) ;
UFRAME_NUM=1 ;
UTOOL_NUM=1 ;
J PR[2:Machine 1 approach] 100% CNT100 ;
CALL SAVE_STATION(4) ;
There are a couple things to notice here:
- Despite calling
TRAVERSE
, I still include a motion statement that actually approaches the station in the operation routine. TheTRAVERSE
routine should only be used to add extra points between the last station’s exit point and this station’s approach. - I don’t
CALL SAVE_STATION(4)
until I know for sure the robot has actually made it to this station. I don’t want to preemtively save the current station and potentially have the robot thinking it’s somewhere it’s not.
With the current implementation of TRAVERSE
, you’ll simply get a user
alarm as soon as the robot starts executing UNLOAD_MACHINE_1
. Let’s
assume that the robot is supposed to move from the regrip station to
machine 1 and add a valid traversal for this case:
! TRAVERSE.LS ;
! =========== ;
! Inserts points as necessary to get ;
! the robot from its current station ;
! to the target station in AR[1]. ;
! ;
! 1. Home ;
! 2. Inbound parts ;
! 3. Regrip station ;
! 4. Machine 1 ;
! 5. Machine 2 ;
! 6. Reject station ;
! 7. Inspect station ;
! 8. Outbound parts ;
;
LBL[1] ;
SELECT R[50:current station]=3,JMP LBL[30] ;
ELSE,JMP LBL[500] ;
;
LBL[500] ;
! invalid traversal ;
UALM[1] ;
JMP LBL[1] ;
;
LBL[30] ;
! traversals from regrip station ;
IF (AR[1]=4),JMP LBL[304] ;
JMP LBL[500] ;
;
LBL[304] ;
! regrip to machine 1 ;
UFRAME_NUM=0 ;
UTOOL_NUM=1 ;
J P[1:regrip to machine 1] 100% CNT100 ;
JMP LBL[999] ;
;
LBL[999] ;
The first thing the program does is see if there are any valid
traversals from the current station stored in R[50]
. It then jumps
down and checks to see if there are any valid traversals to the target
station in AR[1]
. If everything looks good, it executes the motion
statement or two required to get from A to B, otherwise it catches the
error and potentially saves the robot from crashing.
It’s pretty common for the robot to have to traverse to and from itself, so I’ll typically add one simple line to the top of TRAVERSE to allow this:
! TRAVERSE.LS ;
! =========== ;
! Inserts points as necessary to get ;
! the robot from its current station ;
! to the target station in AR[1]. ;
! ;
! 1. Home ;
! 2. Inbound parts ;
! 3. Regrip station ;
! 4. Machine 1 ;
! 5. Machine 2 ;
! 6. Reject station ;
! 7. Inspect station ;
! 8. Outbound parts ;
;
! always safe to move to current station ;
IF (AR[1]=R[50:current station]),JMP LBL[999] ;
;
LBL[1] ;
SELECT R[50:current station]=3,JMP LBL[30] ;
ELSE,JMP LBL[500] ;
It’s also pretty common for no extra motion to be necessary, so I’ll usually have a NOOP (no operation) label to handle this case.
LBL[1001:NOOP] ;
! no additional points necessary ;
JMP LBL[999] ;
I purposefully jump here instead of the end of the program just in case I want to do any logging, etc.
As you add more and more traversals, the TRAVERSE
routine starts to
get pretty large. At this point, it probably makes sense to separate
each station’s traversals into their own routines:
! TRAVERSE_TO_MACHINE_1.LS ;
! =========== ;
! Inserts points as necessary to get ;
! the robot from its current station ;
! to machine 1.
! ;
! 1. Home ;
! 2. Inbound parts ;
! 3. Regrip station ;
! 4. Machine 1 ;
! 5. Machine 2 ;
! 6. Reject station ;
! 7. Inspect station ;
! 8. Outbound parts ;
;
LBL[1] ;
SELECT R[50:current station]=3,JMP LBL[30] ;
=4,JMP LBL[1001] ;
;
! invalid traversal ;
UALM[1] ;
JMP LBL[1] ;
;
LBL[30] ;
! regrip to machine 1 ;
UFRAME_NUM=0 ;
UTOOL_NUM=1 ;
J P[1:regrip to machine 1] 100% CNT100 ;
JMP LBL[999] ;
;
LBL[1001:NOOP] ;
! no points necessary ;
JMP LBL[999] ;
;
LBL[999] ;
This is the cleanest way I’ve found to get the robot from station to station, protect us both from programming errors and keep our actual operation programs clean. How do you handle this problem? Let me know if you have any alternative ideas or ways to improve how I currently handle this.