HardLimitVector
HardLimitVector limits the length of a vector rather than each of its components.6 minute read
HardLimitVector limits the length of a vector rather than each of its
components. When the vector is too long every component is scaled down by the
same factor, so the direction is preserved exactly and only the size
changes.
That is why it exists. Clamping a Cartesian velocity component by component turns a diagonal move into a different diagonal — the axis that reaches its bound first stops growing while the others carry on, and the commanded direction rotates. Scaling the whole vector keeps the direction and just slows the move down.
flowchart LR
i1(["input — the vector's components"]) --> B["HardLimitVector"]
i2(["disable"]) --> B
B --> o1(["output — scaled or unchanged vector"])
B --> o2(["isLimiting"])
B --> o3(["isEnabled"])
upperLimitis the maximum length of the whole vector, as a single number — not a bound on any one component. While the length is inside the limit the output is the input exactly. Beyond it, every component is multiplied byupperLimit÷ the actual length.
Every component must share the same unit. The block computes one length across all of them, so a mixed vector — a pose with metres in some elements and radians in others — has a length with no physical meaning. Use one instance per unit group.
There is no lower limit, and there cannot usefully be one: a zero-length vector has no direction to grow in.
The block ships switched off. enable defaults to false.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The vector’s components, one element per channel. All of them must share a unit. The channel count is fixed by the machine configuration. |
disable |
- | - | True bypasses the limiter: every component passes straight through. Use it for a runtime override from a supervisor; use enable for the configured intent. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The vector, scaled down uniformly if it was too long and unchanged otherwise. Its direction always matches input. Equals input exactly while bypassed. |
isLimiting |
- | True while the vector is being scaled. A single value for the whole vector, not one per channel — the vector is limited as a whole. |
isEnabled |
- | True when enable is true and disable is false. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
enable |
- | false | - | False bypasses the limiter. Set this, or the block does nothing. |
upperLimit |
signal unit | 1 | 0 upward | The maximum vector length, as a single number for the whole vector. 0 collapses the vector to zero. Not checked — a negative value points the output the opposite way. |
Both are persistent and survive a controller restart. No parameters exist below this block.
Setup
-
Confirm every element of
inputcarries the same unit. If they do not, this block cannot limit them together — split them into separate instances. -
Set
upperLimitto the largest vector length the consumer can accept — for a Cartesian velocity, the maximum tool speed. -
Leave
enablefalse and confirmoutputmatchesinputelement for element. -
Set
enabletrue. Command a vector well inside the limit and confirmoutputstill matchesinputexactly andisLimitingreads false. -
Command a vector longer than the limit. Confirm
isLimitinggoes true, and check the ratios between the output components are unchanged from the input’s.Step 5 is the check that direction is preserved. If the ratios change, something else in the chain is clamping components individually — that is exactly the behaviour this block exists to replace.
-
Confirm the output’s length equals
upperLimitwhile limiting.
Tuning
There is nothing to tune beyond the one bound.
- Set
upperLimitfrom the consumer, not from the signal. For a tool velocity it is the machine’s rated speed; for a force it is what the structure can take. - Watch
isLimitingduring normal operation. Persistent limiting means something upstream is commanding more than the machine can deliver, and that is usually the real problem. - If individual components also need bounds — a per-axis speed limit as well
as a tool speed limit — add a
Limiterafter this one. Order matters: this block first keeps the direction, and the per-component clamp afterwards catches anything still out of range. - Nothing here needs re-checking after a task-rate change.
Read each limit 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 |
| The output points the opposite way from the input | upperLimit is negative |
Set it positive |
| The output collapses to zero whenever the input moves | upperLimit is 0 |
Set a positive limit |
| The direction changes when the limit bites | Not possible in this block — something downstream is clamping components individually | Check what follows this block |
| The limit bites at a length that makes no sense | The vector mixes units, so its computed length is not physical | Use one instance per unit group |
| The output stepped when the block was enabled | Expected: the block is memoryless and clamps at once | Enable while the vector is inside the limit |
| One component is still too large | Expected: this block bounds the whole vector’s length, not any one component | Add a per-component limiter after it |
isLimiting is a single value, not one per channel |
Expected: the vector is limited as a whole | Use a per-component limiter if you need per-channel flags |
| Limiting is persistent during ordinary motion | The limit is too low, or something upstream is over-commanding | Check the command before raising the limit |
| A non-numeric component passed straight through | Expected: the length comparison fails and the block copies through | Fix the source; the block holds no state and recovers at once |
| You need a minimum length as well | Not available — a zero-length vector has no direction to grow in | Handle it upstream |
A starting point for bounding a three-axis Cartesian velocity to 0.25 m/s:
enable = true
upperLimit = 0.25
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
upperLimit non-negative |
Nothing | Not checked. A negative value scales every component by a negative factor, so the output points the opposite way at the limit’s magnitude | Not reported |
upperLimit = 0 |
Fixed | Any non-zero vector is collapsed to zero | isLimiting reads true |
output length |
upperLimit while enabled |
Bounded exactly. Unbounded while enable is false |
isLimiting, one flag for the vector |
| Individual components | Nothing | Not bounded. A long vector along one axis is scaled, but nothing caps any single component on its own | Not reported |
| Unit consistency | Nothing | Not checked, and cannot be. A vector mixing units has a length with no physical meaning | Not reported |
| Lower limit | Not available | There is none, by design: a zero-length vector has no direction to scale toward | Not applicable |
| Enabling | Fixed | The block is memoryless, so a vector already too long is scaled on the first enabled cycle — a step | Not reported |
| Non-numeric input | Nothing | Passes through unchanged. The block is stateless and recovers immediately | 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.30.0 (bc348fd).