RateLimiter
RateLimiter caps how fast a signal may change.6 minute read
RateLimiter caps how fast a signal may change. Where a limiter bounds a
signal’s value, this bounds its slope: the output follows the input
wherever it can, and ramps toward it at no more than rateLimit per second
wherever it cannot.
Use it to take a step out of a command — a mode switch, a new setpoint typed in by an operator, a discontinuous reference — without a filter’s lag. Below the rate limit the output is the input exactly. Only when the limit binds does the block do anything at all.
flowchart LR
i1(["input — signal to slew"]) --> B["RateLimiter"]
i2(["disable"]) --> B
B --> o1(["output — rate-limited signal"])
B --> o2(["isLimiting — per channel"])
B --> o3(["isEnabled"])
rateLimitis in units per second, so a step of height $H$ takes $H/$rateLimitseconds to follow. The setting keeps its meaning across task rates — unlike the sample-counted blocks, nothing needs rescaling if the task period changes.
Disabling this block lets the signal jump. The output snaps to the input on
the same cycle. That is the one thing not to do while the machine is moving —
raise rateLimit instead until it no longer binds.
After a controller start the output ramps up from zero, taking
input ÷ rateLimit seconds to catch up.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The signal to slew. One element per channel; the channel count is fixed by the machine configuration. |
disable |
- | - | True bypasses the block: input passes straight to output, as a step. Use it for a runtime override from a supervisor; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The rate-limited signal, one element per channel. Equals input exactly whenever the input’s slope is inside the limit, and while bypassed. Starts from zero after every controller start and is not cleared by a stop. |
isLimiting |
- | True per channel on cycles where the output was actually clamped. It is exact — no false positives. |
isEnabled |
- | True when enable is true and disable is false. A single value for the whole block. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
enable |
- | true | - | Ships on. False bypasses the block, letting the signal step. |
rateLimit |
signal unit per second | 1.0 | 0 upward, per channel | The fastest the output may change. Lower is gentler and slower to follow. 0 freezes the output where it is. Not checked — a negative value makes the output run away. |
Both are persistent and survive a controller restart. rateLimit is per
channel. No parameters exist below this block.
Setup
-
Work out the fastest change the consumer can accept, in units per second. That is your
rateLimit— it is a property of the machine, not of the signal. -
Set
rateLimitfor every channel. The default of 1.0 is a placeholder. -
Set
enabletrue and confirmisEnabledreads true. -
Step the input and time how long
outputtakes to catch up. It should be the step height divided byrateLimit. -
Move the input at a normal working speed and confirm
outputtracks it exactly, withisLimitingreading false.Step 5 is the check that the limit is not in your way. If
isLimitingis true during normal motion, the limit is shaping every move and not just the steps — raise it until it only bites on the discontinuities. -
Trace every element of
input,outputandisLimiting. Rates are per channel.
Tuning
- Set the rate from the machine, not from the signal. It is the slope the consumer downstream can absorb.
- Confirm it does not bind during normal motion, per Setup step 5. A rate limiter that is always active has become a slow ramp generator.
- Lower it only until steps are absorbed comfortably. Every reduction also delays genuine fast movements.
- If you need smoothing as well as slope limiting, add a filter — this block only bounds the slope, and leaves corners at the start and end of every ramp.
- If the corners themselves are a problem, you need acceleration limiting, not rate limiting. A PVA limiter bounds the rate of the rate.
- Nothing here needs re-checking after a task-rate change. The rate is in units per second and keeps its meaning.
Read the rate off the slope of any ramp.
| Symptom | Cause | Action |
|---|---|---|
| The output ramps up from zero for a while after every start | Expected: the output starts at zero and slews to the input | Gate the consumer for input ÷ rateLimit seconds, or bypass for one cycle to seed it |
| The output jumped when the block was bypassed | Expected: bypassing assigns the input directly | Raise rateLimit until it no longer binds, then bypass |
| The output froze and will not move | rateLimit is 0 for that channel |
Set a positive rate |
| The output runs away in one direction | rateLimit is negative |
Set it positive, then bypass for one cycle to re-seed the output |
isLimiting is true during ordinary motion |
The rate is too low for normal moves | Raise it, per Tuning step 2 |
| Motion is slower than commanded everywhere | Same cause | Raise the rate |
| Steps still reach the consumer | enable is false, disable is true, or the rate is far above the step’s slope |
Read isEnabled; lower rateLimit |
| The output has sharp corners at the start and end of each ramp | Expected: this block bounds slope only, not acceleration | Use a PVA limiter if the corners matter |
| A sine wave came out as a triangle | Expected: the rate limit is below the sine’s peak slope | Raise the rate above 2π × frequency × amplitude |
| One channel slews differently from the others | Expected: rateLimit is per channel |
Check every element |
| The behaviour changed after a task-rate change | Not possible — the rate is in units per second | Look elsewhere in the loop |
| The output stuck at a non-numeric value | A non-numeric input latched into the output, which then poisons every bound | Bypass the block for one cycle to clear it, then fix the source |
| The block resumed from an old value after a restart | Expected: the output is not cleared by a stop | Bypass for one cycle at start |
A starting point for slewing a position command that may move at 0.5 units per second:
enable = true
rateLimit = 0.5
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
rateLimit non-negative |
Nothing | Not checked. A negative value makes the output decrease every cycle without bound. Only bypassing the block or a restart clears it | Not reported |
rateLimit = 0 |
Fixed | Freezes the output at its current value. This is legitimate and is the way to hold a signal | isLimiting reads true |
output slope |
rateLimit while enabled |
Bounded exactly, with no overshoot and no lag inside the bound | isLimiting, per channel |
output value |
Nothing | Unbounded — this block limits slope, not magnitude. Follow it with a limiter if the value needs a bound | Not reported |
| Disabled behaviour | Fixed | Exact pass-through, so the output steps to the input | Not reported |
| Output state | Nothing | Not cleared at start or stop. There is no reset input — bypassing for one cycle is the only way to re-seed it | Not reported |
| Non-numeric input | Nothing | Latches permanently: once the output is non-numeric every bound derived from it is too. Bypass for one cycle to clear | Not reported |
| Task rate | Independent | rateLimit is in units per second and keeps its meaning at any task period |
Not applicable |
| 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.30.0 (bc348fd).