Skip to content
Control Systems

control-systems-ai999x

Transfer functions, poles and zeros, step and frequency response, stability (Routh-Hurwitz, gain and phase margins, Nyquist), root locus, state space, controllability and observability, pole placement, LQR, Kalman filters, discretization and PID tuning.

97capabilities
16topics
97formulas typeset
4ways to run: desktop app, command line, browser, Python
1.0.0version
What it does

What Control Systems does

Control Systems (control-systems-ai999x) is an offline Python package for control systems. It covers transfer functions, poles and zeros, step and frequency response, stability (Routh-Hurwitz, gain and phase margins, Nyquist), root locus, state space, controllability and observability, pole placement, LQR, Kalman filters, discretization and PID tuning.

It has 97 capabilities — 97 computational procedures — in 16 topics, the largest being Time response, State space, Discretization, PID tuning, Stability and Frequency response. Ask a question in words with ask(), or call a capability by name with typed inputs with solve(). Every answer carries its value and unit, the steps that produced it and the formula in LaTeX.

When a question is not determined — a value is missing, an input is out of range, or the request is outside the package — it says so and names what it needs, instead of guessing. It runs on an ordinary CPU without a network connection (Python >=3.10), and the same question always gets the same answer.

What it covers

Compensators3Lag compensator placement · Lead compensator design for a required phase lead · Maximum phase lead of a lead compensatorDesign1Desired closed-loop poles from overshoot and settling timeDiscretization12Backward Euler (backward difference) discretization · Continuous equivalent of a discrete pole (s = ln z / T) · DC gain and final value of a discrete transfer function …Estimation4One step of a scalar Kalman filter · Steady-state discrete Kalman filter gain · Steady-state gain of a scalar Kalman filter …Frequency response8-3 dB bandwidth of a transfer function · Bandwidth of a standard second-order system · Bode magnitude and phase at one frequency …Identification3Damping ratio from the logarithmic decrement · First-order-plus-dead-time fit by Smith's two-point method · Second-order model from a step responseModelling1Speed transfer function of a DC motorOptimal control3Continuous-time LQR gain (Riccati equation) · Discrete-time LQR gain (discrete Riccati equation) · LQR for a first-order systemPID tuning9Closed-loop poles of a PID loop · Cohen-Coon tuning of a first-order-plus-dead-time process · PI gains placing the closed-loop poles of a first-order plant …Root locus5Root-locus angle of arrival at a complex zero · Root-locus angle of departure from a complex pole · Root-locus asymptotes (centroid and angles) …Stability9Continuous Lyapunov equation · Discrete Lyapunov equation · Gain and phase margins …State space14Closed-loop poles under state feedback u = -K x · Controllability Gramian · Controllability matrix and rank …Steady-state error1System type, static error constant and steady-state errorTime delay1Pade approximation of a time delayTime response17Damping ratio from percent overshoot · Exact settling time of a second-order step response · Final value theorem …Transfer functions6Closed-loop transfer function of a feedback loop · DC gain and final value of a step response · Parallel connection of two transfer functions …
Get it

Use Control Systems your way

The AI999X desktop app and the command-line program control-systems run on Windows and Linux; together with the browser they run the same compiled program on your own device: offline, with no Python and nothing sent anywhere. The Python package gives the same capabilities in your own code. Or compute in your browser, with nothing to install.

Desktop app

Desktop app for Control Systems: this model in its own app, with search, ask in words, input forms, live figures and explanations. Version 1.2.0.

All models in one app · every AI999X model, this one included

Install guide · Quick start · All models: quick start

Command line

Every model's program in one download, for terminals and scripts. This model's program is control-systems.

Linux commands · Windows commands · Usage

Python

The Python package, for notebooks and your own programs (Python 3.10 or newer).

pip install control-systems-ai999x

Examples · Python guide

Version 1.2.0 · SHA-256 checksums · Latest downloads · Desktop & command-line guide: downloads, installing, asking and searching, figures, offline use, versions and checksums

From download to first answer

Every command can be copied.

Windows

Download ai999x-control-systems-1.2.0-windows-x86_64.zip, right-click it → Extract All, then open the folder and start ai999x-control-systems.exe. If Windows SmartScreen asks, choose More info → Run anyway.

Linux
curl -LO https://www.ai999x.com/downloads/desktop/1.2.0/ai999x-control-systems-1.2.0-linux-x86_64.tar.gz
curl -LO https://www.ai999x.com/downloads/desktop/1.2.0/SHA256SUMS
sha256sum -c --ignore-missing SHA256SUMS
tar xzf ai999x-control-systems-1.2.0-linux-x86_64.tar.gz
./ai999x-control-systems-1.2.0-linux-x86_64/ai999x-control-systems
Then
  1. Start the app: it opens on Control Systems.
  2. Type a question in words, or search for a capability and fill in its form.
  3. Press Compute: the answer, its explanation and live figures appear; change an input and they follow.
In your browser

Compute something now

Ask a question in words, or choose a capability and open its calculator: the inputs come filled in with a worked example; change any of them (any unit of the right kind) and press Compute. It runs on your device; nothing is sent anywhere.

The same capabilities as the desktop app and the command line, as WebAssembly in this page. Every capability below also has its own Compute in your browser button.

Worked examples

One example, every way to run it

Each worked example with its answer, then the same question on the command line, from Python and in your browser. A command-line output is exactly what the program printed for that command.

Stability

stability margins: num = [10], den = [1, 3, 2, 0]

GM = \frac{1}{|L(j\omega_{pc})|},\quad PM = 180^\circ + \angle L(j\omega_{gc})
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s
How it was worked out
  1. Gain and phase margins
    GM = \frac{1}{|L(j\omega_{pc})|},\quad PM = 180^\circ + \angle L(j\omega_{gc})
    with num = [10], den = [1, 3, 2, 0]
    → gm = 0.6, gm_db = -4.436974992, w_pc = 1.414213562 rad/s, pm_deg = -12.99720802, w_gc = 1.802203305 rad/s
Terminal
$ control-systems solve ctrl_stability_margins "num=[10]" "den=[1, 3, 2, 0]"
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s
  by: Gain and phase margins  (crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished)
  assumes: exactly one gain crossover and one phase crossover
Stability

Routh-Hurwitz: coefficients = [1, 2, 3, 4, 5]

b_i = \frac{a_1a_{0,i+1} – a_0a_{1,i+1}}{a_1}
first_column = [1, 2, 1, -6, 5]
rhp_roots = 2
stable = False
How it was worked out
  1. Routh-Hurwitz stability test
    b_i = \frac{a_1a_{0,i+1} – a_0a_{1,i+1}}{a_1}
    with coefficients = [1, 2, 3, 4, 5]
    → first_column = [1, 2, 1, -6, 5], rhp_roots = 2, stable = False
Terminal
$ control-systems solve ctrl_routh_hurwitz "coefficients=[1, 6, 11, 6]"
first_column = [1.0,6.0,10.0,6.0]
rhp_roots = 0
stable = true
  by: Routh-Hurwitz stability test  (Routh array in exact fractions; sign changes of the first column)
  assumes: no zero in the first column (special cases are refused)
Time response

Percent overshoot from the damping ratio

\%OS = 100\,e^{-\zeta\pi/\sqrt{1-\zeta^2}}
PO = 16.30335348
Mp = 0.1630335348
pole_angle_deg = 60
How it was worked out
  1. Percent overshoot from the damping ratio
    \%OS = 100\,e^{-\zeta\pi/\sqrt{1-\zeta^2}}
    with zeta = 0.5
    → PO = 16.30335348, Mp = 0.1630335348, pole_angle_deg = 60
Terminal
$ control-systems solve ctrl_percent_overshoot zeta=0.5
PO = 16.30335348
Mp = 0.1630335348
pole_angle_deg = 60
  by: Percent overshoot from the damping ratio  (peak of the step response at t = pi/wd)
  zeta = 0.5
  assumes: 0 <= zeta < 1
Optimal control

Continuous-time LQR gain (Riccati equation)

A^TP + PA – PBR^{-1}B^TP + Q = 0,\quad K = R^{-1}B^TP
K = [1, 1.732050808]
P = [[1.732050808, 1], [1, 1.732050808]]
poles = [-0.8660254038-0.5i, -0.8660254038+0.5i]
How it was worked out
  1. Continuous-time LQR gain (Riccati equation)
    A^TP + PA – PBR^{-1}B^TP + Q = 0,\quad K = R^{-1}B^TP
    with A = [0 1; 0 0], B = [0, 1], Q = [1 0; 0 1], R = 1
    → K = [1, 1.732050808], P = [[1.732050808, 1], [1, 1.732050808]], poles = [-0.8660254038-0.5i, -0.8660254038+0.5i]
Terminal
$ control-systems solve ctrl_lqr "A=[[0, 1], [0, 0]]" "B=[0, 1]" "Q=[[1, 0], [0, 1]]" R=1
K = [1.0,1.73205080757]
P = [[1.73205080757,1.0],[1.0,1.73205080757]]
poles = [{"re":-0.866025403784,"im":-0.5},{"re":-0.866025403784,"im":0.5}]
  by: Continuous-time LQR gain (Riccati equation)  (Newton-Kleinman iteration (a Lyapunov equation per step) from a stabilising Ackermann gain)
  R = 1
  assumes: (A, B) controllable; Q symmetric positive definite; R > 0
Discretization

Tustin discretization: num = [1], den = [1, 1], T = 0.1

s \leftarrow \frac{2}{T}\,\frac{z-1}{z+1}
numz = [0.04761904762, 0.04761904762]
denz = [1, -0.9047619048]
How it was worked out
  1. Tustin (bilinear) discretization
    s \leftarrow \frac{2}{T}\,\frac{z-1}{z+1}
    with num = [1], den = [1, 1], T = 0.1
    → numz = [0.04761904762, 0.04761904762], denz = [1, -0.9047619048]
Terminal
$ control-systems solve ctrl_tustin "num=[1]" "den=[1, 1]" T=0.1
numz = [0.047619047619,0.047619047619]
denz = [1.0,-0.904761904762]
  by: Tustin (bilinear) discretization  (polynomial substitution, multiplied through by (z + 1)^n)
  T = 0.1
  assumes: proper G(s); T > 0
Compensators

lag compensator design: beta = 10, w_c = 1

C(s) = \frac{s + z}{s + z/\beta},\quad z = \omega_c/10
zero = 0.1
pole = 0.01
dc_gain = 10
phase_deg = -5.13765444
magnitude_db = 0.04277946506
How it was worked out
  1. Lag compensator placement
    C(s) = \frac{s + z}{s + z/\beta},\quad z = \omega_c/10
    with beta = 10, w_c = 1
    → zero = 0.1, pole = 0.01, dc_gain = 10, phase_deg = -5.13765444, magnitude_db = 0.04277946506
Terminal
$ control-systems solve ctrl_lag_design beta=10 w_c=1
zero = 0.1
pole = 0.01
dc_gain = 10
phase_deg = -5.13765444
magnitude_db = 0.04277946506
  by: Lag compensator placement  (zero a decade below crossover, pole beta times lower)
  beta = 10
  w_c = 1
  assumes: beta > 1; ratio >= 2
Design

poles from overshoot and settling time: OS = 0.163033534821, Ts = 2

\zeta = \frac{-\ln OS}{\sqrt{\pi^2+\ln^2 OS}},\quad \sigma = \frac{4}{T_s},\quad \omega_n = \frac{\sigma}{\zeta}
zeta = 0.5
wn = 4 rad/s
sigma = 2
wd = 3.464101615 rad/s
char_poly = [1, 4, 16]
How it was worked out
  1. Desired closed-loop poles from overshoot and settling time
    \zeta = \frac{-\ln OS}{\sqrt{\pi^2+\ln^2 OS}},\quad \sigma = \frac{4}{T_s},\quad \omega_n = \frac{\sigma}{\zeta}
    with OS = 0.1630335348, Ts = 2
    → zeta = 0.5 rad/s, wn = 4 rad/s, sigma = 2 rad/s, wd = 3.464101615 rad/s, char_poly = [1, 4, 16] rad/s
Terminal
$ control-systems solve ctrl_specs_to_poles OS=0.1630335348 Ts=2
zeta = 0.5
wn = 4 rad/s
sigma = 2
wd = 3.464101615 rad/s
char_poly = [1.0,4.0,15.9999999982]
  by: Desired closed-loop poles from overshoot and settling time  (zeta from the overshoot formula; sigma = 4/Ts)
  OS = 0.1630335348
  Ts = 2
  assumes: 0 < OS < 100 %; Ts > 0
Estimation

discrete Kalman gain: A = [[1]], C = [1], Q = [[1]], R = 1

P = APA^T – APC^T(CPC^T + R)^{-1}CPA^T + Q,\quad K = PC^T(CPC^T + R)^{-1}
K = [0.6180339888]
P_prior = [[1.618033989]]
How it was worked out
  1. Steady-state discrete Kalman filter gain
    P = APA^T – APC^T(CPC^T + R)^{-1}CPA^T + Q,\quad K = PC^T(CPC^T + R)^{-1}
    with A = [1], C = [1], Q = [1], R = 1
    → K = [0.6180339888], P_prior = [[1.618033989]]
Terminal
$ control-systems solve ctrl_discrete_kalman_gain A=1 C=1 Q=1 R=1
K = [0.61803398875]
P_prior = [[1.61803398875]]
  by: Steady-state discrete Kalman filter gain  (the dual discrete Riccati equation by Hewer's iteration)
  R = 1
  assumes: (A, C) observable; Q positive definite; R > 0
Command line

Use it from the command line

Every command below was run as shown, and the text under it is exactly what control-systems printed. The same commands work in a Linux terminal and in Windows PowerShell. Install it in a minute. The user guide covers install, inputs, outcomes, scripting, and every capability with a command and what it printed: PDF · text.

1

Check the install

Prints the model, its version and that this is the Rust build.

Terminal
$ control-systems --version
control-systems-ai999x 1.0.0 (Rust build, ax-engine 0.1.0)
2

Ask in words

Write the question as you would say it: the program reads the quantities and their units, picks the capability and answers.

Terminal
$ control-systems ask "stability margins: num = [10], den = [1, 3, 2, 0]"
Read as: Gain and phase margins
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s
  by: Gain and phase margins  (crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished)
  assumes: exactly one gain crossover and one phase crossover
3

Find a capability

Search the capabilities by what you want to know; the first column is the id to pass to info and solve.

Terminal
$ control-systems search "gain and phase margins"
ctrl_stability_margins               Gain and phase margins  (stability / margins)
ctrl_sensitivity_peak                Peak sensitivity Ms and modulus margin  (stability / robustness)
ctrl_lead_max_phase                  Maximum phase lead of a lead compensator  (compensators / lead)
ctrl_bode_point                      Bode magnitude and phase at one frequency  (frequency response / Bode)
ctrl_lead_design                     Lead compensator design for a required phase lead  (compensators / lead)
ctrl_lag_design                      Lag compensator placement  (compensators / lag)
ctrl_pole_stability                  Poles and stability of a characteristic polynomial  (stability / poles)
ctrl_poles_zeros                     Poles, zeros and gain of a transfer function  (transfer functions / poles and zeros)
… 12 more
4

List the capabilities

Every capability the program answers: its id (what you pass to solve) and its name.

Terminal
$ control-systems list
ctrl_ackermann                           Pole placement by Ackermann's formula
ctrl_arrival_angle                       Root-locus angle of arrival at a complex zero
ctrl_backward_euler                      Backward Euler (backward difference) discretization
ctrl_bandwidth                           -3 dB bandwidth of a transfer function
ctrl_bandwidth_second                    Bandwidth of a standard second-order system
ctrl_bode_point                          Bode magnitude and phase at one frequency
ctrl_breakaway_points                    Root-locus breakaway and break-in points
ctrl_c2d_foh                             First-order-hold (triangle-hold) discretization
… 89 more
5

What a capability needs

The equation or method, every input and output with its SI unit. Read this before solve: it tells you the input names.

Terminal
$ control-systems info ctrl_stability_margins
Gain and phase margins
  crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished
  in   num      numerator of the open loop L(s) (vector) []
  in   den      denominator of L(s) (vector) []
  out  gm       gain margin (ratio) []
  out  gm_db    gain margin in dB []
  out  w_pc     phase-crossover frequency [rad/s]
  out  pm_deg   phase margin in degrees []
  out  w_gc     gain-crossover frequency [rad/s]
6

Solve

Inputs as name=value, each with any unit of the right kind. The answer lists every output, the method used, each input converted to SI, and the conditions the answer assumes.

Terminal
$ control-systems solve ctrl_stability_margins "num=[10]" "den=[1, 3, 2, 0]"
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s
  by: Gain and phase margins  (crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished)
  assumes: exactly one gain crossover and one phase crossover
7

Machine-readable answer

–json prints the same answer as one line of JSON (values in SI): for scripts and other programs.

Terminal
$ control-systems solve ctrl_stability_margins "num=[10]" "den=[1, 3, 2, 0]" --json
{"outcome":"SOLVED","message":"","value":-12.9972080155,"unit":"","solved_for":"pm_deg","values":[["gm",0.6,""],["gm_db",-4.43697499233,""],["w_pc",1.41421356237,"rad/s"],["pm_deg",-12.9972080155,""],["w_gc",1.80220330461,"rad/s"]],"target":"pm_deg","phrase":"gain margin (ratio)","assumptions":["exactly one gain crossover and one phase crossover"],"name":"Gain and phase margins","equation":"crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished"}
8

When an input is missing

No number is guessed: the answer names what is missing, shows a complete call, and the program exits with status 1.

Terminal
$ control-systems solve ctrl_stability_margins "num=[10]"
NEEDS_INPUT: ctrl_stability_margins needs den (denominator of L(s)). Example: solve('ctrl_stability_margins', num=[10], den=[1, 3, 2, 0]).
9

Many questions at once

One JSON request per line in, one JSON answer per line out, in order: for scripts, notebooks and agents (the program starts once). In PowerShell: Get-Content cases.jsonl | control-systems batch

Terminal
cases.jsonl
{"key": "ctrl_stability_margins", "inputs": {"num": "[10]", "den": "[1, 3, 2, 0]"}}
{"key": "ctrl_routh_hurwitz", "inputs": {"coefficients": "[1, 2, 3, 4, 5]"}}
$ control-systems batch < cases.jsonl
{"outcome":"SOLVED","message":"","value":-12.9972080155,"unit":"","solved_for":"pm_deg","values":[["gm",0.6,""],["gm_db",-4.43697499233,""],["w_pc",1.41421356237,"rad/s"],["pm_deg",-12.9972080155,""],["w_gc",1.80220330461,"rad/s"]],"target":"pm_deg","phrase":"gain margin (ratio)","assumptions":["exactly one gain crossover and one phase crossover"],"name":"Gain and phase margins","equation":"crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished"}
{"outcome":"SOLVED","message":"","value":2.0,"unit":"","solved_for":"rhp_roots","values":[["first_column",[1.0,2.0,1.0,-6.0,5.0],""],["rhp_roots",2,""],["stable",false,""]],"target":"rhp_roots","phrase":"first column of the Routh array","assumptions":["no zero in the first column (special cases are refused)"],"name":"Routh-Hurwitz stability test","equation":"Routh array in exact fractions; sign changes of the first column"}

All commands

CommandWhat it does
control-systems ask "<question>"answer a question written in words
control-systems search "<words>"find the capabilities that match
control-systems listevery capability: its id and name
control-systems info <capability>the equation or method, its inputs and outputs with their units
control-systems solve <capability> name=value …the answer; add –target X to solve an equation for X, –json for JSON
control-systems figures <capability> name=value … --out DIRthe live figures for those inputs, as SVG files in DIR
control-systems batch < requests.jsonlone JSON answer per line, for scripts and agents
control-systems catalogevery capability with its inputs, as JSON
control-systems --versionthe model and its version

Exit status: 0 when the question was answered, 1 when it was not (the message says why: a missing input, an invalid value, a question outside the model), 2 for a command it does not know.

Python

Use it from Python

After pip install control-systems-ai999x, ask in words or call a capability. Every snippet was run as shown; the output under it is what it printed. The Python guide covers ask in words, solve, and every capability with a Python example: PDF · text.

import control_systems_ai999x as m

a = m.ask("stability margins: num = [10], den = [1, 3, 2, 0]")
print(a.outcome)
print(a.values)
for step in a.steps:
    print(step["name"])
Output
SOLVED
{'gm': 0.6, 'gm_db': -4.43697499233, 'w_pc': 1.41421356237, 'pm_deg': -12.9972080155, 'w_gc': 1.80220330461}
Gain and phase margins
Capabilities

Every capability, explained

Search by name, formula, input or topic. Each capability opens to say what it does, what it needs, what it gives back and why, with its worked example, how to run it (on the command line, from Python, in your browser) and its figures.

97 capabilities

Compensators 3

Lag compensator placement2 fig.ctrl_lag_design

C(s) = (s + z)/(s + p) with z = w_c/ratio (default a decade below the crossover) and p = z/beta: zero, pole, DC gain beta, and the phase lag and gain it adds at w_c.

C(s) = \frac{s + z}{s + z/\beta},\quad z = \omega_c/10

You give

MeaningUnitExample
\beta
Required low-frequency gain increase (> 1)—10
w_{c}
Gain-crossover frequency in rad/s—1
\text{ratio}
w_c / zero (default 10) optional—

You get

MeaningUnitExample
\text{zero}
Zero (rad/s)—0.1
\text{pole}
Pole (rad/s)—0.01
\text{dc\_gain}
DC gain z/p = beta—10
\text{phase\_deg}
Phase added at w_c in degrees (negative)—-5.13765444
\text{magnitude\_db}
Gain added at w_c in dB—0.04277946506

An optional input can be left out: a stated default is used.

Applies when

  • beta > 1
  • ratio >= 2

Outside these conditions it says what is wrong instead of returning a number.

Worked example

lag compensator design: beta = 10, w_c = 1
zero = 0.1
pole = 0.01
dc_gain = 10
phase_deg = -5.13765444
magnitude_db = 0.04277946506

Run it

Terminal
$ control-systems solve ctrl_lag_design beta=10 w_c=1
zero = 0.1
pole = 0.01
dc_gain = 10
phase_deg = -5.13765444
magnitude_db = 0.04277946506
  by: Lag compensator placement  (zero a decade below crossover, pole beta times lower)
  beta = 10
  w_c = 1
  assumes: beta > 1; ratio >= 2

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Lead compensator design for a required phase lead1 fig.ctrl_lead_design

alpha = (1 – sin phi)/(1 + sin phi) and T = 1/(w_m sqrt(alpha)) so that C(s) = (T s + 1)/(alpha T s + 1) adds the phase lead phi at w_m; its zero, pole and gain at w_m.

\alpha = \frac{1-\sin\phi_m}{1+\sin\phi_m},\quad T = \frac{1}{\omega_m\sqrt\alpha}

You give

MeaningUnitExample
\phi_{\mathrm{max},\mathrm{deg}}
Required maximum phase lead in degrees (0 to 90)—30
w_{m}
Frequency of the maximum lead in rad/s—1

You get

MeaningUnitExample
\alpha
alpha (pole/zero ratio z/p)—0.3333333333
T
Time constant Ts1.732050808 s
\text{zero}
Zero corner 1/T (rad/s)—0.5773502692
\text{pole}
Pole corner 1/(alpha T) (rad/s)—1.732050808
\text{gain\_db}
|C(j w_m)| in dB = -10 log10 alpha—4.771212547

Applies when

  • 0 < phi < 90 degrees
  • w_m > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

lead compensator design: phi_max_deg = 30, w_m = 1
alpha = 0.3333333333
T = 1.732050808 s
zero = 0.5773502692
pole = 1.732050808
gain_db = 4.771212547

Run it

Terminal
$ control-systems solve ctrl_lead_design phi_max_deg=30 w_m=1
alpha = 0.3333333333
T = 1.732050808 s
zero = 0.5773502692
pole = 1.732050808
gain_db = 4.771212547
  by: Lead compensator design for a required phase lead  (sin phi_max = (1 - alpha)/(1 + alpha), w_m = 1/(T sqrt(alpha)))
  phi_max_deg = 30
  w_m = 1
  assumes: 0 < phi < 90 degrees; w_m > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Maximum phase lead of a lead compensator2 fig.ctrl_lead_max_phase

For C(s) = (s + z)/(s + p) with z < p: the maximum phase lead asin((p – z)/(p + z)), the frequency sqrt(z p) where it occurs, alpha = z/p and the gain there, 10 log10(z/p) dB.

\sin\phi_{max} = \frac{p-z}{p+z} = \frac{1-\alpha}{1+\alpha},\quad \omega_m = \sqrt{zp}

You give

MeaningUnitExample
z
Zero corner frequency (rad/s)—1
p
Pole corner frequency (rad/s), p > z—9

You get

MeaningUnitExample
\phi_{\mathrm{max},\mathrm{deg}}
Maximum phase lead in degrees—53.13010235
w_{\mathrm{max}}
Frequency of the maximumrad/s3 rad/s
\alpha
alpha = z/p—0.1111111111
\text{magnitude\_db}
|C(j w_max)| in dB—-9.542425094

Applies when

  • 0 < z < p

Outside these conditions it says what is wrong instead of returning a number.

Worked example

lead compensator maximum phase: z = 1, p = 9
phi_max_deg = 53.13010235
w_max = 3 rad/s
alpha = 0.1111111111
magnitude_db = -9.542425094

Run it

Terminal
$ control-systems solve ctrl_lead_max_phase z=1 p=9
phi_max_deg = 53.13010235
w_max = 3 rad/s
alpha = 0.1111111111
magnitude_db = -9.542425094
  by: Maximum phase lead of a lead compensator  (stationary point of the phase)
  z = 1
  p = 9
  assumes: 0 < z < p

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Design 1

Desired closed-loop poles from overshoot and settling time3 fig.ctrl_specs_to_poles

zeta from the overshoot, sigma = 4/Ts from the 2 % settling time, wn = sigma/zeta, the poles -sigma +/- j wd and the desired characteristic polynomial.

\zeta = \frac{-\ln OS}{\sqrt{\pi^2+\ln^2 OS}},\quad \sigma = \frac{4}{T_s},\quad \omega_n = \frac{\sigma}{\zeta}

You give

MeaningUnitExample
\text{OS}
Maximum overshoot (write 10% or 0.1)—0.1630335348
\text{Ts}
2 % settling time in seconds—2

You get

MeaningUnitExample
\zeta
Damping ratio—0.5
\text{wn}
Natural frequencyrad/s4 rad/s
\sigma
Real part magnitude 4/Ts—2
\text{wd}
Imaginary part of the polesrad/s3.464101615 rad/s
\text{char\_poly}
s^2 + 2 sigma s + wn^2—[1, 4, 16]

Applies when

  • 0 < OS < 100 %
  • Ts > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

poles from overshoot and settling time: OS = 0.163033534821, Ts = 2
zeta = 0.5
wn = 4 rad/s
sigma = 2
wd = 3.464101615 rad/s
char_poly = [1, 4, 16]

Run it

Terminal
$ control-systems solve ctrl_specs_to_poles OS=0.1630335348 Ts=2
zeta = 0.5
wn = 4 rad/s
sigma = 2
wd = 3.464101615 rad/s
char_poly = [1.0,4.0,15.9999999982]
  by: Desired closed-loop poles from overshoot and settling time  (zeta from the overshoot formula; sigma = 4/Ts)
  OS = 0.1630335348
  Ts = 2
  assumes: 0 < OS < 100 %; Ts > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Discretization 12

Backward Euler (backward difference) discretization1 fig.ctrl_backward_euler

The discrete transfer function obtained by substituting s = (z – 1)/(T z) into G(s).

s \leftarrow \frac{z-1}{Tz}

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 1]
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0.09090909091, 0]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -0.9090909091]

Applies when

  • proper G(s)
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

backward Euler discretization: num = [1], den = [1, 1], T = 0.1
numz = [0.09090909091, 0]
denz = [1, -0.9090909091]

Run it

Terminal
$ control-systems solve ctrl_backward_euler "num=[1]" "den=[1, 1]" T=0.1
numz = [0.0909090909091,0.0]
denz = [1.0,-0.909090909091]
  by: Backward Euler (backward difference) discretization  (polynomial substitution s = (z - 1)/(T z))
  T = 0.1
  assumes: proper G(s); T > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Continuous equivalent of a discrete pole (s = ln z / T)1 fig.ctrl_z_to_s_pole

The s-plane pole s = ln(z)/T (principal branch) of a discrete pole z = z_re + j z_im, with its natural frequency and damping ratio.

s = \frac{\ln z}{T}

You give

MeaningUnitExample
z_{\mathrm{re}}
Real part of the discrete pole—0.5
z_{\mathrm{im}}
Imaginary part of the discrete pole (>= 0)—0
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
s_{\mathrm{re}}
Real part of s (1/s)—-6.931471806
s_{\mathrm{im}}
Imaginary part of s (rad/s)—0
\text{wn}
Natural frequencyrad/s6.931471806 rad/s
\zeta
Damping ratio—1

Applies when

  • T > 0
  • z not on the negative real axis

Outside these conditions it says what is wrong instead of returning a number.

Worked example

z-plane to s-plane pole: z_re = 0.5, z_im = 0, T = 0.1
s_re = -6.931471806
s_im = 0
wn = 6.931471806 rad/s
zeta = 1

Run it

Terminal
$ control-systems solve ctrl_z_to_s_pole z_re=0.5 z_im=0 T=0.1
s_re = -6.931471806
s_im = 0
wn = 6.931471806 rad/s
zeta = 1
  by: Continuous equivalent of a discrete pole (s = ln z / T)  (s = (ln|z| + j arg z)/T)
  z_re = 0.5
  z_im = 0
  T = 0.1
  assumes: T > 0; z not on the negative real axis

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

DC gain and final value of a discrete transfer function1 fig.ctrl_discrete_dc_gain

G(1) = sum(num)/sum(den) of a stable discrete transfer function G(z), and the final value of its response to a step of size A.

y(\infty) = A\,G(1)

You give

MeaningUnitExample
\text{num}
Numerator coefficients in z, highest power first vector—[0.5]
\text{den}
Denominator coefficients in z, highest power first vector—[1, -0.5]
A
Step size (default 1) optional—

You get

MeaningUnitExample
\text{dc\_gain}
DC gain G(1)—1
\text{final\_value}
Final value of the step response—1

An optional input can be left out: a stated default is used.

Applies when

  • proper G(z)
  • every pole inside the unit circle

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete DC gain: num = [0.5], den = [1, -0.5]
dc_gain = 1
final_value = 1

Run it

Terminal
$ control-systems solve ctrl_discrete_dc_gain "num=[0.5]" "den=[1, -0.5]"
dc_gain = 1
final_value = 1
  by: DC gain and final value of a discrete transfer function  (G(1) after a stability check)
  assumes: proper G(z); every pole inside the unit circle

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Discrete PID in velocity (incremental) form1 fig.ctrl_discrete_pid

Coefficients of u[k] = u[k-1] + q0 e[k] + q1 e[k-1] + q2 e[k-2] for a PID with backward-difference integral and derivative: q0 = Kp + Ki T + Kd/T, q1 = -Kp – 2 Kd/T, q2 = Kd/T.

\Delta u_k = (K_p + K_iT + \tfrac{K_d}{T})e_k – (K_p + \tfrac{2K_d}{T})e_{k-1} + \tfrac{K_d}{T}e_{k-2}

You give

MeaningUnitExample
\text{Kp}
Proportional gain—2
\text{Ki}
Integral gain—1
\text{Kd}
Derivative gain—0.5
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
\text{q0}
Coefficient of e[k]—7.1
\text{q1}
Coefficient of e[k-1]—-12
\text{q2}
Coefficient of e[k-2]—5

Applies when

  • T > 0
  • Ki, Kd >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete PID: Kp = 2, Ki = 1, Kd = 0.5, T = 0.1
q0 = 7.1
q1 = -12
q2 = 5

Run it

Terminal
$ control-systems solve ctrl_discrete_pid Kp=2 Ki=1 Kd=0.5 T=0.1
q0 = 7.1
q1 = -12
q2 = 5
  by: Discrete PID in velocity (incremental) form  (difference of the positional backward-difference PID)
  Kp = 2
  Ki = 1
  Kd = 0.5
  T = 0.1
  assumes: T > 0; Ki, Kd >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

First-order-hold (triangle-hold) discretization1 fig.ctrl_c2d_foh

The ramp-invariant (triangle-hold, non-causal first-order hold) discrete equivalent of a proper G(s) sampled every T seconds.

B_d = \Gamma_1 – \Gamma_2 + \Phi\Gamma_2,\quad D_d = D + C\Gamma_2

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 0]
T
Sampling period in seconds—1

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0.5, 0.5]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -1]

Applies when

  • proper G(s)
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

FOH discretization: num = [1], den = [1, 0], T = 1
numz = [0.5, 0.5]
denz = [1, -1]

Run it

Terminal
$ control-systems solve ctrl_c2d_foh "num=[1]" "den=[1, 0]" T=1
numz = [0.5,0.5]
denz = [1.0,-1.0]
  by: First-order-hold (triangle-hold) discretization  (exponential of the augmented matrix [[A T, B T, 0], [0, 0, 1], [0, 0, 0]])
  T = 1
  assumes: proper G(s); T > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Forward Euler (forward difference) discretization2 fig.ctrl_forward_euler

The discrete transfer function obtained by substituting s = (z – 1)/T into G(s).

s \leftarrow \frac{z-1}{T}

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 1]
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0, 0.1]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -0.9]

Applies when

  • proper G(s)
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

forward Euler discretization: num = [1], den = [1, 1], T = 0.1
numz = [0, 0.1]
denz = [1, -0.9]

Run it

Terminal
$ control-systems solve ctrl_forward_euler "num=[1]" "den=[1, 1]" T=0.1
numz = [0.0,0.1]
denz = [1.0,-0.9]
  by: Forward Euler (forward difference) discretization  (polynomial substitution s = (z - 1)/T)
  T = 0.1
  assumes: proper G(s); T > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Mapping an s-plane pole to the z-plane (z = e^{sT})2 fig.ctrl_s_to_z_pole

The discrete pole z = e^{sT} of a continuous pole s = s_re + j s_im sampled with period T: real and imaginary parts, magnitude e^{s_re T} and angle s_im T.

z = e^{sT} = e^{\sigma T}\angle\,\omega T

You give

MeaningUnitExample
s_{\mathrm{re}}
Real part of the s-plane pole (1/s)—-2
s_{\mathrm{im}}
Imaginary part of the pole, >= 0 (rad/s)—0
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
z_{\mathrm{re}}
Real part of z—0.8187307531
z_{\mathrm{im}}
Imaginary part of z—0
z_{\mathrm{mag}}
|z| = e^{s_re T}—0.8187307531
z_{\mathrm{angle}}
Angle of z in radians, s_im T—0

Applies when

  • T > 0
  • 0 <= s_im T < pi

Outside these conditions it says what is wrong instead of returning a number.

Worked example

s-plane to z-plane pole: s_re = -2, s_im = 0, T = 0.1
z_re = 0.8187307531
z_im = 0
z_mag = 0.8187307531
z_angle = 0

Run it

Terminal
$ control-systems solve ctrl_s_to_z_pole s_re=-2 s_im=0 T=0.1
z_re = 0.8187307531
z_im = 0
z_mag = 0.8187307531
z_angle = 0
  by: Mapping an s-plane pole to the z-plane (z = e^{sT})  (z = e^{s_re T}(cos s_im T + j sin s_im T))
  s_re = -2
  s_im = 0
  T = 0.1
  assumes: T > 0; 0 <= s_im T < pi

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Step response of a discrete transfer function at sample k1 fig.ctrl_discrete_step

y[k] of G(z) = numz/denz after a step of size A applied at k = 0, by the difference equation.

y[k] = \sum_{i} b_i u[k-i] – \sum_{i\ge 1} a_i y[k-i]

You give

MeaningUnitExample
\text{numz}
Numerator in z, highest power first vector—[0.5]
\text{denz}
Denominator in z, highest power first vector—[1, -0.5]
k
Sample index (0 to 500)—2
A
Step size (default 1) optional—

You get

MeaningUnitExample
y
Output at sample k—0.75

An optional input can be left out: a stated default is used.

Applies when

  • proper (causal) G(z)
  • 0 <= k <= 500

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete step response: numz = [0.5], denz = [1, -0.5], k = 2
y = 0.75

Run it

Terminal
$ control-systems solve ctrl_discrete_step "numz=[0.5]" "denz=[1, -0.5]" k=2
y = 0.75
  by: Step response of a discrete transfer function at sample k  (recursion y[j] = sum b_i u[j-i] - sum a_i y[j-i])
  assumes: proper (causal) G(z); 0 <= k <= 500

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Tustin (bilinear) discretization1 fig.ctrl_tustin

The discrete transfer function obtained by substituting s = (2/T)(z – 1)/(z + 1) into G(s).

s \leftarrow \frac{2}{T}\,\frac{z-1}{z+1}

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 1]
T
Sampling period in seconds—0.1

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0.04761904762, 0.04761904762]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -0.9047619048]

Applies when

  • proper G(s)
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Tustin discretization: num = [1], den = [1, 1], T = 0.1
numz = [0.04761904762, 0.04761904762]
denz = [1, -0.9047619048]

Run it

Terminal
$ control-systems solve ctrl_tustin "num=[1]" "den=[1, 1]" T=0.1
numz = [0.047619047619,0.047619047619]
denz = [1.0,-0.904761904762]
  by: Tustin (bilinear) discretization  (polynomial substitution, multiplied through by (z + 1)^n)
  T = 0.1
  assumes: proper G(s); T > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Tustin discretization with frequency prewarping1 fig.ctrl_tustin_prewarp

The bilinear transform s = (w/tan(w T/2)) (z – 1)/(z + 1), which matches the continuous frequency response exactly at the prewarping frequency w.

s \leftarrow \frac{\omega}{\tan(\omega T/2)}\,\frac{z-1}{z+1}

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 1]
T
Sampling period in seconds—1
w
Prewarping frequency in rad/s—1

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0.3532960035, 0.3532960035]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -0.293407993]

Applies when

  • proper G(s)
  • 0 < w T < pi

Outside these conditions it says what is wrong instead of returning a number.

Worked example

prewarped Tustin discretization: num = [1], den = [1, 1], T = 1, w = 1
numz = [0.3532960035, 0.3532960035]
denz = [1, -0.293407993]

Run it

Terminal
$ control-systems solve ctrl_tustin_prewarp "num=[1]" "den=[1, 1]" T=1 w=1
numz = [0.353296003487,0.353296003487]
denz = [1.0,-0.293407993026]
  by: Tustin discretization with frequency prewarping  (polynomial substitution with the prewarped constant)
  T = 1
  w = 1
  assumes: proper G(s); 0 < w T < pi

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Zero-order-hold discretization of a first-order system1 fig.ctrl_zoh_first_order

G(z) = b/(z – a) with a = e^{-T/tau} and b = K (1 – a): the exact step-invariant (ZOH) equivalent of K/(tau s + 1) sampled every T seconds.

G(z) = \frac{K(1 – e^{-T/\tau})}{z – e^{-T/\tau}}

You give

MeaningUnitExample
K
DC gain—2
\tau
Time constant in seconds—1
T
Sampling period in seconds—0.5

You get

MeaningUnitExample
b
Numerator coefficient b—0.7869386806
a
Discrete pole a—0.6065306597

Applies when

  • tau > 0
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

ZOH discretization of a first-order system: K = 2, tau = 1, T = 0.5
b = 0.7869386806
a = 0.6065306597

Run it

Terminal
$ control-systems solve ctrl_zoh_first_order K=2 tau=1 T=0.5
b = 0.7869386806
a = 0.6065306597
  by: Zero-order-hold discretization of a first-order system  (a = exp(-T/tau), b = K (1 - a))
  K = 2
  tau = 1
  T = 0.5
  assumes: tau > 0; T > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Zero-order-hold discretization of a transfer function2 fig.ctrl_c2d_zoh

The step-invariant (ZOH) discrete equivalent G(z) = (1 – z^{-1}) Z{G(s)/s} of a proper G(s) sampled every T seconds.

A_d = e^{AT},\; B_d = \int_0^T e^{A\tau}d\tau\,B

You give

MeaningUnitExample
\text{num}
Numerator in s vector—[1]
\text{den}
Denominator in s vector—[1, 1]
T
Sampling period in seconds—0.5

You get

MeaningUnitExample
\text{numz}
Numerator in z (padded, normalised)—[0, 0.3934693403]
\text{denz}
Denominator in z (leading coefficient 1)—[1, -0.6065306597]

Applies when

  • proper G(s)
  • T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

ZOH discretization: num = [1], den = [1, 1], T = 0.5
numz = [0, 0.3934693403]
denz = [1, -0.6065306597]

Run it

Terminal
$ control-systems solve ctrl_c2d_zoh "num=[1]" "den=[1, 1]" T=0.5
numz = [0.0,0.393469340287]
denz = [1.0,-0.606530659713]
  by: Zero-order-hold discretization of a transfer function  (exponential of [[A, B], [0, 0]] T in state space, then Faddeev-LeVerrier)
  T = 0.5
  assumes: proper G(s); T > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Estimation 4

One step of a scalar Kalman filter1 fig.ctrl_kalman_scalar_step

Predict (x- = a x, P- = a^2 P + q) and update with a measurement z of variance r (gain P-/(P- + r)) for x_k = a x_{k-1} + w, z_k = x_k + v.

K = \frac{P^-}{P^- + r},\quad \hat x = \hat x^- + K(z – \hat x^-),\quad P = (1-K)P^-

You give

MeaningUnitExample
x
Previous estimate—0
P
Previous error variance—1
a
State transition coefficient—1
q
Process noise variance—1
r
Measurement noise variance—2
z
New measurement—4

You get

MeaningUnitExample
x_{\mathrm{pred}}
Predicted estimate—0
P_{\mathrm{pred}}
Predicted variance—2
\text{gain}
Kalman gain—0.5
x_{\mathrm{post}}
Updated estimate—2
P_{\mathrm{post}}
Updated variance—1

Applies when

  • P, q >= 0
  • r > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

scalar Kalman filter step: x = 0, P = 1, a = 1, q = 1, r = 2, z = 4
x_pred = 0
P_pred = 2
gain = 0.5
x_post = 2
P_post = 1

Run it

Terminal
$ control-systems solve ctrl_kalman_scalar_step x=0 P=1 a=1 q=1 r=2 z=4
x_pred = 0
P_pred = 2
gain = 0.5
x_post = 2
P_post = 1
  by: One step of a scalar Kalman filter  (Kalman predict and update equations)
  x = 0
  P = 1
  a = 1
  q = 1
  r = 2
  z = 4
  assumes: P, q >= 0; r > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Steady-state discrete Kalman filter gain1 fig.ctrl_discrete_kalman_gain

For x_{k+1} = A x_k + w (covariance Q), y_k = C x_k + v (variance R): the steady prior covariance P from the discrete filter Riccati equation and the measurement-update gain K = P C^T (C P C^T + R)^{-1}.

P = APA^T – APC^T(CPC^T + R)^{-1}CPA^T + Q,\quad K = PC^T(CPC^T + R)^{-1}

You give

MeaningUnitExample
A
Discrete state matrix (n x n) matrix—[1]
C
Output vector vector—[1]
Q
Process noise covariance (symmetric positive definite) matrix—[1]
R
Measurement noise variance (> 0)—1

You get

MeaningUnitExample
K
Steady Kalman gain—[0.6180339888]
P_{\mathrm{prior}}
Steady prior error covariance—[[1.618033989]]

Applies when

  • (A, C) observable
  • Q positive definite
  • R > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete Kalman gain: A = [[1]], C = [1], Q = [[1]], R = 1
K = [0.6180339888]
P_prior = [[1.618033989]]

Run it

Terminal
$ control-systems solve ctrl_discrete_kalman_gain A=1 C=1 Q=1 R=1
K = [0.61803398875]
P_prior = [[1.61803398875]]
  by: Steady-state discrete Kalman filter gain  (the dual discrete Riccati equation by Hewer's iteration)
  R = 1
  assumes: (A, C) observable; Q positive definite; R > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Steady-state gain of a scalar Kalman filter1 fig.ctrl_kalman_scalar_steady

For x_k = a x_{k-1} + w (variance q) and z_k = c x_k + v (variance r): the steady prior variance P (discrete Riccati equation), the steady Kalman gain and the posterior variance.

P = a^2P – \frac{a^2c^2P^2}{c^2P + r} + q,\quad K = \frac{Pc}{c^2P + r}

You give

MeaningUnitExample
a
State transition coefficient—1
c
Measurement coefficient—1
q
Process noise variance—1
r
Measurement noise variance—1

You get

MeaningUnitExample
P_{\mathrm{prior}}
Steady prior error variance—1.618033989
\text{gain}
Steady Kalman gain—0.6180339888
P_{\mathrm{post}}
Steady posterior error variance—0.6180339888

Applies when

  • c != 0
  • q, r > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

steady-state Kalman gain: a = 1, c = 1, q = 1, r = 1
P_prior = 1.618033989
gain = 0.6180339888
P_post = 0.6180339888

Run it

Terminal
$ control-systems solve ctrl_kalman_scalar_steady a=1 c=1 q=1 r=1
P_prior = 1.618033989
gain = 0.6180339888
P_post = 0.6180339888
  by: Steady-state gain of a scalar Kalman filter  (positive root of c^2 P^2 + (r - a^2 r - q c^2) P - q r = 0)
  a = 1
  c = 1
  q = 1
  r = 1
  assumes: c != 0; q, r > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Steady-state Kalman-Bucy filter gain2 fig.ctrl_kalman_bucy

For x' = A x + w (covariance W), y = C x + v (variance V): the steady error covariance P from the filter Riccati equation A P + P A^T – P C^T V^{-1} C P + W = 0, the gain L = P C^T / V and the estimator poles (eigenvalues of A – L C).

AP + PA^T – PC^TV^{-1}CP + W = 0,\quad L = PC^TV^{-1}

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
C
Output vector vector—[1, 0]
W
Process noise covariance (symmetric positive definite) matrix—[1 0; 0 1]
V
Measurement noise variance (> 0)—1

You get

MeaningUnitExample
L
Kalman gain (column)—[1.732050808, 1]
P
Steady error covariance—[[1.732050808, 1], [1, 1.732050808]]
\text{poles}
Estimator poles—[-0.8660254038-0.5i, -0.8660254038+0.5i]

Applies when

  • (A, C) observable
  • W positive definite
  • V > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Kalman-Bucy gain: A = [[0, 1], [0, 0]], C = [1, 0], W = [[1, 0], [0, 1]], V = 1
L = [1.732050808, 1]
P = [[1.732050808, 1], [1, 1.732050808]]
poles = [-0.8660254038-0.5i, -0.8660254038+0.5i]

Run it

Terminal
$ control-systems solve ctrl_kalman_bucy "A=[[0, 1], [0, 0]]" "C=[1, 0]" "W=[[1, 0], [0, 1]]" V=1
L = [1.73205080757,1.0]
P = [[1.73205080757,1.0],[1.0,1.73205080757]]
poles = [{"re":-0.866025403784,"im":-0.5},{"re":-0.866025403784,"im":0.5}]
  by: Steady-state Kalman-Bucy filter gain  (the dual LQR problem (A^T, C^T, W, V) by Newton-Kleinman iteration)
  V = 1
  assumes: (A, C) observable; W positive definite; V > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Frequency response 8

-3 dB bandwidth of a transfer function1 fig.ctrl_bandwidth

The lowest frequency at which |G(jw)| falls to |G(0)|/sqrt(2) for a stable, strictly proper G(s), and the DC gain.

|G(j\omega_B)| = \frac{|G(0)|}{\sqrt 2}

You give

MeaningUnitExample
\text{num}
Numerator vector—[1]
\text{den}
Denominator vector—[1, 1]

You get

MeaningUnitExample
\text{wb}
-3 dB bandwidthrad/s1 rad/s
\text{dc\_gain}
DC gain—1

Applies when

  • stable
  • strictly proper
  • nonzero DC gain

Outside these conditions it says what is wrong instead of returning a number.

Worked example

bandwidth: num = [1], den = [1, 1]
wb = 1 rad/s
dc_gain = 1

Run it

Terminal
$ control-systems solve ctrl_bandwidth "num=[1]" "den=[1, 1]"
wb = 1 rad/s
dc_gain = 1
  by: -3 dB bandwidth of a transfer function  (smallest positive root of 2|N(jw)|^2 - G(0)^2 |D(jw)|^2)
  assumes: stable; strictly proper; nonzero DC gain

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Bandwidth of a standard second-order system2 fig.ctrl_bandwidth_second

The -3 dB bandwidth wn sqrt(1 – 2 zeta^2 + sqrt(4 zeta^4 – 4 zeta^2 + 2)) of wn^2/(s^2 + 2 zeta wn s + wn^2).

\omega_B = \omega_n\sqrt{1-2\zeta^2+\sqrt{4\zeta^4-4\zeta^2+2}}

You give

MeaningUnitExample
\zeta
Damping ratio (> 0)—0.7071067812
\text{wn}
Natural frequency in rad/s—1

You get

MeaningUnitExample
\text{wb}
-3 dB bandwidthrad/s1 rad/s

Applies when

  • zeta > 0
  • wn > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order bandwidth: zeta = 0.7071067811865476, wn = 1
wb = 1 rad/s

Run it

Terminal
$ control-systems solve ctrl_bandwidth_second zeta=0.7071067812 wn=1
wb = 1 rad/s
  by: Bandwidth of a standard second-order system  (solve |G(jw)| = 1/sqrt(2))
  zeta = 0.7071067812
  wn = 1
  assumes: zeta > 0; wn > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Bode magnitude and phase at one frequency1 fig.ctrl_bode_point

|G(jw)|, 20 log10 |G(jw)| and the phase of G(jw) (principal value) of a transfer function at the angular frequency w.

|G(j\omega)|_{dB} = 20\log_{10}|G(j\omega)|

You give

MeaningUnitExample
\text{num}
Numerator coefficients vector—[10]
\text{den}
Denominator coefficients vector—[1, 1, 0]
w
Angular frequency in rad/s—1

You get

MeaningUnitExample
\text{magnitude}
|G(jw)|—7.071067812
\text{magnitude\_db}
Magnitude in dB—16.98970004
\text{phase\_deg}
Phase in degrees, (-180, 180]—-135

Applies when

  • proper G(s)
  • w > 0
  • not a pole on the imaginary axis

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Bode point: num = [10], den = [1, 1, 0], w = 1
magnitude = 7.071067812
magnitude_db = 16.98970004
phase_deg = -135

Run it

Terminal
$ control-systems solve ctrl_bode_point "num=[10]" "den=[1, 1, 0]" w=1
magnitude = 7.071067812
magnitude_db = 16.98970004
phase_deg = -135
  by: Bode magnitude and phase at one frequency  (G(jw) by complex evaluation)
  w = 1
  assumes: proper G(s); w > 0; not a pole on the imaginary axis

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Frequency response of a first-order lag1 fig.ctrl_first_order_freq

Magnitude K/sqrt(1 + (w tau)^2), its value in dB and the phase -atan(w tau) of K/(tau s + 1) at the angular frequency w.

|G(j\omega)| = \frac{K}{\sqrt{1+\omega^2\tau^2}},\quad \angle G = -\arctan(\omega\tau)

You give

MeaningUnitExample
K
DC gain (positive)—10
\tau
Time constant in seconds—1
w
Angular frequency in rad/s—1

You get

MeaningUnitExample
\text{magnitude}
Gain |G(jw)|—7.071067812
\text{magnitude\_db}
Gain in dB—16.98970004
\text{phase\_deg}
Phase in degrees—-45

Applies when

  • K > 0
  • tau > 0
  • w >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

first-order frequency response: K = 10, tau = 1, w = 1
magnitude = 7.071067812
magnitude_db = 16.98970004
phase_deg = -45

Run it

Terminal
$ control-systems solve ctrl_first_order_freq K=10 tau=1 w=1
magnitude = 7.071067812
magnitude_db = 16.98970004
phase_deg = -45
  by: Frequency response of a first-order lag  (G(jw) = K/(1 + j w tau))
  K = 10
  tau = 1
  w = 1
  assumes: K > 0; tau > 0; w >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Frequency response of a standard second-order system1 fig.ctrl_second_order_freq

Magnitude, dB gain and phase (between 0 and -180 degrees) of wn^2/(s^2 + 2 zeta wn s + wn^2) at the angular frequency w.

|G| = \frac{\omega_n^2}{\sqrt{(\omega_n^2-\omega^2)^2 + (2\zeta\omega_n\omega)^2}}

You give

MeaningUnitExample
\zeta
Damping ratio—0.5
\text{wn}
Natural frequency in rad/s—10
w
Angular frequency in rad/s—10

You get

MeaningUnitExample
\text{magnitude}
|G(jw)|—1
\text{magnitude\_db}
Magnitude in dB—0
\text{phase\_deg}
Phase in degrees, [-180, 0]—-90

Applies when

  • zeta >= 0
  • wn > 0
  • w > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order frequency response: zeta = 0.5, wn = 10, w = 10
magnitude = 1
magnitude_db = 0
phase_deg = -90

Run it

Terminal
$ control-systems solve ctrl_second_order_freq zeta=0.5 wn=10 w=10
magnitude = 1
magnitude_db = 0
phase_deg = -90
  by: Frequency response of a standard second-order system  (G(jw) = wn^2/(wn^2 - w^2 + 2 j zeta wn w))
  zeta = 0.5
  wn = 10
  w = 10
  assumes: zeta >= 0; wn > 0; w > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

H-infinity norm (peak gain) of a stable transfer function1 fig.ctrl_hinf_norm

The peak gain sup_w |G(jw)| of a stable proper G(s), in ratio and dB, and the frequency where it occurs (0 when the peak is the DC gain).

\|G\|_\infty = \sup_\omega |G(j\omega)|

You give

MeaningUnitExample
\text{num}
Numerator vector—[1]
\text{den}
Denominator vector—[1, 1]

You get

MeaningUnitExample
\text{hinf}
H-infinity norm (peak gain)—1
\text{hinf\_db}
Peak gain in dB—0
w_{\mathrm{peak}}
Frequency of the peakrad/s0 rad/s

Applies when

  • stable
  • proper
  • peak at a finite frequency

Outside these conditions it says what is wrong instead of returning a number.

Worked example

H-infinity norm: num = [1], den = [1, 1]
hinf = 1
hinf_db = 0
w_peak = 0 rad/s

Run it

Terminal
$ control-systems solve ctrl_hinf_norm "num=[1]" "den=[1, 1]"
hinf = 1
hinf_db = 0
w_peak = 0 rad/s
  by: H-infinity norm (peak gain) of a stable transfer function  (stationary points of |N(jw)|^2/|D(jw)|^2 (positive real roots of f' g - f g') and w = 0)
  assumes: stable; proper; peak at a finite frequency

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

H2 norm of a stable transfer function1 fig.ctrl_h2_norm

||G||_2 = sqrt((1/(2 pi)) integral of |G(jw)|^2 dw) of a stable, strictly proper G(s), computed exactly as B^T Wo B from the observability Gramian.

\|G\|_2^2 = \frac{1}{2\pi}\int_{-\infty}^{\infty}|G(j\omega)|^2d\omega = B^TW_oB

You give

MeaningUnitExample
\text{num}
Numerator vector—[1]
\text{den}
Denominator vector—[1, 1]

You get

MeaningUnitExample
\text{h2}
H2 norm—0.7071067812
\text{h2\_squared}
Squared H2 norm—0.5

Applies when

  • stable
  • strictly proper

Outside these conditions it says what is wrong instead of returning a number.

Worked example

H2 norm: num = [1], den = [1, 1]
h2 = 0.7071067812
h2_squared = 0.5

Run it

Terminal
$ control-systems solve ctrl_h2_norm "num=[1]" "den=[1, 1]"
h2 = 0.7071067812
h2_squared = 0.5
  by: H2 norm of a stable transfer function  (Lyapunov equation of the controllable canonical form in exact fractions)
  assumes: stable; strictly proper

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Resonant peak and resonant frequency1 fig.ctrl_resonant_peak

Peak magnitude Mr = 1/(2 zeta sqrt(1 – zeta^2)) and resonant frequency wr = wn sqrt(1 – 2 zeta^2) of a standard second-order system.

M_r = \frac{1}{2\zeta\sqrt{1-\zeta^2}},\quad \omega_r = \omega_n\sqrt{1-2\zeta^2}

You give

MeaningUnitExample
\zeta
Damping ratio (0 < zeta < 0.7071)—0.5
\text{wn}
Natural frequency in rad/s—10

You get

MeaningUnitExample
\text{Mr}
Resonant peak (ratio)—1.154700538
\text{Mr\_db}
Resonant peak in dB—1.249387366
\text{wr}
Resonant frequencyrad/s7.071067812 rad/s

Applies when

  • 0 < zeta < 1/sqrt(2)

Outside these conditions it says what is wrong instead of returning a number.

Worked example

resonant peak: zeta = 0.5, wn = 10
Mr = 1.154700538
Mr_db = 1.249387366
wr = 7.071067812 rad/s

Run it

Terminal
$ control-systems solve ctrl_resonant_peak zeta=0.5 wn=10
Mr = 1.154700538
Mr_db = 1.249387366
wr = 7.071067812 rad/s
  by: Resonant peak and resonant frequency  (maximum of |G(jw)|)
  zeta = 0.5
  wn = 10
  assumes: 0 < zeta < 1/sqrt(2)

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Identification 3

Damping ratio from the logarithmic decrement1 fig.ctrl_log_decrement

delta = ln(x1/xn)/n from two peak amplitudes n cycles apart, and zeta = delta/sqrt(4 pi^2 + delta^2).

\delta = \frac{1}{n}\ln\frac{x_1}{x_{n+1}},\quad \zeta = \frac{\delta}{\sqrt{4\pi^2+\delta^2}}

You give

MeaningUnitExample
\text{x1}
First peak amplitude (measured from the final value)—1
\text{xn}
Peak amplitude n cycles later—0.5
n
Number of cycles between the peaks—1

You get

MeaningUnitExample
\delta
Logarithmic decrement per cycle—0.6931471806
\zeta
Damping ratio—0.109652581

Applies when

  • 0 < xn < x1
  • n >= 1

Outside these conditions it says what is wrong instead of returning a number.

Worked example

logarithmic decrement: x1 = 1, xn = 0.5, n = 1
delta = 0.6931471806
zeta = 0.109652581

Run it

Terminal
$ control-systems solve ctrl_log_decrement x1=1 xn=0.5 n=1
delta = 0.6931471806
zeta = 0.109652581
  by: Damping ratio from the logarithmic decrement  (ratio of successive peaks)
  x1 = 1
  xn = 0.5
  assumes: 0 < xn < x1; n >= 1

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

First-order-plus-dead-time fit by Smith's two-point method1 fig.ctrl_fopdt_smith

K = dy/du, tau = 1.5 (t63 – t28) and theta = t63 – tau from the times at which a step response reaches 28.3 % and 63.2 % of its change.

\tau = 1.5(t_{63} – t_{28}),\quad \theta = t_{63} – \tau

You give

MeaningUnitExample
\text{t28}
Time to 28.3 % of the change (s)—4
\text{t63}
Time to 63.2 % of the change (s)—10
\text{dy}
Total output change—6
\text{du}
Input step size—2

You get

MeaningUnitExample
K
Process gain—3
\tau
Time constants9 s
\theta
Dead times1 s

Applies when

  • 0 < t28 < t63
  • theta >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Smith FOPDT fit: t28 = 4, t63 = 10, dy = 6, du = 2
K = 3
tau = 9 s
theta = 1 s

Run it

Terminal
$ control-systems solve ctrl_fopdt_smith t28=4 t63=10 dy=6 du=2
K = 3
tau = 9 s
theta = 1 s
  by: First-order-plus-dead-time fit by Smith's two-point method  (Smith's method)
  t28 = 4
  t63 = 10
  dy = 6
  du = 2
  assumes: 0 < t28 < t63; theta >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Second-order model from a step response1 fig.ctrl_identify_second_order

Gain K = y_final/A, percent overshoot, zeta and wn of a standard second-order model from the peak value, the final value and the peak time of a measured step response.

\omega_n = \frac{\pi}{T_p\sqrt{1-\zeta^2}}

You give

MeaningUnitExample
y_{\mathrm{peak}}
Peak value of the response—1.163033535
y_{\mathrm{final}}
Final (steady) value—1
t_{\mathrm{peak}}
Time of the peak in seconds—0.9068996821
A
Step size (default 1) optional—

You get

MeaningUnitExample
K
DC gain—1
\text{PO}
Percent overshoot (%)—16.30335348
\zeta
Damping ratio—0.5
\text{wn}
Natural frequencyrad/s4 rad/s

An optional input can be left out: a stated default is used.

Applies when

  • 0 < overshoot < 100 %
  • t_peak > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order model from step response: y_peak = 1.163033534821, y_final = 1, t_peak = 0.906899682118
K = 1
PO = 16.30335348
zeta = 0.5
wn = 4 rad/s

Run it

Terminal
$ control-systems solve ctrl_identify_second_order y_peak=1.163033535 y_final=1 t_peak=0.9068996821
K = 1
PO = 16.3033535
zeta = 0.4999999998
wn = 3.999999999 rad/s
  by: Second-order model from a step response  (zeta from the overshoot, wn = pi/(tp sqrt(1 - zeta^2)))
  y_peak = 1.163033535
  y_final = 1
  t_peak = 0.9068996821
  assumes: 0 < overshoot < 100 %; t_peak > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Modelling 1

Speed transfer function of a DC motor2 fig.ctrl_dc_motor

omega(s)/V(s) = Kt/((J s + b)(L s + R) + Kt²) of an armature-controlled DC motor (Ke = Kt in SI units): coefficients, DC gain and poles.

\frac{\Omega(s)}{V(s)} = \frac{K_t}{(Js+b)(Ls+R)+K_t^2}

You give

MeaningUnitExample
J
Rotor inertia in kg*m²—0.01
b
Viscous friction in N*m*s—0.1
\text{Kt}
Torque (= back-emf) constant in N*m/A—0.01
R
Armature resistance in ohm—1
L
Armature inductance in H—0.5

You get

MeaningUnitExample
\text{num}
Numerator—[0.01]
\text{den}
Denominator [J L, b L + J R, b R + Kt^2]—[0.005, 0.06, 0.1001]
\text{dc\_gain}
Speed per volt at steady state (rad/s per V)—0.0999000999
\text{poles}
Poles—[-9.997499218, -2.002500782]

Applies when

  • J, Kt, R, L > 0
  • b >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

DC motor transfer function: J = 0.01, b = 0.1, Kt = 0.01, R = 1, L = 0.5
num = [0.01]
den = [0.005, 0.06, 0.1001]
dc_gain = 0.0999000999
poles = [-9.997499218, -2.002500782]

Run it

Terminal
$ control-systems solve ctrl_dc_motor J=0.01 b=0.1 Kt=0.01 R=1 L=0.5
num = [0.01]
den = [0.005,0.06,0.1001]
dc_gain = 0.0999000999
poles = [-9.99749921826,-2.00250078174]
  by: Speed transfer function of a DC motor  (electrical and mechanical equations combined)
  J = 0.01
  b = 0.1
  Kt = 0.01
  R = 1
  L = 0.5
  assumes: J, Kt, R, L > 0; b >= 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Optimal control 3

Continuous-time LQR gain (Riccati equation)2 fig.ctrl_lqr

The optimal state feedback u = -K x minimising the integral of x^T Q x + R u^2 for x' = A x + B u: the Riccati solution P, K = R^{-1} B^T P and the closed-loop poles.

A^TP + PA – PBR^{-1}B^TP + Q = 0,\quad K = R^{-1}B^TP

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
B
Input vector vector—[0, 1]
Q
State weight (symmetric positive definite) matrix—[1 0; 0 1]
R
Input weight (> 0)—1

You get

MeaningUnitExample
K
Optimal gain—[1, 1.732050808]
P
Riccati solution—[[1.732050808, 1], [1, 1.732050808]]
\text{poles}
Closed-loop poles—[-0.8660254038-0.5i, -0.8660254038+0.5i]

Applies when

  • (A, B) controllable
  • Q symmetric positive definite
  • R > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

LQR gain: A = [[0, 1], [0, 0]], B = [0, 1], Q = [[1, 0], [0, 1]], R = 1
K = [1, 1.732050808]
P = [[1.732050808, 1], [1, 1.732050808]]
poles = [-0.8660254038-0.5i, -0.8660254038+0.5i]

Run it

Terminal
$ control-systems solve ctrl_lqr "A=[[0, 1], [0, 0]]" "B=[0, 1]" "Q=[[1, 0], [0, 1]]" R=1
K = [1.0,1.73205080757]
P = [[1.73205080757,1.0],[1.0,1.73205080757]]
poles = [{"re":-0.866025403784,"im":-0.5},{"re":-0.866025403784,"im":0.5}]
  by: Continuous-time LQR gain (Riccati equation)  (Newton-Kleinman iteration (a Lyapunov equation per step) from a stabilising Ackermann gain)
  R = 1
  assumes: (A, B) controllable; Q symmetric positive definite; R > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Discrete-time LQR gain (discrete Riccati equation)2 fig.ctrl_dlqr

The optimal state feedback u_k = -K x_k minimising the sum of x^T Q x + R u^2 for x_{k+1} = A x_k + B u_k: P from the discrete Riccati equation, K = (R + B^T P B)^{-1} B^T P A and the closed-loop poles.

P = A^TPA – A^TPB(R + B^TPB)^{-1}B^TPA + Q

You give

MeaningUnitExample
A
Discrete state matrix (n x n) matrix—[1 1; 0 1]
B
Input vector vector—[0, 1]
Q
State weight (symmetric positive definite) matrix—[1 0; 0 1]
R
Input weight (> 0)—1

You get

MeaningUnitExample
K
Optimal gain—[0.4220824404, 1.243928854]
P
Riccati solution—[[2.947122967, 2.369205407], [2.369205407, 4.613134261]]
\text{poles}
Closed-loop poles—[0.378035573-0.1877303705i, 0.378035573+0.1877303705i]

Applies when

  • (A, B) controllable
  • Q symmetric positive definite
  • R > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete LQR gain: A = [[1, 1], [0, 1]], B = [0, 1], Q = [[1, 0], [0, 1]], R = 1
K = [0.4220824404, 1.243928854]
P = [[2.947122967, 2.369205407], [2.369205407, 4.613134261]]
poles = [0.378035573-0.1877303705i, 0.378035573+0.1877303705i]

Run it

Terminal
$ control-systems solve ctrl_dlqr "A=[[1, 1], [0, 1]]" "B=[0, 1]" "Q=[[1, 0], [0, 1]]" R=1
K = [0.422082440385,1.2439288539]
P = [[2.94712296671,2.36920540709],[2.36920540709,4.613134261]]
poles = [{"re":0.378035573048,"im":-0.187730370457},{"re":0.378035573048,"im":0.187730370457}]
  by: Discrete-time LQR gain (discrete Riccati equation)  (Hewer's iteration (a discrete Lyapunov equation per step) from the deadbeat gain)
  R = 1
  assumes: (A, B) controllable; Q symmetric positive definite; R > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

LQR for a first-order system1 fig.ctrl_scalar_lqr

For x' = a x + b u minimising the integral of q x^2 + r u^2: the Riccati solution P, the optimal gain K = b P/r and the closed-loop pole -sqrt(a^2 + b^2 q/r).

2aP – \frac{b^2P^2}{r} + q = 0,\quad K = \frac{bP}{r}

You give

MeaningUnitExample
a
System coefficient—1
b
Input coefficient—1
q
State weight (> 0)—3
r
Input weight (> 0)—1

You get

MeaningUnitExample
P
Riccati solution—3
K
Optimal feedback gain—3
\text{pole}
Closed-loop pole a – b K—-2

Applies when

  • b != 0
  • q, r > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

scalar LQR: a = 1, b = 1, q = 3, r = 1
P = 3
K = 3
pole = -2

Run it

Terminal
$ control-systems solve ctrl_scalar_lqr a=1 b=1 q=3 r=1
P = 3
K = 3
pole = -2
  by: LQR for a first-order system  (positive root of 2 a P - b^2 P^2 / r + q = 0)
  a = 1
  b = 1
  q = 3
  r = 1
  assumes: b != 0; q, r > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

PID tuning 9

Closed-loop poles of a PID loop2 fig.ctrl_pid_closed_loop

The characteristic polynomial s D(s) + (Kd s^2 + Kp s + Ki) N(s), the closed-loop poles and stability of a plant N/D under a PID controller with unity feedback.

sD(s) + (K_ds^2 + K_ps + K_i)N(s) = 0

You give

MeaningUnitExample
\text{num}
Plant numerator vector—[1]
\text{den}
Plant denominator vector—[1, 1]
\text{Kp}
Proportional gain—3
\text{Ki}
Integral gain (default 0) optional—2
\text{Kd}
Derivative gain (default 0) optional—

You get

MeaningUnitExample
\text{char\_poly}
Closed-loop characteristic polynomial—[1, 4, 2]
\text{poles}
Closed-loop poles—[-3.414213562, -0.5857864376]
\text{max\_real\_part}
Largest real part—-0.5857864376
\text{stable}
True when every pole is in the left half plane—True

An optional input can be left out: a stated default is used.

Applies when

  • strictly proper plant

Outside these conditions it says what is wrong instead of returning a number.

Worked example

PID closed-loop poles: num = [1], den = [1, 1], Kp = 3, Ki = 2
char_poly = [1, 4, 2]
poles = [-3.414213562, -0.5857864376]
max_real_part = -0.5857864376
stable = True

Run it

Terminal
$ control-systems solve ctrl_pid_closed_loop "num=[1]" "den=[1, 1]" Kp=3 Ki=2
char_poly = [1.0,4.0,2.0]
poles = [-3.41421356237,-0.585786437627]
max_real_part = -0.5857864376
stable = true
  by: Closed-loop poles of a PID loop  (1 + C(s) G(s) = 0, roots by the Aberth-Ehrlich iteration)
  Kp = 3
  Ki = 2
  assumes: strictly proper plant

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Cohen-Coon tuning of a first-order-plus-dead-time process1 fig.ctrl_cohen_coon

P, PI or PID settings from the Cohen-Coon formulas for K e^{-theta s}/(tau s + 1).

\text{PID: } K_p = \frac{\tau}{K\theta}\left(\frac{4}{3} + \frac{\theta}{4\tau}\right),\; T_i = \theta\frac{32+6\theta/\tau}{13+8\theta/\tau},\; T_d = \frac{4\theta}{11+2\theta/\tau}

You give

MeaningUnitExample
K
Process gain—2
\tau
Time constant in seconds—10
\theta
Dead time in seconds—2
\text{controller}
Controller type choice—PID

You get

MeaningUnitExample
\text{Kp}
Proportional gain—3.458333333
\text{Ti}
Integral time (0 when there is no integral action)s4.547945205 s
\text{Td}
Derivative times0.701754386 s
\text{Ki}
Integral gain Kp/Ti—0.7604166667
\text{Kd}
Derivative gain Kp Td—2.426900585

Applies when

  • K, tau, theta > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Cohen-Coon tuning: K = 2, tau = 10, theta = 2, controller = PID
Kp = 3.458333333
Ti = 4.547945205 s
Td = 0.701754386 s
Ki = 0.7604166667
Kd = 2.426900585

Run it

Terminal
$ control-systems solve ctrl_cohen_coon K=2 tau=10 theta=2 controller=PID
Kp = 3.458333333
Ti = 4.547945205 s
Td = 0.701754386 s
Ki = 0.7604166667
Kd = 2.426900585
  by: Cohen-Coon tuning of a first-order-plus-dead-time process  (Cohen-Coon formulas (r = theta/tau))
  K = 2
  tau = 10
  theta = 2
  controller = PID
  assumes: K, tau, theta > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

PI gains placing the closed-loop poles of a first-order plant1 fig.ctrl_pi_pole_placement

Kp = (2 zeta wn tau – 1)/K and Ki = wn^2 tau/K, so that K/(tau s + 1) under PI control with unity feedback has the characteristic polynomial s^2 + 2 zeta wn s + wn^2.

K_p = \frac{2\zeta\omega_n\tau – 1}{K},\quad K_i = \frac{\omega_n^2\tau}{K}

You give

MeaningUnitExample
K
Plant gain—2
\tau
Plant time constant in seconds—5
\zeta
Desired damping ratio—0.7
\text{wn}
Desired natural frequency in rad/s—1

You get

MeaningUnitExample
\text{Kp}
Proportional gain—3
\text{Ki}
Integral gain—2.5

Applies when

  • tau, zeta, wn > 0
  • K != 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

PI pole placement: K = 2, tau = 5, zeta = 0.7, wn = 1
Kp = 3
Ki = 2.5

Run it

Terminal
$ control-systems solve ctrl_pi_pole_placement K=2 tau=5 zeta=0.7 wn=1
Kp = 3
Ki = 2.5
  by: PI gains placing the closed-loop poles of a first-order plant  (match tau s^2 + (1 + K Kp) s + K Ki to tau (s^2 + 2 zeta wn s + wn^2))
  K = 2
  tau = 5
  zeta = 0.7
  wn = 1
  assumes: tau, zeta, wn > 0; K != 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

PID: parallel gains to standard form1 fig.ctrl_pid_parallel_to_standard

The integral time Ti = Kp/Ki and derivative time Td = Kd/Kp of a parallel PID Kp + Ki/s + Kd s.

T_i = K_p/K_i,\quad T_d = K_d/K_p

You give

MeaningUnitExample
\text{Kp}
Proportional gain (> 0)—6
\text{Ki}
Integral gain (> 0)—3
\text{Kd}
Derivative gain—3

You get

MeaningUnitExample
\text{Ti}
Integral times2 s
\text{Td}
Derivative times0.5 s

Applies when

  • Kp > 0
  • Ki > 0
  • Kd >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

PID parallel to standard: Kp = 6, Ki = 3, Kd = 3
Ti = 2 s
Td = 0.5 s

Run it

Terminal
$ control-systems solve ctrl_pid_parallel_to_standard Kp=6 Ki=3 Kd=3
Ti = 2 s
Td = 0.5 s
  by: PID: parallel gains to standard form  (Ti = Kp/Ki, Td = Kd/Kp)
  Kp = 6
  Ki = 3
  Kd = 3
  assumes: Kp > 0; Ki > 0; Kd >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

PID: standard form to parallel gains1 fig.ctrl_pid_standard_to_parallel

Ki = Kp/Ti and Kd = Kp Td from the standard (ideal ISA) form Kp (1 + 1/(Ti s) + Td s).

K_i = K_p/T_i,\quad K_d = K_pT_d

You give

MeaningUnitExample
\text{Kp}
Proportional gain—4
\text{Ti}
Integral (reset) time in seconds—2
\text{Td}
Derivative time in seconds—0.5

You get

MeaningUnitExample
\text{Ki}
Integral gain—2
\text{Kd}
Derivative gain—2

Applies when

  • Ti > 0
  • Td >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

PID standard to parallel: Kp = 4, Ti = 2, Td = 0.5
Ki = 2
Kd = 2

Run it

Terminal
$ control-systems solve ctrl_pid_standard_to_parallel Kp=4 Ti=2 Td=0.5
Ki = 2
Kd = 2
  by: PID: standard form to parallel gains  (Ki = Kp/Ti, Kd = Kp Td)
  Kp = 4
  Ti = 2
  Td = 0.5
  assumes: Ti > 0; Td >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

SIMC (Skogestad) PI tuning1 fig.ctrl_simc_pi

PI settings Kp = tau/(K (tau_c + theta)), Ti = min(tau, 4 (tau_c + theta)) for K e^{-theta s}/(tau s + 1); the default tau_c equals theta.

K_p = \frac{\tau}{K(\tau_c+\theta)},\; T_i = \min(\tau, 4(\tau_c+\theta))

You give

MeaningUnitExample
K
Process gain—2
\tau
Time constant in seconds—10
\theta
Dead time in seconds—1
\tau_{c}
Desired closed-loop time constant (default theta) optional—

You get

MeaningUnitExample
\text{Kp}
Proportional gain—2.5
\text{Ti}
Integral times8 s
\text{Ki}
Integral gain—0.3125

An optional input can be left out: a stated default is used.

Applies when

  • K, tau, theta > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

SIMC PI tuning: K = 2, tau = 10, theta = 1
Kp = 2.5
Ti = 8 s
Ki = 0.3125

Run it

Terminal
$ control-systems solve ctrl_simc_pi K=2 tau=10 theta=1
Kp = 2.5
Ti = 8 s
Ki = 0.3125
  by: SIMC (Skogestad) PI tuning  (Skogestad's SIMC rule)
  K = 2
  tau = 10
  theta = 1
  assumes: K, tau, theta > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Ziegler-Nichols tuning from a plant model1 fig.ctrl_zn_from_plant

The ultimate gain Ku = 1/|G(j w180)| and period Pu = 2 pi/w180 of a plant model, then the Ziegler-Nichols P, PI or PID settings.

K_u = \frac{1}{|G(j\omega_{180})|},\quad P_u = \frac{2\pi}{\omega_{180}}

You give

MeaningUnitExample
\text{num}
Plant numerator vector—[1]
\text{den}
Plant denominator vector—[1, 3, 2, 0]
\text{controller}
Controller type choice—PID

You get

MeaningUnitExample
\text{Kp}
Proportional gain—3.6
\text{Ti}
Integral time (0 when there is no integral action)s2.221441469 s
\text{Td}
Derivative times0.5553603673 s
\text{Ki}
Integral gain Kp/Ti—1.620569369
\text{Kd}
Derivative gain Kp Td—1.999297322
\text{Ku}
Ultimate gain—6
\text{Pu}
Ultimate periods4.442882938 s

Applies when

  • exactly one phase crossover

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Ziegler-Nichols from plant: num = [1], den = [1, 3, 2, 0], controller = PID
Kp = 3.6
Ti = 2.221441469 s
Td = 0.5553603673 s
Ki = 1.620569369
Kd = 1.999297322
Ku = 6

Run it

Terminal
$ control-systems solve ctrl_zn_from_plant "num=[1]" "den=[1, 3, 2, 0]" controller=PID
Kp = 3.6
Ti = 2.221441469 s
Td = 0.5553603673 s
Ki = 1.620569369
Kd = 1.999297322
Ku = 6
Pu = 4.442882938 s
  by: Ziegler-Nichols tuning from a plant model  (phase crossover from Im(N(jw) conj D(jw)) = 0; Ziegler-Nichols table)
  controller = PID
  assumes: exactly one phase crossover

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Ziegler-Nichols tuning from the process reaction curve1 fig.ctrl_zn_reaction

P, PI or PID settings from the open-loop (reaction-curve) Ziegler-Nichols table for a process with gain K, dead time L and time constant T.

\text{PID: } K_p = \frac{1.2\,T}{KL},\; T_i = 2L,\; T_d = 0.5L

You give

MeaningUnitExample
K
Process gain—2
L
Apparent dead time in seconds—1
T
Apparent time constant in seconds—10
\text{controller}
Controller type choice—PID

You get

MeaningUnitExample
\text{Kp}
Proportional gain—6
\text{Ti}
Integral time (0 when there is no integral action)s2 s
\text{Td}
Derivative times0.5 s
\text{Ki}
Integral gain Kp/Ti—3
\text{Kd}
Derivative gain Kp Td—3

Applies when

  • K, L, T > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Ziegler-Nichols reaction curve tuning: K = 2, L = 1, T = 10, controller = PID
Kp = 6
Ti = 2 s
Td = 0.5 s
Ki = 3
Kd = 3

Run it

Terminal
$ control-systems solve ctrl_zn_reaction K=2 L=1 T=10 controller=PID
Kp = 6
Ti = 2 s
Td = 0.5 s
Ki = 3
Kd = 3
  by: Ziegler-Nichols tuning from the process reaction curve  (Ziegler-Nichols reaction-curve table)
  K = 2
  L = 1
  T = 10
  controller = PID
  assumes: K, L, T > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Ziegler-Nichols tuning from the ultimate gain and period1 fig.ctrl_zn_ultimate

P, PI or PID settings from the closed-loop (ultimate-cycle) Ziegler-Nichols table: P 0.5 Ku; PI 0.45 Ku, Ti = Pu/1.2; PID 0.6 Ku, Ti = Pu/2, Td = Pu/8.

\text{PID: } K_p = 0.6K_u,\; T_i = P_u/2,\; T_d = P_u/8

You give

MeaningUnitExample
\text{Ku}
Ultimate gain (gain at sustained oscillation)—10
\text{Pu}
Ultimate period in seconds—4
\text{controller}
Controller type choice—PID

You get

MeaningUnitExample
\text{Kp}
Proportional gain—6
\text{Ti}
Integral time (0 when there is no integral action)s2 s
\text{Td}
Derivative times0.5 s
\text{Ki}
Integral gain Kp/Ti—3
\text{Kd}
Derivative gain Kp Td—3

Applies when

  • Ku > 0
  • Pu > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Ziegler-Nichols ultimate tuning: Ku = 10, Pu = 4, controller = PID
Kp = 6
Ti = 2 s
Td = 0.5 s
Ki = 3
Kd = 3

Run it

Terminal
$ control-systems solve ctrl_zn_ultimate Ku=10 Pu=4 controller=PID
Kp = 6
Ti = 2 s
Td = 0.5 s
Ki = 3
Kd = 3
  by: Ziegler-Nichols tuning from the ultimate gain and period  (Ziegler-Nichols ultimate-cycle table)
  Ku = 10
  Pu = 4
  controller = PID
  assumes: Ku > 0; Pu > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Root locus 5

Root-locus angle of arrival at a complex zero1 fig.ctrl_arrival_angle

The angle at which the root locus arrives at the complex zero z (upper half plane): 180 + sum of angles from the poles – sum of angles from the other zeros.

\theta_a = 180^\circ + \sum\angle(z – p_j) – \sum_{i\ne}\angle(z – z_i)

You give

MeaningUnitExample
\text{num}
Numerator of G(s) (exactly one complex zero pair) vector—[1, 2, 2]
\text{den}
Denominator of G(s) vector—[1, 0]

You get

MeaningUnitExample
\text{zero}
The complex zero (positive imaginary part)—-1+1i
\text{arrival\_deg}
Angle of arrival in degrees, (-180, 180]—-135

Applies when

  • exactly one complex zero pair
  • same-sign leading coefficients

Outside these conditions it says what is wrong instead of returning a number.

Worked example

angle of arrival: num = [1, 2, 2], den = [1, 0]
zero = -1+1i
arrival_deg = -135

Run it

Terminal
$ control-systems solve ctrl_arrival_angle "num=[1, 2, 2]" "den=[1, 0]"
zero = {"re":-1.0,"im":1.0}
arrival_deg = -135
  by: Root-locus angle of arrival at a complex zero  (angle criterion at the zero)
  assumes: exactly one complex zero pair; same-sign leading coefficients

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Root-locus angle of departure from a complex pole1 fig.ctrl_departure_angle

The angle at which the root locus leaves the complex pole p (upper half plane): 180 + sum of angles from the zeros – sum of angles from the other poles.

\theta_d = 180^\circ + \sum\angle(p – z_i) – \sum_{j\ne}\angle(p – p_j)

You give

MeaningUnitExample
\text{num}
Numerator of G(s) vector—[1]
\text{den}
Denominator of G(s) (exactly one complex pole pair) vector—[1, 2, 2, 0]

You get

MeaningUnitExample
\text{pole}
The complex pole (positive imaginary part)—-1+1i
\text{departure\_deg}
Angle of departure in degrees, (-180, 180]—-45

Applies when

  • exactly one complex pole pair
  • same-sign leading coefficients

Outside these conditions it says what is wrong instead of returning a number.

Worked example

angle of departure: num = [1], den = [1, 2, 2, 0]
pole = -1+1i
departure_deg = -45

Run it

Terminal
$ control-systems solve ctrl_departure_angle "num=[1]" "den=[1, 2, 2, 0]"
pole = {"re":-1.0,"im":1.0}
departure_deg = -45
  by: Root-locus angle of departure from a complex pole  (angle criterion at the pole)
  assumes: exactly one complex pole pair; same-sign leading coefficients

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Root-locus asymptotes (centroid and angles)1 fig.ctrl_locus_asymptotes

Number of asymptotes n – m, their real-axis intercept (sum of poles – sum of zeros)/(n – m) and their angles (2k+1) 180/(n – m) degrees.

\sigma_a = \frac{\sum p_i – \sum z_j}{n-m},\quad \theta_k = \frac{(2k+1)180^\circ}{n-m}

You give

MeaningUnitExample
\text{num}
Numerator of the open-loop G(s) vector—[1]
\text{den}
Denominator of G(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
n_{\mathrm{asymptotes}}
Number of asymptotes—3
\text{centroid}
Real-axis intercept—-1
\text{angles\_deg}
Asymptote angles in degrees—[60, 180, 300]

Applies when

  • more poles than zeros

Outside these conditions it says what is wrong instead of returning a number.

Worked example

root locus asymptotes: num = [1], den = [1, 3, 2, 0]
n_asymptotes = 3
centroid = -1
angles_deg = [60, 180, 300]

Run it

Terminal
$ control-systems solve ctrl_locus_asymptotes "num=[1]" "den=[1, 3, 2, 0]"
n_asymptotes = 3
centroid = -1
angles_deg = [60.0,180.0,300.0]
  by: Root-locus asymptotes (centroid and angles)  (sums of poles and zeros from the coefficients)
  assumes: more poles than zeros

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Root-locus breakaway and break-in points1 fig.ctrl_breakaway_points

Real points where root-locus branches meet and leave (or enter) the real axis: real roots of N D' – N' D = 0 with a positive gain K = -D/N there.

\frac{dK}{ds} = 0 \iff N D' – N' D = 0

You give

MeaningUnitExample
\text{num}
Numerator of G(s) vector—[1]
\text{den}
Denominator of G(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
\text{points}
Breakaway/break-in points, ascending—[-0.4226497308]
\text{gains}
Gain K at each point—[0.3849001795]
\text{count}
Number of points—1

Applies when

  • proper G(s)

Outside these conditions it says what is wrong instead of returning a number.

Worked example

breakaway points: num = [1], den = [1, 3, 2, 0]
points = [-0.4226497308]
gains = [0.3849001795]
count = 1

Run it

Terminal
$ control-systems solve ctrl_breakaway_points "num=[1]" "den=[1, 3, 2, 0]"
points = [-0.42264973081]
gains = [0.38490017946]
count = 1
  by: Root-locus breakaway and break-in points  (dK/ds = 0 with K = -D(s)/N(s))
  assumes: proper G(s)

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Root-locus gain and angle at a test point2 fig.ctrl_locus_gain_at_point

The gain K = 1/|G(s0)| that would place a closed-loop pole at s0 (magnitude criterion) and the angle of G(s0); s0 lies on the root locus for K > 0 when the angle is 180 degrees (angle error 0).

K = \frac{1}{|G(s_0)|},\quad \angle G(s_0) = \pm 180^\circ

You give

MeaningUnitExample
\text{num}
Numerator of G(s) vector—[1]
\text{den}
Denominator of G(s) vector—[1, 2, 0]
s_{\mathrm{re}}
Real part of the test point—-1
s_{\mathrm{im}}
Imaginary part of the test point—1

You get

MeaningUnitExample
K
Gain from the magnitude criterion—2
\text{angle\_deg}
Angle of G(s0) in degrees—180
\text{angle\_error\_deg}
180 – |angle|: 0 on the locus—0

Applies when

  • s0 neither a pole nor a zero

Outside these conditions it says what is wrong instead of returning a number.

Worked example

root locus gain at a point: num = [1], den = [1, 2, 0], s_re = -1, s_im = 1
K = 2
angle_deg = 180
angle_error_deg = 0

Run it

Terminal
$ control-systems solve ctrl_locus_gain_at_point "num=[1]" "den=[1, 2, 0]" s_re=-1 s_im=1
K = 2
angle_deg = 180
angle_error_deg = 0
  by: Root-locus gain and angle at a test point  (K = |D(s0)|/|N(s0)|)
  s_re = -1
  s_im = 1
  assumes: s0 neither a pole nor a zero

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Stability 9

Continuous Lyapunov equation1 fig.ctrl_lyapunov

The solution P of A^T P + P A + Q = 0 for a stable A (exact), and whether P is positive definite (with Q > 0 that certifies stability).

A^TP + PA + Q = 0

You give

MeaningUnitExample
A
State matrix (stable, n x n) matrix—[-1 0; 0 -2]
Q
Symmetric matrix (n x n) matrix—[2 0; 0 4]

You get

MeaningUnitExample
P
Solution P—[[1, 0], [0, 1]]
\text{positive\_definite}
True when P is positive definite—True

Applies when

  • A stable
  • Q symmetric

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Lyapunov equation: A = [[-1, 0], [0, -2]], Q = [[2, 0], [0, 4]]
P = [[1, 0], [0, 1]]
positive_definite = True

Run it

Terminal
$ control-systems solve ctrl_lyapunov "A=[[-1, 0], [0, -2]]" "Q=[[2, 0], [0, 4]]"
P = [[1.0,0.0],[0.0,1.0]]
positive_definite = true
  by: Continuous Lyapunov equation  (Kronecker linear system in exact fractions)
  assumes: A stable; Q symmetric

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Discrete Lyapunov equation1 fig.ctrl_discrete_lyapunov

The solution P of A^T P A – P + Q = 0 for a Schur-stable A (exact), and whether P is positive definite.

A^TPA – P + Q = 0

You give

MeaningUnitExample
A
Discrete state matrix (eigenvalues inside the unit circle) matrix—[0.5 0; 0 0.5]
Q
Symmetric matrix (n x n) matrix—[3 0; 0 3]

You get

MeaningUnitExample
P
Solution P—[[4, 0], [0, 4]]
\text{positive\_definite}
True when P is positive definite—True

Applies when

  • A Schur stable
  • Q symmetric

Outside these conditions it says what is wrong instead of returning a number.

Worked example

discrete Lyapunov equation: A = [[0.5, 0], [0, 0.5]], Q = [[3, 0], [0, 3]]
P = [[4, 0], [0, 4]]
positive_definite = True

Run it

Terminal
$ control-systems solve ctrl_discrete_lyapunov "A=[[0.5, 0], [0, 0.5]]" "Q=[[3, 0], [0, 3]]"
P = [[4.0,0.0],[0.0,4.0]]
positive_definite = true
  by: Discrete Lyapunov equation  (Kronecker linear system in exact fractions)
  assumes: A Schur stable; Q symmetric

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Gain and phase margins1 fig.ctrl_stability_margins

Gain margin 1/|L(j w_pc)| (ratio and dB) at the phase crossover and phase margin 180 + arg L(j w_gc) at the gain crossover of an open loop L(s).

GM = \frac{1}{|L(j\omega_{pc})|},\quad PM = 180^\circ + \angle L(j\omega_{gc})

You give

MeaningUnitExample
\text{num}
Numerator of the open loop L(s) vector—[10]
\text{den}
Denominator of L(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
\text{gm}
Gain margin (ratio)—0.6
\text{gm\_db}
Gain margin in dB—-4.436974992
w_{\mathrm{pc}}
Phase-crossover frequencyrad/s1.414213562 rad/s
\text{pm\_deg}
Phase margin in degrees—-12.99720802
w_{\mathrm{gc}}
Gain-crossover frequencyrad/s1.802203305 rad/s

Applies when

  • exactly one gain crossover and one phase crossover

Outside these conditions it says what is wrong instead of returning a number.

Worked example

stability margins: num = [10], den = [1, 3, 2, 0]
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s

Run it

Terminal
$ control-systems solve ctrl_stability_margins "num=[10]" "den=[1, 3, 2, 0]"
gm = 0.6
gm_db = -4.436974992
w_pc = 1.414213562 rad/s
pm_deg = -12.99720802
w_gc = 1.802203305 rad/s
  by: Gain and phase margins  (crossovers as positive real roots of |N|^2 - |D|^2 and Im(N(jw) conj D(jw)), Newton-polished)
  assumes: exactly one gain crossover and one phase crossover

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Jury (Schur-Cohn) stability test for a discrete polynomial1 fig.ctrl_jury_test

Whether every root of a polynomial in z lies inside the unit circle (Jury/Schur-Cohn table in exact arithmetic), with the necessary-condition values P(1) and (-1)^n P(-1).

|a_n| < a_0,\; P(1) > 0,\; (-1)^nP(-1) > 0

You give

MeaningUnitExample
\text{coefficients}
Polynomial coefficients in z, highest power first vector—[1, -1.2, 0.35]

You get

MeaningUnitExample
\text{stable}
True when every root is inside the unit circle—True
\text{P1}
P(1) (must be > 0)—0.15
\text{Pm1}
(-1)^n P(-1) (must be > 0)—2.55

Applies when

  • the table is not singular

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Jury test: coefficients = [1, -1.2, 0.35]
stable = True
P1 = 0.15
Pm1 = 2.55

Run it

Terminal
$ control-systems solve ctrl_jury_test "coefficients=[1, -1.2, 0.35]"
stable = true
P1 = 0.15
Pm1 = 2.55
  by: Jury (Schur-Cohn) stability test for a discrete polynomial  (Schur-Cohn reduction q <- q - (a_n/a_0) q_reversed in exact fractions)
  assumes: the table is not singular

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Nyquist criterion: encirclements of -12 fig.ctrl_nyquist

For the open loop K G(s) under unity negative feedback: the open-loop right-half-plane poles P, the closed-loop right-half-plane poles Z and the net clockwise encirclements N = Z – P of the point -1 by the Nyquist curve.

Z = N + P

You give

MeaningUnitExample
\text{num}
Numerator of G(s) vector—[1]
\text{den}
Denominator of G(s) vector—[1, -1]
K
Loop gain (default 1) optional—2

You get

MeaningUnitExample
P
Open-loop poles in the right half plane—1
Z
Closed-loop poles in the right half plane—0
N
Net clockwise encirclements of -1—-1
\text{stable}
True when Z = 0—True

An optional input can be left out: a stated default is used.

Applies when

  • no open-loop or closed-loop pole on the imaginary axis

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Nyquist encirclements: num = [1], den = [1, -1], K = 2
P = 1
Z = 0
N = -1
stable = True

Run it

Terminal
$ control-systems solve ctrl_nyquist "num=[1]" "den=[1, -1]" K=2
P = 1
Z = 0
N = -1
stable = true
  by: Nyquist criterion: encirclements of -1  (Z = N + P from the poles of G and of 1 + K G)
  K = 2
  assumes: no open-loop or closed-loop pole on the imaginary axis

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Peak sensitivity Ms and modulus margin1 fig.ctrl_sensitivity_peak

Ms = max |1/(1 + L(jw))| of a stable unity-feedback loop, its frequency, and the modulus (stability) margin 1/Ms: the smallest distance from the Nyquist curve to -1.

M_s = \max_\omega \frac{1}{|1 + L(j\omega)|}

You give

MeaningUnitExample
\text{num}
Numerator of the open loop L(s) vector—[2]
\text{den}
Denominator of L(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
\text{Ms}
Peak sensitivity—2.312314015
\text{Ms\_db}
Peak sensitivity in dB—7.280936228
w_{\mathrm{ms}}
Frequency of the peakrad/s0.9252900062 rad/s
\text{sm}
Modulus margin 1/Ms—0.4324672141

Applies when

  • closed loop stable
  • strictly proper L(s)

Outside these conditions it says what is wrong instead of returning a number.

Worked example

peak sensitivity: num = [2], den = [1, 3, 2, 0]
Ms = 2.312314015
Ms_db = 7.280936228
w_ms = 0.9252900062 rad/s
sm = 0.4324672141

Run it

Terminal
$ control-systems solve ctrl_sensitivity_peak "num=[2]" "den=[1, 3, 2, 0]"
Ms = 2.312314015
Ms_db = 7.280936228
w_ms = 0.9252900062 rad/s
sm = 0.4324672141
  by: Peak sensitivity Ms and modulus margin  (stationary points of |D(jw)|^2/|D(jw) + N(jw)|^2 (positive real roots of f' g - f g'))
  assumes: closed loop stable; strictly proper L(s)

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Poles and stability of a characteristic polynomial1 fig.ctrl_pole_stability

The roots (poles) of a characteristic polynomial and whether the system is asymptotically stable, marginally stable or unstable.

\text{stable} \iff \operatorname{Re} p_i < 0 \;\forall i

You give

MeaningUnitExample
\text{den}
Characteristic polynomial (denominator) coefficients, highest power first vector—[1, 6, 11, 6]

You get

MeaningUnitExample
\text{poles}
The poles—[-3, -2, -1]
\text{max\_real\_part}
Largest real part of the poles—-1
\text{stability}
Asymptotically stable, marginally stable or unstable—asymptotically stable

Applies when

  • degree 1 or more

Outside these conditions it says what is wrong instead of returning a number.

Worked example

pole stability: den = [1, 6, 11, 6]
poles = [-3, -2, -1]
max_real_part = -1
stability = asymptotically stable

Run it

Terminal
$ control-systems solve ctrl_pole_stability "den=[1, 6, 11, 6]"
poles = [-3.0,-2.0,-1.0]
max_real_part = -1
stability = asymptotically stable
  by: Poles and stability of a characteristic polynomial  (roots by the Aberth-Ehrlich iteration; simple poles on the imaginary axis are marginal, repeated ones unstable)
  assumes: degree 1 or more

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Range of gain for closed-loop stability (Routh, j-omega crossing)1 fig.ctrl_stable_gain_range

The interval K_min < K < K_max of positive gains for which 1 + K G(s) = 0 has every root in the left half plane, and the frequency at which the root locus crosses the imaginary axis at K_max.

D(j\omega) + K N(j\omega) = 0

You give

MeaningUnitExample
\text{num}
Numerator of G(s) vector—[1]
\text{den}
Denominator of G(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
K_{\mathrm{min}}
Lower end of the stable range (0 when stable from small gains)—0
K_{\mathrm{max}}
Critical gain (upper end)—6
w_{\mathrm{cross}}
Imaginary-axis crossing frequency at K_max (0 for a crossing at s = 0)rad/s1.414213562 rad/s

Applies when

  • one bounded interval of stable positive gains

Outside these conditions it says what is wrong instead of returning a number.

Worked example

stable gain range: num = [1], den = [1, 3, 2, 0]
K_min = 0
K_max = 6
w_cross = 1.414213562 rad/s

Run it

Terminal
$ control-systems solve ctrl_stable_gain_range "num=[1]" "den=[1, 3, 2, 0]"
K_min = 0
K_max = 6
w_cross = 1.414213562 rad/s
  by: Range of gain for closed-loop stability (Routh, j-omega crossing)  (critical gains from D(jw) + K N(jw) = 0 and D(0) + K N(0) = 0; each interval tested by an exact Routh array)
  assumes: one bounded interval of stable positive gains

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Routh-Hurwitz stability test1 fig.ctrl_routh_hurwitz

The first column of the Routh array (exact arithmetic), the number of right-half-plane roots (its sign changes) and whether the characteristic polynomial is stable.

b_i = \frac{a_1a_{0,i+1} – a_0a_{1,i+1}}{a_1}

You give

MeaningUnitExample
\text{coefficients}
Characteristic polynomial coefficients, highest power of s first vector—[1, 6, 11, 6]

You get

MeaningUnitExample
\text{first\_column}
First column of the Routh array—[1, 6, 10, 6]
\text{rhp\_roots}
Number of roots in the right half plane—0
\text{stable}
True when every root has a negative real part—True

Applies when

  • no zero in the first column (special cases are refused)

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Routh-Hurwitz: coefficients = [1, 6, 11, 6]
first_column = [1, 6, 10, 6]
rhp_roots = 0
stable = True

Run it

Terminal
$ control-systems solve ctrl_routh_hurwitz "coefficients=[1, 6, 11, 6]"
first_column = [1.0,6.0,10.0,6.0]
rhp_roots = 0
stable = true
  by: Routh-Hurwitz stability test  (Routh array in exact fractions; sign changes of the first column)
  assumes: no zero in the first column (special cases are refused)

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

State space 14

Closed-loop poles under state feedback u = -K x2 fig.ctrl_state_feedback_poles

The characteristic polynomial det(sI – (A – B K)) (exact), the closed-loop poles and stability for a state-feedback gain K.

\det(sI – A + BK) = 0

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
B
Input vector vector—[0, 1]
K
State-feedback gain (row, n entries) vector—[4, 4]

You get

MeaningUnitExample
\text{char\_poly}
Closed-loop characteristic polynomial—[1, 4, 4]
\text{poles}
Closed-loop poles—[-2, -2]
\text{stable}
True when every pole is in the left half plane—True

Applies when

  • single input

Outside these conditions it says what is wrong instead of returning a number.

Worked example

state feedback poles: A = [[0, 1], [0, 0]], B = [0, 1], K = [4, 4]
char_poly = [1, 4, 4]
poles = [-2, -2]
stable = True

Run it

Terminal
$ control-systems solve ctrl_state_feedback_poles "A=[[0, 1], [0, 0]]" "B=[0, 1]" "K=[4, 4]"
char_poly = [1.0,4.0,4.0]
poles = [-2.0,-2.0]
stable = true
  by: Closed-loop poles under state feedback u = -K x  (Faddeev-LeVerrier on A - B K)
  assumes: single input

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Controllability Gramian1 fig.ctrl_ctrb_gramian

The infinite-horizon controllability Gramian Wc solving A Wc + Wc A^T + B B^T = 0 for a stable A (exact), its determinant and whether (A, B) is controllable (Wc nonsingular).

AW_c + W_cA^T + BB^T = 0

You give

MeaningUnitExample
A
State matrix (stable, n x n) matrix—[-1 0; 0 -2]
B
Input vector vector—[1, 1]

You get

MeaningUnitExample
\text{Wc}
Controllability Gramian—[[0.5, 0.3333333333], [0.3333333333, 0.25]]
\text{controllable}
True when Wc is nonsingular—True

Applies when

  • A stable

Outside these conditions it says what is wrong instead of returning a number.

Worked example

controllability Gramian: A = [[-1, 0], [0, -2]], B = [1, 1]
Wc = [[0.5, 0.3333333333], [0.3333333333, 0.25]]
controllable = True

Run it

Terminal
$ control-systems solve ctrl_ctrb_gramian "A=[[-1, 0], [0, -2]]" "B=[1, 1]"
Wc = [[0.5,0.333333333333],[0.333333333333,0.25]]
controllable = true
  by: Controllability Gramian  (Lyapunov equation in exact fractions)
  assumes: A stable

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Controllability matrix and rank1 fig.ctrl_controllability

The controllability matrix [B, AB, …, A^{n-1}B], its exact rank and determinant, and whether (A, B) is controllable.

\mathcal{C} = [B\;AB\;\cdots\;A^{n-1}B]

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; -2 -3]
B
Input vector (n entries) vector—[0, 1]

You get

MeaningUnitExample
\text{Wc}
Controllability matrix—[[0, 1], [1, -3]]
\text{rank}
Rank of the controllability matrix—2
\text{controllable}
True when the rank is n—True
\text{det\_Wc}
Determinant of the controllability matrix—-1

Applies when

  • single input

Outside these conditions it says what is wrong instead of returning a number.

Worked example

controllability: A = [[0, 1], [-2, -3]], B = [0, 1]
Wc = [[0, 1], [1, -3]]
rank = 2
controllable = True
det_Wc = -1

Run it

Terminal
$ control-systems solve ctrl_controllability "A=[[0, 1], [-2, -3]]" "B=[0, 1]"
Wc = [[0.0,1.0],[1.0,-3.0]]
rank = 2
controllable = true
det_Wc = -1
  by: Controllability matrix and rank  (exact rank by elimination in fractions)
  assumes: single input

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Controllable canonical form of a transfer function2 fig.ctrl_tf_to_ss

A, B, C, D in the controllable canonical form (scipy's convention) of a proper transfer function.

A = \begin{bmatrix}-a_1 & \cdots & -a_n\\ 1 & & 0\\ & \ddots & \end{bmatrix},\; B = e_1

You give

MeaningUnitExample
\text{num}
Numerator coefficients vector—[1]
\text{den}
Denominator coefficients vector—[1, 3, 2]

You get

MeaningUnitExample
A
State matrix—[[-3, -2], [1, 0]]
B
Input vector—[1, 0]
C
Output vector—[0, 1]
D
Feedthrough—0

Applies when

  • proper G(s)

Outside these conditions it says what is wrong instead of returning a number.

Worked example

controllable canonical form: num = [1], den = [1, 3, 2]
A = [[-3, -2], [1, 0]]
B = [1, 0]
C = [0, 1]
D = 0

Run it

Terminal
$ control-systems solve ctrl_tf_to_ss "num=[1]" "den=[1, 3, 2]"
A = [[-3.0,-2.0],[1.0,0.0]]
B = [1.0,0.0]
C = [0.0,1.0]
D = 0
  by: Controllable canonical form of a transfer function  (normalise den; first row of A = -a1..-an; C from num - D den)
  assumes: proper G(s)

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

DC gain of a state-space model1 fig.ctrl_ss_dc_gain

The steady-state gain G(0) = D – C A^{-1} B of a state-space model (exact).

G(0) = D – CA^{-1}B

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; -2 -3]
B
Input vector vector—[0, 1]
C
Output vector vector—[1, 0]
D
Feedthrough (default 0) optional—

You get

MeaningUnitExample
\text{dc\_gain}
DC gain G(0)—0.5

An optional input can be left out: a stated default is used.

Applies when

  • A nonsingular

Outside these conditions it says what is wrong instead of returning a number.

Worked example

state-space DC gain: A = [[0, 1], [-2, -3]], B = [0, 1], C = [1, 0]
dc_gain = 0.5

Run it

Terminal
$ control-systems solve ctrl_ss_dc_gain "A=[[0, 1], [-2, -3]]" "B=[0, 1]" "C=[1, 0]"
dc_gain = 0.5
  by: DC gain of a state-space model  (solve A x = B exactly, then D - C x)
  assumes: A nonsingular

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Deadbeat state feedback (discrete time)2 fig.ctrl_deadbeat_gain

The gain K that places every eigenvalue of A – B K at z = 0, so the discrete closed loop reaches the origin in at most n steps.

K = [0\cdots 1]\,\mathcal{C}^{-1}A^n

You give

MeaningUnitExample
A
Discrete state matrix (n x n) matrix—[1 1; 0 1]
B
Input vector vector—[0, 1]

You get

MeaningUnitExample
K
Deadbeat state-feedback gain—[1, 2]

Applies when

  • (A, B) controllable

Outside these conditions it says what is wrong instead of returning a number.

Worked example

deadbeat gain: A = [[1, 1], [0, 1]], B = [0, 1]
K = [1, 2]

Run it

Terminal
$ control-systems solve ctrl_deadbeat_gain "A=[[1, 1], [0, 1]]" "B=[0, 1]"
K = [1.0,2.0]
  by: Deadbeat state feedback (discrete time)  (Ackermann's formula with alpha(z) = z^n (exact))
  assumes: (A, B) controllable

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Eigenvalues (poles) of a state matrix2 fig.ctrl_ss_eigenvalues

The characteristic polynomial det(sI – A) (exact), the eigenvalues of A and whether the system is asymptotically stable.

\det(sI – A) = 0

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; -2 -3]

You get

MeaningUnitExample
\text{eigenvalues}
Eigenvalues of A—[-2, -1]
\text{char\_poly}
Characteristic polynomial (monic)—[1, 3, 2]
\text{max\_real\_part}
Largest real part—-1
\text{stable}
True when every eigenvalue has a negative real part—True

Applies when

  • A square, at most 6 x 6

Outside these conditions it says what is wrong instead of returning a number.

Worked example

eigenvalues of the state matrix: A = [[0, 1], [-2, -3]]
eigenvalues = [-2, -1]
char_poly = [1, 3, 2]
max_real_part = -1
stable = True

Run it

Terminal
$ control-systems solve ctrl_ss_eigenvalues "A=[[0, 1], [-2, -3]]"
eigenvalues = [-2.0,-1.0]
char_poly = [1.0,3.0,2.0]
max_real_part = -1
stable = true
  by: Eigenvalues (poles) of a state matrix  (Faddeev-LeVerrier, then polynomial roots)
  assumes: A square, at most 6 x 6

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Observability Gramian1 fig.ctrl_obsv_gramian

The infinite-horizon observability Gramian Wo solving A^T Wo + Wo A + C^T C = 0 for a stable A (exact), its determinant and whether (A, C) is observable.

A^TW_o + W_oA + C^TC = 0

You give

MeaningUnitExample
A
State matrix (stable, n x n) matrix—[-1 0; 0 -2]
C
Output vector vector—[1, 1]

You get

MeaningUnitExample
\text{Wo}
Observability Gramian—[[0.5, 0.3333333333], [0.3333333333, 0.25]]
\text{observable}
True when Wo is nonsingular—True

Applies when

  • A stable

Outside these conditions it says what is wrong instead of returning a number.

Worked example

observability Gramian: A = [[-1, 0], [0, -2]], C = [1, 1]
Wo = [[0.5, 0.3333333333], [0.3333333333, 0.25]]
observable = True

Run it

Terminal
$ control-systems solve ctrl_obsv_gramian "A=[[-1, 0], [0, -2]]" "C=[1, 1]"
Wo = [[0.5,0.333333333333],[0.333333333333,0.25]]
observable = true
  by: Observability Gramian  (Lyapunov equation in exact fractions)
  assumes: A stable

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Observability matrix and rank1 fig.ctrl_observability

The observability matrix [C; CA; …; CA^{n-1}], its exact rank and determinant, and whether (A, C) is observable.

\mathcal{O} = \begin{bmatrix}C\\ CA\\ \vdots\\ CA^{n-1}\end{bmatrix}

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; -2 -3]
C
Output vector (n entries) vector—[1, 0]

You get

MeaningUnitExample
\text{Wo}
Observability matrix—[[1, 0], [0, 1]]
\text{rank}
Rank of the observability matrix—2
\text{observable}
True when the rank is n—True
\text{det\_Wo}
Determinant of the observability matrix—1

Applies when

  • single output

Outside these conditions it says what is wrong instead of returning a number.

Worked example

observability: A = [[0, 1], [-2, -3]], C = [1, 0]
Wo = [[1, 0], [0, 1]]
rank = 2
observable = True
det_Wo = 1

Run it

Terminal
$ control-systems solve ctrl_observability "A=[[0, 1], [-2, -3]]" "C=[1, 0]"
Wo = [[1.0,0.0],[0.0,1.0]]
rank = 2
observable = true
det_Wo = 1
  by: Observability matrix and rank  (exact rank by elimination in fractions)
  assumes: single output

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Observer (estimator) gain by pole placement1 fig.ctrl_observer_gain

The observer gain L that gives A – L C the desired characteristic polynomial (single output), by duality with Ackermann's formula.

L = \alpha(A)\,\mathcal{O}^{-1}\begin{bmatrix}0\\ \vdots\\ 1\end{bmatrix}

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
C
Output vector vector—[1, 0]
\alpha
Desired observer characteristic polynomial (n+1 coefficients) vector—[1, 10, 25]

You get

MeaningUnitExample
L
Observer gain (column)—[10, 25]

Applies when

  • (A, C) observable

Outside these conditions it says what is wrong instead of returning a number.

Worked example

observer gain: A = [[0, 1], [0, 0]], C = [1, 0], alpha = [1, 10, 25]
L = [10, 25]

Run it

Terminal
$ control-systems solve ctrl_observer_gain "A=[[0, 1], [0, 0]]" "C=[1, 0]" "alpha=[1, 10, 25]"
L = [10.0,25.0]
  by: Observer (estimator) gain by pole placement  (L^T = Ackermann(A^T, C^T, alpha))
  assumes: (A, C) observable

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Pole placement by Ackermann's formula1 fig.ctrl_ackermann

The state-feedback gain K that gives A – B K the desired characteristic polynomial alpha(s) (single input), computed exactly.

K = \begin{bmatrix}0 & \cdots & 0 & 1\end{bmatrix}\mathcal{C}^{-1}\alpha(A)

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
B
Input vector vector—[0, 1]
\alpha
Desired characteristic polynomial (n+1 coefficients, highest power first) vector—[1, 4, 4]

You get

MeaningUnitExample
K
State-feedback gain (row)—[4, 4]

Applies when

  • (A, B) controllable

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Ackermann pole placement: A = [[0, 1], [0, 0]], B = [0, 1], alpha = [1, 4, 4]
K = [4, 4]

Run it

Terminal
$ control-systems solve ctrl_ackermann "A=[[0, 1], [0, 0]]" "B=[0, 1]" "alpha=[1, 4, 4]"
K = [4.0,4.0]
  by: Pole placement by Ackermann's formula  (K = [0 ... 0 1] Wc^{-1} alpha(A) in exact fractions)
  assumes: (A, B) controllable

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

State response to an initial state and a constant input2 fig.ctrl_ss_response

x(t) = e^{At} x(0) + (integral of e^{A tau} d tau) B u for x' = A x + B u with a constant input u.

x(t) = e^{At}x_0 + \int_0^t e^{A\tau}d\tau\,Bu

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
B
Input vector vector—[0, 1]
\text{x0}
Initial state vector—[0, 0]
u
Constant input—2
t
Time in seconds—3

You get

MeaningUnitExample
x
State at time t—[9, 6]

Applies when

  • |A| t <= 40
  • t >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

state response: A = [[0, 1], [0, 0]], B = [0, 1], x0 = [0, 0], u = 2, t = 3
x = [9, 6]

Run it

Terminal
$ control-systems solve ctrl_ss_response "A=[[0, 1], [0, 0]]" "B=[0, 1]" "x0=[0, 0]" u=2 t=3
x = [9.0,6.0]
  by: State response to an initial state and a constant input  (exponential of the augmented matrix [[A, B u], [0, 0]] t)
  u = 2
  t = 3
  assumes: |A| t <= 40; t >= 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

State-transition matrix e^{At}1 fig.ctrl_state_transition

The state-transition matrix Phi(t) = e^{At} that maps x(0) to x(t) for x' = A x.

\Phi(t) = e^{At} = \sum_k \frac{(At)^k}{k!}

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; 0 0]
t
Time in seconds—2

You get

MeaningUnitExample
\Phi
State-transition matrix—[[1, 2], [0, 1]]

Applies when

  • |A| t <= 40

Outside these conditions it says what is wrong instead of returning a number.

Worked example

state transition matrix: A = [[0, 1], [0, 0]], t = 2
Phi = [[1, 2], [0, 1]]

Run it

Terminal
$ control-systems solve ctrl_state_transition "A=[[0, 1], [0, 0]]" t=2
Phi = [[1.0,2.0],[0.0,1.0]]
  by: State-transition matrix e^{At}  (scaling and squaring with a Taylor series)
  t = 2
  assumes: |A| t <= 40

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Transfer function of a state-space model2 fig.ctrl_ss_to_tf

G(s) = C (sI – A)^{-1} B + D of a single-input single-output state-space model, computed exactly (Faddeev-LeVerrier).

G(s) = C(sI-A)^{-1}B + D = \frac{C\,\mathrm{adj}(sI-A)\,B}{\det(sI-A)} + D

You give

MeaningUnitExample
A
State matrix (n x n) matrix—[0 1; -2 -3]
B
Input vector (column, n entries) vector—[0, 1]
C
Output vector (row, n entries) vector—[1, 0]
D
Feedthrough (default 0) optional—

You get

MeaningUnitExample
\text{num}
Numerator (n+1 coefficients, highest power first)—[0, 0, 1]
\text{den}
Characteristic polynomial (monic)—[1, 3, 2]

An optional input can be left out: a stated default is used.

Applies when

  • A square, at most 6 x 6

Outside these conditions it says what is wrong instead of returning a number.

Worked example

state space to transfer function: A = [[0, 1], [-2, -3]], B = [0, 1], C = [1, 0]
num = [0, 0, 1]
den = [1, 3, 2]

Run it

Terminal
$ control-systems solve ctrl_ss_to_tf "A=[[0, 1], [-2, -3]]" "B=[0, 1]" "C=[1, 0]"
num = [0.0,0.0,1.0]
den = [1.0,3.0,2.0]
  by: Transfer function of a state-space model  (Faddeev-LeVerrier in exact fractions)
  assumes: A square, at most 6 x 6

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Steady-state error 1

System type, static error constant and steady-state error1 fig.ctrl_error_constants

For unity feedback around G(s): the system type N (poles at the origin), the static error constant lim s^N G(s) (Kp, Kv or Ka) and the steady-state error to a step, ramp or parabola of size A.

e_{ss} = \frac{A}{1+K_p},\; \frac{A}{K_v},\; \frac{A}{K_a}

You give

MeaningUnitExample
\text{num}
Numerator of the open-loop G(s) vector—[10]
\text{den}
Denominator of G(s) vector—[1, 3, 2]
\text{input}
Test input choice—step
A
Input size (default 1) optional—

You get

MeaningUnitExample
\text{system\_type}
System type N—0
K_{\mathrm{static}}
Static error constant of type N (Kp if 0, Kv if 1, Ka if 2)—5
\text{ess}
Steady-state error—0.1666666667

An optional input can be left out: a stated default is used.

Applies when

  • closed loop stable
  • proper G(s)
  • type at least the input's order

Outside these conditions it says what is wrong instead of returning a number.

Worked example

steady-state error: num = [10], den = [1, 3, 2], input = step
system_type = 0
K_static = 5
ess = 0.1666666667

Run it

Terminal
$ control-systems solve ctrl_error_constants "num=[10]" "den=[1, 3, 2]" input=step
system_type = 0
K_static = 5
ess = 0.1666666667
  by: System type, static error constant and steady-state error  (ess = A/(1 + Kp) (step, type 0), A/Kv (ramp, type 1), A/Ka (parabola, type 2), 0 for a higher type)
  input = step
  assumes: closed loop stable; proper G(s); type at least the input's order

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Time delay 1

Pade approximation of a time delay1 fig.ctrl_pade

The (n, n) Pade approximation of e^{-theta s} as a rational transfer function (denominator's leading coefficient 1).

e^{-\theta s} \approx \frac{\sum_k c_k(-\theta s)^k}{\sum_k c_k(\theta s)^k}

You give

MeaningUnitExample
\theta
Time delay in seconds—2
n
Approximation order (1 to 6)—1

You get

MeaningUnitExample
\text{num}
Numerator coefficients—[-1, 1]
\text{den}
Denominator coefficients (leading 1)—[1, 1]

Applies when

  • theta > 0
  • 1 <= n <= 6

Outside these conditions it says what is wrong instead of returning a number.

Worked example

Pade approximation: theta = 2, n = 1
num = [-1, 1]
den = [1, 1]

Run it

Terminal
$ control-systems solve ctrl_pade theta=2 n=1
num = [-1.0,1.0]
den = [1.0,1.0]
  by: Pade approximation of a time delay  (Pade coefficients c_k = (2n-k)! n! / ((2n)! k! (n-k)!))
  theta = 2
  assumes: theta > 0; 1 <= n <= 6

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Time response 17

Damping ratio from percent overshoot1 fig.ctrl_damping_from_overshoot

The damping ratio zeta = -ln(OS)/sqrt(pi^2 + ln^2 OS) that gives a stated overshoot.

\zeta = \frac{-\ln(\%OS/100)}{\sqrt{\pi^2 + \ln^2(\%OS/100)}}

You give

MeaningUnitExample
\text{OS}
Overshoot (write 20% or 0.2)—0.2

You get

MeaningUnitExample
\zeta
Damping ratio—0.4559498108
\text{pole\_angle\_deg}
Pole angle from the negative real axis, degrees—62.87393661

Applies when

  • 0 < OS < 100 %

Outside these conditions it says what is wrong instead of returning a number.

Worked example

damping ratio from overshoot: OS = 0.2
zeta = 0.4559498108
pole_angle_deg = 62.87393661

Run it

Terminal
$ control-systems solve ctrl_damping_from_overshoot OS=0.2
zeta = 0.4559498108
pole_angle_deg = 62.87393661
  by: Damping ratio from percent overshoot  (invert OS = exp(-zeta pi / sqrt(1 - zeta^2)))
  OS = 0.2
  assumes: 0 < OS < 100 %

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Exact settling time of a second-order step response2 fig.ctrl_true_settling_time

The last time at which the unit step response of wn^2/(s^2 + 2 zeta wn s + wn^2) leaves the band |y – 1| = band (not the envelope estimate), and which extremum precedes it.

T_s = \max\{t : |y(t) – 1| = \text{band}\}

You give

MeaningUnitExample
\zeta
Damping ratio (0 < zeta < 1)—0.5
\text{wn}
Natural frequency in rad/s—1
\text{band}
Settling band (default 2%) optional—

You get

MeaningUnitExample
\text{Ts}
Exact settling times8.076348974 s
\text{last\_extremum}
Index k of the last extremum outside the band (0 = before the first peak)—2

An optional input can be left out: a stated default is used.

Applies when

  • 0 < zeta < 1
  • 0 < band < 50 %

Outside these conditions it says what is wrong instead of returning a number.

Worked example

exact settling time: zeta = 0.5, wn = 1
Ts = 8.076348974 s
last_extremum = 2

Run it

Terminal
$ control-systems solve ctrl_true_settling_time zeta=0.5 wn=1
Ts = 8.076348974 s
last_extremum = 2
  by: Exact settling time of a second-order step response  (extrema at k pi/wd; the last band crossing lies between the last extremum outside the band and the next zero of y - 1 (bisection))
  zeta = 0.5
  wn = 1
  assumes: 0 < zeta < 1; 0 < band < 50 %

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Final value theorem1 fig.ctrl_final_value_theorem

lim_{t->inf} y(t) = lim_{s->0} s Y(s) for Y(s) = num/den, after checking that s Y(s) has every pole in the open left half plane.

y(\infty) = \lim_{s\to 0} sY(s)

You give

MeaningUnitExample
\text{num}
Numerator of Y(s) vector—[10]
\text{den}
Denominator of Y(s) vector—[1, 3, 2, 0]

You get

MeaningUnitExample
\text{final\_value}
Final value of y(t)—5

Applies when

  • at most one pole at the origin
  • all other poles in the open left half plane

Outside these conditions it says what is wrong instead of returning a number.

Worked example

final value theorem: num = [10], den = [1, 3, 2, 0]
final_value = 5

Run it

Terminal
$ control-systems solve ctrl_final_value_theorem "num=[10]" "den=[1, 3, 2, 0]"
final_value = 5
  by: Final value theorem  (cancel factors s, then evaluate s Y(s) at s = 0)
  assumes: at most one pole at the origin; all other poles in the open left half plane

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Initial value theorem (value and slope at t = 0+)1 fig.ctrl_initial_value_theorem

y(0+) = lim_{s->inf} s Y(s) and y'(0+) = lim_{s->inf} s (s Y(s) – y(0+)) for a strictly proper Y(s).

y(0^+) = \lim_{s\to\infty} sY(s)

You give

MeaningUnitExample
\text{num}
Numerator of Y(s) vector—[2, 3]
\text{den}
Denominator of Y(s) vector—[1, 5, 6]

You get

MeaningUnitExample
\text{y0}
Initial value y(0+)—2
\text{dy0}
Initial slope y'(0+)—-7

Applies when

  • Y(s) strictly proper

Outside these conditions it says what is wrong instead of returning a number.

Worked example

initial value theorem: num = [2, 3], den = [1, 5, 6]
y0 = 2
dy0 = -7

Run it

Terminal
$ control-systems solve ctrl_initial_value_theorem "num=[2, 3]" "den=[1, 5, 6]"
y0 = 2
dy0 = -7
  by: Initial value theorem (value and slope at t = 0+)  (long division of num by den in powers of 1/s)
  assumes: Y(s) strictly proper

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Mass-spring-damper natural frequency and damping ratio2 fig.ctrl_mass_spring_damper

Natural frequency sqrt(k/m), damping ratio c/(2 sqrt(k m)), damped frequency and static compliance 1/k of m x'' + c x' + k x = F.

\omega_n = \sqrt{k/m},\quad \zeta = \frac{c}{2\sqrt{km}}

You give

MeaningUnitExample
m
Mass in kg—1
c
Damping coefficient in N*s/m—2
k
Spring stiffness in N/m—4

You get

MeaningUnitExample
\text{wn}
Undamped natural frequencyrad/s2 rad/s
\zeta
Damping ratio—0.5
\text{wd}
Damped natural frequency (0 when not underdamped)rad/s1.732050808 rad/s
\text{dc\_gain}
Static gain x/F = 1/k in m/N—0.25
\text{damping}
Damping class—underdamped

Applies when

  • m > 0
  • c >= 0
  • k > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

mass-spring-damper: m = 1, c = 2, k = 4
wn = 2 rad/s
zeta = 0.5
wd = 1.732050808 rad/s
dc_gain = 0.25
damping = underdamped

Run it

Terminal
$ control-systems solve ctrl_mass_spring_damper m=1 c=2 k=4
wn = 2 rad/s
zeta = 0.5
wd = 1.732050808 rad/s
dc_gain = 0.25
damping = underdamped
  by: Mass-spring-damper natural frequency and damping ratio  (G(s) = 1/(m s^2 + c s + k))
  m = 1
  c = 2
  k = 4
  assumes: m > 0; c >= 0; k > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Natural frequency and damping ratio of a second-order denominator1 fig.ctrl_second_order_params

wn, zeta, the decay rate sigma = zeta wn, the damped frequency wd and the DC gain of K/(a2 s^2 + a1 s + a0).

s^2 + 2\zeta\omega_n s + \omega_n^2

You give

MeaningUnitExample
\text{den}
Denominator [a2, a1, a0] vector—[1, 4, 25]
K
Numerator constant (default: unit DC gain) optional—

You get

MeaningUnitExample
\text{wn}
Natural frequencyrad/s5 rad/s
\zeta
Damping ratio—0.4
\sigma
Decay rate zeta wn (1/s)—2
\text{wd}
Damped frequency (0 unless underdamped)rad/s4.582575695 rad/s
\text{damping}
Damping class—underdamped
\text{dc\_gain}
DC gain K/a0—1

An optional input can be left out: a stated default is used.

Applies when

  • a0/a2 > 0
  • a1/a2 >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order parameters: den = [1, 4, 25]
wn = 5 rad/s
zeta = 0.4
sigma = 2
wd = 4.582575695 rad/s
damping = underdamped
dc_gain = 1

Run it

Terminal
$ control-systems solve ctrl_second_order_params "den=[1, 4, 25]"
wn = 5 rad/s
zeta = 0.4
sigma = 2
wd = 4.582575695 rad/s
damping = underdamped
dc_gain = 1
  by: Natural frequency and damping ratio of a second-order denominator  (compare with s^2 + 2 zeta wn s + wn^2)
  assumes: a0/a2 > 0; a1/a2 >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Peak time of a second-order step response2 fig.ctrl_peak_time

Peak time Tp = pi/wd and damped frequency wd = wn sqrt(1 – zeta^2) of wn^2/(s^2 + 2 zeta wn s + wn^2).

T_p = \frac{\pi}{\omega_n\sqrt{1-\zeta^2}}

You give

MeaningUnitExample
\zeta
Damping ratio (0 <= zeta < 1)—0.6
\text{wn}
Natural frequency in rad/s—5

You get

MeaningUnitExample
\text{Tp}
Peak times0.7853981634 s
\text{wd}
Damped frequencyrad/s4 rad/s

Applies when

  • 0 <= zeta < 1
  • wn > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

peak time: zeta = 0.6, wn = 5
Tp = 0.7853981634 s
wd = 4 rad/s

Run it

Terminal
$ control-systems solve ctrl_peak_time zeta=0.6 wn=5
Tp = 0.7853981634 s
wd = 4 rad/s
  by: Peak time of a second-order step response  (first zero of the impulse response)
  zeta = 0.6
  wn = 5
  assumes: 0 <= zeta < 1; wn > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Percent overshoot from the damping ratio2 fig.ctrl_percent_overshoot

Percent overshoot 100 e^{-zeta pi/sqrt(1 – zeta^2)} of the step response of a standard second-order system, and the pole angle acos(zeta).

\%OS = 100\,e^{-\zeta\pi/\sqrt{1-\zeta^2}}

You give

MeaningUnitExample
\zeta
Damping ratio (0 <= zeta < 1)—0.5

You get

MeaningUnitExample
\text{PO}
Percent overshoot (%)—16.30335348
\text{Mp}
Overshoot as a fraction of the final value—0.1630335348
\text{pole\_angle\_deg}
Angle of the poles from the negative real axis, degrees—60

Applies when

  • 0 <= zeta < 1

Outside these conditions it says what is wrong instead of returning a number.

Worked example

percent overshoot: zeta = 0.5
PO = 16.30335348
Mp = 0.1630335348
pole_angle_deg = 60

Run it

Terminal
$ control-systems solve ctrl_percent_overshoot zeta=0.5
PO = 16.30335348
Mp = 0.1630335348
pole_angle_deg = 60
  by: Percent overshoot from the damping ratio  (peak of the step response at t = pi/wd)
  zeta = 0.5
  assumes: 0 <= zeta < 1

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Proportional control of a first-order plant1 fig.ctrl_closed_loop_first_order_p

Closed-loop DC gain K Kp/(1 + K Kp), time constant tau/(1 + K Kp), steady-state error 1/(1 + K Kp) to a unit step and the closed-loop pole, for K/(tau s + 1) under unity feedback with gain Kp.

T(s) = \frac{KK_p}{\tau s + 1 + KK_p}

You give

MeaningUnitExample
K
Plant DC gain—2
\tau
Plant time constant in seconds—10
\text{Kp}
Proportional gain—4.5

You get

MeaningUnitExample
\text{dc\_gain\_cl}
Closed-loop DC gain—0.9
\tau_{\mathrm{cl}}
Closed-loop time constants1 s
\text{ess}
Steady-state error to a unit step—0.1
\text{pole}
Closed-loop pole (1/s)—-1

Applies when

  • tau > 0
  • 1 + K Kp > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

proportional control of a first-order plant: K = 2, tau = 10, Kp = 4.5
dc_gain_cl = 0.9
tau_cl = 1 s
ess = 0.1
pole = -1

Run it

Terminal
$ control-systems solve ctrl_closed_loop_first_order_p K=2 tau=10 Kp=4.5
dc_gain_cl = 0.9
tau_cl = 1 s
ess = 0.1
pole = -1
  by: Proportional control of a first-order plant  (T(s) = K Kp/(tau s + 1 + K Kp))
  K = 2
  tau = 10
  Kp = 4.5
  assumes: tau > 0; 1 + K Kp > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Rise time of an underdamped second-order system2 fig.ctrl_rise_time_second

The 0-100 % rise time (pi – acos zeta)/wd and the 10-90 % rise time (found exactly by bisection on the step response).

T_{r,0-100} = \frac{\pi – \cos^{-1}\zeta}{\omega_d}

You give

MeaningUnitExample
\zeta
Damping ratio (0 < zeta < 1)—0.5
\text{wn}
Natural frequency in rad/s—1

You get

MeaningUnitExample
\text{Tr\_0\_100}
0-100 % rise times2.418399152 s
\text{Tr\_10\_90}
10-90 % rise times1.637572947 s
\text{t10}
Time to 10 %s0.4882292958 s
\text{t90}
Time to 90 %s2.125802243 s

Applies when

  • 0 < zeta < 1
  • wn > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

rise time: zeta = 0.5, wn = 1
Tr_0_100 = 2.418399152 s
Tr_10_90 = 1.637572947 s
t10 = 0.4882292958 s
t90 = 2.125802243 s

Run it

Terminal
$ control-systems solve ctrl_rise_time_second zeta=0.5 wn=1
Tr_0_100 = 2.418399152 s
Tr_10_90 = 1.637572947 s
t10 = 0.4882292958 s
t90 = 2.125802243 s
  by: Rise time of an underdamped second-order system  (closed form (0-100 %); bisection on y(t) (10-90 %))
  zeta = 0.5
  wn = 1
  assumes: 0 < zeta < 1; wn > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Second-order specifications from the pole location2 fig.ctrl_second_order_from_poles

From poles at -sigma +/- j wd: wn, zeta, peak time pi/wd, 2 % settling time 4/sigma and percent overshoot 100 e^{-sigma pi/wd}.

\omega_n = \sqrt{\sigma^2+\omega_d^2},\quad \zeta = \sigma/\omega_n,\quad \%OS = 100e^{-\sigma\pi/\omega_d}

You give

MeaningUnitExample
\sigma
Decay rate: the poles' real part is -sigma (sigma > 0)—3
\text{wd}
Imaginary part of the poles (rad/s)—4

You get

MeaningUnitExample
\text{wn}
Natural frequencyrad/s5 rad/s
\zeta
Damping ratio—0.6
\text{Tp}
Peak times0.7853981634 s
\text{Ts}
2 % settling time 4/sigmas1.333333333 s
\text{PO}
Percent overshoot (%)—9.478022484

Applies when

  • sigma > 0
  • wd > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order specifications from poles: sigma = 3, wd = 4
wn = 5 rad/s
zeta = 0.6
Tp = 0.7853981634 s
Ts = 1.333333333 s
PO = 9.478022484

Run it

Terminal
$ control-systems solve ctrl_second_order_from_poles sigma=3 wd=4
wn = 5 rad/s
zeta = 0.6
Tp = 0.7853981634 s
Ts = 1.333333333 s
PO = 9.478022484
  by: Second-order specifications from the pole location  (wn = |p|, zeta = sigma/wn)
  sigma = 3
  wd = 4
  assumes: sigma > 0; wd > 0

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Settling time of a second-order system (rule and envelope)2 fig.ctrl_settling_time

Settling time by the rule 4/(zeta wn) (2 % band) or 3/(zeta wn) (5 %), and the time at which the decay envelope enters the band.

T_s \approx \frac{4}{\zeta\omega_n}\;(2\%),\qquad T_s \approx \frac{3}{\zeta\omega_n}\;(5\%)

You give

MeaningUnitExample
\zeta
Damping ratio (0 < zeta < 1)—0.5
\text{wn}
Natural frequency in rad/s—4
\text{band}
Settling band: 2% (default) or 5% optional—

You get

MeaningUnitExample
\text{Ts}
Settling time by the rule of thumbs2 s
\text{Ts\_envelope}
Time at which e^{-zeta wn t}/sqrt(1-zeta^2) equals the bands2.027932021 s
\text{time\_constant}
Envelope time constant 1/(zeta wn)s0.5 s

An optional input can be left out: a stated default is used.

Applies when

  • 0 < zeta < 1
  • band 2 % or 5 %

Outside these conditions it says what is wrong instead of returning a number.

Worked example

settling time: zeta = 0.5, wn = 4
Ts = 2 s
Ts_envelope = 2.027932021 s
time_constant = 0.5 s

Run it

Terminal
$ control-systems solve ctrl_settling_time zeta=0.5 wn=4
Ts = 2 s
Ts_envelope = 2.027932021 s
time_constant = 0.5 s
  by: Settling time of a second-order system (rule and envelope)  (Ts = 4/(zeta wn) or 3/(zeta wn))
  zeta = 0.5
  wn = 4
  assumes: 0 < zeta < 1; band 2 % or 5 %

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Step response of a first-order system1 fig.ctrl_first_order_step

The output y(t) = K A (1 – e^{-t/tau}) of K/(tau s + 1) at time t after a step of size A, and the fraction of the final value reached.

y(t) = K A\left(1 – e^{-t/\tau}\right)

You give

MeaningUnitExample
K
Steady-state (DC) gain of the system—2
\tau
Time constant in seconds—5
t
Time after the step in seconds—5
A
Step size (default 1) optional—

You get

MeaningUnitExample
y
Output at time t—1.264241118
\text{fraction}
Fraction of the final value reached, 1 – e^{-t/tau}—0.6321205588
y_{\mathrm{final}}
Final value K A—2

An optional input can be left out: a stated default is used.

Applies when

  • tau > 0
  • t >= 0
  • K and A not zero

Outside these conditions it says what is wrong instead of returning a number.

Worked example

first-order step response: K = 2, tau = 5, t = 5
y = 1.264241118
fraction = 0.6321205588
y_final = 2

Run it

Terminal
$ control-systems solve ctrl_first_order_step K=2 tau=5 t=5
y = 1.264241118
fraction = 0.6321205588
y_final = 2
  by: Step response of a first-order system  (y = K A (1 - exp(-t/tau)))
  K = 2
  tau = 5
  t = 5
  assumes: tau > 0; t >= 0; K and A not zero

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Step response of a first-order-plus-dead-time process1 fig.ctrl_fopdt_step

y(t) of K e^{-theta s}/(tau s + 1) after a step of size A: 0 before the dead time, K A (1 – e^{-(t – theta)/tau}) after it.

y(t) = KA\left(1 – e^{-(t-\theta)/\tau}\right)\,u(t-\theta)

You give

MeaningUnitExample
K
Process gain—2
\tau
Time constant in seconds—5
\theta
Dead time in seconds—1
t
Time in seconds—6
A
Step size (default 1) optional—

You get

MeaningUnitExample
y
Output at time t—1.264241118

An optional input can be left out: a stated default is used.

Applies when

  • tau > 0
  • theta >= 0
  • t >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

dead-time step response: K = 2, tau = 5, theta = 1, t = 6
y = 1.264241118

Run it

Terminal
$ control-systems solve ctrl_fopdt_step K=2 tau=5 theta=1 t=6
y = 1.264241118
  by: Step response of a first-order-plus-dead-time process  (delayed first-order response)
  K = 2
  tau = 5
  theta = 1
  t = 6
  assumes: tau > 0; theta >= 0; t >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Step response of a standard second-order system1 fig.ctrl_second_order_step

y(t) of K wn^2/(s^2 + 2 zeta wn s + wn^2) after a unit step, for underdamped, critically damped and overdamped systems, and the remaining error K – y.

y(t) = 1 – e^{-\zeta\omega_n t}\left(\cos\omega_d t + \frac{\zeta}{\sqrt{1-\zeta^2}}\sin\omega_d t\right)

You give

MeaningUnitExample
\zeta
Damping ratio (>= 0)—0.5
\text{wn}
Natural frequency in rad/s—2
t
Time in seconds—1
K
DC gain (default 1) optional—

You get

MeaningUnitExample
y
Output at time t—0.8494256349
\text{error}
K – y(t)—0.1505743651

An optional input can be left out: a stated default is used.

Applies when

  • zeta >= 0
  • wn > 0
  • t >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

second-order step response: zeta = 0.5, wn = 2, t = 1
y = 0.8494256349
error = 0.1505743651

Run it

Terminal
$ control-systems solve ctrl_second_order_step zeta=0.5 wn=2 t=1
y = 0.8494256349
error = 0.1505743651
  by: Step response of a standard second-order system  (closed-form step response for each damping case)
  zeta = 0.5
  wn = 2
  t = 1
  assumes: zeta >= 0; wn > 0; t >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Step response of a transfer function at a given time1 fig.ctrl_step_response

y(t) of any proper G(s) after a step of size A, computed exactly through the state-space matrix exponential.

y(t) = C\int_0^t e^{A\tau}d\tau\,B\,A_u + D A_u

You give

MeaningUnitExample
\text{num}
Numerator vector—[1]
\text{den}
Denominator vector—[1, 1]
t
Time in seconds—1
A
Step size (default 1) optional—

You get

MeaningUnitExample
y
Output at time t—0.6321205588

An optional input can be left out: a stated default is used.

Applies when

  • proper G(s)
  • t >= 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

step response at time: num = [1], den = [1, 1], t = 1
y = 0.6321205588

Run it

Terminal
$ control-systems solve ctrl_step_response "num=[1]" "den=[1, 1]" t=1
y = 0.6321205588
  by: Step response of a transfer function at a given time  (controllable canonical form; exponential of [[A, B], [0, 0]] t)
  t = 1
  assumes: proper G(s); t >= 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Time and frequency specifications of a first-order system1 fig.ctrl_first_order_specs

Time constant, 10-90 % rise time tau ln 9, 2 % and 5 % settling times tau ln 50 and tau ln 20, bandwidth 1/tau and DC gain of K/(tau s + 1).

t_r = \tau\ln 9,\quad t_{s,2\%} = \tau\ln 50,\quad \omega_B = 1/\tau

You give

MeaningUnitExample
K
Steady-state (DC) gain—3
\tau
Time constant in seconds—2

You get

MeaningUnitExample
\text{time\_constant}
Time constant (63.2 % of the final value)s2 s
\text{rise\_time}
10-90 % rise times4.394449155 s
\text{settling\_time\_2}
2 % settling times7.824046011 s
\text{settling\_time\_5}
5 % settling times5.991464547 s
\text{bandwidth}
-3 dB bandwidthrad/s0.5 rad/s
\text{dc\_gain}
DC gain—3

Applies when

  • tau > 0

Outside these conditions it says what is wrong instead of returning a number.

Worked example

first-order specifications: K = 3, tau = 2
time_constant = 2 s
rise_time = 4.394449155 s
settling_time_2 = 7.824046011 s
settling_time_5 = 5.991464547 s
bandwidth = 0.5 rad/s
dc_gain = 3

Run it

Terminal
$ control-systems solve ctrl_first_order_specs K=3 tau=2
time_constant = 2 s
rise_time = 4.394449155 s
settling_time_2 = 7.824046011 s
settling_time_5 = 5.991464547 s
bandwidth = 0.5 rad/s
dc_gain = 3
  by: Time and frequency specifications of a first-order system  (exact crossing times of 1 - exp(-t/tau))
  K = 3
  tau = 2
  assumes: tau > 0

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Transfer functions 6

Closed-loop transfer function of a feedback loop1 fig.ctrl_feedback_connection

G/(1 + G H) for negative feedback (or G/(1 – G H) for positive feedback): numerator N_G D_H and denominator D_G D_H +/- N_G N_H.

T(s) = \frac{G}{1 + GH} = \frac{N_GD_H}{D_GD_H + N_GN_H}

You give

MeaningUnitExample
\text{num\_g}
Numerator of the forward path G vector—[10]
\text{den\_g}
Denominator of G vector—[1, 2, 0]
\text{num\_h}
Numerator of the feedback path H (default 1) optional vector—
\text{den\_h}
Denominator of H (default 1) optional vector—
\text{sign}
Feedback sign (default negative) optional choice—

You get

MeaningUnitExample
\text{num}
Closed-loop numerator—[10]
\text{den}
Closed-loop denominator—[1, 2, 10]

An optional input can be left out: a stated default is used.

Worked example

feedback connection: num_g = [10], den_g = [1, 2, 0]
num = [10]
den = [1, 2, 10]

Run it

Terminal
$ control-systems solve ctrl_feedback_connection "num_g=[10]" "den_g=[1, 2, 0]"
num = [10.0]
den = [1.0,2.0,10.0]
  by: Closed-loop transfer function of a feedback loop  (T = N_G D_H / (D_G D_H + N_G N_H))

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

DC gain and final value of a step response1 fig.ctrl_dc_gain

The DC gain G(0) of a stable transfer function and the final value G(0) A of its response to a step of size A.

y(\infty) = A\,G(0)

You give

MeaningUnitExample
\text{num}
Numerator coefficients, highest power of s first vector—[10]
\text{den}
Denominator coefficients, highest power of s first vector—[1, 3, 2]
A
Step size (default 1) optional—

You get

MeaningUnitExample
\text{dc\_gain}
DC gain G(0)—5
\text{final\_value}
Final value of the step response—5

An optional input can be left out: a stated default is used.

Applies when

  • proper G(s)
  • every pole in the left half plane

Outside these conditions it says what is wrong instead of returning a number.

Worked example

DC gain: num = [10], den = [1, 3, 2]
dc_gain = 5
final_value = 5

Run it

Terminal
$ control-systems solve ctrl_dc_gain "num=[10]" "den=[1, 3, 2]"
dc_gain = 5
final_value = 5
  by: DC gain and final value of a step response  (G(0) = num(0)/den(0) after a stability check of the poles)
  assumes: proper G(s); every pole in the left half plane

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Parallel connection of two transfer functions2 fig.ctrl_parallel_connection

The transfer function G1 + G2 of two blocks in parallel: (N1 D2 + N2 D1)/(D1 D2).

G_1 + G_2 = \frac{N_1D_2 + N_2D_1}{D_1D_2}

You give

MeaningUnitExample
\text{num1}
Numerator of G1 vector—[1]
\text{den1}
Denominator of G1 vector—[1, 1]
\text{num2}
Numerator of G2 vector—[2]
\text{den2}
Denominator of G2 vector—[1, 3]

You get

MeaningUnitExample
\text{num}
Numerator of G1 + G2—[3, 5]
\text{den}
Denominator of G1 + G2—[1, 4, 3]

Worked example

parallel connection: num1 = [1], den1 = [1, 1], num2 = [2], den2 = [1, 3]
num = [3, 5]
den = [1, 4, 3]

Run it

Terminal
$ control-systems solve ctrl_parallel_connection "num1=[1]" "den1=[1, 1]" "num2=[2]" "den2=[1, 3]"
num = [3.0,5.0]
den = [1.0,4.0,3.0]
  by: Parallel connection of two transfer functions  (N = N1 D2 + N2 D1, D = D1 D2)

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Poles, zeros and gain of a transfer function1 fig.ctrl_poles_zeros

The zeros (roots of the numerator), the poles (roots of the denominator) and the zero-pole-gain constant k of G(s) = num/den.

G(s) = k\,\frac{\prod (s – z_i)}{\prod (s – p_j)}

You give

MeaningUnitExample
\text{num}
Numerator coefficients, highest power of s first vector—[1, 2]
\text{den}
Denominator coefficients, highest power of s first vector—[1, 4, 3]

You get

MeaningUnitExample
\text{zeros}
The zeros—[-2]
\text{poles}
The poles—[-3, -1]
\text{gain}
Gain k of the zero-pole-gain form (num[0]/den[0])—1

Worked example

poles and zeros: num = [1, 2], den = [1, 4, 3]
zeros = [-2]
poles = [-3, -1]
gain = 1

Run it

Terminal
$ control-systems solve ctrl_poles_zeros "num=[1, 2]" "den=[1, 4, 3]"
zeros = [-2.0]
poles = [-3.0,-1.0]
gain = 1
  by: Poles, zeros and gain of a transfer function  (roots of both polynomials (Aberth-Ehrlich iteration))

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Series (cascade) connection of two transfer functions1 fig.ctrl_series_connection

The transfer function G1 G2 of two blocks in series: numerators multiplied, denominators multiplied (no cancellation).

G(s) = G_1(s)G_2(s) = \frac{N_1N_2}{D_1D_2}

You give

MeaningUnitExample
\text{num1}
Numerator of G1 vector—[1]
\text{den1}
Denominator of G1 vector—[1, 1]
\text{num2}
Numerator of G2 vector—[2]
\text{den2}
Denominator of G2 vector—[1, 3]

You get

MeaningUnitExample
\text{num}
Numerator of G1 G2—[2]
\text{den}
Denominator of G1 G2—[1, 4, 3]

Worked example

series connection: num1 = [1], den1 = [1, 1], num2 = [2], den2 = [1, 3]
num = [2]
den = [1, 4, 3]

Run it

Terminal
$ control-systems solve ctrl_series_connection "num1=[1]" "den1=[1, 1]" "num2=[2]" "den2=[1, 3]"
num = [2.0]
den = [1.0,4.0,3.0]
  by: Series (cascade) connection of two transfer functions  (polynomial multiplication)

Figure

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Value of a transfer function at a complex point2 fig.ctrl_tf_evaluate

G(s0) = num(s0)/den(s0) at s0 = s_re + j s_im: real and imaginary parts, magnitude and angle.

G(s_0) = \frac{N(s_0)}{D(s_0)}

You give

MeaningUnitExample
\text{num}
Numerator coefficients vector—[1]
\text{den}
Denominator coefficients vector—[1, 1]
s_{\mathrm{re}}
Real part of s0—0
s_{\mathrm{im}}
Imaginary part of s0—1

You get

MeaningUnitExample
G_{\mathrm{re}}
Real part of G(s0)—0.5
G_{\mathrm{im}}
Imaginary part of G(s0)—-0.5
\text{magnitude}
|G(s0)|—0.7071067812
\text{angle\_deg}
Angle of G(s0) in degrees, (-180, 180]—-45

Applies when

  • s0 is not a pole

Outside these conditions it says what is wrong instead of returning a number.

Worked example

evaluate the transfer function: num = [1], den = [1, 1], s_re = 0, s_im = 1
G_re = 0.5
G_im = -0.5
magnitude = 0.7071067812
angle_deg = -45

Run it

Terminal
$ control-systems solve ctrl_tf_evaluate "num=[1]" "den=[1, 1]" s_re=0 s_im=1
G_re = 0.5
G_im = -0.5
magnitude = 0.7071067812
angle_deg = -45
  by: Value of a transfer function at a complex point  (Horner evaluation in complex arithmetic)
  s_re = 0
  s_im = 1
  assumes: s0 is not a pole

Figures

Each figure varies one input around the worked example and recomputes the result at every point. Press Compute above and they are redrawn for your own inputs.

Figures

Computed, not drawn by hand

Every point in these figures is an answer of the model: one input is varied, the others are held at the worked example, and the curve shows how the result responds. The marked point is the worked example itself.

Lag compensator placement

C(s) = \frac{s + z}{s + z/\beta},\quad z = \omega_c/10
phase_deg against beta: Lag compensator placementphase_deg (phase added at w_c in degrees (negative)) against beta (required low-frequency gain increase (> 1)), with w_c = 1. 31 points computed by control-systems-ai999x; marked: beta = 10, phase_deg = -5.13765.57.51012.515-5.2-5-4.8-4.6-5.13765 beta — required low-frequency gain increase (> 1)phase_deg
phase_deg (phase added at w_c in degrees (negative)) against beta (required low-frequency gain increase (> 1)), with w_c = 1. 31 points computed by control-systems-ai999x; marked: beta = 10, phase_deg = -5.13765.

Desired closed-loop poles from overshoot and settling time

\zeta = \frac{-\ln OS}{\sqrt{\pi^2+\ln^2 OS}},\quad \sigma = \frac{4}{T_s},\quad \omega_n = \frac{\sigma}{\zeta}
wn against OS: Desired closed-loop poles from overshoot and settling timewn (natural frequency) against OS (maximum overshoot (write 10% or 0.1)), with Ts = 2. 31 points computed by control-systems-ai999x; marked: OS = 0.163034, wn = 4 rad/s.0.10.150.23.544.554 rad/sOS — maximum overshoot (write 10% or 0.1)wn [rad/s]
wn (natural frequency) against OS (maximum overshoot (write 10% or 0.1)), with Ts = 2. 31 points computed by control-systems-ai999x; marked: OS = 0.163034, wn = 4 rad/s.

Percent overshoot from the damping ratio

\%OS = 100\,e^{-\zeta\pi/\sqrt{1-\zeta^2}}
PO against zeta: Percent overshoot from the damping ratioPO (percent overshoot (%)) against zeta (damping ratio (0 <= zeta < 1)). 31 points computed by control-systems-ai999x; marked: zeta = 0.5, PO = 16.3034.0.40.601020304016.3034 zeta — damping ratio (0 <= zeta < 1)PO
PO (percent overshoot (%)) against zeta (damping ratio (0 <= zeta < 1)). 31 points computed by control-systems-ai999x; marked: zeta = 0.5, PO = 16.3034.

Mapping an s-plane pole to the z-plane (z = e^{sT})

z = e^{sT} = e^{\sigma T}\angle\,\omega T
z_mag against s_re: Mapping an s-plane pole to the z-plane (z = e^{sT})z_mag (|z| = e^{s_re T}) against s_re (real part of the s-plane pole (1/s)), with T = 0.1, s_im = 0. 31 points computed by control-systems-ai999x; marked: s_re = -2, z_mag = 0.818731.-3-2.5-2-1.5-10.750.80.850.90.818731 s_re — real part of the s-plane pole (1/s)z_mag
z_mag (|z| = e^{s_re T}) against s_re (real part of the s-plane pole (1/s)), with T = 0.1, s_im = 0. 31 points computed by control-systems-ai999x; marked: s_re = -2, z_mag = 0.818731.
Questions

Frequently asked

What does control-systems-ai999x do?

Control Systems answers questions in control systems: transfer functions, poles and zeros, step and frequency response, stability (Routh-Hurwitz, gain and phase margins, Nyquist), root locus, state space, controllability and observability, pole placement, LQR, Kalman filters, discretization and PID tuning. Each answer gives the value with its unit, the steps and the formula in LaTeX.

Is there a desktop app or a command-line program?

Yes, both, for Windows and Linux. AI999X Control Systems is a desktop app of its own, and the AI999X desktop app holds all AI999X models in one window (search, ask in words, forms, figures, explanations). The command-line bundle holds one program per model; for this one run `control-systems list` for its capabilities, `control-systems info <capability>` for what one needs and `control-systems solve <capability> name=value …` for an answer. Both run offline and need no Python. Downloads and checksums: https://www.ai999x.com/desktop-cli/

How do I install control-systems-ai999x?

With pip: pip install control-systems-ai999x. The same files are on Hugging Face (hf download AI999X/control-systems-ai999x –local-dir control-systems-ai999x) and GitHub.

Does it run offline?

Yes. It is a Python package that runs on an ordinary CPU without a network connection (Python >=3.10); no GPU and no API key are needed.

How many capabilities does control-systems-ai999x have?

97 capabilities in 16 topics, including Time response, State space, Discretization, PID tuning, Stability.

Which field and tasks does it cover?

Field: Engineering. Tasks: Question Answering, Sentence Similarity, Feature Extraction.

What does an answer look like?

Asked "stability margins: num = [10], den = [1, 3, 2, 0]", it answers: gm = 0.6, gm_db = -4.43697, w_pc = 1.41421 rad/s, pm_deg = -12.9972, w_gc = 1.8022 rad/s.

What happens when a question cannot be answered?

It says so and names what is missing or out of range, instead of guessing a number.

More from AI999X

Explore every model.