AdmittanceModel

AdmittanceModel turns a measured force into a motion setpoint.
3.30–3.34

AdmittanceModel turns a measured force into a motion setpoint. It solves the equation of motion of a virtual mass on a spring and damper, in real time, and publishes the resulting position, velocity and acceleration for a position controller to track. That is what makes a stiff machine feel compliant: you push, the virtual mass moves, and the servo follows it.

Every physical coefficient is a lookup table, not a constant. Mass, damping and stiffness each vary with their own input, and two further tables scale mass and damping by position and velocity — so a machine whose apparent inertia should change across its workspace needs no gain scheduling elsewhere.

flowchart LR
    i1(["measuredTorque — the external force"]) --> B["AdmittanceModel"]
    i2(["actuatorFeedForwardTorque"]) --> B
    i3(["actuatorVirtualMass — input to the mass table"]) --> B
    i4(["inputPVA — jog and offset"]) --> B
    i5(["referencePVA — integrator references"]) --> B
    i6(["externalMassFactor"]) --> B
    i7(["externalDampingFactor"]) --> B
    i8(["connectSpringReferencePosition"]) --> B
    i9(["disableDynamics"]) --> B
    i10(["disable"]) --> B
    B --> o1(["outputPVA — the motion setpoint"])
    B --> o2(["outputTorque — spring plus damper force"])
    B --> o3(["springForce"])
    B --> o4(["damperForce"])
    B --> o5(["massCoefficient — the effective mass"])
    B --> o6(["accelerationTorque"])
    B --> o7(["accelerationTorqueCorrection"])
    B --> o8(["springReferencePosition"])
    B --> o9(["enabled"])

The virtual system is $M\ddot{x} + D\dot{x} + Kx = F$. With the default stiffness of 0 it is first order: terminal velocity $F/D$, time constant $M/D$. With stiffness set, aim at $\omega_n = \sqrt{K/M}$ [rad/s] and $\zeta = D/(2\sqrt{KM})$ — target $\zeta$ near 1 to avoid a bouncy contact. Read the effective mass off massCoefficient; it is the mass table’s output times the position and velocity scalings, not the number in the table.

A positive measuredTorque produces motion in the negative direction by default. Set useNegativeCoefficients true to reverse that.

The spring produces no force until connectSpringReferencePosition is set true. Until then the spring’s anchor follows the output, so there is nothing to push against. If you configured stiffness and see no spring force, this is why.

Signals

Inputs

Path Unit Range Description
measuredTorque N or N·m unbounded The external force the operator or environment applies. Non-numeric values are treated as zero.
actuatorFeedForwardTorque N or N·m unbounded The actuator’s own modelled torque, added to the force target after actuatorFeedForwardGain. Non-numeric values are treated as zero.
actuatorVirtualMass kg or kg·m² unbounded The input to the mass lookup table, not a mass. The table converts it into the mass the virtual system uses.
inputPVA/position m or rad unbounded Added directly to outputPVA/position. An offset, not a setpoint.
inputPVA/velocity m/s or rad/s unbounded Added directly to outputPVA/velocity — this is the jog path. It also trims the spring anchor while the anchor is connected.
inputPVA/acceleration m/s² or rad/s² unbounded Feeds the mass-correction term only.
referencePVA/position m or rad unbounded The position integrator’s reference.
referencePVA/velocity m/s or rad/s unbounded The velocity integrator’s reference, and what the velocity is pinned to while the block is disabled.
referencePVA/acceleration m/s² or rad/s² - Registered but never used. Ignore it.
externalMassFactor - 0.1 – 100 Runtime scaling on the mass. The range is not enforced on this path — see Limits and errors.
externalDampingFactor - 0.1 – 100 Runtime scaling on the damping. Not enforced, and a negative value is unsafe — see Limits and errors.
connectSpringReferencePosition - - True freezes the spring’s anchor where the machine is, so the spring starts pulling back toward it. False re-anchors it to the current position every cycle, which means no spring force. The block clears this itself on a position reset.
disableDynamics - - True pins the velocity to referencePVA/velocity and forces outputTorque to zero, while leaving the block enabled. Use it to hold the model rigidly at a commanded state.
disable - - True does the same and also reads back on enabled. Use it for a runtime override from a supervisor; use enable for the configured intent.

Outputs

Path Unit Description
outputPVA/position m or rad The motion setpoint — the reason to use this block. Position is held while the block is disabled, not zeroed.
outputPVA/velocity m/s or rad/s Velocity setpoint, including anything added through inputPVA/velocity.
outputPVA/acceleration m/s² or rad/s² Acceleration setpoint, optionally smoothed.
outputTorque N or N·m Spring plus damper force. Chain this to the next model’s force input to build a coupled chain. Zero while disabled or while disableDynamics is set.
springForce N or N·m The spring’s contribution after its own limit, including any position-limit push-back.
damperForce N or N·m The damper’s contribution after its own limit.
massCoefficient kg or kg·m² The effective mass actually used — the table output times both scalings. Watch this, not the table.
accelerationTorque N or N·m The net accelerating force before limiting.
accelerationTorqueCorrection N or N·m What the noise canceller contributed, zero when it is off.
springReferencePosition m or rad Where the spring is currently anchored.
enabled - True when enable is true and disable is false. Note the path is enabled, not isEnabled.

Parameters

Path Unit Default Range Effect
enable - true - False pins the velocity to its reference and zeroes outputTorque. Position still holds.
useNegativeCoefficients - false - False negates the force, so a positive measuredTorque gives negative motion. True passes the sign through. The name reads backwards against its effect.
measuredTorqueDeadZone N or N·m 0.1 0 upward Forces smaller than this produce no motion. Raise it to stop a noisy sensor creeping the machine.
actuatorFeedForwardGain - 1.0 any Scales actuatorFeedForwardTorque before it joins the force target. Not checked.
massCorrection - 0.0 any Scales an acceleration feedback term that partly cancels the virtual mass. Leave at 0 unless commissioning with support. Not checked.
springTorqueLimit N or N·m 10 0 upward Caps springForce. Set it to what the machine may push back with.
damperTorqueLimit N or N·m 10 0 upward Caps damperForce.
anc/enable - false - Switches on active noise cancellation, which projects a repeating disturbance forward and subtracts it. Leave off unless you have a periodic disturbance.
anc/averageMaxSamples samples 250 1 upward Fast averaging window. Not checked.
anc/slowAverageMaxSamples samples 1000 100 – 2000 Slow averaging window. Corrected silently if out of range.

All ten are persistent and survive a restart.

Most of the configuration is below this block, in fifteen sub-trees. The ones you will use: massLookup, dampingLookup and stiffnessLookup for the three physical coefficients; positionCoefficientLookup and velocityCoefficientLookup for their position and velocity scaling; velocityIntegrator and positionIntegrator for the motion limits; positionLimitLookup for push-back near a limit; velocityLimiterLookup for the taper that slows the machine as it approaches one; torqueLimiter for the accelerating force; and five low-pass filters — massLowPass, accelerationLowPass, massLookupLowPass, dampingLookupLowPass and stiffnessLookupLowPass — that keep table steps from stepping the coefficients. See lookup.md, integrator.md and low-pass-1.md.

velocityCoefficientLookup ships unconfigured, and an unconfigured table returns zero, which is then floored at 0.01. That multiplies your mass by 0.01. Configure it — a single point returning 1.0 leaves the mass alone — before you trust any mass number. See Limits and errors.

Setup

  1. Configure velocityCoefficientLookup first: one point, y = 1.0, numPoints = 1. Without this the effective mass is a fiftieth of what the mass table says.

  2. Configure massLookup with the mass or inertia you want the operator to feel. A single point is enough if it does not vary.

  3. Set stiffnessLookup to zero and leave connectSpringReferencePosition false. Tune the mass and damper first; the spring comes later.

  4. Configure dampingLookup. Start high — a heavily damped virtual system is safe to push on.

  5. Set springTorqueLimit and damperTorqueLimit to forces the machine may safely produce, and set the two integrators' output limits to the axis’s real travel and speed.

  6. Set measuredTorqueDeadZone above your force sensor’s noise floor. Confirm the machine does not creep with nobody touching it.

  7. Set enable true and check enabled reads true. Push the machine and confirm it moves away from the push. If it moves into your hand, flip useNegativeCoefficients.

    Step 7 is the first time the machine moves under operator force. Have the axis clear and a hand on the stop. A mass that is too light or damping that is too low will make it run away from a light touch.

  8. Read massCoefficient and confirm it is the mass you intended. It is the table output times both scalings, so it is the number that matters.

  9. Only now add the spring: configure stiffnessLookup, then set connectSpringReferencePosition true where you want the anchor.

Tuning

  1. Tune in this order — mass, damper, spring — and change one at a time. The three interact, and the spring is meaningless until the first two feel right.
  2. Set the mass by feel, then verify with massCoefficient. Heavier feels more substantial and responds more slowly; lighter is quicker and easier to destabilise.
  3. Set the damper from the terminal velocity you want: a steady push of $F$ settles at $F/D$. Read the terminal velocity off a trace and divide.
  4. Check the time constant: $M/D$, in seconds. That is how long the machine takes to reach 63% of its terminal velocity. Too short and the machine feels twitchy; too long and it feels sluggish.
  5. Add stiffness only when the machine should return to a position. Aim for $\zeta = D/(2\sqrt{KM})$ near 1 — below about 0.5 the contact will bounce.
  6. If friction or inertia varies across the workspace, use the position and velocity coefficient tables to raise mass and damping where the machine feels light. They can only raise, never lower — the position scaling is floored at 1 and the velocity scaling at 0.01.
  7. Use velocityLimiterLookup to taper the speed as the machine nears a position limit, and positionLimitLookup to push back. A hard clamp on the integrator alone arrives as a jolt.
  8. Leave massCorrection at 0 and the noise canceller off unless you are commissioning with support. Both change the force balance in ways that are hard to read off a trace.

Velocity from a 10 N·m force step with a virtual mass of 1, at three dampingsettings: damping 5 coasts up toward 2.0 rad/s, damping 20 settles at 0.5, anddamping 100 settles at 0.1.

Read the time constant off any curve as the point it crosses 63% of its final value.

Symptom Cause Action
The machine feels far lighter than the mass table says velocityCoefficientLookup is unconfigured, so the mass is multiplied by 0.01 Configure it: one point, y = 1.0. Then read massCoefficient back
The machine moves into the push instead of away The sign convention Flip useNegativeCoefficients
The machine creeps with nobody touching it measuredTorqueDeadZone below the sensor’s noise floor Raise it until the creep stops
The machine runs away from a light touch Mass too low or damping too low Raise dampingLookup first, then massLookup
Motion is sticky, then jumps The dead zone is too large, or friction elsewhere is not compensated Lower the dead zone; check the feedforward path’s friction model
No spring force at all, with stiffness configured Expected: the anchor follows the output until you connect it Set connectSpringReferencePosition true
The spring snapped the machine when engaged The anchor was set while the machine was displaced Engage it where you want the rest position to be
Contact bounces or oscillates Damping too low for the stiffness Raise dampingLookup until $\zeta$ is near 1, or lower the stiffness
The machine feels heavy in one part of the workspace only Expected if positionCoefficientLookup is shaped that way Flatten that table, or accept it — it exists to stabilise light regions
massCoefficient moved on its own Expected: the mass table is driven by actuatorVirtualMass and scaled by position and velocity Trace all three inputs to see which moved
Motion is jerky when crossing a table breakpoint A step in a lookup table The coefficient low-passes smooth this; lower their cut-offs, or soften the table
Reaching a position limit arrives as a jolt Only the integrator’s hard clamp is set Configure velocityLimiterLookup to taper the speed and positionLimitLookup to push back
outputTorque went to zero and motion stopped Expected while disable or disableDynamics is set Read enabled
Position held but velocity went to zero on disable Expected: velocity is pinned to its reference, position holds This is the designed idle state
The machine drifted over a long session Two integrations with no reset reachable from the parameter tree Restart the controller, or have the application call its reset
A force spike appeared after a bad sensor reading Expected: non-numeric torque readings are treated as zero, but a large finite spike passes Lower torqueLimiter’s limits
Everything went non-numeric and stayed there A non-numeric value reached inputPVA or a lookup table, which are not guarded Restart the controller
The machine became unstable after an external factor was written A zero or negative externalDampingFactor — negative damping adds energy Write a value between 0.1 and 100 and restart
Motion is right but forces look wrong The two limits are clipping Read springForce and damperForce against springTorqueLimit and damperTorqueLimit

A conservative starting point for a 1 ms task, damper-only compliance with no spring, on an axis whose travel is ±0.5:

enable                    = true
useNegativeCoefficients   = false
measuredTorqueDeadZone    = 0.5
actuatorFeedForwardGain   = 1.0
massCorrection            = 0.0
springTorqueLimit         = 10.0
damperTorqueLimit         = 10.0
anc/enable                = false
velocityCoefficientLookup: numPoints = 1, x = 0, y = 1.0
massLookup:                numPoints = 1, x = 0, y = 2.0
dampingLookup:             numPoints = 1, x = 0, y = 40.0
stiffnessLookup:           numPoints = 1, x = 0, y = 0.0

That gives an effective mass of 4 (mass 2 × position scaling 2), damping 80, and a time constant of 0.05 s. This is a starting point, not a final tuning.

Limits and errors

Limit Set by What happens Reported
velocityCoefficientLookup configured Nothing Not checked. Unconfigured it returns zero, floored to 0.01, multiplying your mass by 0.01. Nothing warns Not reported; visible on massCoefficient
externalMassFactor, externalDampingFactor within 0.1 – 100 Nothing on the input path Not checked when written as an input. A zero or negative mass factor is caught by the mass floor; a negative damping factor is not caught anywhere and makes the virtual system unstable Not reported
Effective mass ≥ 0.00001 Fixed A smaller or non-numeric mass is replaced by that floor, and the mass table’s gain is permanently rewritten as a side effect Not reported; visible on massCoefficient and in the table’s gain
springForce springTorqueLimit Clamped Not reported; compare springForce against the limit
damperForce damperTorqueLimit Clamped Not reported
Accelerating force torqueLimiter sub-tree Clamped when that limiter is enabled. It is disabled by default, so the force is unbounded until you enable it Not reported
outputPVA/velocity velocityIntegrator output limits, tapered by velocityLimiterLookup Clamped, and tapered toward zero near a position limit Not reported
outputPVA/position positionIntegrator output limits Clamped hard. Without a velocity taper the clamp arrives as a jolt Not reported
anc/averageMaxSamples Nothing Not checked Not reported
actuatorFeedForwardGain, massCorrection Nothing Not checked Not reported
Model state Nothing reachable There is no reset input. The block integrates twice and cannot be re-zeroed from the parameter tree — only an application call or a restart Not reported
Non-numeric inputs Partial measuredTorque, actuatorFeedForwardTorque and the mass-correction term are guarded and treated as zero. inputPVA and the lookup tables are not Not reported
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).