Control systems in the command window#
Every name on this page is typed at the command window (or passed to
ICoreBlocks --console "<line>") and answers what MATLAB's Control System
Toolbox answers for the same arguments. A model is a VALUE here — it goes into
a variable, it does arithmetic, it prints the way MATLAB prints it — so
analysis and design code written for MATLAB should paste in and run.
>>> G = tf([1], [1, 2, 5])
G =
1
-------------
s^2 + 2 s + 5
Continuous-time transfer function. # Transfer Function
Three model kinds cross: tf and zpk are SISO, ss may be MIMO.
A discrete model carries its own sample time and every verb that has a time
base reads it from the model rather than from an argument.
The families, and what to reach for#
| You want to | Use |
|---|---|
| Build a model | tf(num, den[, Ts]), tf('s'), tf('z', Ts), ss(A, B, C, D[, Ts]), zpk(z, p, k[, Ts]), and each of them on another model to convert |
| Read a model back out | tfdata(sys, "v"), zpkdata(sys, "v"), ssdata(sys), G.num, G.den, G.Ts, sys.A … sys.D |
| Combine models | *, +, -, /, inv, ', ^, and series, parallel, feedback(G, H[, sign]), append, connect |
| Ask what a model IS | pole, zero, damp, dcgain, isstable, order, isct, isdt, isproper, issiso, size |
| Simplify or re-coordinate | minreal, canon, ss2ss, balreal, modred |
| Change time base | c2d(sys, Ts[, method]), d2c, d2d |
| Simulate | step, impulse, lsim(sys, u, t[, x0]), initial(sys, x0), gensig |
| Measure a response | stepinfo, covar(sys, W) for the stationary covariance under white noise |
| Look at frequency | bode, bodemag, nyquist, nichols, sigma, freqresp, evalfr, margin, bandwidth, getPeakGain, norm |
| Look at the roots | pzmap, rlocus(sys, k), sgrid, zgrid |
| Design a controller | place, acker, lqr, dlqr, lqi, lqry, pid, pidstd, pidtune, pidtuneOptions("PhaseMargin", d) |
| Design an estimator | kalman, lqe, estim, reg |
| Solve the matrix equations | lyap, dlyap, care, dare, icare, idare, gram, ctrb, obsv, ctrbf, obsvf |
| Handle a delay | pade(tau, n), sys.InputDelay, sys.OutputDelay, totaldelay, absorbDelay |
A worked line or two#
A second-order plant, its factored form, and its poles with damping:
>>> G = tf(1, [1 2 5]); zpk(G)
1
--------------
(s^2 + 2s + 5)
Continuous-time zero/pole/gain model. # Zero Pole Gain
>>> G = tf(1, [1 2 5]); [wn, zeta, p] = damp(G)
wn = [[2.23607], [2.23607]] # Matrix of Double
zeta = [[0.447214], [0.447214]] # Matrix of Double
p = [-1 + 2i; -1 - 2i] # Matrix of Complex
Its step response on a grid you choose, and the response measured:
>>> G = tf(1, [1 2 5]); [y, t] = step(G, 0:0.5:3); y'
[[0, 0.0834202, 0.197167, 0.241031, 0.227934, 0.203214, 0.19183]] # Matrix of Double
>>> G = tf(1, [1 2 5]); s = stepinfo(G); [s.RiseTime, s.SettlingTime, s.Overshoot]
[[0.690343, 3.73522, 20.7866]] # Matrix of Double
Feedback, discretization, and the stability margins of a loop:
>>> G = tf(1,[1 2 5]); feedback(G, 1)
1
-------------
s^2 + 2 s + 6
Continuous-time transfer function. # Transfer Function
>>> G = tf(1, [1 2 5]); c2d(G, 0.1, "tustin")
0.002247 z^2 + 0.004494 z + 0.002247
------------------------------------
z^2 - 1.775 z + 0.8202
Sample time: 0.1 seconds
Discrete-time transfer function. # Transfer Function
>>> L = tf(1, [1 2 5 0]); [Gm, Pm, Wcg, Wcp] = margin(L)
Gm = 10 # Double
Pm = 85.3667 # Double
Wcg = 2.23607 # Double
Wcp = 0.20097 # Double
State feedback, two ways:
>>> A = [0 1; -5 -2]; B = [0; 1]; K = place(A, B, [-3, -4])
K = [[7, 5]] # Matrix of Double
>>> lqr([0 1; -5 -2], [0; 1], eye(2), 1)
[[0.0990195, 0.279921]] # Matrix of Double
Every transcript on this page is real output, captured with
ICoreBlocks --console "<line>" on 2026-09-05 at commit bd974745.
Nine things that surprise people#
tfdata's numerator is LEFT-PADDED to the denominator's length.tfdata(tf([1 3],[1 2 5]), "v")is[0 1 3], not[1 3]— sonumel(num) == numel(den)always, and indexing from the front lands on a zero. And its third outputTsis 0 for a continuous model, which is MATLAB's convention.``
>>> tfdata(tf([1 3],[1 2 5]), "v") [[0, 1, 3]] # Matrix of Double``- A
zpk's zeros and poles are the numbers you PUT IN, not the roots of the polynomial they multiply out to. That is what makes azpksurvive being stored and multiplied. Arithmetic on azpkanswers azpk—H*2,H+1,inv(H),H^2andH*tf(1,[1 1])in either order — exactly as MATLAB's does;tf(H)andss(H)convert. - The bare
step(sys)picks its own horizon, and MATLAB's is longer. The step SIZE is the same on both sides; the final TIME comes from a settling detection that ships compiled in MATLAB, so MATLAB simply runs further.step(sys, Tfinal)andstep(sys, t)— where you name the horizon — agree digit for digit. If you are comparing against MATLAB, name the grid. - A DISCRETE
impulsehas height1/Ts, not 1. A unit sample scaled so the area is one, on both sides. A continuousimpulseis impulse-invariant (the free evolution fromx(0+) = B), not the zero-order hold thatstepandlsimuse. lsimPICKS the hold from your input, and it is a hard switch. An input whose largest step is at most 0.75 of its own range is called smooth and gets a first-order hold; anything jumpier gets a zero-order hold. Both sides decide it the same way, so a sine and a square wave both cross to the digit — but the sameuat a different amplitude can change the rule's verdict.initial's time grid is notstep's, even for the same model: the grid is built fromx0rather than from the input matrix, and on one measured model came out at exactly halfstep's spacing. And a transfer function is refused on both sides — initial conditions need a state-space model.A PID's filter pole belongs to the DERIVATIVE term.
pid(2, 3, 0, 0.5)hasKd = 0, so the filter has nothing to filter and the answer is a first-order PI controller, not a second-order model:``` >>> pid(2, 3, 0, 0.5) 1 Kp + Ki * --- s
with Kp = 2, Ki = 3 Continuous-time PI controller in parallel form. # Transfer Function ```
And a discrete PID is not the continuous one with
sreplaced: with a filter the derivative term isKd/(Tf + Ts/(z-1)), which is notKd*(z-1)/Tsover anything.pidtune's gains are NOT MATLAB's, and this is the one row on the page where that is true by design. MATLAB's loop shaping ships unpublished, so this console runs a documented rule of its own: put the crossover where the structure's phase range is widest, then meet|L| = 1and the target margin there exactly. What agrees is the PROPERTYpidtuneitself promises — a phase margin at or above the target. Measured on MATLAB,pidtune(tf(1,[1 3 3 1]), "pi")gives margin 60 exactly and"pid"gives 64.7 — at or above 60, not equal to it. A named crossover overrides the margin on both sides.stepinfo'sTransientTimeandSettlingTimeare different numbers. The first settles againstthreshold * max|y - yfinal|, the second againstthreshold * |yfinal - yinit|. They coincide only when the largest error in the record is the initial one — which is the usual case, and is why the difference surprises people when it appears.
What is not here#
Refused, with the reason in the message:
allmargin— it answers a struct of seven fields, and this console holds a record of numbers only.margin(sys)answers the crossing nearest instability, which is what the list is usually read for.lqgandreg— the assembled LQG regulator.hinfnorm— usenorm(sys, Inf).c2d(..., "matched" | "least-squares" | "damped")— the four that cross are"zoh","foh","tustin"and"impulse"."matched"is refused because the routine behind it is measurably the zoh routine, not because the word is unknown: accepting it would compare a zero-order hold against a matched pole-zero and call the difference a tolerance.rlocus(sys)without gains, andzpk('z')without a sample time.- MIMO
tfandzpk(the cell form). SISOtf/zpkand MIMOssare the three kinds;appendof two transfer functions therefore answers the state-space model where MATLAB answers a MIMOtf— the same system, a different kind.
Not known to the console at all — typing one of these answers
unknown function '<name>':
| If you reach for | Use |
|---|---|
controlSystemDesigner, sisotool, pidTuner | pidtune, place, lqr and margin typed directly; there is no interactive tool here |
linearSystemAnalyzer, ltiview | step, bode, nyquist, stepinfo |
rlocfind | rlocus(sys, k) and read the roots |
frd, genss, uss | nothing — this console has three model kinds and no object types |
Four console-only spellings sit beside these names and are marked as the console's own in Command glossary — console commands, verbs, functions. Each keeps its own signature, and the MATLAB call it corresponds to is not always the one the name suggests:
| Console's own | MATLAB | What differs beyond the name |
|---|---|---|
rootlocus(G, gains) | rlocus(sys, k) | nothing but the spelling — both take the gain vector, and both leave the pole order within a gain step to the implementation |
polezero(G) | pzmap(sys) | polezero only ever draws; pzmap also answers, as [p, z] = pzmap(sys) |
bode(G, w0, w1, n) | bode(sys, {wmin, wmax}) | the four-argument sweep answers an N x 3 matrix of [omega, magnitude in dB, phase in radians]; MATLAB's answers the magnitude as an absolute ratio and the phase in unwrapped degrees |
ramp(G) | — | MATLAB has no ramp; write it as lsim(G, t, t), the ramp as its own input over its own time vector |
New work should use MATLAB's names where the table gives one.
Where to look next#
- Command glossary — console commands, verbs, functions — every name the console answers, grouped by toolbox, with each one's arguments.
- The command window — the command engine for a user — the engine itself: variables, multiple outputs, the model types and how they print, and running a line headlessly.
- System identification in the command window — fitting a model to measured data, which is where most of these models come from.
- Sample time and loops — how the simulator paces a diagram — what a discrete sample time means to a diagram, and why a loop carries one extra delay.
- Numerics — what the solver will and will not do — what a double can carry, and where these answers stop being exact.