FrictionCompensation

FrictionCompensation computes the friction force an axis is fighting at a given velocity, so a feedforward path can cancel it before the feedback loop has to.
Current release

FrictionCompensation computes the friction force an axis is fighting at a given velocity, so a feedforward path can cancel it before the feedback loop has to. It is what turns a machine that sticks and then jumps into one that moves smoothly from rest.

It offers three friction models, each from a published paper, chosen with one parameter. They differ in how they behave at zero velocity, and that difference is the whole reason to pick one over another.

flowchart LR
    i1(["input — velocity"]) --> B["FrictionCompensation"]
    i2(["outputScalingFactor"]) --> B
    i3(["disable"]) --> B
    B --> o1(["output — friction force"])
    B --> o2(["isEnabled"])

The shared curve is $g(v) = F_c + (F_s - F_c)e^{-(v/v_s)^2}$: friction falls from stictionFrictionForce at rest to coulombFrictionForce once the velocity is well past stribeckVelocity. That drop is why a machine breaks away and then runs light. Viscous damping adds viscousFrictionDamping × velocity on top. Below a velocity of coulombFrictionForce × preSlidingDisplacement / stictionFrictionForce — 0.001 at the defaults — models 0 and 1 produce no friction force at all.

The default model is 1, STATIC_SIGN, and it is the wrong one for feedforward. It steps by twice the stiction force as the axis reverses. For feedforward use model 2, CONTINUOUS_TANH, which is smooth through zero. Model 1 is the default only for backward compatibility.

The block starts disabled — enable defaults to false.

Signals

Inputs

Path Unit Range Description
input m/s or rad/s unbounded The velocity to compute friction for — normally the velocity reference, not the measurement, since this is a feedforward term. One element per channel.
outputScalingFactor - 0 – 1 Fades the output force. The block never writes this path, so reading it back gives exactly what you wrote — write it once and leave it, and the fade still completes. The value actually multiplying the output is that command clamped to 0 – 1 and moved towards by at most outputScalingFactorRate per second; it is not published, so read the fade off output itself. Inside a feedforward controller this path is written every cycle from a position table and a link into it has no effect.
disable - - True forces the output to zero and clears the model’s internal state. Use it for a runtime override; use enable for the configured intent.

Outputs

Path Unit Description
output N or N·m The friction force at the given velocity, after scaling. One element per channel. Zero while disabled — not a pass-through, because a friction model’s idle value is no force.
isEnabled - True when enable is true and disable is false. Note it can read true while outputScalingFactor has faded the output to zero.

The applied scaling factor is deliberately not a published signal. output carries its effect, and outputScalingFactor carries the command.

Parameters

Path Unit Default Range Effect
model - 1 0, 1 or 2 Which friction model to use. See the table below. Set this explicitly — leaving it unset gives model 1.
enable - false - False forces the output to zero and clears the internal state.
coulombFrictionForce N or N·m 10 0 upward Friction once the axis is moving. The floor of the curve. Corrected to 0 if negative.
stictionFrictionForce N or N·m 20 at least coulombFrictionForce Friction at rest — the force needed to break away. Corrected up to the Coulomb force if you set it lower.
viscousFrictionDamping N·s/m or N·m·s/rad 1.0 0 upward in practice Friction proportional to speed. Not checked — a negative value makes friction drive the axis.
stribeckVelocity m/s or rad/s 0.01 1e-6 – 0.01 How quickly friction falls from stiction to Coulomb. Corrected silently if out of range.
preSlidingDisplacement m or rad 0.002 1e-9 – 0.01 How far the axis deflects before it breaks away. Models 0 and 1 only. It also sets the zero-velocity dead band — see the blockquote.
contactDamping N·s/m 0.0 0 upward Bristle contact damping, model 0 only. Leave at 0 for feedforward — non-zero adds transient forces during velocity changes.
coulombVelocity m/s or rad/s 0.001 1e-6 upward How sharply model 2 rises through zero. Smaller is sharper and closer to model 1. Model 2 only.
outputScalingFactorRate 1/s 10.0 above 0 How fast outputScalingFactor may change. The default fades fully in 0.1 s on any task rate. Not checked — 0 freezes the scaling factor where it is.
model What the machine does Parameters that matter
0 LUGRE_DYNAMIC Models the deflection before breakaway, so stick-slip and hysteresis at reversal are reproduced. Has internal state preSlidingDisplacement, contactDamping, plus the shared curve
1 STATIC_SIGN Classical Stribeck curve. Steps discontinuously across zero velocity — use it for steady-state analysis, not feedforward The shared curve, preSlidingDisplacement for the dead band
2 CONTINUOUS_TANH Smooth everywhere, including through zero. The right choice for friction feedforward coulombVelocity, plus the shared curve

All parameters are persistent and survive a controller restart. No parameters exist below this block.

A legacy useStaticFriction parameter is still read from older configuration files and mapped onto model at startup, with a warning in the log. It is not saved back. Update your configuration to set model directly.

Setup

  1. Set model to 2 if this is a feedforward path, which is the usual case. Set it explicitly — an unset model gives model 1.

  2. Set enable false while you measure.

  3. Measure Coulomb friction: move the axis at a steady moderate speed with the loop closed and no friction compensation, and read the force the feedback controller supplies. That is coulombFrictionForce.

  4. Measure stiction: command a slowly rising force from rest and note the force at which the axis breaks away. That is stictionFrictionForce. It must be at least the Coulomb force, and the block will correct it upward if it is not.

  5. Measure viscous damping: repeat step 3 at two or three speeds. The slope of force against speed is viscousFrictionDamping.

  6. Leave stribeckVelocity at 0.01 and preSlidingDisplacement at 0.002 for a first pass. Both are refinements.

  7. Set enable true and confirm isEnabled reads true. Check the sign: the output must oppose motion — positive force for positive velocity.

    Step 7 adds real force to the actuator. If the sign is wrong the compensation adds to friction instead of cancelling it, and the axis will accelerate away. Have the axis clear.

  8. Check the feedback controller’s own output has dropped at steady speed. That drop is what the compensation bought you.

Tuning

  1. Measure, do not dial. Every parameter here is a physical property of your axis, and steps 3 to 5 of Setup measure them directly. Tuning by watching the error is how you end up over-compensating.
  2. Get the Coulomb force right first — it dominates everywhere except near rest.
  3. Then stiction. Too much and the axis lurches from rest; too little and it still sticks.
  4. Then viscous damping, at the top of your speed range where it matters most.
  5. Refine stribeckVelocity only if the transition from rest to moving feels wrong. Lower it to make friction drop off sooner.
  6. With model 2, use coulombVelocity to set how sharply the force rises through zero. Smaller is sharper and cancels more friction near rest, but a very small value approaches model 1’s discontinuity and reintroduces chatter.
  7. With model 0, preSlidingDisplacement is the real deflection before breakaway. Leave contactDamping at 0 for feedforward.
  8. Check both directions of travel. Friction is rarely symmetric, and this block models it as symmetric — if the two differ a lot, this block will compensate the average.
  9. Use outputScalingFactor to fade compensation in and out — for example to reduce it where the axis is well supported. Its rate limit is in units per second and holds its meaning across task rates. A single write is enough; the fade runs to completion on its own.

Friction force against velocity for the three models: model 1 stepsdiscontinuously across zero to plus or minus 20 N, model 2 rises smoothlythrough zero, and model 0’s sliding steady state matches model 1 away fromrest.

Read the stiction force off the peak near zero and the Coulomb force off the flat part beyond it.

Symptom Cause Action
The axis chatters or buzzes around zero velocity Model 1’s discontinuity at zero Set model to 2
The axis lurches from rest stictionFrictionForce too high Re-measure it with Setup step 4
The axis still sticks at rest Stiction too low, or the dead band is swallowing the command Raise stictionFrictionForce; if that does not help, lower preSlidingDisplacement to narrow the dead band
No friction force at all at very low speed Expected: models 0 and 1 have a dead band below the velocity in the blockquote Use model 2, which has no dead band
The axis accelerates away instead of moving smoothly The sign is wrong, or friction is over-compensated Check the sign first; then halve every force parameter
The axis runs faster the harder it is pushed, with no friction viscousFrictionDamping is negative Set it to zero or positive
Compensation is right at low speed and too strong at high speed viscousFrictionDamping too high Re-measure with Setup step 5
Compensation is right at high speed and too weak near rest stictionFrictionForce too low, or stribeckVelocity too high Raise stiction; lower the Stribeck velocity
stictionFrictionForce reads back higher than written Expected: it is corrected up to the Coulomb force Raise the Coulomb force, or accept the correction
stribeckVelocity or preSlidingDisplacement reads back different Outside the accepted band Work within the value you read back
The output faded to zero but isEnabled reads true outputScalingFactor has been driven to 0 Read that path; inside a feedforward controller it is written from a position table
The output will not fade after writing outputScalingFactor outputScalingFactorRate is 0 Set a positive rate
A scaling change arrived as a slow ramp Expected: it is rate-limited Raise outputScalingFactorRate
outputScalingFactor reads back exactly what was written while the force is still ramping Expected: the path is the command, and the block does not write it Watch output to see the fade; there is no signal carrying the applied factor
outputScalingFactor was written above 1 or below 0 and the force did not follow Expected: the command is clamped to 0 – 1 on the way to the output, and the path keeps the raw value Write a value inside the range so the path and the effect agree
The output ramps up over a moment after enabling, with model 0 Expected: the deflection starts from zero and loads up Use model 1 or 2 if that transient matters
The block behaves like model 1 when you configured model 2 model was left unset, so it resolved to 1 Write model = 2 explicitly and check the log for a deprecation warning
A warning about useStaticFriction at startup An old configuration file still sets the retired parameter Set model directly and remove it
Friction differs between directions and the block cannot match both The model is symmetric by construction Compensate the average, or handle the asymmetry upstream
Every channel gets the same friction Expected: all parameters are shared across channels Use one instance per axis
The output went non-numeric A non-numeric velocity reached input Fix the source, then disable and re-enable to clear the state

A starting point for feedforward on a rotary axis on a 1 ms task, with measured values substituted at steps 3 to 5 of Setup:

model                   = 2
enable                  = true
coulombFrictionForce    = 10.0
stictionFrictionForce   = 20.0
viscousFrictionDamping  = 1.0
stribeckVelocity        = 0.01
coulombVelocity         = 0.001
preSlidingDisplacement  = 0.002
contactDamping          = 0.0
outputScalingFactorRate = 10.0

Limits and errors

Limit Set by What happens Reported
stictionFrictionForce ≥ coulombFrictionForce Fixed Stiction is raised to match the Coulomb force. This is what keeps model 2’s curve the right way up Silently; read the value back
coulombFrictionForce ≥ 0 Fixed A negative value is replaced by 0 Silently; read the value back
stribeckVelocity within 1e-6 – 0.01 Fixed Replaced by the nearest edge Silently; read the value back
preSlidingDisplacement within 1e-9 – 0.01 Fixed Replaced by the nearest edge. The bound is the same for a linear or rotary axis Silently; read the value back
contactDamping ≥ 0, coulombVelocity ≥ 1e-6 Fixed Replaced by the nearest edge Silently; read the value back
viscousFrictionDamping Nothing Not checked. A negative value makes friction drive the axis instead of resisting it Not reported
outputScalingFactorRate Nothing Not checked. 0 freezes the scaling factor at its present value — including at 0, which silences the output while isEnabled still reads true. A negative value is unsafe Not reported
outputScalingFactor within 0 – 1 Fixed The command is clamped, then approached at outputScalingFactorRate. The input path itself is never modified Not reported, and the applied factor is not published; read the fade off output
output Nothing Unbounded — whatever the model computes. Bound it in the actuator’s own limiter Not reported
Model state disable, or enable false A disabled cycle clears the internal state completely Not reported
Channels Shared Every channel uses the same parameters. One instance cannot model two different axes Not reported

The block logs one warning, at startup only: that a configuration file still sets the retired useStaticFriction parameter, naming the model it was mapped to. Every other failure above shows as a value on a trace, not as a message.


Verified against motorcortex-control3 3.32.1 (340db23).