Chapter 5: From Higher-Order ODEs to State-Space Form
Lesson 1: Converting n-th Order Scalar ODEs to First-Order Vector Form
This lesson develops the foundational construction that converts a single scalar n-th order differential equation into an equivalent first-order vector differential equation by introducing a structured set of state variables. We formalize the construction for both nonlinear and linear time-invariant (LTI) cases, prove equivalence (solution correspondence and initial-condition mapping), and provide implementation templates in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.
1. Learning Objectives and Prerequisites
By the end of this lesson, you will be able to:
- Convert an n-th order scalar ODE into a first-order vector ODE by defining a state vector \( \mathbf{x}(t) \in \mathbb{R}^n \).
- Write the resulting model compactly as \( \dot{\mathbf{x}} = \mathbf{f}(t,\mathbf{x},u) \) and, for LTI cases, as \( \dot{\mathbf{x}} = \mathbf{A}\mathbf{x} + \mathbf{B}u \).
- Prove equivalence: every solution of the scalar ODE induces a unique solution of the vector model and vice versa.
- Implement the construction and validate it numerically across multiple programming environments.
Prerequisites from earlier chapters: vector/matrix notation (Chapter 2), vector-matrix differential equations and existence/uniqueness ideas (Chapter 3), and the basic meaning of state, input, and output (Chapter 4).
2. Problem Setup — General n-th Order Scalar ODE
Consider a scalar function \( y(t) \in \mathbb{R} \) governed by an n-th order ordinary differential equation (ODE) with an input \( u(t) \in \mathbb{R} \):
\[ y^{(n)}(t) = F\!\Big(t,\;y(t),\;\dot{y}(t),\;\dots,\;y^{(n-1)}(t),\;u(t)\Big), \quad t \in [t_0,t_f]. \]
The ODE becomes an initial value problem (IVP) once we specify the initial derivatives: \( y(t_0),\dot{y}(t_0),\dots,y^{(n-1)}(t_0) \). Our objective is to rewrite this single n-th order equation as a system of n first-order equations in a vector state.
Key idea: package the “hidden” derivative information into a vector so that the dynamics become first-order in time. This is precisely what state variables encode in modern (state-space) control.
3. State Definition and First-Order Vector Construction
Define the state components as successive derivatives of \( y(t) \):
\[ x_1(t) \triangleq y(t),\quad x_2(t) \triangleq \dot{y}(t),\quad \dots,\quad x_n(t) \triangleq y^{(n-1)}(t). \]
Stack them into a vector \( \mathbf{x}(t) = [x_1(t)\;x_2(t)\;\dots\;x_n(t)]^\top \in \mathbb{R}^n \). Then, for \( i=1,2,\dots,n-1 \) we have the “shift” relations:
\[ \dot{x}_i(t) = x_{i+1}(t), \quad i=1,2,\dots,n-1. \]
The last equation uses the original ODE, because \( x_n(t)=y^{(n-1)}(t) \) implies \( \dot{x}_n(t)=y^{(n)}(t) \). Hence:
\[ \dot{x}_n(t) = F\!\Big(t,\;x_1(t),\;x_2(t),\;\dots,\;x_n(t),\;u(t)\Big). \]
Therefore, the n-th order scalar ODE is equivalent to the first-order vector system:
\[ \dot{\mathbf{x}}(t) = \mathbf{f}\!\big(t,\mathbf{x}(t),u(t)\big) \triangleq \begin{bmatrix} x_2(t)\\ x_3(t)\\ \vdots\\ x_n(t)\\ F\!\big(t,x_1(t),x_2(t),\dots,x_n(t),u(t)\big) \end{bmatrix}. \]
If the measured output is the original variable \( y(t) \), then: \( y(t)=x_1(t) \). (Output mappings are formalized in Chapter 4 and extended in later lessons.)
flowchart TD
A["Start with scalar ODE: y^(n) = F(t, y, y', ..., y^(n-1), u)"] --> B["Define states: x1=y, x2=y', ..., xn=y^(n-1)"]
B --> C["Differentiate: x1dot=x2, x2dot=x3, ..., x(n-1)dot=xn"]
C --> D["Use ODE: xndot = F(t, x1, x2, ..., xn, u)"]
D --> E["Assemble vector model: xdot = f(t, x, u)"]
E --> F["Output (if needed): y = x1"]
4. Linear Time-Invariant Specialization
A central and highly common case is an LTI scalar ODE where the highest derivative is expressed linearly in terms of lower derivatives and input:
\[ y^{(n)}(t) + a_{n-1}y^{(n-1)}(t) + a_{n-2}y^{(n-2)}(t) + \dots + a_1\dot{y}(t) + a_0 y(t) = b\,u(t), \]
where the coefficients \( a_0,\dots,a_{n-1},b \in \mathbb{R} \) are constants. Using the state definition in Section 3, the last state equation becomes:
\[ \dot{x}_n(t) = -a_0 x_1(t) - a_1 x_2(t) - \dots - a_{n-2}x_{n-1}(t) - a_{n-1}x_n(t) + b\,u(t). \]
Combining with \( \dot{x}_i=x_{i+1} \) for \( i=1,\dots,n-1 \), we obtain:
\[ \dot{\mathbf{x}}(t) = \mathbf{A}\mathbf{x}(t) + \mathbf{B}u(t), \quad y(t) = \mathbf{C}\mathbf{x}(t) + \mathbf{D}u(t), \]
with:
\[ \mathbf{A} = \begin{bmatrix} 0 & 1 & 0 & \dots & 0\\ 0 & 0 & 1 & \dots & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & 0 & \dots & 1\\ -a_0 & -a_1 & -a_2 & \dots & -a_{n-1} \end{bmatrix}, \quad \mathbf{B} = \begin{bmatrix} 0\\0\\ \vdots\\0\\ b \end{bmatrix}, \]
\[ \mathbf{C} = \begin{bmatrix} 1 & 0 & 0 & \dots & 0 \end{bmatrix}, \quad \mathbf{D} = [0]. \]
The specific structure of \( \mathbf{A} \) that you see here will be examined systematically in the next lesson. For now, interpret it as a direct encoding of the derivative chain plus the ODE closure in the last row.
flowchart TD
U["u(t)"] --> S["sum: b*u - a0*x1 - a1*x2 - ... - a(n-1)*xn"]
S --> I_n["Integrator"] --> Xn["xn = y^(n-1)"]
Xn --> I_n1["Integrator"] --> Xn1["x(n-1) = y^(n-2)"]
Xn1 --> Dots["..."] --> I2["Integrator"] --> X2["x2 = y'"]
X2 --> I1["Integrator"] --> X1["x1 = y"]
X1 --> Y["y(t)=x1"]
5. Equivalence Theorem and Proof
We now prove that the conversion is mathematically exact: it does not approximate or “simplify” the dynamics. It only changes coordinates from derivatives of \( y \) to a state vector.
Theorem 1 (Solution Correspondence).
Assume \( F(t,\cdot) \) is such that the IVP for the scalar ODE admits a unique solution on some interval \( [t_0,T] \). Define the state mapping: \( \mathbf{x}(t) = [y(t)\;\dot{y}(t)\;\dots\;y^{(n-1)}(t)]^\top \). Then:
- If \( y(t) \) solves the scalar ODE, then \( \mathbf{x}(t) \) solves \( \dot{\mathbf{x}} = \mathbf{f}(t,\mathbf{x},u) \).
- Conversely, if \( \mathbf{x}(t) \) solves \( \dot{\mathbf{x}} = \mathbf{f}(t,\mathbf{x},u) \), then \( y(t)=x_1(t) \) solves the scalar ODE.
Proof.
(Forward direction) Suppose \( y(t) \) satisfies: \( y^{(n)}(t)=F(t,y(t),\dot{y}(t),\dots,y^{(n-1)}(t),u(t)) \). Define \( x_i(t)=y^{(i-1)}(t) \) for \( i=1,\dots,n \). Then for \( i=1,\dots,n-1 \), differentiating gives:
\[ \dot{x}_i(t) = \frac{d}{dt}y^{(i-1)}(t)=y^{(i)}(t)=x_{i+1}(t). \]
For \( i=n \), we have \( \dot{x}_n(t)=y^{(n)}(t) \), hence:
\[ \dot{x}_n(t) = F\!\big(t,x_1(t),x_2(t),\dots,x_n(t),u(t)\big). \]
Collecting these n equations yields \( \dot{\mathbf{x}}(t)=\mathbf{f}(t,\mathbf{x}(t),u(t)) \).
(Reverse direction) Suppose \( \mathbf{x}(t) \) satisfies the vector system with the same \( \mathbf{f} \). Define \( y(t)=x_1(t) \). From the system, \( \dot{x}_1(t)=x_2(t) \), hence \( \dot{y}(t)=x_2(t) \). Differentiating repeatedly and using the shift relations gives:
\[ y^{(k)}(t) = x_{k+1}(t),\quad k=1,2,\dots,n-1. \]
Finally, \( y^{(n)}(t)=\dot{x}_n(t) \), and the last state equation enforces:
\[ y^{(n)}(t)=F\!\big(t,y(t),\dot{y}(t),\dots,y^{(n-1)}(t),u(t)\big). \]
Hence \( y(t) \) satisfies the original scalar ODE. This completes the proof.
Corollary 1 (Initial-Condition Mapping).
The scalar initial conditions map to the vector initial condition by: \( \mathbf{x}(t_0) = [y(t_0)\;\dot{y}(t_0)\;\dots\;y^{(n-1)}(t_0)]^\top. \) Conversely, specifying \( \mathbf{x}(t_0) \) uniquely specifies the scalar initial derivatives up to order \( n-1 \).
6. Computational Construction Checklist
For a scalar ODE given explicitly as \( y^{(n)} = F(t,y,\dot{y},\dots,y^{(n-1)},u) \), the conversion is mechanical:
- Choose states \( x_1=y,\;x_2=\dot{y},\;\dots,\;x_n=y^{(n-1)} \).
- Write \( \dot{x}_1=x_2,\;\dot{x}_2=x_3,\;\dots,\;\dot{x}_{n-1}=x_n \).
- Write \( \dot{x}_n = F(t,x_1,\dots,x_n,u) \).
- If the output is the original variable, set \( y=x_1 \).
- (Validation) Differentiate \( y=x_1 \) repeatedly and eliminate intermediate states to recover the scalar ODE.
For the LTI case, steps 1–3 automatically produce matrices \( \mathbf{A},\mathbf{B} \) (and optionally \( \mathbf{C},\mathbf{D} \)). Numerical simulations can then be performed with any ODE solver.
7. Python Implementation (NumPy/SciPy + python-control)
The function below constructs
\( \mathbf{A},\mathbf{B},\mathbf{C},\mathbf{D} \) for
the LTI scalar ODE:
\( y^{(n)} + a_{n-1}y^{(n-1)} + \dots + a_0 y = b u \).
We then simulate using scipy.integrate.solve_ivp and
optionally wrap as a state-space object using control.ss.
import numpy as np
from dataclasses import dataclass
@dataclass
class StateSpace:
A: np.ndarray
B: np.ndarray
C: np.ndarray
D: np.ndarray
def nth_order_ode_to_ss(a, b=1.0):
"""
Build state-space matrices for:
y^(n) + a[n-1] y^(n-1) + ... + a[1] y' + a[0] y = b u
Parameters
----------
a : array_like, shape (n,)
Coefficients [a0, a1, ..., a(n-1)].
b : float
Input gain.
Returns
-------
StateSpace(A,B,C,D)
"""
a = np.asarray(a, dtype=float).ravel()
n = a.size
A = np.zeros((n, n), dtype=float)
# shift rows
for i in range(n - 1):
A[i, i + 1] = 1.0
# last row
A[n - 1, :] = -a # [-a0, -a1, ..., -a(n-1)]
B = np.zeros((n, 1), dtype=float)
B[n - 1, 0] = float(b)
C = np.zeros((1, n), dtype=float)
C[0, 0] = 1.0
D = np.zeros((1, 1), dtype=float)
return StateSpace(A, B, C, D)
# Example: third-order system: y''' + 3 y'' + 3 y' + 1 y = 2 u
ss = nth_order_ode_to_ss(a=[1.0, 3.0, 3.0], b=2.0)
# Simulate: xdot = A x + B u(t)
from scipy.integrate import solve_ivp
def u_of_t(t):
# step input
return 1.0 if t >= 0.0 else 0.0
def f(t, x):
x = x.reshape(-1, 1)
dx = ss.A @ x + ss.B * u_of_t(t)
return dx.ravel()
t0, tf = 0.0, 10.0
x0 = np.array([0.0, 0.0, 0.0]) # y(0), y'(0), y''(0)
sol = solve_ivp(f, (t0, tf), x0, max_step=0.01, rtol=1e-8, atol=1e-10)
# Output y(t) = C x(t)
y = (ss.C @ sol.y).ravel()
print("A=\n", ss.A)
print("B=\n", ss.B)
print("Final y(tf)=", y[-1])
# Optional: python-control wrapper (state-space object)
try:
import control
sys = control.ss(ss.A, ss.B, ss.C, ss.D)
# sys can be used with control.forced_response, control.initial_response, etc.
print(sys)
except ImportError:
print("python-control not installed; install with: pip install control")
Recommended Python libraries (as they appear across modern control
workflows):
numpy, scipy (ODE solvers),
control (state-space objects and responses), and
sympy (symbolic checks).
8. C++ Implementation (Eigen + RK4 from Scratch)
The following C++ template constructs the matrices and simulates \( \dot{\mathbf{x}}=\mathbf{A}\mathbf{x}+\mathbf{B}u(t) \) using a fixed-step 4th-order Runge–Kutta method (RK4). Matrix operations use Eigen.
#include <Eigen/Dense>
#include <iostream>
#include <vector>
#include <functional>
struct StateSpace {
Eigen::MatrixXd A;
Eigen::MatrixXd B;
Eigen::MatrixXd C;
Eigen::MatrixXd D;
};
StateSpace nthOrderOdeToSS(const std::vector<double>& a, double b) {
const int n = static_cast<int>(a.size());
StateSpace ss;
ss.A = Eigen::MatrixXd::Zero(n, n);
for (int i = 0; i < n - 1; ++i) ss.A(i, i + 1) = 1.0;
for (int j = 0; j < n; ++j) ss.A(n - 1, j) = -a[j];
ss.B = Eigen::MatrixXd::Zero(n, 1);
ss.B(n - 1, 0) = b;
ss.C = Eigen::MatrixXd::Zero(1, n);
ss.C(0, 0) = 1.0;
ss.D = Eigen::MatrixXd::Zero(1, 1);
return ss;
}
Eigen::VectorXd rk4Step(
const std::function<Eigen::VectorXd(double, const Eigen::VectorXd&)>& f,
double t, const Eigen::VectorXd& x, double h
) {
Eigen::VectorXd k1 = f(t, x);
Eigen::VectorXd k2 = f(t + 0.5*h, x + 0.5*h*k1);
Eigen::VectorXd k3 = f(t + 0.5*h, x + 0.5*h*k2);
Eigen::VectorXd k4 = f(t + h, x + h*k3);
return x + (h/6.0) * (k1 + 2.0*k2 + 2.0*k3 + k4);
}
int main() {
// Example: y''' + 3 y'' + 3 y' + 1 y = 2 u
std::vector<double> a = {1.0, 3.0, 3.0}; // [a0, a1, a2]
double b = 2.0;
StateSpace ss = nthOrderOdeToSS(a, b);
auto u = [](double t) { return (t >= 0.0) ? 1.0 : 0.0; };
auto f = [&ss, &u](double t, const Eigen::VectorXd& x) {
return ss.A * x + ss.B * u(t);
};
double t0 = 0.0, tf = 10.0, h = 1e-3;
int N = static_cast<int>((tf - t0) / h);
Eigen::VectorXd x = Eigen::VectorXd::Zero(3); // [y, y', y'']
double t = t0;
for (int k = 0; k < N; ++k) {
x = rk4Step(f, t, x, h);
t += h;
}
double y = (ss.C * x)(0, 0);
std::cout << "Final y(tf) = " << y << std::endl;
return 0;
}
Typical C++ libraries in control-oriented workflows include Eigen (linear algebra) and custom or library ODE integrators. The state construction itself is pure linear algebra; simulation requires numerical integration.
9. Java Implementation (EJML + Simple RK4)
In Java, EJML provides matrix operations. Below is a compact RK4 simulator for the same LTI construction.
import org.ejml.data.DMatrixRMaj;
import org.ejml.dense.row.CommonOps_DDRM;
import java.util.function.DoubleFunction;
public class NthOrderOdeToStateSpace {
static class StateSpace {
DMatrixRMaj A, B, C, D;
StateSpace(DMatrixRMaj A, DMatrixRMaj B, DMatrixRMaj C, DMatrixRMaj D){
this.A = A; this.B = B; this.C = C; this.D = D;
}
}
static StateSpace nthOrderOdeToSS(double[] a, double b) {
int n = a.length;
DMatrixRMaj A = new DMatrixRMaj(n, n);
for (int i = 0; i < n - 1; i++) A.set(i, i + 1, 1.0);
for (int j = 0; j < n; j++) A.set(n - 1, j, -a[j]);
DMatrixRMaj B = new DMatrixRMaj(n, 1);
B.set(n - 1, 0, b);
DMatrixRMaj C = new DMatrixRMaj(1, n);
C.set(0, 0, 1.0);
DMatrixRMaj D = new DMatrixRMaj(1, 1); // zero
return new StateSpace(A, B, C, D);
}
static DMatrixRMaj f(StateSpace ss, DoubleFunction<Double> u, double t, DMatrixRMaj x) {
// dx = A x + B u(t)
DMatrixRMaj dx = new DMatrixRMaj(x.numRows, 1);
CommonOps_DDRM.mult(ss.A, x, dx);
double ut = u.apply(t);
for (int i = 0; i < x.numRows; i++) {
dx.add(i, 0, ss.B.get(i, 0) * ut);
}
return dx;
}
static DMatrixRMaj rk4Step(StateSpace ss, DoubleFunction<Double> u, double t, DMatrixRMaj x, double h) {
DMatrixRMaj k1 = f(ss, u, t, x);
DMatrixRMaj x2 = x.copy(); CommonOps_DDRM.addEquals(x2, 0.5*h, k1);
DMatrixRMaj k2 = f(ss, u, t + 0.5*h, x2);
DMatrixRMaj x3 = x.copy(); CommonOps_DDRM.addEquals(x3, 0.5*h, k2);
DMatrixRMaj k3 = f(ss, u, t + 0.5*h, x3);
DMatrixRMaj x4 = x.copy(); CommonOps_DDRM.addEquals(x4, h, k3);
DMatrixRMaj k4 = f(ss, u, t + h, x4);
DMatrixRMaj out = x.copy();
for (int i = 0; i < out.numRows; i++) {
double incr = (h/6.0) * (k1.get(i,0) + 2.0*k2.get(i,0) + 2.0*k3.get(i,0) + k4.get(i,0));
out.add(i, 0, incr);
}
return out;
}
public static void main(String[] args) {
// Example: y''' + 3 y'' + 3 y' + 1 y = 2 u
double[] a = new double[]{1.0, 3.0, 3.0};
double b = 2.0;
StateSpace ss = nthOrderOdeToSS(a, b);
DoubleFunction<Double> u = (t) -> (t >= 0.0) ? 1.0 : 0.0;
double t0 = 0.0, tf = 10.0, h = 1e-3;
int N = (int)((tf - t0)/h);
DMatrixRMaj x = new DMatrixRMaj(3, 1); // [y, y', y''] initially zeros
double t = t0;
for (int k = 0; k < N; k++) {
x = rk4Step(ss, u, t, x, h);
t += h;
}
DMatrixRMaj y = new DMatrixRMaj(1,1);
CommonOps_DDRM.mult(ss.C, x, y);
System.out.println("Final y(tf) = " + y.get(0,0));
}
}
Common Java libraries used in control/estimation pipelines include EJML (linear algebra) and Apache Commons Math (ODE integrators). Here we used EJML plus a self-contained RK4 to keep the construction transparent.
10. MATLAB + Simulink Implementation
MATLAB’s Control System Toolbox supports continuous-time state-space
models directly. The code below builds
\( \mathbf{A},\mathbf{B},\mathbf{C},\mathbf{D} \),
creates an ss object, and simulates.
% Example: y''' + 3 y'' + 3 y' + 1 y = 2 u
a = [1; 3; 3]; % [a0; a1; a2]
b = 2;
n = length(a);
A = zeros(n);
A(1:n-1, 2:n) = eye(n-1);
A(n, :) = -a(:)';
B = zeros(n,1);
B(n) = b;
C = zeros(1,n);
C(1) = 1;
D = 0;
sys = ss(A,B,C,D);
t = linspace(0,10,5001);
u = ones(size(t)); % step input
x0 = [0;0;0]; % [y(0); y'(0); y''(0)]
[y,t_out,x] = lsim(sys,u,t,x0);
disp('A='), disp(A)
disp('Final y(tf)='), disp(y(end))
For Simulink, one natural realization is a chain of integrators that produces \( x_n \to x_{n-1} \to \dots \to x_1=y \), with feedback gains implementing the last equation. The following script programmatically creates such a model using basic blocks (Integrator, Sum, Gain, Inport, Outport). This avoids manual drawing and remains fully text-based.
% Programmatic Simulink construction for the same third-order example
model = 'nth_order_chain';
new_system(model); open_system(model);
% Blocks
add_block('simulink/Sources/In1', [model '/u']);
add_block('simulink/Math Operations/Sum', [model '/Sum'], 'Inputs', '++++');
add_block('simulink/Math Operations/Gain', [model '/bGain'], 'Gain', '2');
add_block('simulink/Continuous/Integrator', [model '/Int3']);
add_block('simulink/Continuous/Integrator', [model '/Int2']);
add_block('simulink/Continuous/Integrator', [model '/Int1']);
add_block('simulink/Sinks/Out1', [model '/y']);
% Feedback gains for -a0*x1 - a1*x2 - a2*x3 (a0=1,a1=3,a2=3)
add_block('simulink/Math Operations/Gain', [model '/a0'], 'Gain', '-1');
add_block('simulink/Math Operations/Gain', [model '/a1'], 'Gain', '-3');
add_block('simulink/Math Operations/Gain', [model '/a2'], 'Gain', '-3');
% Layout positions (minimal; adjust as desired)
set_param([model '/u'], 'Position', [30 80 60 100]);
set_param([model '/bGain'], 'Position', [90 75 130 105]);
set_param([model '/Sum'], 'Position', [170 70 200 110]);
set_param([model '/Int3'], 'Position', [240 70 270 110]);
set_param([model '/Int2'], 'Position', [320 70 350 110]);
set_param([model '/Int1'], 'Position', [400 70 430 110]);
set_param([model '/y'], 'Position', [470 80 500 100]);
set_param([model '/a2'], 'Position', [240 150 270 180]);
set_param([model '/a1'], 'Position', [320 150 350 180]);
set_param([model '/a0'], 'Position', [400 150 430 180]);
% Connections: u -> bGain -> Sum -> Int3 -> Int2 -> Int1 -> y
add_line(model, 'u/1', 'bGain/1');
add_line(model, 'bGain/1', 'Sum/1');
add_line(model, 'Sum/1', 'Int3/1');
add_line(model, 'Int3/1', 'Int2/1');
add_line(model, 'Int2/1', 'Int1/1');
add_line(model, 'Int1/1', 'y/1');
% Feedback taps: x3 from Int3 output, x2 from Int2 output, x1 from Int1 output
add_line(model, 'Int3/1', 'a2/1');
add_line(model, 'Int2/1', 'a1/1');
add_line(model, 'Int1/1', 'a0/1');
% Gains feed into Sum remaining ports
add_line(model, 'a2/1', 'Sum/2');
add_line(model, 'a1/1', 'Sum/3');
add_line(model, 'a0/1', 'Sum/4');
save_system(model);
This Simulink construction directly matches the integrator-chain interpretation in Section 4 and is a faithful implementation of the first-order vector system.
11. Wolfram Mathematica Implementation
Mathematica supports state-space representations via
StateSpaceModel. The code below constructs the LTI matrices
and computes an output response.
(* Example: y''' + 3 y'' + 3 y' + 1 y = 2 u *)
a = {1, 3, 3}; (* {a0, a1, a2} *)
b = 2;
n = Length[a];
A = ConstantArray[0, {n, n}];
Do[A[[i, i + 1]] = 1, {i, 1, n - 1}];
A[[n, All]] = -a;
B = ConstantArray[0, {n, 1}];
B[[n, 1]] = b;
C = ConstantArray[0, {1, n}];
C[[1, 1]] = 1;
D = {{0}};
sys = StateSpaceModel[{A, B, C, D}];
u[t_] := 1; (* step input *)
x0 = {0, 0, 0}; (* {y(0), y'(0), y''(0)} *)
(* Output response y(t) over [0,10] *)
resp = OutputResponse[sys, u, {t, 0, 10}, x0];
(* Evaluate at final time *)
N[resp /. t -> 10]
Mathematica can also solve the vector system directly with
NDSolve if you prefer explicit ODE form; the state
construction from Section 3 gives the system immediately.
12. Problems and Solutions
The problems below reinforce the construction, the initial-condition mapping, and the equivalence proof. All problems are self-contained and use only concepts introduced up to this lesson.
Problem 1 (Second-Order ODE to First-Order Vector Form). Convert the scalar ODE \( \ddot{y}(t) + 4\dot{y}(t) + 5y(t) = 3u(t) \) into a first-order vector system \( \dot{\mathbf{x}}=\mathbf{A}\mathbf{x}+\mathbf{B}u \) with output \( y=x_1 \).
Solution. Let \( x_1=y \), \( x_2=\dot{y} \). Then:
\[ \dot{x}_1 = x_2,\qquad \dot{x}_2 = -5x_1 - 4x_2 + 3u. \]
Thus:
\[ \mathbf{A}=\begin{bmatrix}0 & 1\\ -5 & -4\end{bmatrix},\quad \mathbf{B}=\begin{bmatrix}0\\3\end{bmatrix},\quad \mathbf{C}=\begin{bmatrix}1 & 0\end{bmatrix},\quad \mathbf{D}=[0]. \]
Problem 2 (Nonlinear Example). Consider the nonlinear ODE: \( \ddot{y}(t)= -y(t)^3 + u(t) \). Write the equivalent first-order vector system \( \dot{\mathbf{x}}=\mathbf{f}(t,\mathbf{x},u) \) with output \( y=x_1 \).
Solution. Let \( x_1=y \), \( x_2=\dot{y} \). Then:
\[ \dot{x}_1 = x_2,\qquad \dot{x}_2 = -x_1^3 + u(t). \]
Therefore \( \dot{\mathbf{x}} = [x_2\;\; -x_1^3+u]^\top \) and \( y=x_1 \).
Problem 3 (Recover the Scalar ODE by Elimination). Suppose a state model is given by: \( \dot{x}_1=x_2 \), \( \dot{x}_2=x_3 \), and \( \dot{x}_3=-2x_1-5x_2-4x_3+u(t) \), with \( y=x_1 \). Eliminate the states to obtain a single scalar ODE in \( y \).
Solution. Since \( y=x_1 \), we have:
\[ \dot{y}=\dot{x}_1=x_2,\quad \ddot{y}=\dot{x}_2=x_3,\quad y^{(3)}=\dot{x}_3. \]
Substitute \( x_2=\dot{y} \), \( x_3=\ddot{y} \) into the third equation:
\[ y^{(3)}(t) = -2y(t) - 5\dot{y}(t) - 4\ddot{y}(t) + u(t). \]
Rearranging yields the scalar ODE:
\[ y^{(3)}(t) + 4\ddot{y}(t) + 5\dot{y}(t) + 2y(t) = u(t). \]
Problem 4 (Initial-Condition Mapping). For the third-order scalar ODE \( y^{(3)} + a_2 y^{(2)} + a_1 \dot{y} + a_0 y = b u(t) \), specify precisely how the scalar initial conditions map to the vector initial condition.
Solution. With \( x_1=y \), \( x_2=\dot{y} \), \( x_3=y^{(2)} \), we have:
\[ \mathbf{x}(t_0)=\begin{bmatrix}x_1(t_0)\\x_2(t_0)\\x_3(t_0)\end{bmatrix} = \begin{bmatrix} y(t_0)\\ \dot{y}(t_0)\\ y^{(2)}(t_0) \end{bmatrix}. \]
Problem 5 (Equivalence Proof Detail). In the reverse direction of Theorem 1, justify carefully why \( y^{(k)}(t)=x_{k+1}(t) \) for \( k=1,2,\dots,n-1 \) holds for all \( t \) in the existence interval.
Solution.
Define \( y(t)=x_1(t) \). The state equations give \( \dot{x}_1=x_2 \), hence: \( \dot{y}=\dot{x}_1=x_2 \), so the claim holds for \( k=1 \). Now assume for some \( k \) with \( 1 \le k \le n-2 \) that \( y^{(k)}=x_{k+1} \). Differentiate both sides:
\[ y^{(k+1)}(t)=\frac{d}{dt}x_{k+1}(t)=\dot{x}_{k+1}(t). \]
By the shift relation in the state model, \( \dot{x}_{k+1}=x_{k+2} \), hence \( y^{(k+1)}=x_{k+2} \). By induction, the identity holds for all \( k=1,2,\dots,n-1 \) on the interval where the solution exists.
13. Summary
We converted an n-th order scalar ODE into an equivalent first-order vector system by defining the state as successive derivatives of the scalar variable. We proved exact equivalence (two-way solution correspondence) and established the precise mapping of initial conditions. For the LTI case, we derived explicit matrices \( \mathbf{A},\mathbf{B},\mathbf{C},\mathbf{D} \) and provided consistent implementations in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica. This construction is the entry point to systematic state-space modeling and will be refined structurally in the next lesson.
14. References
- Kalman, R.E. (1960). On the general theory of control systems. Proceedings of the First International Conference on Automatic Control, 481–492.
- Kalman, R.E., & Bucy, R.S. (1961). New results in linear filtering and prediction theory. Journal of Basic Engineering (ASME), 83(1), 95–108.
- Gilbert, E.G. (1963). Controllability and observability in multivariable control systems. Journal of the Society for Industrial and Applied Mathematics, Series A: Control, 1(2), 128–151.
- Wonham, W.M. (1967). On pole assignment in multi-input controllable linear systems. IEEE Transactions on Automatic Control, 12(6), 660–665.
- Rosenbrock, H.H. (1970). State-space and multivariable theory (foundational developments in realization viewpoints). Proceedings/Journal contributions in control theory.
- Brockett, R.W. (1972). System theory on group manifolds and coset spaces (foundational system-theoretic viewpoints). SIAM Journal on Control, 10(2), 265–284.
- Hautus, M.L.J. (1970). Controllability and observability conditions of linear autonomous systems. Nederl. Akad. Wetensch. Proc. Ser. A, 73, 443–448.
- Silverman, L.M. (1969). Inversion of multivariable linear systems. IEEE Transactions on Automatic Control, 14(3), 270–276.