SmoothStop
SmoothStop brings a moving axis to a controlled standstill on command.9 minute read
SmoothStop brings a moving axis to a controlled standstill on command. When
you raise gotoStandstill it takes over from the live motion reference, follows
a jerk-limited braking profile down to zero velocity, and holds there. Release
the command and it fades back to the live reference.
The stop is bounded three ways at once — by deceleration, by jerk, and by whichever of a time budget or a distance budget runs out first. If the natural profile would exceed a budget, the block computes a shorter one rather than letting the stop overrun.
flowchart LR
i1(["u — live position reference"]) --> B["SmoothStop"]
i2(["uDot — live velocity reference"]) --> B
i3(["uDDot — live acceleration reference"]) --> B
i4(["gotoStandstill"]) --> B
i5(["resetStandstill"]) --> B
B --> o1(["y — position output"])
B --> o2(["ydot — velocity output"])
B --> o3(["yddot — acceleration output"])
B --> o4(["yss — braking profile position"])
B --> o5(["yssDot — braking profile velocity"])
B --> o6(["yssDDot — braking profile acceleration"])
B --> o7(["smoothStopActive — per channel"])
B --> o8(["isAtStandstill"])
B --> o9(["standstillSwitchIsOn"])
B --> o10(["standstillSwitchIsOff"])
Stopping distance from velocity $v$ with deceleration $a$ is roughly $v^2/2a$, and jerk rounds the corners at each end. The stop takes about $v/a + a/j$ seconds.
maxStopJerkFactoris the fraction of the stop spent ramping the deceleration, from 0 (a pure trapezoid, sharpest) to 0.5 (a pure triangle, gentlest).
Both limits default to zero, which means “stop instantly”. With
smoothStopAcc and smoothStopJerk at 0 the block jumps the output to where
the axis was and holds it — a hard stop from a block called SmoothStop. Set
both before you use it.
gotoStandstill is a level, not a pulse. Hold it true and the axis stays
stopped; release it and the block fades back to the live reference.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
u |
m or rad | unbounded | The live position reference the axis normally follows. One element per channel. |
uDot |
m/s or rad/s | unbounded | The live velocity reference. |
uDDot |
m/s² or rad/s² | unbounded | The live acceleration reference. |
gotoStandstill |
- | - | Raise it to begin stopping; hold it to stay stopped; release it to fade back to the live reference. A level, and one value for the whole block. |
resetStandstill |
- | - | Makes the changeover between the live reference and the braking profile happen instantly rather than fading. Use it when a fade would itself be a disturbance. |
Outputs
| Path | Unit | Description |
|---|---|---|
y |
m or rad | The position to follow — the live reference, the braking profile, or a fade between them. This is what the axis should track. |
ydot |
m/s or rad/s | The matching velocity. Note the lower-case path name, where the input is uDot. |
yddot |
m/s² or rad/s² | The matching acceleration. |
yss |
m or rad | The braking profile’s own position, whether or not it is currently selected. Useful for watching a stop that has not been engaged. |
yssDot |
m/s or rad/s | The braking profile’s velocity — watch this to see the stop shape. |
yssDDot |
m/s² or rad/s² | The braking profile’s acceleration. |
smoothStopActive |
- | True per channel while that channel’s braking profile is still running. |
isAtStandstill |
- | True when every channel has finished stopping and is at rest. One value for the whole block. |
standstillSwitchIsOn |
- | The changeover has fully reached the braking profile. |
standstillSwitchIsOff |
- | The changeover has fully reached the live reference. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
smoothStopAcc |
m/s² or rad/s² | 0.0 | above 0, per channel | Deceleration limit. 0 means an instant stop. Higher stops sooner and harder. Negative values are used as their magnitude. |
smoothStopJerk |
m/s³ or rad/s³ | 0.0 | above 0, per channel | Jerk limit — how fast the deceleration may build. 0 means an instant stop. Higher gives sharper corners. |
maxStopTime |
s | 0.0 | at least one task period, per channel | The longest the stop may take. If the natural profile would exceed it, the block computes a harder one that fits. Corrected up to one task period if set lower. |
maxStopDistance |
m or rad | 0.0 | 0 upward, per channel | The furthest the axis may travel while stopping. Same treatment as the time budget. |
maxStopJerkFactor |
- | 0.0 | 0 – 0.5, per channel | Only used when a budget forces a recomputed profile. 0 gives the sharpest stop that fits, 0.5 the gentlest. Corrected silently if out of range. |
startAtStandstill |
- | false | - | When true, the block comes up stopped on the first cycle instead of following the reference. |
All six are persistent and survive a restart, and the first five are per channel.
One parameter lives below this block: the changeover’s fade time, in the
standstillSwitch sub-tree. See soft-switch.md.
Setup
-
Set
smoothStopAccandsmoothStopJerkfirst. Both default to zero, which makes the block stop the axis instantly. Start with the deceleration the machine uses for a normal decelerating move, and a jerk about ten times that. -
Set
maxStopTimeandmaxStopDistancefrom the machine’s real safety budget — how long and how far the axis may keep moving after a stop is commanded. -
Set
maxStopJerkFactorto 0.25 as a starting point. It only matters when a budget forces a shorter profile. -
Link
u,uDotanduDDotfrom the motion source, andy,ydot,yddotto whatever follows. Confirm the outputs track the inputs exactly withgotoStandstillfalse. -
With the axis moving slowly, raise
gotoStandstill. WatchyssDotcome down to zero andisAtStandstillgo true.Step 5 stops a moving machine. Do it at low speed first, with the axis clear, and confirm the distance travelled is inside your budget before trying it at full speed.
-
Measure the stop: time it, and measure how far the axis travelled. Compare both against
maxStopTimeandmaxStopDistance. -
Release
gotoStandstilland confirm the outputs fade back to the live reference without a bump.
Tuning
- Set the deceleration from the machine — what the mechanics, the drive and the load can take. This is a physical limit, not a preference.
- Set the jerk from what the structure tolerates. Lower jerk gives rounder corners and a longer stop; higher jerk stops sooner and shakes more.
- Check the resulting stopping distance at full speed: roughly $v^2/2a$ plus a little for the jerk ramps. If it exceeds the budget, either raise the deceleration or accept that the block will override your limits.
- Watch
yssDoton a trace during a stop. It should fall smoothly to zero with no overshoot and no residual crawl. - If a budget is forcing a recomputed profile, the actual deceleration and jerk used will be higher than what you configured — and the block does not publish them. Infer them from the trace.
- Use
maxStopJerkFactorto shape those forced stops: lower for the shortest stop that fits, higher for the gentlest. - Set the changeover fade time in the
standstillSwitchsub-tree so releasing the stop is smooth. The engagement is instant by design — the profile starts exactly where the axis is, so there is nothing to fade.
Read the deceleration off the slope of the straight section and the jerk off how rounded the corners are.
| Symptom | Cause | Action |
|---|---|---|
| The axis stops dead instead of smoothly | smoothStopAcc or smoothStopJerk is 0 — the default |
Set both to real values |
| The stop is harsher than configured | A budget forced a shorter profile | Raise maxStopTime or maxStopDistance, or accept it |
| The stop overshoots the distance budget | The budget branch cannot fully compensate when the axis is still accelerating at the moment of the stop | Command the stop from a steady speed where possible, and leave margin |
| A jolt at the start of the stop | The axis was accelerating when the stop was commanded, and the recomputed profile starts from zero acceleration | Leave margin in the budgets so the natural profile is used |
| The stop takes longer than expected | Jerk too low, so the corners dominate | Raise smoothStopJerk, or lower maxStopJerkFactor |
| The machine shakes during the stop | Jerk too high | Lower smoothStopJerk, or raise maxStopJerkFactor |
isAtStandstill never goes true |
One channel has not finished — it needs every channel stopped | Check smoothStopActive per channel |
| The axis crawls instead of settling exactly at zero | Not possible — the profile lands on exactly zero by construction | Check what consumes y |
| A bump when the stop is released | The changeover fade is too fast | Lengthen the fade time in the standstillSwitch sub-tree |
| A bump when the stop is engaged | Expected: engagement is instant, but the profile starts where the axis is, so it should be seamless. A bump means u and y had already diverged |
Check the outputs were tracking before the stop |
| The changeover jumps instead of fading | resetStandstill is being held true |
Release it |
| The axis comes up stopped after a restart | Expected: startAtStandstill is set |
Clear it if the axis should follow the reference immediately |
| One channel stops differently from the others | Expected: all five limits are per channel | Check every element |
| I cannot tell what deceleration is actually being used | The block does not publish the limits it computes when a budget binds | Measure the slope of yssDot on a trace |
| A non-numeric value appeared and will not clear | There is no reset input | Restart the controller |
A starting point for an axis that decelerates at 2 units/s² and must stop within half a second and a quarter of a unit:
smoothStopAcc = 2.0
smoothStopJerk = 20.0
maxStopTime = 0.5
maxStopDistance = 0.25
maxStopJerkFactor = 0.25
startAtStandstill = false
This is a starting point, not a final tuning. Work Setup step 6 with your own machine.
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
smoothStopAcc, smoothStopJerk above 0 |
Nothing | Not checked, and both default to 0, which makes the stop instant. Negative values are used as their magnitude | Not reported |
| Stop time | maxStopTime |
If the natural profile is longer, a harder profile is computed to fit. Corrected up to one task period if set below it | Not reported; the actual limits used are not published |
| Stop distance | maxStopDistance |
Same treatment. Corrected up to 0 if set negative | Not reported |
maxStopJerkFactor within 0 – 0.5 |
Fixed | Corrected to the nearest edge | Silently; read it back |
| Starting acceleration | Not handled in the budget branch | When a budget forces a recomputed profile, the axis’s acceleration at the moment of the stop is ignored, so the deceleration begins with a step | Not reported |
| Actual deceleration and jerk | Not published | When a budget binds, the limits actually used are higher than the configured ones and cannot be read from the parameter tree | Not reported |
y, ydot, yddot |
Nothing | Unbounded — the block shapes the stop, it does not bound the reference | Not reported |
| Block state | Nothing | The latched profile and the changeover state are not cleared at start. There is no reset input | Not reported |
| Non-numeric input | Nothing | Not guarded anywhere | Not reported |
| Channel count | Machine configuration | Fixed once the controller starts | Not reported |
The block raises no errors or warnings. It writes one debug log line per channel at the moment a stop is triggered, recording the position, velocity and acceleration it latched. Every failure above shows as a value on a trace, not as a message.
Verified against motorcortex-control3 3.30.0 (bc348fd).