Transducer
Transducer converts between a sensor’s raw units — encoder ticks, resolver counts, an analogue reading — and engineering units, in both directions.10 minute read
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/reconnectToleranceHardwareSideof 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.resetclears it.
Set
referencing/reconnectToleranceHardwareSidefor 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.
ticksPerRevolutionof 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
enablePersistencesays. 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
-
Establish your sensor’s resolution and set
ticksPerRevolution. Never write 0. -
Set
gainNumandgainDento express the rest of the conversion — a lead screw pitch, a pulley ratio, a unit change. -
Read
gainback. It should begainNum ÷ gainDen × ticksPerRevolution, and it is the number of ticks per engineering unit. Check it against a hand calculation before going further. -
Move the machine a known distance by hand and confirm
outputSoftwareSidechanges by that amount. -
Decide whether you need persistence. An absolute encoder does not — leave
enablePersistencefalse. A resolver or incremental encoder does. -
Reference the machine: bring it to a known position, set the reference value, and trigger referencing from your application.
-
Confirm
outputSoftwareSidenow 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.
-
Save your configuration, or the referenced offset is lost on the next reload.
Tuning
- There is nothing dynamic to tune. Get the conversion right and the rest follows.
- Derive the three gain factors from the drivetrain rather than measuring them — exact integers avoid rounding in a long-running position count.
- Verify over a long move, not a short one. A 1% gain error is invisible over a millimetre and obvious over a metre.
- Check the direction. A sensor wired backwards shows as
outputSoftwareSidemoving the wrong way, and is fixed with a negativegainNum. - Reference at a repeatable physical feature — a hard stop, a switch — not at an arbitrary position.
- Set
referencing/reconnectToleranceHardwareSidefor 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. - If you use persistence, set
deltaTicksMaxto how far the machine could plausibly be moved while powered down. Leave it at -1 only if you accept the stored position unconditionally. - Re-check
atStoredTicksafter each start, together withisPersistenceEnabled— the first reads true whenever the second is false.
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).