TorqueSensorModule
TorqueSensorModule is the signal-conditioning chain for a joint torque sensor.8 minute read
TorqueSensorModule is the signal-conditioning chain for a joint torque
sensor. It converts raw sensor counts to engineering units, corrects the
sensor’s non-linearity, removes its dominant noise frequency, and then splits
the result into two signals: a slow one that is the load the joint is
carrying, and a fast one that is the structure vibrating around it.
That split is the point. The two need different treatment — the load goes to a force controller, the vibration drives an active-damping correction that this block also produces.
flowchart LR
i1(["sensorTorqueActual — raw sensor counts"]) --> B["TorqueSensorModule"]
i2(["sensorTorqueReference — commanded torque"]) --> B
i3(["disable"]) --> B
B --> o1(["staticTorqueActual — the load"])
B --> o2(["dynamicTorqueActual — the vibration"])
B --> o3(["disturbanceCorrection — damping output"])
B --> o4(["isEnabled"])
The chain is: counts → engineering units → non-linearity correction → notch filter → split. The static branch low-passes the result. The dynamic branch subtracts
sensorTorqueReferencefirst, then high-passes it, so it measures deviation from what was commanded. That deviation drives a controller whose smoothed output isdisturbanceCorrection.
Almost everything is configured below this block, in seven sub-trees. This page’s own eight paths are the wiring; the tuning is in the sub-trees.
disturbanceCorrection reads zero until you configure the controller
sub-tree. Its gains all default to zero. The two torque outputs work
regardless.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
sensorTorqueActual |
counts | unbounded | The raw torque sensor reading, before any conversion. |
sensorTorqueReference |
N·m | unbounded | The torque being commanded. It is subtracted in the dynamic branch only — the static output is the absolute load and does not use it. Leave it at zero if you only want the load. |
disable |
- | - | True forces disturbanceCorrection to zero. The two torque outputs keep working. Use it for a runtime override; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
staticTorqueActual |
N·m | The load the joint is carrying — the slow content of the conditioned signal. Published whether or not the block is enabled, so you can commission the whole chain with the correction disconnected. |
dynamicTorqueActual |
N·m | The vibration: the fast content of the conditioned signal, measured relative to sensorTorqueReference. Also published while disabled. |
disturbanceCorrection |
N·m | The damping correction derived from the vibration. The only output gated by enable. Zero until the controller sub-tree is configured. |
isEnabled |
- | True when enable is true and disable is false. It does not mean a correction is being produced. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
enable |
- | true | - | False forces disturbanceCorrection to zero and leaves both torque outputs reporting. |
One parameter at this level. The configuration lives in seven sub-trees:
| Sub-tree | What it does | Where to read |
|---|---|---|
transducer |
Counts to newton-metres, and the sensor tare | transducer.md |
polynomialCorrection |
Corrects the sensor’s non-linearity, up to 5th order | polynomial.md |
IIRFilter |
Notch filter for the sensor’s dominant noise frequency. Ships as a pass-through — it does nothing until you supply coefficients. Note the capitalised path name | iir-filter.md |
staticTorqueFilter |
Low-pass that extracts the load. Its cut-off sets the split frequency | low-pass-1.md |
dynamicTorqueFilter |
High-pass that extracts the vibration. Its cut-off is the other half of the split | high-pass-2.md |
controller |
The damping controller. Its gains default to zero | pid.md |
vibrationCorrectionFilter |
Smooths the correction. Its cut-off is forced to 1 Hz at every start and cannot be configured — see Limits and errors | low-pass-1.md |
enable is persistent and survives a restart, as is everything in the
sub-trees.
Setup
-
Set
enablefalse. Both torque outputs still report, so the whole conditioning chain can be commissioned with nothing reaching the machine. -
Configure
transducerwith your sensor’s counts-per-newton-metre. Apply a known load and confirmstaticTorqueActualreads the right value in N·m. -
Tare the sensor with no load applied. This needs an application call — there is no parameter for it. If your application does not offer one, work around it with an offset in the
transducersub-tree. -
If the sensor is non-linear, configure
polynomialCorrection. Check it against known loads across the range, not at one point. -
Trace
staticTorqueActualwith the joint held still and look for a dominant noise frequency. If there is one, design a notch for it and write the coefficients into theIIRFiltersub-tree. Design them for your task rate — those coefficients are in samples, not seconds. -
Set the split:
staticTorqueFilter’s cut-off decides what counts as load, anddynamicTorqueFilter’s decides what counts as vibration. Start with both around the frequency where your structure rings. -
Feed
sensorTorqueReferencefrom your torque command and confirmdynamicTorqueActualsits near zero while the joint tracks that command. -
Configure the
controllersub-tree. Set its output limits and integrator limits before any gain — its integrator bounds default to ±0.1, which binds almost at once.Step 8 is what makes the block act on the machine. Until now every output has been observation only. Set
enabletrue last, and raise the controller’s gains from zero with the joint clear.
Tuning
- Get the units right before anything else. If
staticTorqueActualis not in real newton-metres, every downstream number is wrong. - Tune the notch against a measurement, not a guess. Trace the raw signal at rest, find the peak, and design for that frequency.
- Set the split frequency from the structure, not from feel. Below your structure’s ringing frequency is load; above it is vibration. If the two overlap, this block cannot separate them.
- Check the split works: apply a steady load and confirm it appears on
staticTorqueActualand not ondynamicTorqueActual. Then excite the structure and confirm the reverse. - Tune the
controllerlast, and as a controller — set its output limits and integrator limits first, then raise the proportional gain until the ringing is damped, then back off. - Watch
disturbanceCorrectionon a trace while you do it. If it saturates or sits pinned, the controller’s integrator limits are too tight. - Re-check the notch coefficients after any task-rate change. They are defined in samples, so the same coefficients are a different filter at a different rate.
Read the split off the two curves: the load appears on one and the ringing on the other.
| Symptom | Cause | Action |
|---|---|---|
disturbanceCorrection reads zero with isEnabled true |
Expected: the controller sub-tree’s gains all default to zero |
Configure the controller, per Setup step 8 |
disturbanceCorrection saturates or sits pinned |
The controller’s integrator limits default to ±0.1 | Raise them in the controller sub-tree before raising ki |
| A correction appears at full value the moment the block is enabled | The controller keeps integrating while disabled, so it re-enables already wound | Enable with the joint at rest, or reset the controller first |
| Both torque outputs read zero | The notch filter was disabled in its own sub-tree, which outputs zero rather than passing through | Re-enable it, or leave it as the pass-through it ships as |
| The notch does nothing | Expected: it ships as a pass-through with no coefficients | Design a notch and write num and den into the IIRFilter sub-tree |
| The notch behaves nothing like the design | Its coefficients were designed for a different sample rate | Redesign at the controller’s actual task period |
staticTorqueActual is in the wrong units |
The transducer sub-tree is not configured for your sensor |
Work Setup step 2 |
staticTorqueActual has an offset with no load |
The sensor is not tared | Tare it — this needs an application call, not a parameter |
staticTorqueActual is right at one load and wrong at another |
The sensor is non-linear and polynomialCorrection is not configured |
Work Setup step 4 with several known loads |
The load appears on dynamicTorqueActual |
The split frequency is too low | Raise dynamicTorqueFilter’s cut-off |
Vibration appears on staticTorqueActual |
The split frequency is too high | Lower staticTorqueFilter’s cut-off |
dynamicTorqueActual is large even when the joint is tracking well |
sensorTorqueReference is not linked, so the whole load reads as deviation |
Link the commanded torque |
Changing vibrationCorrectionFilter’s cut-off had no effect |
Expected: it is forced to 1 Hz at every start and your value is overwritten | It cannot be changed from configuration |
| A non-numeric value appeared and will not clear | There is no reset in this block, and the filters hold it | Restart the controller |
| You need this on several joints | Not possible — one sensor per instance | Use one instance per joint |
A starting point for a joint whose structure rings near 25 Hz on a 1 ms task, with the correction path not yet active:
enable = false
transducer: configure counts per Nm for your sensor
polynomialCorrection: leave at its pass-through default at first
IIRFilter: leave as pass-through until a noise peak is measured
staticTorqueFilter: omega = 63.0 (10 Hz)
dynamicTorqueFilter: omega = 157.0 (25 Hz), beta = 1.0
controller: kp = 0, ki = 0, kd = 0, output limits set first
Then work Setup step 8. This is a starting point, not a final tuning.
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
vibrationCorrectionFilter cut-off |
Fixed in code | Forced to 1 Hz every time the controller initialises, overriding whatever your configuration sets. It is the one setting in the sub-trees you cannot change | Not reported; read the sub-tree value back after a restart |
controller gains |
Nothing | All default to zero, so disturbanceCorrection is zero until configured. Its integrator bounds default to ±0.1 |
Not reported |
| Notch coefficients | Nothing | Not checked for stability. A badly designed set can make the whole chain grow without bound — see the filter’s own page | Not reported |
| Sensor tare | Not exposed | Taring is available only through an application call, not from the parameter tree | Not reported |
staticTorqueActual, dynamicTorqueActual |
Nothing | Unbounded, and published whether or not the block is enabled | Not reported |
disturbanceCorrection |
The controller sub-tree’s own output limits |
Bounded only by what you set there. Zero while disabled | Not reported |
| Chain state | Nothing | There is no reset input. The filters and the controller hold their state across a stop, and a non-numeric value can only be cleared by a restart | Not reported |
| Channel count | Fixed | One sensor 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).