PvajLimiter

PvajLimiter bounds a position setpoint in jerk, acceleration, velocity and position at the same time, and guarantees the position limits are never crossed.
Current release

PvajLimiter bounds a position setpoint in jerk, acceleration, velocity and position at the same time, and guarantees the position limits are never crossed. The output satisfies positionLowerLimit ≤ output ≤ positionUpperLimit exactly — not to within a margin, because every cycle the block asks “if I apply this jerk, can the setpoint still be brought to rest inside the box?” before it applies it.

It replaces PVALimiter for new work. That block tracks with a filter and clamps acceleration; this one plans a trajectory and bounds jerk as well, so motion starts and stops without the corners that shake a machine. It also runs as a velocity limiter driven by inputDot. Even then it brings the axis to rest on a position limit rather than through it.

flowchart LR
    i1(["input — position setpoint"]) --> B["PvajLimiter"]
    i2(["inputDot — setpoint velocity, or the velocity command"]) --> B
    i3(["inputDDot — setpoint acceleration"]) --> B
    i4(["disable"]) --> B
    p1(["Limits0N/positionLowerLimit, positionUpperLimit"]) --> B
    p2(["Limits0N/velocityLimit, accelerationLimit, jerkLimit"]) --> B
    p3(["Limits0N/maxAccelerationBraking, maxJerkBraking"]) --> B
    p4(["Limits0N/enableInput, enableInputDot, enableInputDDot"]) --> B
    B --> o1(["output — bounded position"])
    B --> o2(["outputDot — velocity"])
    B --> o3(["outputDDot — acceleration"])
    B --> o4(["outputDDDot — jerk applied this cycle"])
    B --> o5(["isEnabled"])
    B --> o6(["Limits0N/isLimiting and the four limit flags"])

It runs in one of two modes, chosen per channel by enableInput:

Mode enableInput enableInputDot What is commanded What you read
PVAJ (default) 1 optional — 1 only if inputDot really carries the setpoint velocity input is a position setpoint output, outputDot, outputDDot, outputDDDot
VAJ 0 1 — it is the switch that says inputDot carries a velocity; with it 0 the leaf is not read and the command is zero inputDot is a velocity command; input is not read at all outputDot, outputDDot, outputDDDot

Both modes shape the motion with the same J, A and V limits. §7 and §8 give a complete configuration for each.

enableInputDot means one thing in both modes: the inputDot leaf carries a real velocity, read it. Off, the leaf is never read. In PVAJ mode the block then estimates the setpoint velocity by differencing input; in VAJ mode there is no input to difference, so the command is zero and the axis is brought to rest. The three switches that shape the position tracking lead — enableInputDDot, feedforwardGain and suppressInputDDotEstimation — are optional in PVAJ mode and inert in VAJ mode.

A full stop from velocityLimit needs $V^2/2A_b + V A_b/2J_b$ of travel, with $A_b$ and $J_b$ the braking authority. If twice that does not fit between your position limits, states exist inside the box from which the setpoint can no longer be stopped in time — the block tells you so through warningSettingsMayCausePositionLimitOvershoot rather than refusing the settings.

Every parameter can be changed while the machine is running, and the change takes effect on the very next cycle. Nothing is refused: a value that is not usable as a limit is fixed into one and written back to the leaf, so there is one thing to look at — the limit set — and no status code to cross-check it against (§3).

This block bounds the setpoint, not the machine. The axis follows through a servo loop with a following error, so the mechanism will pass the limit by roughly that error. Back your limits off by the certified worst case.

Everything is per channel: an N-channel instance publishes Limits01..LimitsNN, and a six-axis stage can run channel 1 in PVAJ mode and channel 2 in VAJ mode. Every parameter named below lives under the channel’s folder — for channel 1 of an instance called pvajLimiter, positionUpperLimit is root/Control/pvajLimiter/Limits01/positionUpperLimit. The examples use channel 1.

1. Signals

input, inputDot, inputDDot, output, outputDot, outputDDot and outputDDDot are N-element arrays at the top of the module. enable, disable and isEnabled are single leaves that apply to the whole stage.

Inputs

Node Type Description
input double[N] Position setpoint. Ignored entirely while enableInput is 0. A non-finite value is held, not followed.
inputDot double[N] Read only while enableInputDot is 1. In PVAJ mode it is then the setpoint velocity, for the tracking lead; in VAJ mode it is the commanded velocity. With enableInputDot 0 the leaf is ignored in both modes.
inputDDot double[N] The setpoint acceleration, used only if enableInputDDot says it is real. Unused in VAJ mode.
disable bool Stage-wide. While set, every channel passes its input through and limits nothing.

Outputs

Node Type Description
output double[N] Limited position. Never outside the box. Reads zero in VAJ mode unless suppressPositionOutput is cleared.
outputDot double[N] Limited velocity.
outputDDot double[N] Limited acceleration.
outputDDDot double[N] The jerk applied this cycle.
isEnabled bool enable && !disable. Stage-wide; per-channel enables do not affect it.

Per-channel status, Limits0N/

Node Description
isLimiting One of the four limits clamped the output this cycle. Converging on the setpoint is not limiting.
positionLimitActive The position limiter is shaping this cycle.
velocityLimitActive The velocity limit is binding.
accelerationLimitActive The acceleration limit is binding.
jerkLimitActive The jerk limit is binding.
softEntryActive The state is outside a limit and only motion that makes the violation worse is forbidden (§4). Always false while disabled.
warningSettingsMayCausePositionLimitOvershoot The limit set cannot guarantee the box: a stop from velocityLimit does not fit in half of it. Fix the settings (§3).
warningNotAbleToStopBeforeLimit The current state can no longer be stopped inside the box — the guarantee is already lost. Only reachable by seeding a bad state or narrowing the limits under a moving setpoint.

Per-channel diagnostics, Limits0N/debug/

Indicative only; nothing here feeds the control law.

Node Description
trackingError input - output. Zero while disabled and in VAJ mode.
trackingErrorDot The setpoint velocity in use minus outputDot: inputDot where enableInputDot is set, the estimate differenced from input otherwise (zero in VAJ mode without enableInputDot). This is the tracking error in VAJ mode.
trackingErrorDDot The setpoint acceleration in use minus outputDDot: inputDDot where enableInputDDot is set, the estimate otherwise.
predictedStopPosition Where a full brake from the current state would come to rest, as an absolute position. Plot it next to output and the limits to see the braking margin.

State0N/

The same facts as the flags above, in the shape GainSchedulingPVALimiter published them, so existing dashboards keep resolving. Prefer the Limits0N/ flags in anything new.

Node Equals
active isLimiting
positionUpperLimitActive positionLimitActive, while the setpoint is in the upper half of the box
positionLowerLimitActive positionLimitActive, while it is in the lower half
velocityUpperLimitActive velocityLimitActive, while outputDot is positive
velocityLowerLimitActive velocityLimitActive, while outputDot is negative
accelerationUpperLimitActive accelerationLimitActive, while outputDDot is positive
accelerationLowerLimitActive accelerationLimitActive, while outputDDot is negative

There is no jerk counterpart — GainSchedulingPVALimiter had no jerk limit.

2. Parameters

All under Limits0N/, all writable while the loop runs. The names are the same on the tree and in C++.

The limit set

Node Default Description
positionLowerLimit -1.0 Lower travel limit.
positionUpperLimit 1.0 Upper travel limit.
velocityLimit 1.0 Maximum speed, symmetric.
accelerationLimit 10.0 Maximum acceleration, symmetric.
jerkLimit 100.0 Maximum jerk, symmetric.
maxAccelerationBraking 0.0 Elevated acceleration reserved for defending the travel limits. 0 inherits accelerationLimit.
maxJerkBraking 0.0 Elevated jerk for the same purpose. 0 inherits jerkLimit.

The block never assumes a unit: whatever input is in, velocityLimit is that per second, accelerationLimit per second squared and jerkLimit per second cubed. Linear or rotary, the arithmetic is the same.

Mode and behaviour switches

Node Default Description
enable 1 This channel’s own enable, ANDed with the stage-wide enable/disable. Set 0 to hold one axis out while the rest of the stage runs.
enableInput 1 1 = PVAJ mode, 0 = VAJ mode.
suppressPositionOutput 1 In VAJ mode, publish output as zero instead of the position the block integrates along the way. No effect in PVAJ mode.
enablePositionLimit 1 Enforce the travel limits. 0 bypasses them outright — the bounds become infinite and no position action is taken.
enableInputDot 0 inputDot carries a real velocity — read it. PVAJ mode: optional, with it off the block differences input instead. VAJ mode: required — with it off there is no velocity input and nothing to difference, so the command is zero.
enableInputDDot 0 inputDDot carries the real setpoint acceleration. Optional, PVAJ mode only, and independent of enableInputDot. Do not set it on an unconnected leaf — that declares the acceleration to be exactly zero and the block believes it.
suppressInputDDotEstimation 0 Take the setpoint acceleration as zero instead of estimating it from the input’s history. Set it for one reason only: a measured input whose noise the estimate would amplify.
feedforwardGain 0.0 How much of the braking-distance lead the tracking target is shifted by, 0..1. 1 tracks accurately; 0 keeps the setpoint inside the commanded amplitude. Values outside the range are clamped and written back.
enableSoftEntry 1 On enabling with the setpoint outside a limit, ratchet that limit to the state instead of driving the setpoint back inside at once (§4).
enableMaxBrakingAtLimits 0 Plan every limit approach with the braking authority, not just recovery: braking starts later and is harder. Does nothing while the braking values inherit the ordinary ones.
autoAdjustBraking 0 Raise maxAccelerationBraking / maxJerkBraking automatically until a stop from velocityLimit fits inside the box, and write the result back. See the warning in §3.

3. Configuring the limits

Nothing is refused. A value that is not usable as a limit is fixed into one, so there is one thing to look at — the limit set — and no status leaf to cross-check it against:

You write In force
velocityLimit or accelerationLimit = 0, negative, or non-finite no limit (infinite)
jerkLimit = 0, negative, or non-finite no jerk limit: 1e9 stands in, because the jerk is the block’s control input and an infinite value is not-a-number in its arithmetic
maxAccelerationBraking / maxJerkBraking = 0 or below the ordinary limit inherits or is raised to the ordinary limit, and written back to the leaf
positionLowerLimit > positionUpperLimit swapped
the two position limits equal, or either non-finite no box at all, even with enablePositionLimit set

A small but positive value is taken literally: velocityLimit = 1e-9 is a very slow axis, not a mistake the block can recognise — it will freeze the setpoint. 0 is what means “no limit”.

Edits take effect on the next cycle. Start-up fails only if the task period is unknown.

Check warningSettingsMayCausePositionLimitOvershoot after configuring. It is true when a stop from velocityLimit does not fit in half the box, which means states exist inside the box from which the setpoint could no longer be stopped in time. Three ways out: lower velocityLimit, raise accelerationLimit / jerkLimit, or reserve maxAccelerationBraking / maxJerkBraking explicitly. autoAdjustBraking does the last one for you — but it commands whatever authority the arithmetic demands, which may exceed what the drive can deliver, and authority the machine does not have is authority the guarantee does not have either. Read the two braking leaves back afterwards and check them against the worst-case deliverable figures.

4. Enabling with the setpoint outside a limit (soft entry)

This is about the moment you enable. A disabled block passes input straight through, so if the input sits outside the travel limits when you enable, the limiter starts from a state that is already outside the box. Soft entry, on by default (enableSoftEntry), decides what happens next.

Without it, the limiter treats the box as gospel and drives the setpoint back inside immediately, at the braking authority — the setpoint snaps to the limit whether the input wants that or not.

With it, the violated limit is ratcheted to the state: the box in force is widened just enough to contain where the setpoint already is, so the only motion that remains forbidden is motion that makes the violation worse. Two cases follow, measured on a ±1.0 box with the input parked at 1.5 at the moment of enabling:

The input then What output does
moves further from the limit (1.5 → 3.5) held where it was, at 1.500 — the setpoint does not follow the input further out
moves back toward the limit follows the input inward, and entered the box as the input did (at 0.944 in the measurement)

The ratchet re-tightens every cycle against the new position, so the setpoint can approach the limit but never retreat from it again. The moment the state is inside, softEntryActive clears and the real box applies in full.

While the state is outside, the velocity and acceleration limits are relaxed to whatever the state already carries, for the same reason: an inherited over-speed is not required to vanish instantly, only not to grow. Nothing about normal operation depends on any of this — every effective value equals the configured one whenever the state is inside the box.

The consequence to plan for: a setpoint outside the box stays outside while the input asks it to. Getting back inside is the caller’s business — command the input back — and the setpoint comes with it. softEntryActive tells you it is happening. Clear enableSoftEntry if you would rather the limiter recover by itself, accepting that the setpoint then moves against the input.

Enabling is not the only way in: resetState() can seed a state outside the box, and narrowing the travel limits underneath a moving setpoint leaves it outside. Soft entry handles all three the same way.

5. Enabling with a noisy input

§4 covered enabling with the setpoint outside the box. This one is about the derivatives it inherits.

While the limiter is disabled — the stage-wide enable cleared, disable set, or the channel’s own enable cleared — input passes straight through to output and the internal state is slaved to it, so enabling is bumpless in position. The derivatives it hands over come from differencing that input: outputDot is the first difference of input unless enableInputDot supplies a real velocity, and the acceleration and jerk are further differences.

On the first enabled cycle the block zeroes the inherited acceleration and jerk, and keeps the inherited velocity. Zeroing the acceleration is essential: differencing multiplies input noise by 1/dt², so noise of 1e-4 on the input at a 1 ms cycle arrives as an acceleration of 100 — ten times a typical accelerationLimit — and unwinding that takes |a| / jerkLimit seconds with the velocity integrating the whole way. Measured at jerkLimit = 100, accelerationLimit = 10, velocityLimit = 1 with that much noise, the output flew 625 away from the input before returning; with the acceleration zeroed the same case peaks at 0.056. The velocity is kept because it is mostly the input’s real velocity, and zeroing it would be a sudden stop when the limiter is enabled under a moving input.

That is why a noisy input still costs you something at enable. The velocity handed over is noise / dt — 0.1 for noise of 1e-4 at a 1 ms cycle — and it is real as far as the limiter is concerned: the setpoint sets off at that speed and then has to be brought back within accelerationLimit and jerkLimit, which moves the position by roughly v²/(2·accelerationLimit) before it settles. On a path, that is a deviation from the path.

So, for a clean hand-over:

  • Give the limiter a smooth input. Filter a measured setpoint upstream rather than feeding raw encoder or network-quantised values into input.
  • Better still, supply the velocity. Connect a filtered velocity to inputDot and set enableInputDot: no differencing happens at all, so there is no noise to inherit. Do the same for inputDDot with enableInputDDot if the filter provides it.
  • Enable while the input is settled where the application allows it. With the input at rest there is no velocity to inherit, noisy or not.
  • If the input is measured and cannot be filtered, set suppressInputDDotEstimation as well (§2), so the tracking lead does not differentiate that noise a second time.

Disabling is unconditional and immediate: the channel stops limiting that cycle and goes back to passing input through. Note that a disabled block bounds nothing, so a command beyond velocityLimit reaches the limiter unbounded and has to be braked back once it takes over — bring the command inside the envelope before re-enabling if that matters.

6. Tuning the tracking accuracy

The switches below change only how accurately the limited output follows an unconstrained input. None of them can affect the position guarantee — they all live in the tracking proposal, which is clamped into the box before it is applied.

enableInputDot / enableInputDDot

Measured settled error on a 0.2 amplitude, 1 Hz sine, with suppressInputDDotEstimation set so that each row shows what the two switches alone buy. Left at its default the estimate fills the acceleration in, and the first two rows collapse to the accuracy of the last two:

enableInputDot enableInputDDot Setpoint velocity Setpoint acceleration Error
false false first difference of input zero when suppressInputDDotEstimation is set, otherwise estimated 1.92e-2
true false inputDot zero when suppressed, otherwise estimated 1.81e-2
false true first difference of input, advanced by a·dt/2 inputDDot < 1e-6
true true inputDot inputDDot 8.0e-8

The last two rows are worth five orders of magnitude because the tracking lead is how far the input travels before it could come to rest — leaving its acceleration out pretends the input is coasting.

The two switches are independent claims about two separate leaves. Each says “this one carries the real value”, and each is honoured on its own: a source that publishes position and acceleration but no velocity is a usable combination (third row), where the differenced velocity is lined up with the supplied acceleration by half a step. enableInputDDot also wins over suppressInputDDotEstimation — an explicit claim is not second-guessed — which is the trap to know about: set it on an unconnected leaf and you have declared the acceleration to be exactly zero. The limiter then believes you and locks the setpoint’s acceleration to zero, which tracks the position perfectly on a moving input while publishing an outputDDot of zero. Leave both off if you only have a position.

It needs an exact value. An acceleration differenced from an input carrying 1e-3 of noise measured 6.53e-2, three times worse than leaving it off. Nothing is estimated here unless the estimate is left on (below), and the switch defaults to off for that reason: turn it on if the value is real, leave it off if it is estimated, unconnected or unknown.

feedforwardGain

The feedforward shifts the tracking target ahead by the braking distance at the setpoint’s own speed, cancelling the lag that “always be able to stop on the setpoint” would otherwise cost. feedforwardGain is how much of that lead is applied, and it is the feedforward’s only control: 0 is off, 1 is the full lead. The two ends are far apart, which is what makes the value in between worth tuning. Measured on a 0.5 amplitude sine at jerkLimit = 1745, accelerationLimit = 30, velocityLimit = 3.14, position input only:

gain 0.5 Hz: envelope excess / error / lag 1.0 Hz
0.00 (= off) −1.4e-5 / 5.4e-2 / 31 ms −3.7e-4 / 1.67e-1 / 51 ms
0.50 +8.6e-7 / 2.7e-2 / 15 ms −1.5e-4 / 8.6e-2 / 26 ms
0.75 +1.6e-5 / 1.4e-2 / 8 ms +4.7e-4 / 4.8e-2 / 14 ms
1.00 (= on) +4.6e-5 / 2.4e-3 / 0 ms +2.0e-3 / 2.1e-2 / 5 ms

Monotone and smooth throughout: roughly 7–12 ms of lag and a factor of 2–5 in overshoot per 0.25 of gain.

The landmark worth tuning to is where the envelope excess changes sign — around 0.5 at 0.5 Hz and 0.6–0.7 at 1 Hz here. Just below it the output never exceeds what was commanded while most of the lag reduction is still there. It moves with frequency, so it is an application tune, not a constant.

Prefer the derivatives where you have them. The overshoot exists because the lead is computed as how far the input travels before it could come to rest, which assumes the input is coasting when only input is available. A sine is decelerating into its peak, so the lead is overestimated exactly there. Supplying exact inputDot and inputDDot removes the cause instead of trading against it, and beats any gain below 1: the same measurement gives 1.3e-7 of tracking error, no lag, and zero envelope excess. Reach for the gain when only input is real.

The gain is bounded to [0, 1]. 0 is no lead, 1 is the full lead, and there is nothing useful past it: over-leading was measured — at gain 2, 5.0e-2 excess against 9.0e-2 error; by gain 5 the target is permanently clamped to the position limit, so the setpoint just parks at the box edge and stops following at all. Out-of-range values are clamped, not refused, so a bad config value leaves the limiter running on the nearest usable gain rather than on nothing. The clamp is applied by the setters and again at the top of every cycle — a control.xml or parameter-server write lands straight on the leaf without passing a setter — and the clamped value is written back, so feedforwardGain reads what is in force rather than what was asked for. A non-finite value becomes 0.

No gain can touch the position guarantee — the bound is a convenience, not the safety argument. Verified to gain 100 before the bound existed: the position never left the box by any amount, and the emitted motion gained no oscillation. The lead moves the tracking target only, and the target is clamped into the box before the braking-set test runs.

Gain 0: the feedforward off

feedforwardGain = 0 is the feedforward switched off outright — no lead is computed and the tracking target is the input itself. It costs a factor of 38 in tracking accuracy and buys exactly one thing: the output then stays inside the commanded amplitude envelope instead of leading it by the lead distance. On a 0.5 amplitude, 0.2 Hz sine, tracking error is 2.5e-2 off against 6.5e-4 at the full lead; envelope excess is −1.2e-6 off against +4.3e-6. That is the only reason the off end exists — for machines where the setpoint must not exceed what was commanded, quite apart from the position limits.

With enableInputDot and enableInputDDot both set the trade-off evaporates: 1.1e-8 of tracking error and −1.1e-12 of envelope excess. Better on both counts, so leave the gain at 1.0 there.

suppressInputDDotEstimation: the input’s acceleration, and the reversal transient without it

Set it for one reason only: to suppress noise on the input. By default the acceleration is estimated; setting this takes it as zero instead. The estimate is a second difference, so input noise reaches the lead multiplied by 1/dt² — and above roughly 1e-7 of jitter at a 1 ms cycle, taking the acceleration as zero is the better guess. Everywhere else — any computed input, and any input whose derivatives you supply — leave it clear. If the input is measured and you can filter it, do that and supply inputDot / inputDDot instead: that beats both settings at every noise level.

With only input connected and the acceleration taken as zero, the lead is computed as if the input were coasting. That is exact on a ramp, but it grows as the square root of the distance-to-stop, so its curvature is unbounded where the input’s velocity passes through zero. The setpoint answers with full jerk both ways, and outputDDot shows a spike at every velocity reversal — on a 0.0975 amplitude, 1 rad/s sine at jerkLimit = accelerationLimit = 10 π, the target’s acceleration one millisecond before the reversal reaches 0.127, more than the sine’s own 0.0975. With the gain at 0 the same reversal shows a longer, lower bump instead: the output then has to arrive at every instantaneous input position able to stop there, so it over-brakes into a reversal and re-accelerates only as the input opens a gap again.

By default the limiter supplies the missing acceleration from the input’s own history: the acceleration is the second backward difference of input, and the velocity is the first difference advanced by half a step (a · dt / 2), because a plain backward difference is the velocity at k − ½ and that half-step error is larger than the velocity itself in the last millisecond before a reversal. When enableInputDot is set and enableInputDDot is not, the acceleration is the difference of inputDot instead. Measured on the sine above, gain 1:

derivative source peak |aOut − aIn| cycles at ±J per five periods peak error
none 0.179 (184 % of aIn) 118 1.6e-4
backward differences, not aligned 0.056 (58 %) 21 2.5e-6
estimated (the default) 0.000 0 0 (locked)
exact inputDot + inputDDot 0.000 0 0 (locked)

The zero errors are the rest window locking onto the reference state once the derivatives are known; before it did, the estimator measured 3.0e-7 and exact derivatives 1.6e-9.

It is for computed inputs — a trajectory generator, an interpolated path, a kinematics output — not for measured ones. Differencing amplifies noise by 1/dt and 1/dt²: on a position carrying 1e-8 of noise at a 1 ms cycle the acceleration estimate is already 10 of noise against 0.1 of signal and the jerk sits on its limit most of the time. (From 1e-6 of noise upwards even exact derivatives no longer help at this jerk limit, because the position target itself jitters by e/dt³ ≈ 1000 against jerkLimit = 31 — the limiter is then smoothing a noisy setpoint, and the reversal is not the problem.) For a sensor-driven input, filter first and hand over the filter’s derivatives with enableInputDot / enableInputDDot. Off by default.

It does not change what the limits do at a step or a corner. A block wave is shaped by J, A and V alone and tracks identically with the estimator on or off; a triangle wave tracks identically to the exact-derivative case. Note that the lead itself overshoots a triangle’s corner by the braking distance at the ramp velocity — 0.058 at a ramp velocity of 0.5 with jerkLimit = accelerationLimit = 10 π, on and off the estimator alike and with exact derivatives too — because a lead is a prediction that the input keeps its velocity, and at a non-differentiable corner that prediction is wrong by exactly that distance. Use gain 0 where a setpoint may never exceed what was commanded.


7. Example configuration — PVAJ mode (position)

An axis driven by a trajectory generator that publishes a position only. Accurate tracking wanted, the travel limits guaranteed at an authority the drive can actually deliver. The limit values here are the block’s defaults, so on a fresh instance only the last three rows are edits:

Parameter Set to Why
positionLowerLimit -1.0 the travel, backed off by the worst-case servo following error (default)
positionUpperLimit 1.0 " (default)
velocityLimit 1.0 what the mechanism can deliver (default)
accelerationLimit 10.0 " (default)
jerkLimit 100.0 " (default)
maxAccelerationBraking 15.0 authority reserved for defending the travel, at worst-case deliverable figures — 0.0 by default, which inherits accelerationLimit
maxJerkBraking 1500.0 " — 0.0 by default, which inherits jerkLimit
feedforwardGain 1.0 accurate tracking — 0.0 by default, which is the feedforward off

enableInput and enablePositionLimit are already 1, which is PVAJ mode with the travel enforced, so they need no edit. Everything else can stay at its default too.

Connect input to the generator’s position output. enableInputDot and enableInputDDot are optional: leave them at 0 unless the generator really publishes a velocity and an acceleration on those leaves. With them off the block differences the input for the velocity and estimates the acceleration from its history, which is what keeps the lead accurate through a velocity reversal — so a position-only setpoint needs no extra configuration.

What to expect: output follows input closely and never leaves ±1.0; trackingError is the lag; isLimiting is false while merely converging on a new setpoint and true when J, A, V or the box actually binds; warningSettingsMayCausePositionLimitOvershoot reads false with these numbers — a stop from 1.0 at 15.0 needs 0.038 of travel, against the 1.0 of half-box available.

Two adjustments worth knowing:

  • feedforwardGain 1 lets the setpoint exceed the commanded amplitude slightly on a curved input. Set it to 0 where that must never happen, at the cost of tracking lag.
  • If the generator publishes exact derivatives, set enableInputDot and enableInputDDot — that is more accurate still than any estimate.

8. Example configuration — VAJ mode (velocity)

A conveyor driven by a velocity command, with no end stops. Only J, A and V are to be enforced; the position is meaningless and must not be published. The envelope is again left at the block’s defaults, so the three mode switches are the only edits:

Parameter Set to Why
enableInput 0 VAJ mode: input is not read at all, inputDot is the commanded velocity
enableInputDot 1 says the inputDot leaf carries that velocity — off, the leaf is not read and the command is zero
enablePositionLimit 0 no travel limits on this axis — and with the position output suppressed there is no published position for a limit to mean anything against
suppressPositionOutput 1 output reads zero rather than a position nobody commanded (default)
velocityLimit 1.0 the envelope the command is shaped into (default)
accelerationLimit 10.0 " (default)
jerkLimit 100.0 " (default)

Everything else can stay at its default.

Connect the velocity command to inputDot. Leave input unconnected — it is not read, and it may hold anything.

enableInputDot must be 1 here. It is the one switch that says the inputDot leaf carries a velocity; with it 0 the leaf is not read in either mode, and in this mode there is no input to estimate a velocity from, so the command is zero and the axis comes to rest. In PVAJ mode the same switch declares that the leaf carries a real setpoint velocity for the tracking lead. enableInputDDot, feedforwardGain and suppressInputDDotEstimation, by contrast, change nothing here: they shape the position tracking lead, which this mode does not have.

What to expect: outputDot is brought onto the command within accelerationLimit and jerkLimit without overshooting it, and is clipped to velocityLimit; outputDot, outputDDot and outputDDDot are a consistent triple; output reads 0; trackingErrorDot is the tracking error to watch. A command beyond velocityLimit is clipped, not refused, and a non-finite command is held rather than followed.

suppressPositionOutput is only about the published leaf — the block keeps integrating the position internally either way, and the trajectory is identical. Clear it if you want to see how far the axis has travelled; with no box in force that number is unbounded and drifts as far as the commanded motion takes it.

With end stops instead, set enablePositionLimit to 1 and give the travel: the box is then defended on the integrated position while the command is still followed in velocity, which is the reason to use this block rather than a rate limiter. Note that suppressing the output does not switch the box off — the two are independent, so with both set the box is enforced on a position output does not show. Clear suppressPositionOutput when you need to see that position, or read it in C++ with getIntegratedPosition().

9. Diagnosing

Symptom Likely cause
Output frozen after a limit edit A tiny positive J, A or V taken literally (§3). 0 would have meant no limit.
Output ignores the travel limits The two position limits are equal, or one is non-finite, which is no box at all (§3).
warningSettingsMayCausePositionLimitOvershoot true A stop from velocityLimit does not fit in half the box. §3.
warningNotAbleToStopBeforeLimit true The limits were narrowed under a moving setpoint, or a bad state was seeded. The guarantee is already lost; debug/predictedStopPosition says how far past the limit the setpoint will go.
Setpoint sits outside the box and stays there Soft entry doing its job — softEntryActive is true, and the input is still asking for it. §4.
Output lags badly feedforwardGain is 0, or enableInputDot is off on a generator that publishes a real velocity.
outputDDot spikes at every velocity reversal of the input suppressInputDDotEstimation is set, so the lead assumes the input coasts. Clear it, or supply inputDDot.
outputDDot reads zero on a moving axis enableInputDDot is set on an unconnected inputDDot, which declares the acceleration to be zero.
output reads zero in VAJ mode suppressPositionOutput, by design (§8).
Stops at a limit harsher than accelerationLimit enableMaxBrakingAtLimits is set, so approaches are planned at the braking authority.
Deviation from the path just after enabling A noisy input: the velocity inherited from differencing it is real to the limiter, and removing it moves the setpoint. Filter the input, or supply inputDot. §5.
Module refuses to start The task period is unknown at start-up.