Multi-Axis Feedrates

Multi-Axis Feedrates

bob.schultz
Alumni Alumni
4,575 Views
13 Replies
Message 1 of 14

Multi-Axis Feedrates

bob.schultz
Alumni
Alumni

During multi-axis contouring moves the machine control will typically expect the feedrate numbers to be either in Inverse Time or some form of Degrees per Minute.  Inverse Time feedrates are simply the inverse of the time that the move takes, i.e. 1 / movetime.  If your control supports both Inverse Time and Degrees Per Minute feedrates, it is recommended that you use Inverse Time as this is the most accurate.  Please note that if your machine supports TCP (Tool Control Point) programming, then it probably supports direct Feed Per Minute (FPM) feed rates during multi-axis contouring moves and does not require either Inverse Time or DPM feedrates.

 

To implement multi-axis feedrate support into your post, you will first need to copy the code from a post processor that already supports this feature. You will find this code in various multi-axis post processors including the following.

 

  1. 5axismaker.cps
  2. fadal.cps
  3. fanuc with a-axis.cps
  4. haas trunnion.cps
  5. machmill4.cps
  6. pocket nc.cps
  7. tormach.cps

 

All the lines between and including the following lines should be copied.  

 

 

// Start of multi-axis feedrate logic
…
// End of multi-axis feedrate logic

This code is generic in nature and will work with all machine configurations; table/table, head/head, and head/table.  Because of this most of the functions included in this code will not have to be modified by you, though you will have to modify other sections of the post processor to fully implement this feedrate logic.

 

One capability of the multi-axis feedrate calculation is that it considers the actual tool tip movement in reference to the rotary axes movement and not just the straight line move along the programmed tool tip, creating more accurate multi-axis feedrates.  In the following picture the move along the arc caused by the movement of the rotary axis (green arc) is used in the calculation instead of the straight-line move generated by HSM (blue line).

 Feedrate.png

The useInverseTimeOutput property can be added to the post property table at the start of the post processor file.  It is typically used to allow the user to select either Inverse Time (true) or DPM feedrates (false) for multi-axis moves.  If the post processor only supports one of these modes, then this property does not have to be defined.

 

properties = {
…
  useInverseTime: true // true = inverse time feedrates, false = degrees per  minute feedrates
}

If Inverse Time feedrates are supported you will need to create the inverseTimeOutput variable at the top of the post processor code and if the accuracy of the Inverse Time feedrates is different than the standard FPM feedrate you will also need to create a new format to associate with it.

 

var inverseFormat = createFormat({decimals:4, forceDecimal:true});
…
var inverseTimeOutput = createVariable({prefix:"F", force:true}, feedFormat);

The variables at the top of the multi-axis feedrate code define variables used in the calculation of Inverse Time and DPM feedrates.

 

Variable

Description

dpmBPW

Defines the pulse weight ratio for the rotary axes when DPM feedrates are supported.  The pulse weight is the minimum increment that each axis can move and can be different for the linear and rotary axes.  For example, if the accuracy of linear axes is .0001 and the accuracy for rotary axis is .001, then the dpmBPW variable should be set to 0.1.

inverseTimeUnits

Defines the unit of time for Inverse Time feedrates. Specify 1.0 for minutes and 60.0 for seconds.

maxInverseTime

Specifies the maximum value that can be output for Inverse Time feedrates.

headOffset

For machines that have a rotary head, the headOffset variable must be defined.  It contains the fixed pivot length combined with the tool length and is used to calculate the length of the move.  Basically, it is the distance from the tool tip to the pivot point of the head.  This variable is typically defined in post processors that support rotary heads.

 

getMultiaxisFeed is the controlling function used for multi-axis feedrate calculations.  It retrieves the total move length and determines whether to use Inverse Time or DPM feedrates.

 

/** Calculate the multi-axis feedrate number. */
function getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed) {
  var f = {frn:0, fmode:0};
  if (feed <= 0) {
    error(localize("Feedrate is less than or equal to 0."));
    return f;
  }

  var length = getMoveLength(_x, _y, _z, _a, _b, _c);

  if (properties.useInverseTime) { // inverse time
    f.frn = inverseTimeOutput.format(getInverseTime(length[0], feed));
    f.fmode = 93;
    feedOutput.reset();
  } else { // degrees per minute
    f.frn = feedOutput.format(getFeedDPM(length, feed));
    f.fmode = 94;
  }
  return f;
}

If your machine does not support DPM feedrates you can change the following line in this function so that Inverse Time feedrates are always calculated.  Conversely, you can set the condition to false for support of only DPM feedrates for multi-axis moves.

 

if (true) { // inverse time

The only other function that you may have to modify in the included code is the getFeedDPM function that calculates the Degrees Per Minute feedrates.  By default, it is setup to calculate the feedrate number based on the combined movement of the linear and rotary axes, sometimes referred to as Pulses Per Minute.  This is standard on most controls, though there are some controls that use a proprietary calculation for DPM feedrates, such as the Fadal control.  You can see a sample of this calculation in the generic fadal.cps post processor.

 

 

/** Calculate the DPM feedrate number. */
function getFeedDPM(_moveLength, _feed) {
  // moveLength[0] = Tool tip, [1] = XYZ, [2] = ABC

  if (false) { // TCP mode is supported, output feed as FPM
    return feed;
  } else { // DPM feedrate calculation
    var moveTime = ((_moveLength[0] < 1.e-6) ? 0.001 : _moveLength[0]) / _feed;
    var length = Math.sqrt(Math.pow(_moveLength[1], 2.0) + 
                        Math.pow((toDeg(_moveLength[2]) * dpmBPW), 2.0));
    return length / moveTime;
  }
}

 

 

Now there are other areas of the post processor that have to be changed to support these feedrate modes.  First, the onLinear5D function must have support added to call the function and output the correct feedrate codes.

 

 

function onLinear5D(_x, _y, _z, _a, _b, _c, feed) {
  …
  // get feedrate number
  var f = {frn:0, fmode:0};
  if (a || b || c) {
    f = getMultiaxisFeed(_x, _y, _z, _a, _b, _c, feed);
  } else {
    f.frn = getFeed(feed);
    f.fmode = 94;
  }
  if (x || y || z || a || b || c) {
    writeBlock(gFeedModeModal.format(f.fmode), gMotionModal.format(1), x, y, z, a, b, c, f.frn);
  } else if (f.frn) {
if (getNextRecord().isMotion()) { // try not to output feed without motion
      feedOutput.reset(); // force feed on next line
  } else {
writeBlock(gFeedModeModal.format(f.fmode), gMotionModal.format(1), f.frn);
}

You will need to reset the feedrate mode to FPM either at the end of the multi-axis operation or on a standard 3-axis move. 

 

function onSectionEnd() {
…
  if (currentSection.isMultiAxis()) {
    writeBlock(gFeedModeModal.format(94)); // inverse time feed off
  }

It is much easier to do this at the end of the section, otherwise you would have to modify all instances that output feedrates, such as in onLinear, onCircular, onCycle, etc.

 

  writeBlock(gFeedModeModal.format(94), gMotionModal.format(1), gFormat.format(40), x, y, z, f);

 



Bob Schultz
Sr. Post Processor Developer

4,576 Views
13 Replies
Replies (13)
Message 2 of 14

matthew.buettner
Explorer
Explorer

Hello, this was very helpful, thank you.

 

I think I followed the instructions correctly, and Fusion is outputting g93 codes but I appear to be missing the f for feed in front of the times on every line. for example the output is g93 X0.125 Z.01 B-270 1075.xxx-xxxxxxxx The line would work if it had a f in front of the 1075. 

 

Any advice?

0 Likes
Message 3 of 14

BrandonTBFBF
Collaborator
Collaborator

I just want to share my findings on this after spending many hours getting it to work.

 

First, the control I'm working with is a Mitsubishi M80 on a swiss lathe.

 

The manual for this control has a formula for calculating a Multi-Axis Feedrate that made my brain hurt. After a little searching I came across the attached PDF. It appears to be an excerpt from a Doosan programming manual. The formula inside works for my Mits control, I'd imagine it should work for a Fanuc control as well.

 

How I made it work:

 

This is my getFeedDpm function. The "machine specific calculation" is the formula from the Doosan manual. I have my dpmBPW variable set to 1.0.

 

/** Calculate the DPM feedrate number. */
function getFeedDPM(_moveLength, _feed) {
  if ((_feed == 0) || (_moveLength.tool < 0.0001) || (toDeg(_moveLength.abcLength) < 0.0005)) {
    previousDPMFeed = 0;
    return _feed;
  }
  var moveTime = _moveLength.tool / _feed;
  if (moveTime == 0) {
    previousDPMFeed = 0;
    return _feed;
  }

  var dpmFeed;
  var tcp = false;   // set to false for rotary heads
  if (tcp) { // TCP mode is supported, output feed as FPM
    dpmFeed = _feed;
  } else if (false) { // standard DPM
    dpmFeed = Math.min(toDeg(_moveLength.abcLength) / moveTime, maxDPM);
    if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) {
      dpmFeed = previousDPMFeed;
    }
  } else if (false) { // combination FPM/DPM
    var length = Math.sqrt(Math.pow(_moveLength.xyzLength, 2.0) + Math.pow((toDeg(_moveLength.abcLength) * dpmBPW), 2.0));
    dpmFeed = Math.min((length / moveTime), maxDPM);
    if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) {
      dpmFeed = previousDPMFeed;
    }
  } else { // machine specific calculation
    var length = Math.sqrt(Math.pow(_moveLength.xyzLength, 2.0) + Math.pow((_moveLength.radialLength * dpmBPW), 2.0));
    dpmFeed = Math.min((_feed * toDeg(_moveLength.abc.z)) / length, maxDPM);
    if (Math.abs(dpmFeed - previousDPMFeed) < dpmFeedToler) {
      dpmFeed = previousDPMFeed;
    }
  }
  previousDPMFeed = dpmFeed;
  return dpmFeed;
}

 

 

 

In the getMoveLength function I changed the radialLength variable to moveLength.radialLength so I could use it in the getFeedDPM function:

 

 

// calculate the radial portion of the tool tip movement
  moveLength.radialLength = Math.sqrt(
    Math.pow(getRadialDistance(moveLength.radius.x, startABC.x, endABC.x), 2.0) +
    Math.pow(getRadialDistance(moveLength.radius.y, startABC.y, endABC.y), 2.0) +
    Math.pow(getRadialDistance(moveLength.radius.z, startABC.z, endABC.z), 2.0)
  );

 

 

I'm an absolute novice with Javascript and programming in general so this might not be the 100% correct way to do it but it works for me.

 

With these changes my machine will complete the Test toolpaths I made in the calculated time, telling me that specified feedrate is correct.

 

Hopefully someone finds this useful.

 

Message 4 of 14

bob.schultz
Alumni
Alumni

Hello @BrandonTBFBF,

Thanks for taking the time to test and find a solution for the issues with the DPM feed rate calculation.  The main issue is that the post processor does not calculate the tool tip distance correctly for DPM feed rates.  The following line in the getMoveLength function ...

  // calculate the tool tip move length
  // tool tip distance is the move distance based on a combination of linear and rotary axes movement
  moveLength.tool = moveLength.xyzLength + radialLength;

... should read like this ...

  // calculate the tool tip move length
  // tool tip distance is the move distance based on a combination of linear and rotary axes movement
  moveLength.tool = Math.sqrt((moveLength.xyzLength * moveLength.xyzLength) + (radialLength * radialLength));

This should give you the same results as your changes.



Bob Schultz
Sr. Post Processor Developer

0 Likes
Message 5 of 14

BrandonTBFBF
Collaborator
Collaborator

Hey @bob.schultz ,

 

I actually use this in my Okuma LB post:

 

// calculate the tool tip move length
  // tool tip distance is the move distance based on a combination of linear and rotary axes movement
  moveLength.tool = Math.sqrt(Math.pow(moveLength.xyzLength, 2.0) + Math.pow(radialLength, 2.0));

 

I got so deep in the weeds on this that I didn't think to go grab this snippet and try it.

 

Thanks!

 

-Brandon

0 Likes
Message 6 of 14

materialeimportante
Contributor
Contributor

The solution is very good... there seems to be something wrong with it. Can you show me what it has?
Thank you!

0 Likes
Message 7 of 14

materialeimportante
Contributor
Contributor

Hello, Bob!

The solution is very good... there seems to be something wrong with it. Can you show me what it has?
Thank you!

0 Likes
Message 8 of 14

bob.schultz
Alumni
Alumni

Hello @materialeimportante,

 

Can you tell me what post processor you are using and what the exact problem is?  If it is custom post, then you will need to provide it here.

 

Thanks!



Bob Schultz
Sr. Post Processor Developer

0 Likes
Message 9 of 14

materialeimportante
Contributor
Contributor

Good morning!

This post processor is the only one that works well. But it has errors very often. All other post processors for the Siemens 840 D sl on the Autodesk platform have an interrupted tool path and break the cutter. The route has different feeds. It goes fast or too slow. 

The post processor i find here: https://forums.autodesk.com/t5/fusion-360-manufacture/siemens-4th-axis-wrapped-toolpath-erratic-feed...

Thanks!

0 Likes
Message 10 of 14

bob.schultz
Alumni
Alumni

The error is generated because the A-axis is defined with limits of -360,360 degrees and is non-cyclic, which means that when the limits are reached it will want to perform a retract/reconfiguration sequence to keep the machine within its limits.  You can change the rotary table to be cyclic by adding the cyclic:true parameter to the creation of the A-axis in onOpen.

  if (true) {
    // <<< ADD cyclic:true TO THE FOLLOWING COMMAND >>>
    var aAxis = createAxis({coordinate:0, table:true, axis:[1, 0, 0], range:[-360, 360], cyclic:true, preference:1});
    machineConfiguration = new MachineConfiguration(aAxis);

 

It looks like the feed rates are being output in inverse time mode, which may not be supported on your control if the feedrate continually changes on the machine.  You can change these to DPM feeds by changing the following property default to false.

// <<< CHANGE TO false >>>
useInverseTime          : false // true = inverse time feedrates, false = degrees per  minute feedrates

 

You will also need to change the following value to 1.

// <<< CHANGE VALUE TO 1.0 >>>
var dpmBPW = 1.0; // ratio of rotary accuracy to linear accuracy for DPM calculations

 



Bob Schultz
Sr. Post Processor Developer

Message 11 of 14

materialeimportante
Contributor
Contributor

Hello.

I tested this post processor. It's the best... of all the ones I've tried. Requests: The post processor needs feed at the beginning. If I put it manually it works, but it's a shame because it's in very good shape.

0 Likes
Message 12 of 14

bob.schultz
Alumni
Alumni

You can add the cutting feedrate to the WCS block by making the following changes in the setWCS function.

 

function setWCS() {
  var workOffset = currentSection.workOffset;
  // <<< ADD THE FOLLOWING 2 LINES
  forceFeed();
  var feed = feedOutput.format(getParameter("operation:tool_feedCutting"));
...
    writeBlock(gFormat.format(code), feed); // <<< ADD ', feed'
...
    writeBlock(gFormat.format(53 + workOffset), feed); // G54->G59 <<< ADD ', feed'

 



Bob Schultz
Sr. Post Processor Developer

Message 13 of 14

materialeimportante
Contributor
Contributor

Hello, BOB!

I made different pieces with your modification. In one case, for a piece, we have a situation: the CNC machine wants a work advance. The post processor doesn't put the Feed and the machine gives me errors. Can it be modified to avoid this situation? We still need feed for other lines of code. After I manually fill in the feed values... the program works.
For this code, Feed must be manually set to these lines: N26, N96, N165, N234, N303.
Can we modify the post processor so that I no longer add this feed?
Thank you!

0 Likes
Message 14 of 14

bob.schultz
Alumni
Alumni

Commenting out the following line in onLinear5D should get you the proper feed rate output.

 

  if (currentSection.isOptimizedForMachine()) {
    var x = xOutput.format(_x);
    var y = yOutput.format(_y);
    var z = zOutput.format(_z);
    var a = aOutput.format(_a);
    var b = bOutput.format(_b);
    var c = cOutput.format(_c);
    // var f = getFeed(feed); // <<< COMMENT OUT THIS LINE

 



Bob Schultz
Sr. Post Processor Developer

0 Likes