MassSpringDamperModel

MassSpringDamperModel simulates a point mass on a spring and damper, coupled to a neighbouring model.
3.30–3.34

MassSpringDamperModel simulates a point mass on a spring and damper, coupled to a neighbouring model. Its purpose is chains: connect several instances and you have a flexible structure, a series-elastic joint, or a multi-body compliance model built from copies of one block.

It does both compliance directions in one block. By default it behaves like an admittance — force in, motion out. Set enableImpedance and it behaves like an impedance — motion in, force out — and adds an inertia term to its output force.

flowchart LR
    i1(["inputForce — external force on the mass"]) --> B["MassSpringDamperModel"]
    i2(["inputState — external state, impedance mode only"]) --> B
    i3(["connectedForce — force from the next model"]) --> B
    i4(["connectedState — state of the previous model"]) --> B
    B --> o1(["outputState — this mass's motion"])
    B --> o2(["outputForce — coupling force"])
    B --> o3(["mass — effective mass"])
    B --> o4(["damping — effective damping"])
    B --> o5(["stiffness — effective stiffness"])
    B --> o6(["relativePosition — spring extension"])
    B --> o7(["relativeVelocity"])

The mass obeys $M\ddot{x} =$ inputForce $+$ connectedForce $-$ outputForce, and the coupling force is $K,\Delta x + D,\Delta\dot{x}$, plus $M,\Delta\ddot{x}$ in impedance mode. Aim at $\omega_n = \sqrt{K/M}$ [rad/s] and $\zeta = D/(2\sqrt{KM})$ — target $\zeta$ near 1. Keep the task period below $2/\omega_n$ or the simulation grows without bound; nothing checks this.

To build a chain: connect this model’s outputForce to the previous model’s connectedForce, and the previous model’s outputState to this one’s connectedState. The spring anchors on connectedState.

This block has no enable and no disable. disableDynamics pins the motion to connectedState but still publishes a coupling force, so a chain keeps transmitting while it is set.

Signals

Inputs

Path Unit Range Description
inputForce N or N·m unbounded The external force pushing on this mass. Forces smaller than inputForceDeadZone are ignored.
inputState m, m/s, m/s² unbounded An externally measured state, as x, xDot and xDDot together. Only read in impedance mode — in the default admittance mode it is ignored entirely.
connectedForce N or N·m unbounded The coupling force from the next model in the chain. Added directly to the net force.
connectedState m, m/s, m/s² unbounded The previous model’s state. It is the spring’s anchor and the reference both integrators return to.

Outputs

Path Unit Description
outputState m, m/s, m/s² This mass’s position, velocity and acceleration, together. Chain it to the next model’s connectedState. Starts at zero and is not cleared by a stop.
outputForce N or N·m The coupling force the spring and damper transmit. Chain it to the previous model’s connectedForce. Not zeroed by disableDynamics.
mass kg or kg·m² The effective mass — massGain times the mass table’s output. Watch this, not the table.
damping N·s/m The effective damping.
stiffness N/m The effective stiffness.
relativePosition m or rad The spring’s extension or compression. In admittance mode this is outputState minus connectedState; in impedance mode inputState minus connectedState.
relativeVelocity m/s or rad/s The velocity across the damper, from the same source as above.

Parameters

Path Unit Default Range Effect
enableImpedance - false - False gives admittance: the coupling force is spring + damper, computed from this block’s own motion. True gives impedance: the coupling force is computed from inputState instead, and gains an inertia term.
massGain - 1 0.001 upward Scales the mass table. Values below 0.001 are corrected up, so the mass gain can never be zero or negative.
stiffnessGain - 1 any Scales the stiffness table. Its sign is forced positive, so a negative value acts as its magnitude.
dampingGain - 1 any Scales the damping table. Its sign is forced positive too.
disableDynamics - false - True pins outputState to connectedState every cycle, so the mass stops moving on its own. It does not stop the coupling force.
inputForceDeadZone N or N·m 0.1 0 upward Forces smaller than this do not push the mass. Raise it to stop a noisy force reading creeping the model.

All six are persistent and survive a restart.

The three coefficients live below this block, as lookup tables: massLookup driven by this mass’s own position, dampingLookup by the relative velocity, and stiffnessLookup by the relative position. Each ships as a single point at 1.0. The two integrators — velocityIntegrator and positionIntegrator — carry the motion limits. See lookup.md and integrator.md.

Never let massLookup return zero. The mass divides the net force, and a zero mass makes the simulation non-numeric. A table that is not strictly increasing in x also returns zero, so check monotonicity as well as values.

Setup

  1. Configure massLookup with the mass you want this point to have. A single point is enough if it does not vary with position. Confirm it never returns zero.

  2. Configure dampingLookup and stiffnessLookup. Start with a low stiffness and generous damping — a soft, well-damped model is safe to experiment with.

  3. Compute $\omega_n = \sqrt{K/M}$ and check the task period is below $2/\omega_n$, comfortably. A stiff spring on a slow task will not simulate; it will diverge.

    Step 3 is the check nothing does for you. There is no warning and no clamp. Too stiff a spring for the task rate makes outputState grow without bound, and the only way out is a restart.

  4. Set the integrators' output limits to the travel and speed this mass may have.

  5. Leave enableImpedance false for a first pass. Link inputForce and watch outputState respond.

  6. Read mass, damping and stiffness back and confirm they are the values you intended. They are the gain times the table output.

  7. To chain: link this model’s outputForce to the previous model’s connectedForce, and that model’s outputState to this one’s connectedState. Build the chain one link at a time and check each is stable before adding the next.

Tuning

  1. Set the mass first, from the physical mass this point represents.
  2. Set the stiffness from the compliance you are modelling. For a series-elastic joint that is the spring’s real rate.
  3. Set the damping for stability: compute $\zeta = D/(2\sqrt{KM})$ and aim near
    1. Below about 0.5 the model rings, and in a chain that ringing couples between links.
  4. Re-check step 3 of Setup after every stiffness or mass change. The stability bound moves with both.
  5. Use the tables rather than the gains for anything non-linear — a progressive spring, or a damper that softens at speed.
  6. Choose the mode deliberately, and not mid-run. Switching enableImpedance changes which inputs matter: admittance mode ignores inputState, and impedance mode stops using this block’s own position for the coupling force.
  7. In a chain, tune from one end. Each link’s connectedState is the previous link’s output, so an unstable link destabilises everything after it.

Position from a 10 N step with mass 1 and damping 2, at three stiffnesses:stiffness 5 rings and peaks at 2.4, stiffness 20 peaks at 0.74 and settles at0.5, and stiffness 100 settles at 0.1.

Read the damping ratio off the overshoot on any curve.

Symptom Cause Action
outputState grew without bound The task period is too slow for the stiffness and mass Work step 3 of Setup, then restart the controller
Everything went non-numeric massLookup returned zero — either its value is zero or its x values are not strictly increasing Fix the table, then restart the controller
The mass does not move at all disableDynamics is set, or inputForce is inside the dead zone Clear disableDynamics; lower inputForceDeadZone
The mass still transmits force while disableDynamics is set Expected: that parameter pins the motion, not the force Set the stiffness and damping gains to 0 as well
outputForce does not respond to this mass’s own motion Expected in impedance mode: the coupling force is computed from inputState Clear enableImpedance, or link inputState
inputState seems to be ignored Expected in admittance mode, which is the default Set enableImpedance if you meant impedance
The force gained an extra term when the mode changed Expected: impedance mode adds an inertia term to the coupling force This is the mode’s purpose
The model rings after a force step Damping too low for the stiffness Raise dampingGain until $\zeta$ is near 1
The model is sluggish Damping too high, or the mass too large Lower dampingGain, then the mass
A chain oscillates although each link looks fine alone Links couple, and a lightly damped link excites its neighbours Raise damping across the chain, and tune from one end
The effective coefficients are not what the table says Expected: each is the gain times the table output Read mass, damping and stiffness — all three are published
A negative stiffness or damping gain had no effect on the sign Expected: both are forced positive Use the table values if you need a sign change, though a negative spring is unphysical
massGain reads back as 0.001 after writing 0 Expected: it is floored Set a real value
The simulation kept running after a controller stop and start Expected: the state is not cleared at start Set disableDynamics for a cycle to pin it back to connectedState
The mass feels heavier at one end of its travel Expected if massLookup is shaped that way — it is driven by this mass’s own position Flatten the table
A non-numeric value appeared and will not clear There is no reset input, and the integrators hold it Restart the controller

A starting point for a soft, well-damped single mass on a 1 ms task — $\omega_n$ = 4.5 rad/s, $\zeta$ = 1.1, comfortably inside the stability bound:

enableImpedance    = false
massGain           = 1.0
stiffnessGain      = 1.0
dampingGain        = 1.0
disableDynamics    = false
inputForceDeadZone = 0.1
massLookup:      numPoints = 1, x = 0, y = 1.0
stiffnessLookup: numPoints = 1, x = 0, y = 20.0
dampingLookup:   numPoints = 1, x = 0, y = 10.0

This is a starting point, not a final tuning.

Limits and errors

Limit Set by What happens Reported
Task period against the model’s natural frequency Nothing Not checked. The task period must stay below 2 divided by $\sqrt{K/M}$. Too stiff a spring for the task rate makes the state grow without bound; only a restart clears it Not reported
Effective mass above zero Nothing Not checked. massGain is floored at 0.001, but the mass table can still return zero — including when its x values are not strictly increasing, which returns zero by design. The result is a division by zero Not reported; visible on mass
stiffnessGain, dampingGain Sign forced Both are used as their magnitude, so a negative value cannot invert the force Not reported; read the value back
massGain ≥ 0.001 Fixed Corrected up, which also excludes negatives Not reported; read the value back
outputForce Nothing Unbounded. Bound it downstream, and remember it is not zeroed by disableDynamics Not reported
outputState The two integrators' output limits Clamped by whatever those sub-trees are set to Not reported
Non-numeric inputs Nothing Not guarded anywhere. A non-numeric value latches into the integrators and there is no reset input to clear it Not reported
Model state disableDynamics Pins the state to connectedState. That is the only reset, and it is a parameter rather than a linkable input Not reported
Channel count Fixed One mass per instance. A chain is several instances 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).