Interpolator
Interpolator fills in the gaps between values that arrive more slowly than the control loop runs.8 minute read
Interpolator fills in the gaps between values that arrive more slowly than
the control loop runs. A trajectory point every 10 ms on a 1 ms task leaves
nine cycles with nothing new to follow; this block generates those nine, so the
controller downstream sees a continuous command instead of a staircase.
It offers two shapes. Straight-line segments are the default and cost one segment of delay. Cubic segments with matched slopes at the joins are smoother — the only interpolation here with no corner at a join — and cost two.
flowchart LR
i1(["input — the slowly arriving value"]) --> B["Interpolator"]
i2(["disable"]) --> B
B --> o1(["output — interpolated value, every cycle"])
B --> o2(["isEnabled"])
Set
sampleFactorto the number of task cycles between arrivals: input every 10 ms on a 1 ms task means 10. Delay issampleFactor× task period withlookAheadfalse, and twice that withlookAheadtrue — 10 ms and 20 ms in that example. WithfilterSamplesat 0 the output passes exactly through every arrived value; above 0 it no longer does, and adds a lag of (filterSamples+ 1) × task period on top.
Nothing here detects when a new value actually arrives. The block counts
cycles, so sampleFactor must match the arrival rate — see the symptom table.
outputFiltered no longer exists; if you are upgrading, see Limits and
errors.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The slowly arriving value. One element per channel; the channel count is fixed by the machine configuration. The block reads it once per segment, not every cycle. |
disable |
- | - | True bypasses the interpolation: input passes straight to output. Use it for a runtime override from a supervisor; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The interpolated value, fresh every cycle, one element per channel. Equals input exactly while bypassed. Ramps from zero to the first arrived value over one segment after every controller start. With filterSamples at 0 it passes exactly through each arrived value at the end of its segment. |
isEnabled |
- | True when enable is true and disable is false. A single value for the whole block, not one per channel. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
sampleFactor |
cycles | 1 | 1 – 1000 | Task cycles per arriving value. Must match your input’s arrival rate. Out-of-range values are corrected silently, and 1 makes the block a one-cycle delay. |
filterSamples |
samples | 0 (off) | 0 – 1000 | Extra smoothing applied after interpolation. 0 is off and is the right default. Above 0 it adds a lag of (filterSamples + 1) cycles and the output stops passing exactly through the arrived values. Corrected silently if out of range. |
enable |
- | true | - | False bypasses the interpolation: input passes through unchanged. |
lookAhead |
- | false | - | False gives straight segments with a corner at each join. True gives cubic segments with matched slopes and no corner, at twice the delay. Change it with the machine at rest. |
All four parameters are persistent and survive a controller restart. The inputs and outputs do not. No parameters exist below this block.
Setup
-
Measure how often the source updates
input, in task cycles. Divide the update period by the task period: a 10 ms source on a 1 ms task is 10. -
Link the source into
inputand confirm on a trace thatinputmoves in steps at the rate you measured. -
Set
enablefalse.outputequalsinputsample for sample andisEnabledreads false. -
Set
sampleFactorto the number from step 1,filterSamplesto 0, andlookAheadfalse. Read both numbers back to confirm they were accepted. -
Set
enabletrue.outputshould now move smoothly every cycle instead of in steps. -
Overlay
inputandoutputon one trace.outputshould reach each arrived value exactly at the moment the next one arrives, and never hold flat mid-segment.Step 6 is the check that
sampleFactormatches. A flat stretch at the end of each segment means the value is too low;outputstill moving in steps means it is too high. Get this right before tuning anything else. -
Trace every element of
inputandoutput. An element that reads zero while the machine moves is an unwired channel.
Tuning
- Get
sampleFactorright first, with Setup step 6. Nothing else in this block works until the segment length matches the arrival rate. - Decide whether you need
lookAhead. Leave it false unless the corner at each join is visibly reaching the machine — as a tick in the velocity, a bump in the torque, or audible roughness at the update rate. - If you enable
lookAhead, re-measure your total delay. It doubles, and whatever loop consumesoutputpays for it in phase margin. - With
lookAheadtrue, watch a sharp change of direction on a trace. Cubic segments overshoot on a corner — the output will go past the commanded value and come back. If that overshoot is beyond what the machine can accept, use straight segments instead. - Leave
filterSamplesat 0 unless you have residual noise the interpolation itself cannot handle. It costs lag on top of the interpolation delay and it stops the output passing through the arrived values exactly. - Re-check
sampleFactorafter any task-rate change. It counts cycles, so the same value is a different segment length in seconds — rescale it by the ratio of the task periods, and re-check that it still matches the arrival rate.
Read the delay off either curve as its horizontal offset from the dashed input.
| Symptom | Cause | Action |
|---|---|---|
output holds flat at the end of every segment |
sampleFactor is lower than the actual arrival rate, so each segment finishes early |
Raise sampleFactor to the measured number of cycles between arrivals |
output still moves in visible steps |
sampleFactor is higher than the arrival rate, so segments are cut short |
Lower sampleFactor to the measured number |
output drifts in and out of matching the input |
The source’s update rate is not constant | Fix the source’s timing; this block cannot follow a varying rate |
| A tick in velocity or torque at the update rate | Expected with straight segments: the slope jumps at each join | Set lookAhead true, and accept twice the delay |
output overshoots past a commanded value on a sharp turn |
Expected with lookAhead true: cubic segments overshoot at a corner |
Set lookAhead false, or bound the output downstream |
The delay doubled after switching to lookAhead |
Expected: cubic segments need the next value before they can start | Budget for it, or go back to straight segments |
A glitch when lookAhead was changed while running |
The block does not clean up its state on a mode change | Change lookAhead with the machine at rest |
output no longer reaches the arrived values exactly |
filterSamples is above 0 |
Set filterSamples to 0 if the arrived values must be honoured |
The loop consuming output went soft after raising filterSamples |
Expected: the extra smoothing adds lag on top of the interpolation delay | Set filterSamples back to 0 and fix noise at its source |
output ramps up from zero for one segment after every start |
Expected: the block has no previous value to interpolate from | Gate the consumer off isEnabled plus one segment |
A large ramp on output after re-enabling with straight segments |
Expected: the block interpolates from the value it last held to the current input | Re-enable at rest, or use lookAhead, whose first segment after a re-enable is deliberately straight and clean |
sampleFactor or filterSamples reads back different from what you wrote |
Outside the accepted range | Work within the value you read back |
Setting sampleFactor to 0 gave 1 |
Expected: 1 is the minimum, and it makes the block a one-cycle delay | Set the real cycle count |
| The delay in seconds changed after a task-rate change | Expected: sampleFactor counts cycles, not seconds |
Rescale it by the ratio of the task periods |
| Some channels interpolate differently from others | Not possible — all channels share one segment length and one mode | Use a separate instance per channel group |
output went to a non-numeric value |
A non-numeric value reached input |
Fix the source, then bypass the block for one cycle — that clears its state completely |
A starting point for a 10 ms trajectory source on a 1 ms task, with straight segments and no extra smoothing:
sampleFactor = 10
filterSamples = 0
lookAhead = false
enable = true
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
sampleFactor within 1 – 1000 |
Fixed | A value outside the range is replaced by the nearest edge. 1 is a one-cycle delay | Silently; read sampleFactor back |
filterSamples within 0 – 1000 |
Fixed | A value outside the range is replaced by the nearest edge. 0 switches the extra smoothing off | Silently; read filterSamples back |
| Arrival rate | Nothing | Not checked. The block counts cycles and never looks at whether input actually changed, so a mismatch between sampleFactor and the real arrival rate is silent — it shows only as the shape of output |
Not reported |
Overshoot with lookAhead true |
Nothing | Cubic segments overshoot at a corner, and there is no limit on by how much. Bound the output downstream if the machine cannot accept it | Not reported |
output |
Nothing | Unbounded. Limit it downstream if the consumer needs a bound | Not reported |
| Block state | A bypassed cycle | Bypassing the block clears its state completely. There is no separate reset | Not reported |
outputFiltered |
Removed | This path no longer exists. It was removed in an earlier release and its value was always identical to output. A linking file still referencing it will not resolve — point it at output instead |
Not reported; the link fails to resolve |
| Channel count | Machine configuration | Fixed once the controller starts; it cannot be changed at runtime | 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).