Chapter 6: Input–Output Description and Transfer Functions

Lesson 2: Deriving Transfer Functions from ODEs (Zero Initial Conditions, Laplace Approach)

This lesson formalizes the core input–output pathway from linear time-invariant (LTI) ordinary differential equations (ODEs) to transfer functions using the Laplace transform under zero initial conditions (rest conditions). We emphasize mathematically rigorous derivations, clarify what is (and is not) encoded in a transfer function, and provide multi-language computational workflows that map an ODE model into numerator/denominator polynomials for analysis.

1. Prerequisites and Learning Outcomes

From earlier chapters, students already know: (i) ordinary differential equations, (ii) Laplace transform basics and common transform pairs, and (iii) convolution and impulse response for LTI systems (Lesson 1 of this chapter).

By the end of this lesson you will be able to:

  • Convert a constant-coefficient linear ODE into an algebraic equation in the Laplace domain under \( \text{IC}=0 \).
  • Derive the transfer function \( G(s) \) as a rational function and interpret numerator/denominator structure.
  • Separate the forced response from the natural response and explain why transfer functions omit initial conditions.
  • Implement ODE-to-transfer-function pipelines in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica.

2. The Laplace Derivative Property and the Role of Zero Initial Conditions

Consider an LTI SISO system described by an \( n \)-th order constant-coefficient ODE relating input \( u(t) \) to output \( y(t) \):

\[ \sum_{k=0}^{n} a_k \frac{d^k y(t)}{dt^k} = \sum_{k=0}^{m} b_k \frac{d^k u(t)}{dt^k}, \quad m \le n,\quad a_n \ne 0. \]

Let \( Y(s)=\mathcal{L}\{y(t)\} \) and \( U(s)=\mathcal{L}\{u(t)\} \). The Laplace transform of derivatives is the critical bridge from differential equations to algebra.

2.1 Theorem (Laplace transform of derivatives)

If \( y(t) \) is piecewise continuous on every finite interval and of exponential order, then for integer \( n \ge 1 \),

\[ \mathcal{L}\left\{\frac{d^n y(t)}{dt^n}\right\} = s^n Y(s) - \sum_{r=0}^{n-1} s^{n-1-r} y^{(r)}(0^-). \]

2.2 Proof (by induction using integration by parts)

For \( n=1 \), by definition and integration by parts:

\[ \mathcal{L}\{y'(t)\} = \int_{0^-}^{\infty} e^{-st} y'(t)\,dt = \Big[e^{-st}y(t)\Big]_{0^-}^{\infty} + s\int_{0^-}^{\infty} e^{-st}y(t)\,dt = -y(0^-) + sY(s), \]

where the boundary term at infinity vanishes for signals of exponential order. Assume the formula holds for \( n \). Then for \( n+1 \):

\[ \mathcal{L}\left\{\frac{d^{n+1}y(t)}{dt^{n+1}}\right\} = s\,\mathcal{L}\left\{\frac{d^n y(t)}{dt^n}\right\} - y^{(n)}(0^-) = s\left(s^n Y(s) - \sum_{r=0}^{n-1} s^{n-1-r} y^{(r)}(0^-)\right) - y^{(n)}(0^-), \]

which simplifies to the stated formula with the sum extended to \( r=0,\dots,n \). This completes the induction.

2.3 Zero initial conditions (rest conditions)

The phrase zero initial conditions means: \( y^{(r)}(0^-)=0 \) for \( r=0,1,\dots,n-1 \), and (when needed) input derivatives \( u^{(r)}(0^-) \) are also taken as zero. Under these rest conditions,

\[ \mathcal{L}\left\{\frac{d^n y(t)}{dt^n}\right\} = s^n Y(s), \qquad \mathcal{L}\left\{\frac{d^n u(t)}{dt^n}\right\} = s^n U(s). \]

flowchart TD
  A["Start: constant-coefficient linear ODE"] --> B["Take Laplace of both sides"]
  B --> C["Apply derivative rule: L{d^k y/dt^k} = s^k Y(s) - IC terms"]
  C --> D["Impose rest conditions: IC terms = 0"]
  D --> E["Collect Y(s) terms on left, U(s) terms on right"]
  E --> F["Solve algebraically: Y(s) = G(s) U(s)"]
  F --> G["Transfer function: G(s) = Y(s)/U(s) with IC=0"]
        

3. Transfer Function from an ODE: General Formula

Apply Laplace to the general ODE in Section 2 under rest conditions:

\[ \sum_{k=0}^{n} a_k s^k Y(s) = \sum_{k=0}^{m} b_k s^k U(s). \]

Define the polynomials: \( A(s)=\sum_{k=0}^{n} a_k s^k \) and \( B(s)=\sum_{k=0}^{m} b_k s^k \). Then the Laplace-domain input–output relationship becomes

\[ A(s)Y(s) = B(s)U(s). \]

Provided \( A(s) \ne 0 \) (as an algebraic object), solve for \( Y(s) \):

\[ Y(s) = \frac{B(s)}{A(s)}\,U(s). \]

3.1 Definition (transfer function)

The transfer function \( G(s) \) is defined as the Laplace-domain ratio under rest conditions:

\[ G(s) \triangleq \left.\frac{Y(s)}{U(s)}\right|_{\text{IC}=0} = \frac{B(s)}{A(s)}. \]

3.2 Consequences

  • Rational structure: If the system is described by a finite-order constant-coefficient ODE, then \( G(s) \) is a rational function in \( s \).
  • Order: The system order (in this input–output sense) is typically \( n \), the degree of \( A(s) \).
  • Properness: Physical systems commonly satisfy \( \deg B(s) \le \deg A(s) \). If \( \deg B(s) < \deg A(s) \), the transfer function is strictly proper.
flowchart LR
  U["Input u(t)"] --> G["LTI dynamics as G(s)=B(s)/A(s)"]
  G --> Y["Output y(t)"]
  note1["IC effects are not inside G(s); \nthey appear as extra terms in Y(s) when IC != 0"] --- G
        

4. Worked Example I — Mass–Spring–Damper (Force to Displacement)

From translational mechanical modeling (Chapter 3), a standard mass–spring–damper driven by force \( F(t) \) with displacement output \( x(t) \) satisfies:

\[ m\ddot{x}(t) + b\dot{x}(t) + kx(t) = F(t). \]

Take Laplace under rest conditions. Using \( \mathcal{L}\{\dot{x}\}=sX(s) \) and \( \mathcal{L}\{\ddot{x}\}=s^2 X(s) \):

\[ \big(ms^2 + bs + k\big)X(s) = F(s). \]

Therefore the transfer function from force input to displacement output is:

\[ G_{xF}(s) \triangleq \frac{X(s)}{F(s)} = \frac{1}{ms^2 + bs + k}. \]

If instead the output is velocity \( v(t)=\dot{x}(t) \), then \( V(s)=sX(s) \) under rest conditions, so:

\[ G_{vF}(s) \triangleq \frac{V(s)}{F(s)} = \frac{s}{ms^2 + bs + k}. \]

5. Worked Example II — Series RLC (Input Voltage to Capacitor Voltage)

From electrical modeling (Chapter 5), consider a series RLC with input voltage \( v_{in}(t) \) applied across the series chain and output as the capacitor voltage \( v_C(t) \). The current is \( i(t) \). Using element laws:

  • \( v_R(t)=Ri(t) \)
  • \( v_L(t)=L\,\frac{di(t)}{dt} \)
  • \( i(t)=C\,\frac{dv_C(t)}{dt} \) (since \( i=C\dot{v}_C \))

By KVL: \( v_{in}(t)=v_R(t)+v_L(t)+v_C(t) \). Substitute \( i=C\dot{v}_C \):

\[ v_{in}(t) = R\big(C\dot{v}_C(t)\big) + L\frac{d}{dt}\big(C\dot{v}_C(t)\big) + v_C(t) = RC\,\dot{v}_C(t) + LC\,\ddot{v}_C(t) + v_C(t). \]

Rearranged into standard ODE form:

\[ LC\,\ddot{v}_C(t) + RC\,\dot{v}_C(t) + v_C(t) = v_{in}(t). \]

Taking Laplace under rest conditions:

\[ (LC\,s^2 + RC\,s + 1)V_C(s) = V_{in}(s), \quad\Rightarrow\quad G_{Cv}(s)\triangleq \frac{V_C(s)}{V_{in}(s)} = \frac{1}{LC\,s^2 + RC\,s + 1}. \]

6. What If the Initial Conditions Are Not Zero?

A transfer function is defined only under rest conditions, but real experiments often start with stored energy (nonzero IC). The Laplace transform then produces additional terms that act like extra inputs.

For the mass–spring–damper:

\[ m\ddot{x}(t)+b\dot{x}(t)+kx(t)=F(t). \]

Taking Laplace without assuming rest conditions:

\[ \big(ms^2 + bs + k\big)X(s) = F(s) + m\big(sx(0^-)+\dot{x}(0^-)\big) + b\,x(0^-). \]

Hence,

\[ X(s) = \underbrace{\frac{1}{ms^2+bs+k}F(s)}_{\text{forced response}} + \underbrace{\frac{m\big(sx(0^-)+\dot{x}(0^-)\big)+b\,x(0^-)}{ms^2+bs+k}}_{\text{natural response from IC}}. \]

The forced response is governed by \( G(s)=\frac{1}{ms^2+bs+k} \), while the IC-dependent terms represent stored-energy effects (natural response). This is precisely why transfer functions are defined with \( \text{IC}=0 \): they encode only the input–output mapping when the system starts from rest.

7. Practical Checklist and Common Pitfalls

Checklist for deriving transfer functions from ODEs:

  1. Write the ODE with all output terms on the left and all input terms on the right.
  2. Confirm the ODE has constant coefficients (time invariance) and is linear in signals and derivatives.
  3. Apply Laplace transform to each term using derivative formulas.
  4. Impose rest conditions: set all IC terms to zero.
  5. Factor \( Y(s) \) and \( U(s) \) and compute \( G(s)=\frac{Y(s)}{U(s)} \).
  6. Optionally scale to make the leading denominator coefficient equal to 1.

Common pitfalls:

  • Mixing IC conventions: Laplace derivative formulas use \( 0^- \) to capture possible impulses at \( t=0 \). For this course, treat rest conditions as \( y^{(r)}(0^-)=0 \).
  • Algebra mistakes in polynomial collection: always factor \( Y(s) \) and \( U(s) \) explicitly as \( A(s)Y(s)=B(s)U(s) \).
  • Non-proper models: if \( \deg B(s) > \deg A(s) \), you likely differentiated inputs in a way that hides an algebraic constraint or omitted physical dynamics.
  • Units inconsistency: coefficients must be dimensionally consistent; transfer function gains carry physical units unless normalized.

8. Python Lab — From ODE Coefficients to Transfer Function

We show (i) symbolic derivation using sympy, and (ii) numerical transfer function construction using scipy.signal and (optionally) control (python-control).


# Symbolic derivation of G(s) from an ODE under rest conditions
import sympy as sp

t, s = sp.symbols('t s', positive=True, real=True)
m, b, k = sp.symbols('m b k', positive=True, real=True)

x = sp.Function('x')
F = sp.Function('F')

# ODE: m x'' + b x' + k x = F(t)
ode = sp.Eq(m*sp.diff(x(t), t, 2) + b*sp.diff(x(t), t, 1) + k*x(t), F(t))

# Laplace with explicit rest conditions x(0)=0, x'(0)=0
X = sp.Function('X')
Fs = sp.Function('Fs')

# sympy uses laplace_transform; we substitute IC=0 after transform
LHS = sp.laplace_transform(m*sp.diff(x(t), t, 2) + b*sp.diff(x(t), t, 1) + k*x(t), t, s, noconds=True)
RHS = sp.laplace_transform(F(t), t, s, noconds=True)

# Replace Laplace(x(t)) by X(s) for readability
Xsym = sp.Symbol('X')
Fsym = sp.Symbol('F')
expr = sp.Eq(LHS, RHS).subs({sp.LaplaceTransform(x(t), t, s): Xsym,
                            sp.LaplaceTransform(F(t), t, s): Fsym})

# Impose rest conditions: sympy already encodes them as terms with x(0), x'(0) etc in LHS; set them to zero
# (A robust method is to substitute x(0)=0, diff(x(t),t).subs(t,0)=0 before Laplace, but for this demo we directly solve algebraically)
# Solve for X(s)/F(s)
sol_X = sp.solve(expr, Xsym)[0]
G = sp.simplify(sol_X / Fsym)
print("G(s) =", G)

# Numeric construction using scipy.signal
import numpy as np
from scipy import signal

m_val, b_val, k_val = 1.0, 2.0, 10.0
num = [1.0]
den = [m_val, b_val, k_val]  # m s^2 + b s + k
sys = signal.TransferFunction(num, den)
print("scipy TF:", sys)

# Optional: python-control for time response (if installed)
try:
    import control
    Gc = control.tf(num, den)
    print("control TF:", Gc)

    # Step response where input is force step of magnitude 1 (units depend on model)
    # Note: plotting is optional; you can also just compute arrays.
    T = np.linspace(0.0, 10.0, 2000)
    T, y = control.step_response(Gc, T)
    print("Computed step response samples:", y[:5])
except Exception as e:
    print("python-control not available or failed to import:", e)
      

Interpretation: you entered the ODE coefficients \( (m,b,k) \) and obtained numerator/denominator arrays. This is precisely the computational representation of \( G(s)=\frac{B(s)}{A(s)} \).

9. C++ Lab — Representing Transfer Functions and Evaluating \( G(j\omega) \)

In C++, a lightweight and instructive workflow is: (i) store numerator/denominator coefficients, (ii) evaluate the rational function at \( s=j\omega \) using std::complex, and (iii) compute magnitude/phase.


#include <iostream>
#include <vector>
#include <complex>
#include <cmath>

static std::complex<double> eval_poly_desc(const std::vector<double>& coeff_desc,
                                           const std::complex<double>& s)
{
    // coeff_desc = [a_n, a_{n-1}, ..., a_0] for a_n s^n + ... + a_0
    std::complex<double> acc(0.0, 0.0);
    for (size_t i = 0; i < coeff_desc.size(); ++i) {
        acc = acc * s + coeff_desc[i]; // Horner form
    }
    return acc;
}

int main()
{
    // Example: mass-spring-damper G(s) = 1 / (m s^2 + b s + k)
    const double m = 1.0, b = 2.0, k = 10.0;

    std::vector<double> num = {1.0};              // [1]
    std::vector<double> den = {m, b, k};          // [m, b, k] corresponds to m s^2 + b s + k in descending powers

    for (int wi = 0; wi <= 10; ++wi) {
        double w = static_cast<double>(wi);
        std::complex<double> s(0.0, w);           // s = j w
        std::complex<double> G = eval_poly_desc(num, s) / eval_poly_desc(den, s);

        double mag = std::abs(G);
        double ph  = std::arg(G);                 // radians
        std::cout << "w=" << w << "  |G(jw)|=" << mag << "  arg=" << ph << "\n";
    }
    return 0;
}
      

This C++ implementation stays faithful to the mathematical definition: \( G(s)=\frac{B(s)}{A(s)} \), evaluated at \( s=j\omega \). Time-domain simulation will be treated later when numerical methods are introduced.

10. Java Lab — Evaluating Transfer Functions with Complex Arithmetic

Java does not ship with a complex-number type. A common choice is Apache Commons Math (org.apache.commons.math3.complex.Complex). The pattern matches the C++ lab: Horner evaluation of polynomials.


import java.util.Arrays;
import org.apache.commons.math3.complex.Complex;

public class TransferFunctionEval {

    // coeffDesc = [a_n, a_{n-1}, ..., a_0] for a_n s^n + ... + a_0
    public static Complex evalPolyDesc(double[] coeffDesc, Complex s) {
        Complex acc = Complex.ZERO;
        for (double c : coeffDesc) {
            acc = acc.multiply(s).add(c);
        }
        return acc;
    }

    public static void main(String[] args) {
        // Example: G(s) = 1 / (m s^2 + b s + k)
        double m = 1.0, b = 2.0, k = 10.0;

        double[] num = new double[] {1.0};
        double[] den = new double[] {m, b, k};

        for (int wi = 0; wi <= 10; wi++) {
            double w = (double) wi;
            Complex s = new Complex(0.0, w); // j w
            Complex G = evalPolyDesc(num, s).divide(evalPolyDesc(den, s));

            double mag = G.abs();
            double ph  = G.getArgument(); // radians
            System.out.println("w=" + w + "  |G(jw)|=" + mag + "  arg=" + ph);
        }
    }
}
      

Practical note: in later lessons (block diagrams, interconnections), you will use this representation to combine transfer functions algebraically (series/parallel/feedback), but the core object remains the same rational function.

11. MATLAB/Simulink Lab — Building \( G(s) \) and Using a Transfer Function Block

MATLAB’s Control System Toolbox provides the tf object. You enter numerator/denominator coefficient vectors that represent \( B(s) \) and \( A(s) \) in descending powers of \( s \).


% Mass-spring-damper: m x'' + b x' + k x = F(t)
m = 1; b = 2; k = 10;

num = [1];
den = [m b k];

G = tf(num, den);
disp(G);

% Step response (force step of magnitude 1)
t = linspace(0, 10, 2000);
[y, t] = step(G, t);

% Optional plot
figure; plot(t, y); grid on;
xlabel('t (s)'); ylabel('x(t)');
title('Step response of G(s) = X(s)/F(s)');
      

Simulink mapping (conceptual workflow)

To implement the same model in Simulink (without images), follow these steps:

  1. Open Simulink and create a new model.
  2. Add a Step block as the input \( F(t) \).
  3. Add a Transfer Fcn block and set: Numerator = \( [1] \), Denominator = \( [m\;\; b\;\; k] \).
  4. Add a Scope block to visualize \( x(t) \).
  5. Connect Step → Transfer Fcn → Scope, then run the simulation.

This is the direct software realization of the derivation: your ODE coefficients become the transfer function coefficients.

12. Wolfram Mathematica Lab — Symbolic Laplace and TransferFunctionModel

Mathematica can derive \( G(s) \) symbolically and construct a transfer-function model object.


(* Mass-spring-damper: m x''(t) + b x'(t) + k x(t) = F(t) *)
ClearAll[t, s, m, b, k, x, F];
x = x[t]; F = F[t];

ode = m*D[x, {t, 2}] + b*D[x, t] + k*x == F;

(* Laplace transform under rest conditions: x(0)=0, x'(0)=0 *)
X = LaplaceTransform[x, t, s];
Fs = LaplaceTransform[F, t, s];

eqS = LaplaceTransform[m*D[x, {t, 2}] + b*D[x, t] + k*x, t, s] == Fs /. {
  x /. t -> 0 -> 0,
  (D[x, t] /. t -> 0) -> 0
};

(* Solve for X(s)/F(s) *)
sol = Solve[eqS, X][[1, 1, 2]];
G = FullSimplify[sol/Fs]
Print["G(s) = ", G];

(* Create a transfer function model with numeric parameters *)
mval = 1.0; bval = 2.0; kval = 10.0;
sys = TransferFunctionModel[ { {1} }, { {mval, bval, kval} }, s];
sys

(* Step response *)
StepResponse[sys, {t, 0, 10}]
      

Practical note: Mathematica’s symbolic environment can also be used to perform partial fraction expansion and inverse Laplace, but detailed time-domain decomposition is best aligned with later lessons on response analysis.

13. Problems and Solutions

The problems below mirror standard textbook exercises and reinforce the Laplace-based workflow under \( \text{IC}=0 \). Each solution is fully derived.


Problem 1 (General second-order ODE): Consider the ODE \( \ddot{y}(t) + 3\dot{y}(t) + 2y(t) = 5\dot{u}(t) + u(t) \). Under rest conditions, derive the transfer function \( G(s)=\frac{Y(s)}{U(s)} \).

Solution:

Apply Laplace under rest conditions: \( \mathcal{L}\{\ddot{y}\}=s^2Y(s) \), \( \mathcal{L}\{\dot{y}\}=sY(s) \), \( \mathcal{L}\{\dot{u}\}=sU(s) \).

\[ \big(s^2 + 3s + 2\big)Y(s) = \big(5s + 1\big)U(s). \]

Therefore,

\[ G(s)=\frac{Y(s)}{U(s)}=\frac{5s+1}{s^2+3s+2}. \]


Problem 2 (Mass–spring–damper with velocity output): A mass–spring–damper satisfies \( m\ddot{x}(t)+b\dot{x}(t)+kx(t)=F(t) \). Derive \( \frac{V(s)}{F(s)} \) where \( v(t)=\dot{x}(t) \).

Solution:

From Section 4, under rest conditions:

\[ (ms^2+bs+k)X(s)=F(s). \]

Since \( v(t)=\dot{x}(t) \), under rest conditions \( V(s)=sX(s) \). Multiply:

\[ V(s)=sX(s)=\frac{s}{ms^2+bs+k}F(s) \quad\Rightarrow\quad \frac{V(s)}{F(s)}=\frac{s}{ms^2+bs+k}. \]


Problem 3 (Series RLC): For the series RLC in Section 5, verify that the ODE \( LC\,\ddot{v}_C(t)+RC\,\dot{v}_C(t)+v_C(t)=v_{in}(t) \) yields \( \frac{V_C(s)}{V_{in}(s)}=\frac{1}{LC\,s^2+RC\,s+1} \) under rest conditions.

Solution:

Apply Laplace under rest conditions: \( \mathcal{L}\{\ddot{v}_C\}=s^2V_C(s) \), \( \mathcal{L}\{\dot{v}_C\}=sV_C(s) \).

\[ LC\,s^2V_C(s) + RC\,sV_C(s) + V_C(s) = V_{in}(s). \]

Factor \( V_C(s) \):

\[ (LC\,s^2 + RC\,s + 1)V_C(s)=V_{in}(s) \quad\Rightarrow\quad \frac{V_C(s)}{V_{in}(s)}=\frac{1}{LC\,s^2+RC\,s+1}. \]


Problem 4 (Recovering an ODE from a transfer function): Suppose a system has \( G(s)=\frac{Y(s)}{U(s)}=\frac{2s+4}{s^2+5s+6} \). Under rest conditions, find a time-domain ODE that relates \( y(t) \) and \( u(t) \).

Solution:

Start from \( Y(s)=G(s)U(s) \) and clear the denominator:

\[ (s^2+5s+6)Y(s)=(2s+4)U(s). \]

Interpret multiplication by \( s \) as differentiation in time under rest conditions: \( sY(s)\leftrightarrow \dot{y}(t) \) and \( s^2Y(s)\leftrightarrow \ddot{y}(t) \), similarly for \( U(s) \).

\[ s^2Y(s) + 5sY(s) + 6Y(s) = 2sU(s) + 4U(s) \;\;\Longleftrightarrow\;\; \ddot{y}(t) + 5\dot{y}(t) + 6y(t) = 2\dot{u}(t) + 4u(t). \]

This ODE is valid in the same sense as the transfer-function definition: it assumes rest conditions when mapping between Laplace and time derivatives.

14. Summary

You derived transfer functions by (i) applying the Laplace transform to constant-coefficient linear ODEs, (ii) imposing zero initial conditions to remove natural-response terms, and (iii) solving the resulting algebraic equation \( A(s)Y(s)=B(s)U(s) \) to obtain \( G(s)=\frac{B(s)}{A(s)} \). Worked examples in mechanical and electrical domains showed how physical parameters directly become transfer-function coefficients. Multi-language implementations demonstrated how the same rational function is represented computationally.

15. References

  1. Nyquist, H. (1932). Regeneration theory. Bell System Technical Journal, 11(1), 126–147.
  2. Bode, H.W. (1940). Relations between attenuation and phase in feedback amplifier design. Bell System Technical Journal, 19(3), 421–454.
  3. Youla, D.C. (1961). On the factorization of rational matrices. IRE Transactions on Circuit Theory, 8(3), 172–189.
  4. Zames, G. (1966). On the input–output stability of time-varying nonlinear feedback systems—Part I. IEEE Transactions on Automatic Control, 11(2), 228–238.
  5. Kalman, R.E. (1960). On the general theory of control systems. Proceedings of the First IFAC Congress, 481–492.