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.11 minute read
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
stictionFrictionForceat rest tocoulombFrictionForceonce the velocity is well paststribeckVelocity. That drop is why a machine breaks away and then runs light. Viscous damping addsviscousFrictionDamping× velocity on top. Below a velocity ofcoulombFrictionForce×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
-
Set
modelto 2 if this is a feedforward path, which is the usual case. Set it explicitly — an unsetmodelgives model 1. -
Set
enablefalse while you measure. -
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. -
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. -
Measure viscous damping: repeat step 3 at two or three speeds. The slope of force against speed is
viscousFrictionDamping. -
Leave
stribeckVelocityat 0.01 andpreSlidingDisplacementat 0.002 for a first pass. Both are refinements. -
Set
enabletrue and confirmisEnabledreads 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.
-
Check the feedback controller’s own output has dropped at steady speed. That drop is what the compensation bought you.
Tuning
- 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.
- Get the Coulomb force right first — it dominates everywhere except near rest.
- Then stiction. Too much and the axis lurches from rest; too little and it still sticks.
- Then viscous damping, at the top of your speed range where it matters most.
- Refine
stribeckVelocityonly if the transition from rest to moving feels wrong. Lower it to make friction drop off sooner. - With model 2, use
coulombVelocityto 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. - With model 0,
preSlidingDisplacementis the real deflection before breakaway. LeavecontactDampingat 0 for feedforward. - 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.
- Use
outputScalingFactorto 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.
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).