ImpedanceModel
ImpedanceModel is the opposite of AdmittanceModel: motion goes in, force comes out.8 minute read
ImpedanceModel is the opposite of AdmittanceModel:
motion goes in, force comes out. Given how far the machine has been displaced
from a desired state, it produces the reaction force a virtual mass, spring and
damper would exert. Use it to make a force-controlled machine behave like a
defined mechanical system.
It stays stable against a stiff environment, because it has no internal state — no integration, nothing to wind up. Its output depends only on this cycle’s inputs.
flowchart LR
i1(["inputPVA — where the machine is"]) --> B["ImpedanceModel"]
i2(["measuredPVA — where it should be"]) --> B
i3(["externalTorque"]) --> B
i4(["disable"]) --> B
B --> o1(["outputTorque — the reaction force"])
B --> o2(["isEnabled"])
Force $= K,\Delta x + D,\Delta\dot{x} + M,\Delta\ddot{x}$ plus
externalTorque, where each $\Delta$ isinputPVAminusmeasuredPVA. Each coefficient is a gain times a lookup table, so all three can be non-linear. Aim at $\omega_n = \sqrt{K/M}$ [rad/s] and $\zeta = D/(2\sqrt{KM})$ — target $\zeta$ near 1 so contact does not bounce.
Stiffness ships engaged at 100, unlike the admittance block whose spring defaults to off. An unconfigured instance with any displacement produces force straight away.
Enabling is a step, not a ramp. The block is algebraic, so the full force appears on the first cycle. Bring it in by raising the gains from zero instead.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
inputPVA/position |
m or rad | unbounded | Where the machine actually is. It also drives the mass table, so the virtual mass can vary with position. |
inputPVA/velocity |
m/s or rad/s | unbounded | |
inputPVA/acceleration |
m/s² or rad/s² | unbounded | Only matters when the mass term is non-zero. |
measuredPVA/position |
m or rad | unbounded | Where the machine should be — the rest position the spring pulls toward. Despite the name, this is the reference, not a measurement. |
measuredPVA/velocity |
m/s or rad/s | unbounded | The velocity the damper measures against. |
measuredPVA/acceleration |
m/s² or rad/s² | unbounded | |
externalTorque |
N or N·m | unbounded | Added straight to the output, unscaled. Use it to chain another model’s force in. |
disable |
- | - | True forces outputTorque to zero. Use it for a runtime override; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
outputTorque |
N or N·m | The reaction force: spring plus damper plus inertia plus externalTorque. Zero while disabled. It appears at full value on the first enabled cycle — there is no ramp. |
isEnabled |
- | True when enable is true and disable is false. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
enable |
- | true | - | False forces outputTorque to zero. |
stiffnessGain |
- | 1 | 0 upward | Scales the stiffness table. Higher resists displacement more. Not checked — a negative value pushes the machine away from the rest position instead of toward it. |
dampingGain |
- | 1 | 0 upward | Scales the damping table. Higher resists motion more. Not checked — a negative value drives the machine instead of damping it. |
massGain |
- | 1 | 0 upward | Scales the mass table. Higher makes the machine resist acceleration more. Forced away from zero, but not away from negative. |
All four are persistent and survive a restart.
The three coefficients live below this block, as lookup tables:
stiffnessLookup driven by the relative position, dampingLookup by the
relative velocity, and massLookup by the absolute input position. Each
ships as a single point — stiffness 100, damping 10, mass 1 — so a constant
coefficient needs no table work. See lookup.md.
The effective coefficients are not published. What the block actually uses is the gain times the table output, and neither is visible on an output. To check one, read the table’s own output in its sub-tree and multiply by the gain yourself.
Setup
-
Set all three gains to 0. With no coefficients the output is only
externalTorque, so nothing unexpected reaches the actuator. -
Link
inputPVAfrom the machine’s measured state andmeasuredPVAfrom the state you want it to hold. Confirm both on a trace. -
Confirm the displacement is what you expect: subtract the two positions on a trace. That difference is what the spring will act on.
-
Set
enabletrue and confirmisEnabledreads true.outputTorqueshould still be zero, because the gains are zero. -
Raise
stiffnessGainfrom 0 toward 1 in steps.outputTorqueshould oppose the displacement — pushing the machine back towardmeasuredPVA.Step 5 puts real force into the actuator, at full value. The block has no ramp: whatever gain you write takes effect on the next cycle, against whatever displacement exists at that moment. Raise it in small steps with the machine near its rest position.
-
Check the sign. If the force pushes the machine away from
measuredPVA, your two PVA inputs are swapped. -
Raise
dampingGainnext, thenmassGainonly if you need the machine to resist acceleration.
Tuning
- Set the stiffness from the process: how hard should the machine resist being pushed off position? That is $K$ in newtons per metre.
- Set the damping for stability, not for feel. Compute $\zeta = D/(2\sqrt{KM})$ and aim near 1. Below about 0.5 contact bounces.
- Set the mass last, and usually leave it small. It resists acceleration and
amplifies any noise on
inputPVA/acceleration. - Use the tables rather than the gains for anything non-linear — a spring that stiffens with displacement, or a damper that softens at speed. The gains are one number each; the tables are a curve.
- Use
massLookupif the machine’s apparent inertia changes across its workspace. It is driven by absolute position, unlike the other two. - Verify each coefficient by reading its table output in the sub-tree and multiplying by the gain. Nothing publishes the product.
- Nothing here needs re-checking after a task-rate change. The block does not integrate and has no task-rate-dependent limit.
Read the effective stiffness off the slope of any line.
| Symptom | Cause | Action |
|---|---|---|
| The machine is pushed away from the rest position instead of toward it | The two PVA inputs are swapped, or a gain is negative | Check step 3 of Setup, then check every gain is positive |
| Force appeared as soon as the block was enabled | Expected: the block is algebraic and has no ramp, and stiffness defaults to 100 | Set the gains to 0 before enabling and raise them from there |
| The machine runs away instead of settling | A negative dampingGain — negative damping adds energy |
Set it positive |
| Contact bounces or oscillates | Damping too low for the stiffness | Raise dampingGain until $\zeta$ is near 1, or lower the stiffness |
| Contact feels dead and heavy | Damping too high, or the mass term too large | Lower dampingGain, then massGain |
| The force is noisy | massGain is amplifying noise on the acceleration input |
Lower massGain, or filter the acceleration upstream |
| The force does not match the stiffness you configured | Expected: the effective stiffness is the gain times the table output | Read the table’s output in its sub-tree and multiply |
| Force with no displacement at all | externalTorque is non-zero and passes straight through |
Trace that input |
| The machine feels stiffer in one part of its travel | Expected if massLookup or a coefficient table is shaped that way |
Flatten the table, or accept it |
| Changing the stiffness table changed the force at a different displacement than expected | The stiffness table is driven by the relative position, so its own input moves with the displacement | Shape the table against the displacements you actually work at |
| Changing the mass table did nothing at standstill | Expected: the mass term multiplies relative acceleration, which is zero at rest | Check it during a move |
outputTorque went to zero |
enable is false or disable is true |
Read isEnabled |
| A non-numeric value appeared and then cleared itself | Expected: the block holds no state, so it recovers as soon as the inputs are clean | Fix the source |
| The block drifted over a long session | Not possible — there is no state to drift | Look upstream |
| You need this on several axes | Not possible — one axis per instance | Use one instance per axis |
A starting point for a 1 N·m per 0.01 rad spring with critical damping, mass term off:
enable = true
stiffnessGain = 1.0
dampingGain = 1.0
massGain = 0.0
stiffnessLookup: numPoints = 1, x = 0, y = 100.0
dampingLookup: numPoints = 1, x = 0, y = 20.0
massLookup: numPoints = 1, x = 0, y = 1.0
Raise the gains from 0 as Setup step 5 describes. This is a starting point, not a final tuning.
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
stiffnessGain, dampingGain |
Nothing | Not checked, including negative. A negative stiffness pushes the machine away from its rest position; a negative damping drives it instead of damping it. Both make the virtual system an energy source | Not reported |
massGain |
Forced away from zero | A value within 0.001 of zero is replaced by ±0.001. Negative values are still accepted | Not reported |
| Effective coefficients | Nothing | The gain times the table output. Neither product is published — read the table output in its sub-tree and multiply | Not reported |
outputTorque |
Nothing | Unbounded — whatever the coefficients and displacement produce. Bound it in the actuator’s own limiter | Not reported |
| Enabling | Nothing | The full force appears on the first enabled cycle. There is no ramp | Not reported |
| Stability | Structural | The block has no state and does not integrate, so it cannot diverge on its own. There is no task-rate limit to respect and nothing to rescale after a task-rate change | Not applicable |
| Block state | None | Nothing to reset, nothing survives a stop | Not applicable |
| Channel count | Fixed | One axis per instance, always | Not reported |
The block raises no errors or warnings and logs nothing. Every failure above shows as a value on a trace, not as a message.
Verified against motorcortex-control3 3.30.0 (bc348fd).