Home / Articles / FANUC
FANUC Robot Happiness TP Programming

Why Joint Moves with Offsets Can Bite You on FANUC Robots

I ran into an issue many years ago at a manufacturing plant. A pretty big robot (R-2000iB) would rarely yank out its own dressout during third shift, and I had a difficult time tracking down the issue until I witnessed it one morning.

Evidently this customer had experienced some issues with vision previously, so we were forced to use a laser mounted to the end-of-arm tool to triangulate the position and orientation of a stack of dunnages. Not a huge deal, but this is part of what made the issue difficult to diagnose.

Imagine two stacks of dunnages directly in front of the robot. Each dunnage contained maybe 6 parts.

The unload routine was probably something like this:

J PR[1:perch] 100% CNT100 ;
L P[1:pick] 500mm/sec CNT100 Offset,PR[10:pick app os] ;
L P[1:pick] 250mm/sec FINE ;
CALL GRIP ;
L P[1:pick] 250mm/sec CNT50 Offset,PR[11:pick ret os];

Unfortunately the gripper was pretty much in-line with the faceplate, so depending on what layer we were on, there was a high likelihood of running into a wrist singularity (and no Singularity Avoidance option that I can remember).

It was often the move from perch to the pick offset that would fault with singularities. The quick fix my ignorant 20-something-year-old self came up with was to add the Wjnt (wrist joint) motion option to the instruction:

L P[1:pick] 500mm/sec CNT100 Offset,PR[10:pick app os] Wjnt ;
L P[1:pick] 250mm/sec FINE ;

This is actually a really cool motion option: it allows the three wrist axes to be interpolated like a joint move (passing through singularities without issue), and the remaining major axes will compensate to keep the path linear. Cool, right?

Most of the time.

It turns out that some combination of layer height, dunnage orientation and part orientation would occasionally cause the wrist to spin ~360deg the wrong way and yank the dressout out.

I should have known better. To understand the issue, we need to understand the difference between position and configuration.

Position vs. configuration

What’s the difference between these two positions?

P[1]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 0',
	X =  3220.512  mm,	Y =      .000  mm,	Z =   485.833  mm,
	W =  -180.000 deg,	P =   -90.000 deg,	R =     0.000 deg
};
P[2]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, -1',
	X =  3220.512  mm,	Y =      .000  mm,	Z =   485.833  mm,
	W =  -180.000 deg,	P =   -90.000 deg,	R =     0.000 deg
};

You’ll have to look closely. The positions are the same, but the configurations are slightly different. P[1] uses turn count 0 for J6 while P[2] uses -1. This means J6 is 0deg at P[1] and -360deg at P[2]. Same position, different configuration.

Turn counts are part of the configuration string, which pins down the kinematic solution when the arm has more than one way to reach a position. In the example above, P[1] uses turn counts 0 0 0, meaning we are looking for a solution where J1, J4 and J6 are all between -180 and 180. P[2] uses turn counts 0 0 -1, which means we want a solution where J1 and J4 are between -180 and 180, and J6 is between -180 and -540.

What about these two positions?

P[3]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 0',
	X =  3220.512  mm,	Y =     -.000  mm,	Z =  -174.167  mm,
	W =  -150.761 deg,	P =   -90.000 deg,	R =     0.000 deg
};
P[4]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'N U T, 0, 0, 0',
	X =  3220.513  mm,	Y =     -.000  mm,	Z =  -174.167  mm,
	W =  -150.761 deg,	P =   -90.000 deg,	R =     0.000 deg
};

The only difference is the wrist configuration. P[3] uses the “flip” wrist configuration while P[4] uses the “no flip” configuration. An easy way to picture wrist configurations is to hold your arm straight out in front of you. If you bend your wrist upward, that’s a “flip” configuration. If you bend your wrist downward, that’s a “no flip” configuration. It’s basically “flip” when the wrist angle is above the line drawn between your elbow and wrist and “no flip” when the wrist angle is below that line. There’s a bit more to it than that, but you should get the general idea.

An R-2000 robot with two overlays through the wrist, each split by the line running from elbow to wrist: red FLIP above the line, blue NO FLIP below. The upper overlay is level and the lower one is rotated to match the arm's angle, showing the boundary follows the arm rather than the floor.

Here you can see that the flip/no flip zones depend entirely on the relative position between J5 and the J3-J4 arm.

Linear vs. joint moves

Ok so why are we talking about this?

Every time you write a motion statement, you have to give it an interpolation type: generally linear (L) or joint (J). I’m sure you’ve noticed that the resulting motion looks quite different in each, but there’s more to it than just that.

Linear plans a straight line between two points. It is not possible to go from a “flip” to a “no flip” configuration with a linear move. You can cheat with the Wjnt motion option, but it may not always be a good idea.

Linear motion also ignores the turn counts! A linear move between P[1] and P[2] above will actually produce zero motion. The position has been satisfied, and we are ok with not having the exact config.

This is where a lot of people run into trouble.

If you’re like me, you’re probably more comfortable jogging in WORLD or USER vs. JOINT. There are some wizards who are able to have four fingers jogging four separate joints simultaneously, but I’m not one of them. The problems come when you are jogging linearly and recording a bunch of linear moves without paying attention to your turn counts and wrist configuration. It’s very easy to get all wrapped up without a joint move here and there to guarantee you’re in a good configuration.

Joint moves respect both the position and the configuration of the destination. A joint move between P[1] and P[2] will simply rotate J6, and a joint move between P[3] and P[4] will work just fine. However, attempting to move linearly between P[3] and P[4] will produce a MOTN-063 Position config change (G:1) alarm.

So what about Offsets?

Right. The problem with Offsets is that they effectively change the destination position while keeping the original destination’s configuration.

Let me say that again: offsets change the destination but keep the configuration.

This can be problematic, particularly when you are close to a configuration boundary.

Here’s a somewhat contrived example:

Let’s say I have a pick position here:

P[10]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, -1',
	X =  2093.009  mm,	Y =   574.998  mm,	Z =  -444.000  mm,
	W =     1.600 deg,	P =    90.000 deg,	R =     0.000 deg
};

If I jog the robot up 200mm in Z, I reach this position:

P[11]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 0',
	X =  2093.009  mm,	Y =   574.998  mm,	Z =  -244.000  mm,
	W =     1.600 deg,	P =    90.000 deg,	R =     0.000 deg
};

But we don’t like too many unique positions, so we use a 200mm PR Offset, right?

J P[6:perch] 100% FINE ;
L P[10] 1000mm/sec FINE Offset,PR[1:z200] ;
L P[10] 500mm/sec FINE ;

This works without issue, but what happens if we change the approach move to joint or wrist joint?

J P[6:perch] 100% FINE ;
J P[10] 25% FINE Offset,PR[1:z200] ;
L P[10] 500mm/sec FINE ;

You end up with a MOTN-017 Limit error (G:1, A:6). Why? Look at the difference between P[11] and P[10] above. P[10] has a -1 J6 turn count, and P[11] has a J6 turn count of 0.

The issue when we execute J P[10] 25% FINE Offset,PR[1:z200] is that the motion planner is attempting to move to this position, which is unreachable given our joint limits:

P[]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, -1',
	X =  2093.009  mm,	Y =   574.998  mm,	Z =  -244.000  mm,
	W =     1.600 deg,	P =    90.000 deg,	R =     0.000 deg
};

The controller is trying to find the solution where J6 is in P[10]’s -1 turn count range of -180 to -540, but our J6 axis limits are +/- 360 so we get the MOTN-017 error.

We’re honestly lucky that we ran into a runtime error here. It’s much worse when the robot can actually reach.

Here’s another contrived example position:

P[12]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 0',
	X =  2093.009  mm,	Y =    88.999  mm,	Z =   302.000  mm,
	W =   103.994 deg,	P =    22.395 deg,	R =    19.293 deg
};

If I jog 200mm up in Z, we end up here:

P[13]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 1',
	X =  2093.009  mm,	Y =    88.999  mm,	Z =   502.000  mm,
	W =   103.994 deg,	P =    22.395 deg,	R =    19.293 deg
};

Similar to the scenario above, we are fine if we use linear motion:

J P[5:perch2] 100% FINE ;
L P[12] 1000mm/sec FINE Offset,PR[1:z200] ;
L P[12] 500mm/sec FINE ;

But what happens if we mistakenly use Joint?

J P[5:perch2] 100% FINE ;
J P[12] 25% FINE Offset,PR[1:z200] ;
L P[12] 500mm/sec FINE ;

When we execute J P[12] 25% FINE Offset,PR[1:z200] the robot actually plans a joint move here:

P[]{
   GP1:
	UF : 0, UT : 1,		CONFIG : 'F U T, 0, 0, 0',
	X =  2093.009  mm,	Y =    88.999  mm,	Z =   502.000  mm,
	W =   103.994 deg,	P =    22.395 deg,	R =    19.293 deg
};

The position is the same, but the J6 turn count is not what we want. J6 rotates 371deg—from 193 in my perch position to -178 at the offset destination. This is the kind of thing that causes crashes and broken dressouts.

What about Wjnt?

J P[5:perch2] 100% FINE ;
L P[12] 1000mm/sec FINE Offset,PR[1:z200] Wjnt ;
L P[12] 500mm/sec FINE ;

It turns out that Wjnt, at least with respect to configuration, behaves more like a Joint move than a Linear one. The motion planner plans a move from P[5] to the bad offset destination with P[12]’s 0 J6 turn count, just like the joint move. This is exactly what happened at that customer all those years ago.

I ended up fixing this by completely overengineering it. I wrote a KAREL program to dynamically modify the turn counts of the destination position so that it matched what the Wjnt move required. Singularity Avoidance probably would have been faster, but we got it going.

The takeaway

The lesson here is to be very careful when using offsets with joint moves (or linear moves with the Wjnt option!), particularly when your destination is close to a configuration/turn count boundary. It may be wise to just not use offsets with joint moves entirely, though if your positions are static (e.g. no dynamic frame/position changes, no vision, etc.) you should be ok after testing.

Robot Whispering
Want the whole picture?
Frames, offsets, KAREL and more — all in Robot Whispering.
Get the book →
// (ALMOST) EVERY TUESDAY

Get the next article in your inbox.