StateController

StateController closes a loop on the whole state vector at once, with an independent gain per state, rather than on a single error signal.
3.30–3.34

StateController closes a loop on the whole state vector at once, with an independent gain per state, rather than on a single error signal. Given a pole placement or LQR design done offline, it gives a response no cascade of single-loop controllers can. Pair it with Observer when the states you need are not all measured.

It is purely algebraic — no integration, no memory. Its output depends only on this cycle’s inputs, and it has no integral action, so a constant disturbance leaves a constant error. Add a PID alongside it if you need that error removed.

flowchart LR
    i1(["stateVector — the measured or estimated states"]) --> B["StateController"]
    B --> o1(["stateErrorVector — error per state"])
    B --> o2(["controlOutputVector — the control output"])
    B --> o3(["controlOutput — first element, as a single value"])

The control law is $u = -K(x - x_d)$, with $x_d$ = stateTargetVector and $K$ = controllerGainVector. The closed-loop poles are set by your plant and that gain together, and the design is done offline — this block only applies the result. controllerGainVector at zero makes the block inert.

controllerGainVector is written as one flat list, filled column by column. For a 4-state 2-input system the first four values are the gains for the first input.

Both limiters default to zero at both ends. Switching either on without setting its bounds forces the limited quantity to zero — see Limits and errors. The block has no enable and no disable.

Signals

Inputs

Path Unit Range Description
stateVector model state units unbounded, or the input limiter’s bounds The measured or estimated state, one element per state. While the input limiter is on, this path is overwritten with the clamped value, so a trace shows the limited state and not what was linked in.

Outputs

Path Unit Description
stateErrorVector model state units The state error, one element per state: stateVector minus stateTargetVector. The most useful signal for confirming the controller sees what you think it does.
controlOutputVector actuator units The control output, one element per input. It appears at full value on the first cycle — there is no ramp, because the block has no memory.
controlOutput actuator units Element 0 of controlOutputVector, as a single value, for the common single-input case.

Parameters

Path Unit Default Range Effect
controllerGainVector actuator units per state unit all zeros any The gain, and the whole of the design. States × inputs, column-major. Comes from your offline pole placement or LQR. Zero makes the block inert.
stateTargetVector model state units all zeros any The state to drive toward. This is configuration, not a linked signal — it cannot be driven from another block, only written directly.
inputLimiterEnable - false - Bounds each element of stateVector before the error is formed. Set the bounds before switching this on.
inputLimiterMin model state units all zeros below inputLimiterMax Lower bound per state.
inputLimiterMax model state units all zeros above inputLimiterMin Upper bound per state. Equal to the minimum — as both defaults are — forces every state to that value.
outputLimiterEnable - false - Bounds each element of controlOutputVector. This is the only thing that limits what reaches the actuator.
outputLimiterMin actuator units all zeros below outputLimiterMax Lower bound per input.
outputLimiterMax actuator units all zeros above outputLimiterMin Upper bound per input. Same trap as the input limiter.

All eight parameters are persistent and survive a controller restart. The input and outputs do not. No parameters exist below this block.

Setup

  1. Do the design offline. Choose the closed-loop poles you want from your plant model and compute the gain. This block applies a gain; it cannot help you find one.

  2. Read the length of stateVector from the parameter tree and confirm it matches your model’s state count. It is fixed when the machine is built.

  3. Leave controllerGainVector at zeros, and leave both limiter enables false.

  4. Link stateVector from your measurement or from an observer’s estimate. Confirm each element on a trace against what you expect it to be — a state in the wrong slot is the most common wiring error and produces a plausible, wrong controller.

  5. Set stateTargetVector to the state you want held. Confirm stateErrorVector reads the difference you expect, element by element.

  6. Set outputLimiterMin and outputLimiterMax to what the actuator can accept, then set outputLimiterEnable true. Confirm controlOutputVector is still zero — the gain is still zeros.

  7. Write controllerGainVector from your design, column by column. For a single-input system it is simply one value per state, in state order.

    Step 7 produces full control output on the very first cycle. The block is algebraic, so there is no ramp and no transient — if the state is away from the target when you write the gain, the actuator gets the whole commanded output at once. Write it with the axis at rest and near its target, and rely on step 6’s limiter.

  8. Confirm the closed loop behaves as the offline design predicted. If it does not, check step 4 and the column order in step 7 before touching the gain.

Tuning

There is nothing to tune here in the usual sense. The gain comes from an offline design, and this block applies it.

  1. Verify the state wiring before the gain, per Setup step 4. Every diagnosis below assumes the states are in the right slots.
  2. Confirm the sign. A gain of the wrong sign is positive feedback, and the symptom is an axis that runs away as soon as the gain is written. Set the output limiter first so this is survivable.
  3. Check the column order with a deliberately asymmetric gain on a multi-input system. A symmetric test gain cannot tell a transposed matrix from a correct one.
  4. Scale the whole gain vector down by a factor of 2 or 4 for the first trial, then work back up to the designed value. This is the safe way to approach a design you have not run on hardware.
  5. Compare the closed-loop response against your offline prediction. A mismatch in shape means the state wiring or the column order; a mismatch in magnitude means the gain or the plant model.
  6. Accept the steady-state error, or add integral action elsewhere. This block has none and cannot acquire any.
  7. Use the input limiter only to protect against a bad estimate — a wild value from an observer that has not converged, say. It is not a tuning knob, and it changes what the controller sees.
  8. Nothing here needs re-checking after a task-rate change. The block does not integrate and has no task-rate-dependent limit.

Control output against state error at gain 2, with three output limits: theunbounded line runs straight through, the plus-or-minus 3 limit clips beyond anerror of 1.5, and the plus-or-minus 1 limit clips beyond0.5.

Read where each line flattens as the point the output limiter took over.

Symptom Cause Action
Everything reads zero Expected: controllerGainVector defaults to zeros Write the gain from your design
Every state reads zero and the controller drives hard The input limiter is on with its default bounds of zero, so it forces every state to zero Set inputLimiterMin and inputLimiterMax, or switch the limiter off
controlOutputVector is stuck at zero with a non-zero gain and error The output limiter is on with its default bounds of zero Set outputLimiterMin and outputLimiterMax
The axis ran away as soon as the gain was written The gain has the wrong sign — this is positive feedback Negate the gain; set the output limiter before retrying
The response is nothing like the offline design A state is in the wrong slot, or the gain is transposed Check stateErrorVector element by element, then the column order
One state seems to affect the wrong actuator controllerGainVector is transposed Rewrite it column-major: states × inputs
A constant error never goes away Expected: this block has no integral action Add a PID alongside it, or add an integrating state to your model
stateVector on a trace does not match what is linked into it Expected: the input limiter overwrites this path in place Switch the input limiter off, or read the source signal instead
The output jumped to full value the instant the gain was written Expected: the block is algebraic and has no ramp Write the gain at rest, near the target, with the output limiter set
The output is noisy The state estimate is noisy, and the gain passes it straight through Fix the estimate; this block has no filtering
The response changed after a task-rate change Not possible — this block is task-rate independent Look for the change elsewhere in the loop
A limiter behaved oddly with the minimum above the maximum The minimum wins, so the value is pinned at the minimum Keep every minimum below its maximum
controlOutput disagrees with controlOutputVector Not possible — it is element 0 of that vector Check you are reading the element you mean
A non-numeric value appeared and then cleared itself Expected: the block holds no state, so it recovers as soon as the input is clean Fix the source
You need a different number of states Not possible — the dimensions are fixed when the machine is built It needs a configuration change

A starting point for a two-state axis — position and velocity — with one actuator input, on any task rate. The gain is scaled to a quarter of a designed value for the first trial:

controllerGainVector = 25.0, 1.5
stateTargetVector    = 0.0, 0.0
outputLimiterEnable  = true
outputLimiterMin     = -10.0
outputLimiterMax     = 10.0
inputLimiterEnable   = false

Work up to the designed gain as Tuning step 4 describes. This is a starting point, not a final tuning.

Limits and errors

Limit Set by What happens Reported
Both limiters' bounds Nothing Not checked, and both default to zero at each end. Enabling a limiter without setting its bounds forces the limited quantity to zero. Set the bounds first, always Not reported
Limiter minimum below maximum Nothing Not checked. With the minimum above the maximum the value is pinned at the minimum Not reported
controlOutputVector outputLimiterMin, outputLimiterMax while enabled Clamped per element. With the limiter off it is unbounded — whatever the gain and error produce reaches the actuator Not reported
stateVector inputLimiterMin, inputLimiterMax while enabled Clamped per element, and the input path is overwritten with the clamped value Not reported
Closed-loop stability Nothing Not checked, and cannot be — the block knows nothing about your plant. A gain that destabilises the machine is accepted without comment. Your offline design is the only safeguard Not reported
Gain element order Fixed Column-major, always. A row-major list silently produces a transposed gain Not reported
Steady-state error Inherent There is no integral action, so a constant disturbance leaves a constant error Not reported
stateTargetVector Fixed Configuration only. It cannot be driven from another block Not reported
Dimensions Machine configuration Fixed when the machine is built. Read the array lengths to discover them Not reported
Block state None The block has no memory, so there is nothing to reset and nothing survives a stop Not applicable

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).