Limiter

Limiter clamps a signal between a lower and an upper bound.
Current release

Limiter clamps a signal between a lower and an upper bound. It is the block that sits between a controller and an actuator that cannot take more than a certain torque, and it is the simplest protection in the library.

An adaptive variant is also available. It starts from wherever the signal already is and closes in on the configured bounds only as the signal allows — so switching it on around a signal that is already outside its band does not produce a step. A plain limiter in the same situation clamps at once.

flowchart LR
    i1(["input — signal to clamp"]) --> B["Limiter"]
    i2(["disable"]) --> B
    B --> o1(["output — clamped signal"])
    B --> o2(["isLimiting — per channel"])
    B --> o3(["isEnabled"])

The adaptive variant adds two outputs:

flowchart LR
    i1(["input"]) --> B["AdaptiveLimiter"]
    i2(["disable"]) --> B
    B --> o1(["output"])
    B --> o2(["isLimiting"])
    B --> o3(["isEnabled"])
    B --> o4(["activeLowerLimit — the bound in use now"])
    B --> o5(["activeUpperLimit"])

output = input clamped to [lowerLimit, upperLimit], per channel. There is no time constant, no lag and no filtering — the clamp is exact and instant. Nothing here depends on the task rate.

The limiter ships switched off. enable defaults to false, so a fresh limiter passes everything through. This matters most when a limiter is one block inside a larger one: its bound is not protecting anything until it is enabled.

If you write the limits the wrong way round, the block swaps them for you and you will read back the swapped pair.

Signals

Inputs

Path Unit Range Description
input signal unit unbounded The signal to clamp. One element per channel; the channel count is fixed by the machine configuration.
disable - - True bypasses the limiter: input passes straight to output and every flag clears. Use it for a runtime override from a supervisor; use enable for the configured intent.

Outputs

Path Unit Description
output signal unit The clamped signal, one element per channel. Equals input exactly while bypassed, and exactly whenever the input is inside the bounds.
isLimiting - True per channel while input is outside the configured bounds. On the adaptive variant this means “the signal wants more than you allowed”, which is not the same as “the output was changed” — see below.
isEnabled - True when enable is true and disable is false. A single value for the whole block.
activeLowerLimit signal unit Adaptive variant only. The bound actually in use this cycle, per channel. It is never tighter than lowerLimit and may be looser while the signal is outside.
activeUpperLimit signal unit Adaptive variant only. The upper equivalent.

Parameters

Path Unit Default Range Effect
enable - false - False bypasses the limiter entirely. Set this, or the block does nothing.
lowerLimit signal unit −1 below upperLimit The lower bound, per channel. Set it to what the consumer can actually accept. If it ends up above upperLimit the two are swapped silently.
upperLimit signal unit +1 above lowerLimit The upper bound, per channel.
adaptive - true - Adaptive variant only. False makes it behave as a plain limiter. A plain limiter cannot be made adaptive at runtime.

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

Setup

  1. Set lowerLimit and upperLimit to what the consumer can accept. The defaults of ±1 are placeholders, not machine values.

  2. Leave enable false and confirm output follows input exactly.

  3. Drive the input past a bound and confirm isLimiting goes true for that channel while output still follows — the flag works while bypassed only in the sense that it stays false; here it will read false, which confirms the bypass.

  4. Set enable true and repeat. output should now stop at the bound and isLimiting should read true.

    Step 4 changes what reaches the consumer. If the signal is already outside the band when you enable a plain limiter, the output steps to the bound. Enable it with the signal inside the band, or use the adaptive variant.

  5. Trace every element of input, output and isLimiting. Limits are per channel, so an element with the wrong bound is easy to miss.

  6. On the adaptive variant, watch activeLowerLimit and activeUpperLimit. They should converge onto the configured bounds as the signal moves inside them.

Tuning

There is nothing to tune. The bounds are a machine property, and the only real decision is which variant to use.

  1. Set the bounds from the consumer’s datasheet, not by watching the signal. A limiter tuned to what the signal happens to do is not protecting anything.
  2. Use the plain limiter wherever the bound is a hard safety limit. It clamps immediately and always, which is what a protection limit must do.
  3. Use the adaptive variant wherever the bound shapes a signal rather than protecting the machine, and a step on engagement would be felt. It never introduces a discontinuity, and it tightens toward the configured bounds only as the signal allows.
  4. Do not use the adaptive variant as a safety limit. If the signal never returns inside the band, the active bounds never tighten and nothing is ever clipped.
  5. Watch isLimiting during commissioning. Persistent limiting means either the bound is too tight or something upstream is commanding more than the machine can deliver — and the second is usually the real problem.
  6. Nothing here needs re-checking after a task-rate change.

Output against input at three limit pairs: with no limit the line runsstraight through, plus-or-minus 1 flattens at both ends, and a lower limit of-0.5 with an upper of 1.5 flattens asymmetrically.

Read each bound off the height at which its line goes flat.

Symptom Cause Action
Nothing is limited and isLimiting stays false enable defaults to false Set enable true
isLimiting reads true but output equals input You are on the adaptive variant, and the active band still admits the signal Read activeLowerLimit and activeUpperLimit; use the plain limiter if you need a hard bound
The output stepped when the limiter was enabled Expected on the plain limiter: it clamps at once Enable with the signal inside the band, or use the adaptive variant
The adaptive limiter never clips anything Expected: the active bounds only tighten when the signal comes back inside them Use the plain limiter for a hard bound
The limits read back swapped Expected: they were written the wrong way round and the block corrected them Write the lower bound to lowerLimit
The output is pinned at one value lowerLimit and upperLimit are equal Separate them
One channel limits and the others do not Expected: the bounds are per channel Check every element of both limit arrays
The output is unbounded inside a larger block That block’s internal limiter has not been enabled Enable it in its own sub-tree
A non-numeric value passed straight through Expected: the clamp does not reject non-numeric values Fix the source; on the adaptive variant, bypass the block for a cycle to clear its active bounds
The signal is clipped harshly and the machine bumps A hard clamp is what this block does Use a soft limiter if you need a rounded approach to the bound

A starting point for bounding an actuator command at ±10 in its own units:

enable     = true
lowerLimit = -10.0
upperLimit = 10.0

Limits and errors

Limit Set by What happens Reported
lowerLimit below upperLimit Fixed If they are inverted the two are swapped in place Silently; read both back
output lowerLimit, upperLimit while enabled Clamped exactly, with no lag. Unbounded while enable is false isLimiting, per channel
Limit values Nothing Not checked. Any finite pair is accepted, and equal limits pin the output to that value Not reported
isLimiting meaning Fixed Reports the input against the configured bounds. On the adaptive variant the output may not have been changed at all Not reported
Adaptive band Fixed The active bounds can only ever be wider than the configured ones, never tighter. They are not reset at start Published as activeLowerLimit / activeUpperLimit
Non-numeric input Nothing Passes through unchanged. On the adaptive variant it also poisons the active bounds until a bypassed cycle re-derives them Not reported
Channel count Machine configuration Fixed once the controller starts 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.32.1 (340db23).