ObserverKalmanFilter

ObserverKalmanFilter estimates a joint’s position, velocity and torque from noisy sensor readings, using a model of the joint to reject noise the sensor cannot distinguish from signal.
3.30–3.34

ObserverKalmanFilter estimates a joint’s position, velocity and torque from noisy sensor readings, using a model of the joint to reject noise the sensor cannot distinguish from signal. Its purpose is torque estimation: turning a noisy torque reading into a smooth one you can control against.

Unlike Observer, you do not tune a gain. You tell the filter how much to trust the model and how much to trust the sensors, and it works the gain out for itself, every update. That is the right structure when the model is approximate and the sensors are noisy.

flowchart LR
    i1(["inputVector — commanded torque"]) --> B["ObserverKalmanFilter"]
    i2(["measurementVector — measured position, velocity, torque"]) --> B
    i3(["stateVectorInit"]) --> B
    i4(["disable"]) --> B
    B --> o1(["stateVector — estimated position, velocity, torque"])
    B --> o2(["covarianceMatrix — the filter's own uncertainty"])
    B --> o3(["isEnabled"])

The model is $I\ddot{\theta} + b\dot{\theta} + k\theta = \tau - F$, where the friction force $F$ is $c_v\dot{\theta} + c_c\operatorname{sign}(\dot{\theta})$ — a velocity-proportional part plus a constant-magnitude part. Tuning is the ratio of processNoiseCovariance to measurementNoiseCovariance: raise the first to follow the sensors, raise the second to trust the model and smooth harder. The third diagonal element of processNoiseCovariance is what decides how fast the torque estimate may move.

measurementVector needs all three values — position, velocity and torque. Leaving the velocity element at zero tells the filter the joint is measured to be stationary, which fights its own estimate.

Never set measurementNoiseCovariance to zero. It is the natural way to say “trust the sensors completely” and it makes the filter’s arithmetic ill-conditioned. Never set frequencyDivider to zero either — see Limits and errors.

Signals

Inputs

Path Unit Range Description
inputVector N·m unbounded The torque commanded into the joint. The filter needs it to predict how the joint will respond. A single value.
measurementVector mixed: rad, rad/s, N·m unbounded What the sensors read, as three values in this order: position, velocity, torque. All three are used.
stateVectorInit mixed: rad, rad/s, N·m unbounded Intended as a starting estimate. It has no effect — nothing in the block reads it, because there is no reset to apply it.
disable - - True freezes the filter: the estimate and the uncertainty both hold their last values and nothing advances. It is not a bypass and not a zeroing.

Outputs

Path Unit Description
stateVector mixed: rad, rad/s, N·m The estimate, as three values: position, velocity, and filtered torque — the reason to use this block. It starts at zero after a controller start and converges from there. Not cleared by a stop.
covarianceMatrix mixed The filter’s own estimate of its uncertainty, 3×3. Useful for watching convergence — the diagonal should settle and stay settled. Do not read it as a true confidence bound; see Limits and errors.
isEnabled - True when enable is true and disable is false.

Parameters

Path Unit Default Range Effect
enable - false - Off by default. False freezes the filter entirely.
processNoiseCovariance mixed identity any positive-definite How much you distrust the model, 3×3. Raise it to let the estimate follow the sensors more closely. Its third diagonal element governs how fast the torque estimate may change.
measurementNoiseCovariance mixed identity positive-definite; never zero How much you distrust the sensors, 3×3. Raise it to smooth harder and converge more slowly.
frequencyDivider cycles 1 1 or more Task cycles between corrections. The prediction runs every cycle; the correction — the expensive part — runs every Nth. 0 faults the controller.
model/inertia kg·m² 1.0 above 0 Joint inertia. Zero is not checked and breaks the filter.
model/damping N·m·s/rad 10.0 0 upward Viscous damping in the joint.
model/stiffness N·m/rad 0.0 0 upward Joint stiffness. Leave at 0 for a rigid joint.
model/viscousFrictionCoefficient N·m·s/rad 0.10 0 upward Velocity-proportional friction.
model/coulombFrictionCoefficient_ N·m 0.10 0 upward Constant-magnitude friction. Note the trailing underscore in the path name — it is not a typo in your configuration.

All nine parameters are persistent and survive a controller restart. The inputs and outputs do not. No parameters exist below this block other than the five model/ entries listed above.

Setup

  1. Measure or estimate the joint’s inertia, damping and stiffness. These are physical properties, not tuning knobs — get them roughly right before touching the covariances.

  2. Set model/inertia, model/damping and model/stiffness. model/inertia must be above zero.

  3. Set the two friction coefficients from the joint’s own friction measurement, or leave them at their defaults if you have no figure.

  4. Link inputVector from the commanded torque, and all three elements of measurementVector from the sensors.

  5. Confirm all three measurement elements move. Trace each one. A stuck zero on the velocity element is the most damaging misconfiguration of this block, because the filter treats it as a measurement of standstill.

  6. Leave frequencyDivider at 1, and both covariances at identity.

  7. Set enable true and watch element 2 of stateVector against the raw torque measurement on one trace. The estimate should be a smoothed version of the measurement, following it without the noise.

    Step 7 is the only check that the model is right. If the estimate drifts away from the measurement rather than smoothing it, the model is wrong — go back to step 1. No covariance setting fixes a wrong model.

  8. Raise frequencyDivider only if the block costs more processing time than you can afford. Its correction step is the expensive part.

Tuning

  1. Get the model right first, per Setup step 7. The covariances trade noise against lag; they cannot correct a wrong inertia.
  2. Tune the ratio, not the absolute values. Only the ratio of processNoiseCovariance to measurementNoiseCovariance affects the result, so leave one at identity and move the other.
  3. Start by raising measurementNoiseCovariance — multiply the whole matrix by 10 — and watch element 2 of stateVector. It should get smoother and slower.
  4. Stop when the lag becomes unacceptable for whatever consumes the estimate. Smoothness and lag are the whole trade here.
  5. If you need the torque estimate to respond faster without touching the others, raise only the third diagonal element of processNoiseCovariance. That element governs the torque state alone.
  6. Watch the diagonal of covarianceMatrix while you tune. It should settle to steady values. A diagonal that keeps growing means the filter is not getting useful corrections — check step 5 of Setup.
  7. Tune empirically, from the traces. Do not compute the covariances from sensor datasheets and expect the theoretical result: this filter’s internal gain does not correspond exactly to the model it runs, so the numbers that work will not be the numbers theory predicts.
  8. Re-check nothing after a task-rate change to the covariances themselves, but do re-check frequencyDivider — it counts cycles, so the correction rate in hertz moves with the task period.

Torque estimate through a step at three measurement-noise settings: a lowsetting follows the noisy measurement closely, identity gives a balancedresponse, and a high setting is very smooth and reaches the stepslowly.

Read the trade off any curve: smoother means slower.

Symptom Cause Action
The controller faulted as soon as frequencyDivider was written It was set to 0 Set it to 1 or more and restart the controller
Everything reads zero and never moves enable is false, or disable is true Read isEnabled
The estimate is frozen at an old value Expected: a disabled filter freezes rather than zeroing Set enable true and disable false
The estimate drifts away from the torque measurement The model is wrong — most often model/inertia Re-measure the inertia; no covariance setting fixes this
The estimate fights the measured velocity The velocity element of measurementVector is stuck at zero, so the filter reads standstill Link all three measurement elements
The estimate is as noisy as the raw sensor measurementNoiseCovariance too low relative to processNoiseCovariance Multiply measurementNoiseCovariance by 10
The estimate lags the real torque badly measurementNoiseCovariance too high Lower it, or raise the third diagonal element of processNoiseCovariance
The torque estimate is slow but position and velocity are fine The third diagonal element of processNoiseCovariance is too small Raise that element alone
The estimate went to a non-numeric value and stayed there model/inertia is 0, or a non-numeric value reached an input Set a non-zero inertia, then restart the controller
covarianceMatrix looks healthy but the estimate is nonsense The filter protects its uncertainty from bad values but not its estimate Restart the controller
The filter behaved oddly after measurementNoiseCovariance was set to zero Expected: zero makes the internal arithmetic ill-conditioned Use a small positive value instead of zero
covarianceMatrix’s diagonal keeps growing The corrections are not helping — check the measurements and frequencyDivider Work Setup step 5
A stale estimate for a while after re-enabling Expected: the filter resumes from where it froze, and still believes its old uncertainty Re-enable with the joint near where it was, or restart
The estimate is wrong and there is no way to clear it Expected: there is no reset in this block, and stateVectorInit has no effect Restart the controller
The torque estimate carries a small constant offset at standstill Expected: the friction model applies its constant term even at zero velocity Lower model/coulombFrictionCoefficient_, or accept the offset
The correction rate changed after a task-rate change Expected: frequencyDivider counts cycles, not seconds Rescale it by the ratio of the task periods
Tuning from the sensor datasheet did not give the expected result Expected: tune this filter empirically from traces Work Tuning steps 2 to 4

A starting point for a joint with 0.5 kg·m² inertia on a 1 ms task, correcting every cycle, trusting the sensors and the model equally:

enable                          = true
frequencyDivider                = 1
model/inertia                   = 0.5
model/damping                   = 10.0
model/stiffness                 = 0.0
model/viscousFrictionCoefficient  = 0.10
model/coulombFrictionCoefficient_ = 0.10
processNoiseCovariance          = identity
measurementNoiseCovariance      = identity

Then work Tuning step 3. This is a starting point, not a final tuning.

Limits and errors

Limit Set by What happens Reported
frequencyDivider ≥ 1 Nothing Not checked. A value of 0 faults the controller. Always set 1 or more Not reported
model/inertia > 0 Nothing Not checked. Zero makes the estimate and the uncertainty non-numeric. Only a restart clears the estimate Not reported
measurementNoiseCovariance Nothing Not checked. Zero or near-zero makes the filter’s internal arithmetic ill-conditioned, and the bad result reaches the estimate unprotected Not reported
Estimate protection Partial The filter protects its uncertainty from non-numeric values but not its estimate, so covarianceMatrix can look healthy while stateVector is ruined Not reported
Covariance meaning Inherent covarianceMatrix is useful for watching convergence but is not a true confidence bound — the filter’s internal gain does not correspond exactly to the model it runs. Tune empirically Not reported
Measurement count Fixed Three measurements are required: position, velocity, torque. Supplying fewer leaves zeros that the filter treats as real readings Not reported
Model accuracy Nothing Not checked, and cannot be. A wrong model gives a confident, wrong estimate Not reported
Estimate state Nothing There is no reset input. stateVectorInit has no effect. A restart is the only way to clear a wrong estimate Not reported
Estimate across a stop Nothing Not cleared by a stop or a start. A restart of the task resumes the old estimate Not reported
stateVector, covarianceMatrix Nothing Unbounded 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).