SetpointGenerator
SetpointGenerator turns a step change in a target into a smooth profiled move.7 minute read
SetpointGenerator turns a step change in a target into a smooth profiled
move. Write a new number to input and the output travels there while
respecting velocity, acceleration and jerk limits — and publishes the matching
velocity and acceleration as it goes, so a feedforward path can use them.
The move time is computed, not configured. You give it the limits; it works out the fastest move that respects them.
flowchart LR
i1(["input — the target value"]) --> B["SetpointGenerator"]
i2(["timeScaleFactor"]) --> B
i3(["timeScaleFactorDot"]) --> B
B --> o1(["output — profiled position"])
B --> o2(["outputDot — velocity"])
B --> o3(["outputDDot — acceleration"])
B --> o4(["done"])
B --> o5(["numTargets — how many are queued"])
timeScaleFactorstretches the profile’s clock while it runs — 0.5 makes the move take twice as long at half the velocity and a quarter of the acceleration. That is what a jog-override wheel needs.timeScaleFactorDotlets the override itself be turned smoothly.
All three limits default to 0, and a generator with a zero limit refuses
every target. Nothing moves, an error is logged once, and no output changes
to tell you why. Set maxVel, maxAcc and maxJerk before anything else.
An unachievable target is discarded, not clamped. The generator keeps the previous target and carries on. That is the safe failure, but it is silent apart from that one log line.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
signal unit | unbounded | The target. A change starts a new move; writing the same value again does nothing. If the move cannot be profiled with the current limits, the target is discarded and the previous one kept. |
timeScaleFactor |
- | above 0 | Stretches or compresses the profile’s clock while it runs. 1 is normal speed, 0.5 is half speed, 0 freezes the move. Not checked. |
timeScaleFactorDot |
1/s | any | How fast timeScaleFactor is itself changing, so an override can be turned without a jolt. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
signal unit | The profiled value on its way to the target. |
outputDot |
signal unit per second | Its velocity — feed this to a feedforward path. |
outputDDot |
signal unit per second² | Its acceleration. |
done |
- | True when the profile has finished. It starts true, so a freshly started generator reports done before it has done anything. |
numTargets |
- | How many targets are queued. Writing a new target while a move is running adds to the queue rather than replacing it. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
maxVel |
signal unit per second | 0.0 | above 0 | Velocity limit. A zero here makes every target be refused. Negative values are used as their magnitude and written back. |
maxAcc |
signal unit per second² | 0.0 | above 0 | Acceleration limit. Same treatment. |
maxJerk |
signal unit per second³ | 0.0 | above 0 | Jerk limit. Only used by the jerk-limited profile, but still needs a value. |
faderType |
- | 0 | 0 or 1 | Which profile shape. See the table below. |
delayTime |
s | 0.0 | 0 upward | How long to wait after a new target before the move starts. |
applyTimeScaleToDelay |
- | — | - | Whether timeScaleFactor stretches the delay as well as the move. |
maxTargets |
- | 10 | 1 upward | How many targets may be queued at once. |
faderType |
What the machine does | Parameters that matter |
|---|---|---|
0 QUINTIC |
Position, velocity and acceleration all start and end at zero. The smoothest shape, and the default | maxVel, maxAcc |
1 JERK_LIMITED |
Reaches the acceleration and velocity limits and holds them, so the move is faster for the same limits, with a slightly harsher start | all three limits |
All are persistent and survive a controller restart. No parameters exist below this block.
Setup
-
Set
maxVel,maxAccandmaxJerkfirst. They default to 0, and a generator with a zero limit refuses every target you give it. -
Leave
faderTypeat 0 for a first pass — the quintic profile is the gentler of the two. -
Have your application seed the generator at the machine’s current position before enabling anything downstream, so the first move does not start from zero.
-
Write a small target to
inputand watchoutputtravel there. Confirmdonegoes false at the start and true at the end.Step 4 moves the machine. The generator will use the full velocity and acceleration you configured, immediately. Start with a small target and limits well below the machine’s capability.
-
Check
outputDotpeaks at or belowmaxVel, andoutputDDotat or belowmaxAcc. -
Write a second target while the first move is running and watch
numTargets. The moves queue rather than replacing each other.
Tuning
- Set the three limits from the machine, not from the move you want. They are what the mechanics can take.
- Choose the profile shape from what the machine minds more. Quintic is smoother and slower; jerk-limited reaches the limits and holds them, so it is faster for the same numbers.
- Time a move and compare it against what the limits imply. If it is slower than expected, the quintic shape is the reason — switch to jerk-limited.
- Use
delayTimewhen several axes should start together after a common trigger. - Use
timeScaleFactorfor a speed override. Ramp it withtimeScaleFactorDotrather than stepping it, or the move’s velocity jumps. - Raise
maxTargetsif your application streams targets faster than the moves complete. WatchnumTargetsto see whether the queue is filling. - Nothing here needs re-checking after a task-rate change — but note the block captures the task period once at startup, so if the rate changes while running, restart the controller.
Read how much of the move is spent at constant speed off the straight section in the middle.
| Symptom | Cause | Action |
|---|---|---|
| Nothing moves and no output changes | A limit is 0 — the default — so every target is refused | Set maxVel, maxAcc and maxJerk |
| A new target had no effect | Either it was refused, or it is the same value as the last one | Check the limits; the generator only reacts to a change |
| An error about invalid settings appeared once | The limits cannot produce a valid profile | Fix the limits; the previous target is still in force |
done reads true before the first move |
Expected: it starts true | Wait for it to go false after a target is written |
| The move is slower than the limits suggest | Expected with the quintic profile, which eases throughout | Set faderType to 1 |
| The start of a move is harsher than expected | Expected with the jerk-limited profile, which pushes to its limits | Set faderType to 0 |
| The move takes the wrong time after a task-rate change | The block captured the task period at startup | Restart the controller |
| A limit reads back as a positive number after writing a negative one | Expected: the magnitude is used | Write positive values |
| The move froze part-way | timeScaleFactor is 0 |
Set it above 0 |
| The velocity jumped when the override was changed | timeScaleFactor was stepped |
Ramp it, using timeScaleFactorDot |
| Targets are being dropped | The queue is full — check numTargets against maxTargets |
Raise maxTargets, or write targets less often |
| The first move went to the wrong place | The generator was not seeded at the machine’s position | Have the application seed it before enabling |
| I cannot change the jerk-limited profile’s shape | Those two shape factors are not exposed | Only the two profile types are selectable |
| I need this on several axes | Not possible — this block is single channel | Use one instance per axis |
A starting point for an axis that may move at 0.5 units per second:
maxVel = 0.5
maxAcc = 5.0
maxJerk = 50.0
faderType = 0
delayTime = 0.0
maxTargets = 10
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
maxVel, maxAcc, maxJerk above 0 |
Nothing | Not checked, and all three default to 0. A zero limit makes every target be refused | Logged once when the settings first become invalid; no output changes |
| Target achievability | Checked on every change | A target that cannot be profiled is discarded and the previous one kept — the generator keeps running on the last valid profile | Logged once on the transition. No output flag |
output |
The profile | Reaches the target exactly. Velocity and acceleration stay inside their limits | Not reported |
| Limit signs | Fixed | Negative limits are used as their magnitude and written back | Read the values back |
timeScaleFactor |
Nothing | Not checked. 0 freezes the move and a negative value is not guarded | Not reported |
| Profile shape factors | Not exposed | The jerk-limited shape’s two factors are fixed and cannot be configured | Not applicable |
| Task period | Captured at startup | Read once when the controller starts. A task-rate change while running is not picked up and the move takes the wrong time | Not reported |
| Target queue | maxTargets |
Targets written while a move is running are queued, up to this depth | numTargets |
| Channel count | Fixed | One channel per instance, always | Not reported |
The block logs one error when the limit set first becomes invalid. Every other condition above shows as a value on a trace, or not at all.
Verified against motorcortex-control3 3.30.0 (bc348fd).