Transducer

Transducer converts between a sensor’s raw units — encoder ticks, resolver counts, an analogue reading — and engineering units, in both directions.
Current release

Transducer converts between a sensor’s raw units — encoder ticks, resolver counts, an analogue reading — and engineering units, in both directions. It also handles referencing: telling the machine that a particular raw reading corresponds to a particular real position.

For an incremental sensor it can additionally store that position to disk, so the machine knows where it is after a power cycle.

flowchart LR
    i1(["inputSoftwareSide"]) --> B["Transducer"]
    i2(["inputHardwareSide"]) --> B
    i3(["persistenceEvent"]) --> B
    i4(["disablePersistence"]) --> B
    i5(["referencing/softwareReferenceExternal"]) --> B
    p1(["gainNum, gainDen, ticksPerRevolution, offset"]) --> B
    p2(["referencing/softwareReferenceInternal, useSoftwareReferenceExternal"]) --> B
    p4(["referencing/reconnectToleranceHardwareSide"]) --> B
    p3(["enablePersistence, deltaTicksMax, numberOfPolePairs, enableSingleTurnCounter"]) --> B
    B --> o1(["outputSoftwareSide"])
    B --> o2(["outputHardwareSide"])
    B --> o3(["gain"])
    B --> o4(["referencing/hardwareSnapshot"])
    B --> o5(["atStoredTicks, deltaTicks, singleTurnCounter"])
    B --> o7(["referencing/isOpenLoop"])
    B --> o6(["isPersistenceEnabled"])

After referencing, the output to the drive is deliberately frozen until the software side has been re-based onto the new offset. Precisely: until recomputing the hardware output would land within referencing/reconnectToleranceHardwareSide of the frozen value, for more than 100 consecutive cycles. Because the test is against the frozen output rather than the measurement, reconnecting cannot step the drive by more than that tolerance, whatever the measurement did meanwhile.

It is a re-basing wait, not a timer, so it does not time out. If nothing re-bases the software side the output stays frozen indefinitely, with nothing reported — the safe outcome, but the axis stays held, and a further reference request is ignored while it is. If a drive stops responding right after referencing, this is where to look. referencing/:fromState.reset clears it.

Set referencing/reconnectToleranceHardwareSide for your hardware side. The default of 2 is sized for a hardware side carrying encoder ticks. On an instance whose hardware side carries metres or radians, 2 is enormous and would let a whole offset step through.

ticksPerRevolution of 0 breaks the conversion. The other two gain factors refuse a zero and keep their previous value; this one does not. Never write 0 to it.

Persistence is switched off at every startup, whatever enablePersistence says. Your application must re-enable it after each start.

Some of the parameters below only exist on an encoder-type instance. The type is fixed when the controller is built.

Signals

Inputs

Path Unit Range Description
inputSoftwareSide engineering unit unbounded The setpoint to convert to raw units.
inputHardwareSide ticks unbounded The sensor reading to convert to engineering units.
persistenceEvent - enumeration Commands a store or load of the position file. Encoder instances only.
disablePersistence - true or false Suspends the file handling. Forced true at every startup.
referencing/softwareReferenceExternal engineering unit unbounded The value the captured raw reading should correspond to, when useSoftwareReferenceExternal is true.

Outputs

Path Unit Description
outputSoftwareSide engineering unit The converted sensor reading — where the machine thinks it is.
outputHardwareSide ticks The converted setpoint, for the drive. Held after referencing until the interlock clears.
gain ticks per engineering unit The combined conversion factor. Read this back to check your three factors multiply to what you expect.
referencing/hardwareSnapshot ticks The raw reading captured for referencing.
atStoredTicks - The position from the file matches the sensor. Reads true whenever persistence is off, so read isPersistenceEnabled alongside it.
deltaTicks ticks How far the sensor is from the stored position.
singleTurnCounter revolutions Whole turns counted, for a sensor that only reports position within one turn.
isPersistenceEnabled - The file handling is active.
referencing/isOpenLoop - The open-loop flag the parent block feeds in, published so the value stays visible. It is no longer part of the reconnect test — an open loop does not release the post-referencing freeze.

Parameters

Path Unit Default Range Effect
gainNum - 1 non-zero Numerator of the conversion. A 0 is refused and the previous value kept.
gainDen - 1 above 0 Denominator. A value of 0 or below is refused and the previous value kept.
ticksPerRevolution ticks 1 above 0 The sensor’s resolution. Multiplied into the gain. A negative value is used as its magnitude, but 0 is not refused and will break the conversion. Encoder instances only.
offset ticks 0.0 unbounded The raw reading that corresponds to zero engineering units. Referencing overwrites this — save your configuration afterwards, or a reload will undo it.
referencing/softwareReferenceInternal engineering unit 0.0 unbounded The value referencing aims at, when the external one is not selected.
referencing/useSoftwareReferenceExternal - true - Whether to use the external reference input instead of the internal parameter.
referencing/reconnectToleranceHardwareSide hardware-side unit 2 above 0 How close the recomputed hardware output must come to the frozen one before the output reconnects, for more than 100 consecutive cycles. A write is taken as its absolute value. The default suits encoder ticks only — see Limits and errors.
enablePersistence - false - Whether to store the position to a file. Only for a sensor that does not remember its own absolute position. Turn it off for an absolute encoder.
deltaTicksMax ticks -1 -1, or 0 upward How far the sensor may differ from the stored position and still be trusted. The default of -1 means no check.
numberOfPolePairs - 1 1 upward For resolver-type sensors, how many electrical revolutions make one mechanical one.
enableSingleTurnCounter - false - Counts whole turns, for a sensor that reports only within one turn.

All are persistent and survive a controller restart. No parameters exist below this block.

Setup

  1. Establish your sensor’s resolution and set ticksPerRevolution. Never write 0.

  2. Set gainNum and gainDen to express the rest of the conversion — a lead screw pitch, a pulley ratio, a unit change.

  3. Read gain back. It should be gainNum ÷ gainDen × ticksPerRevolution, and it is the number of ticks per engineering unit. Check it against a hand calculation before going further.

  4. Move the machine a known distance by hand and confirm outputSoftwareSide changes by that amount.

  5. Decide whether you need persistence. An absolute encoder does not — leave enablePersistence false. A resolver or incremental encoder does.

  6. Reference the machine: bring it to a known position, set the reference value, and trigger referencing from your application.

  7. Confirm outputSoftwareSide now reads the true position.

    After referencing, the drive output is frozen until the commanded and measured positions agree. Expect a short pause before the axis responds again, and investigate if it never does.

  8. Save your configuration, or the referenced offset is lost on the next reload.

Tuning

  1. There is nothing dynamic to tune. Get the conversion right and the rest follows.
  2. Derive the three gain factors from the drivetrain rather than measuring them — exact integers avoid rounding in a long-running position count.
  3. Verify over a long move, not a short one. A 1% gain error is invisible over a millimetre and obvious over a metre.
  4. Check the direction. A sensor wired backwards shows as outputSoftwareSide moving the wrong way, and is fixed with a negative gainNum.
  5. Reference at a repeatable physical feature — a hard stop, a switch — not at an arbitrary position.
  6. Set referencing/reconnectToleranceHardwareSide for the unit your hardware side carries. Leave it at 2 for encoder ticks; set a few thousandths for metres or radians. Do this before referencing.
  7. If you use persistence, set deltaTicksMax to how far the machine could plausibly be moved while powered down. Leave it at -1 only if you accept the stored position unconditionally.
  8. Re-check atStoredTicks after each start, together with isPersistenceEnabled — the first reads true whenever the second is false.

Hardware ticks against engineering units for a 4096-tick sensor. The offsetshifts the line without changing its slope; the gain is theslope.

Referencing moves the line up or down. It never changes the slope.

Symptom Cause Action
The conversion is wildly wrong or invalid ticksPerRevolution is 0, which is not refused Set it to the sensor’s real resolution
A gain factor I wrote did not take gainNum was 0, or gainDen was 0 or negative — both are refused Read them back
The machine moves the wrong way The gain’s sign is wrong Use a negative gainNum
The distance is proportionally wrong The three factors do not multiply to the right ratio Check gain against a hand calculation
The drive stopped responding after referencing Expected briefly: the output is held until the positions agree If it never resumes, the positions are not converging — check the drive
The referenced position was lost after a restart Referencing overwrites offset, and a configuration reload restores the saved value Save the configuration after referencing
atStoredTicks reads true and I do not trust it It reads true whenever persistence is off Read isPersistenceEnabled too
Persistence is off even though I enabled it It is forced off at every startup Re-enable it from your application after each start
The stored position is accepted when it should not be deltaTicksMax is -1, which means no check Set a real window
The drive stopped responding right after referencing, with nothing reported Expected: the output is frozen until the software side is re-based. It is a wait, not a timer Have the application follow the disconnect flag and re-base its setpoint onto the measured actual; or assert referencing/:fromState.reset
The drive stepped when the output reconnected referencing/reconnectToleranceHardwareSide is too large for the unit the hardware side carries — the default of 2 on a metres-or-radians instance Set it to a few thousandths and re-reference
Reconnecting takes far longer than expected The tolerance is tight enough that the re-based value rarely holds it for 100 consecutive cycles Loosen it, or reduce the noise on the software side
A reference request was ignored Expected: requests are ignored while the hardware side is still disconnected from the previous one Wait for the freeze to clear, or reset it
The freeze did not clear on an open-loop axis Expected: referencing/isOpenLoop does not release the freeze Re-base the software side; the flag is published for visibility only
The position jumps by a whole revolution The single-turn counter is needed, or miscounted Enable it and set numberOfPolePairs
Everything became invalid A gain factor that is not a valid number passes the zero checks Rewrite all three
Some parameters are missing from the tree Your instance is not an encoder type The type is fixed when the controller is built
I need a non-linear conversion Wrong block Put a Lookup after it

A starting point for a 4096-tick encoder on a direct-driven axis:

ticksPerRevolution = 4096
gainNum            = 1
gainDen            = 1
offset             = 0
enablePersistence  = false
deltaTicksMax      = -1

Then read gain back and confirm it is 4096.

Limits and errors

Limit Set by What happens Reported
Conversion gainNum, gainDen, ticksPerRevolution Their product is the ticks per engineering unit, both directions gain
gainNum of 0 Checked Refused — the previous value is kept Read it back
gainDen of 0 or below Checked Refused — the previous value is kept Read it back
ticksPerRevolution of 0 Not checked The gain becomes 0 and the conversion divides by zero Not reported
Values that are not numbers Nothing Not checked. They pass the zero tests and poison both directions Not reported
Referencing The reference value and a captured raw snapshot Solves for the offset that makes them correspond, and overwrites offset referencing/hardwareSnapshot, offset
Output after referencing Fixed Held until the commanded and measured positions agree closely for a short, fixed period. No timeout, nothing reported if they never do Not reported
Persistence at startup Fixed Always disabled, whatever the parameter says isPersistenceEnabled
Stored position check deltaTicksMax Compares the sensor against the stored position. A default of -1 means no check atStoredTicks, deltaTicks
Post-referencing freeze The reconnect condition The hardware output is held until recomputing it would land within referencing/reconnectToleranceHardwareSide of the frozen value, for more than 100 consecutive cycles. A re-basing wait, not a timer — it does not time out, and a further reference request is ignored while it holds Not reported directly; read the referencing state group
referencing/reconnectToleranceHardwareSide Default 2 Sized for a hardware side carrying encoder ticks. On one carrying metres or radians, 2 would let a whole offset step through on reconnect. A negative write is taken as its absolute value Read the value back
Reconnect step The tolerance Bounded by construction: the test is against the frozen output, not the measurement, so reconnecting cannot move the hardware output by more than the tolerance Not reported
atStoredTicks Fixed Reads true when persistence is off, so it means “nothing to distrust”, not “a check passed” Read isPersistenceEnabled too
Persistence file errors Partly A zero or negative ticksPerRevolution skips the stored-position check, and is logged Controller log
Instance type Fixed at build time A non-encoder instance has no persistence, no turn counter and no ticksPerRevolution Not reported
Channel count Fixed One sensor per instance, always Not reported

The block logs when a stored-position check is skipped for a bad ticksPerRevolution, and logs the loaded position at startup. Every other condition above shows as a value on a trace, or not at all.


Verified against motorcortex-control3 3.33.0 (69625c1).