Chapter 24: Pole Placement for Multi-Input Systems
Lesson 5: Design Examples for Multi-Input Systems
This lesson develops complete design examples for multi-input pole placement. We connect controllability, the algebraic structure of state-feedback, eigenvector selection, numerical conditioning, and implementation in Python, C++, Java, MATLAB/Simulink, and Wolfram Mathematica. The central message is that in MIMO pole placement the poles are only part of the design: the remaining degrees of freedom should be used to improve eigenstructure, conditioning, and control effort.
1. Design Problem and MIMO Degrees of Freedom
Consider the continuous-time LTI plant \( \dot{\mathbf{x} }=\mathbf{A}\mathbf{x}+\mathbf{B}\mathbf{u} \), where \( \mathbf{x}\in\mathbb{R}^n \) and \( \mathbf{u}\in\mathbb{R}^m \). A full-state feedback controller has the form \( \mathbf{u}=-\mathbf{K}\mathbf{x} \), giving the closed-loop matrix
\[ \mathbf{A}_c=\mathbf{A}-\mathbf{B}\mathbf{K}. \]
The pole-placement objective is to choose \( \mathbf{K}\in\mathbb{R}^{m\times n} \) so that \( \sigma(\mathbf{A}_c)=\{\lambda_1,\ldots,\lambda_n\} \), with desired stability normally requiring \( \operatorname{Re}(\lambda_i)<0 \) for all \( i \). For a controllable pair \( (\mathbf{A},\mathbf{B}) \), arbitrary closed-loop pole assignment is possible. The Kalman condition is
\[ \operatorname{rank}\!\left[\mathbf{B}\;\;\mathbf{A}\mathbf{B}\;\;\cdots\;\;\mathbf{A}^{n-1}\mathbf{B}\right]=n. \]
In a single-input system, the pole assignment gain is usually unique once the pole set is fixed. In a multi-input system, however, \( mn \) feedback entries are available but the characteristic polynomial imposes only \( n \) scalar constraints. Thus, MIMO pole placement has additional design degrees of freedom that can shape eigenvectors, reduce gain magnitude, improve conditioning, or respect actuator preferences.
flowchart TD
A["Plant matrices A and B"] --> B["Check rank of controllability matrix"]
B --> C{"rank equals n?"}
C -->|"no"| D["Only controllable modes \ncan be assigned"]
C -->|"yes"| E["Choose desired poles \nfrom response specs"]
E --> F["Select MIMO design method"]
F --> G["Library algorithm: \nplace / place_poles"]
F --> H["Eigenstructure assignment"]
G --> I["Verify eig(A - B K), effort, conditioning"]
H --> I
I --> J["Simulate closed-loop response"]
2. Eigenstructure-Based Construction
A constructive way to understand multi-input pole placement is to assign eigenvalue-eigenvector pairs. Suppose \( \lambda_i \) is a desired closed-loop eigenvalue and \( \mathbf{v}_i\neq\mathbf{0} \) is the associated closed-loop eigenvector. Since \( \mathbf{A}_c\mathbf{v}_i=\lambda_i\mathbf{v}_i \),
\[ (\mathbf{A}-\mathbf{B}\mathbf{K})\mathbf{v}_i=\lambda_i\mathbf{v}_i. \]
Define \( \mathbf{f}_i=\mathbf{K}\mathbf{v}_i \). Then the pair \( (\mathbf{v}_i,\mathbf{f}_i) \) must satisfy the homogeneous equation
\[ \left[\mathbf{A}-\lambda_i\mathbf{I}\;\;-\mathbf{B}\right] \begin{bmatrix}\mathbf{v}_i\\ \mathbf{f}_i\end{bmatrix}= \mathbf{0}. \]
Hence, for each desired pole, choose a vector from the null space of \( [\mathbf{A}-\lambda_i\mathbf{I}\;\; -\mathbf{B}] \). If the selected \( \mathbf{v}_i \) form an invertible matrix \( \mathbf{V}=[\mathbf{v}_1\;\cdots\;\mathbf{v}_n] \) and \( \mathbf{F}=[\mathbf{f}_1\;\cdots\;\mathbf{f}_n] \), then
\[ \mathbf{K}=\mathbf{F}\mathbf{V}^{-1}. \]
Proof. Since \( \mathbf{K}\mathbf{V}=\mathbf{F} \), the constructed gain satisfies \( \mathbf{K}\mathbf{v}_i=\mathbf{f}_i \) for each \( i \). The null-space equation gives \( (\mathbf{A}-\lambda_i\mathbf{I})\mathbf{v}_i=\mathbf{B}\mathbf{f}_i \). Therefore,
\[ (\mathbf{A}-\mathbf{B}\mathbf{K})\mathbf{v}_i =\mathbf{A}\mathbf{v}_i-\mathbf{B}\mathbf{f}_i =\lambda_i\mathbf{v}_i. \]
Because \( \mathbf{V} \) is nonsingular, these \( n \) eigenvectors form a basis, so the closed-loop spectrum is exactly the assigned pole set. This proof also explains why MIMO pole placement is not merely a polynomial-matching calculation: eigenvector choices influence both \( \mathbf{K} \) and the transient response.
flowchart TD
P["Desired pole lambda_i"] --> M["Build matrix [A - lambda_i I, -B]"]
M --> N["Compute nullspace basis"]
N --> S["Select vector [v_i; f_i]"]
S --> R{"v vectors independent?"}
R -->|"no"| S
R -->|"yes"| V["Assemble V and F"]
V --> K["Compute K = F inv(V)"]
K --> T["Check eigenvalues of A - B K"]
3. Worked Design Example: Fourth-Order Plant with Two Inputs
Consider the two-input plant
\[ \mathbf{A}=\begin{bmatrix} 0&1&0&0\\ 0&0&1&0\\ 0&0&0&1\\ -2&-5&-4&-1 \end{bmatrix},\qquad \mathbf{B}=\begin{bmatrix} 0&0\\ 1&0\\ 0&0\\ 0&1 \end{bmatrix}. \]
The controllability matrix is
\[ \mathcal{C}=\left[\mathbf{B}\;\;\mathbf{A}\mathbf{B}\;\;\mathbf{A}^2\mathbf{B}\;\;\mathbf{A}^3\mathbf{B}\right]. \]
Direct computation gives \( \operatorname{rank}(\mathcal{C})=4 \), so all four poles are assignable. Choose the desired closed-loop poles
\[ \Lambda_d=\{-1,-2,-3,-4\}. \]
A robust library method such as MATLAB place or SciPy
place_poles returns one valid gain. The eigenstructure
construction returns another valid gain because it selects a particular
set of eigenvectors from each null space. Both are correct if
\[ \det\!\left(s\mathbf{I}-(\mathbf{A}-\mathbf{B}\mathbf{K})\right) =(s+1)(s+2)(s+3)(s+4). \]
The expanded desired characteristic polynomial is
\[ \phi_d(s)=s^4+10s^3+35s^2+50s+24. \]
The matrix equality above is the final verification; because this is a multi-input plant, matching only the polynomial does not identify a unique \( \mathbf{K} \).
4. Conditioning, Gain Size, and Design Selection
For a diagonalizable closed-loop matrix, \( \mathbf{A}_c=\mathbf{V}\boldsymbol{\Lambda}\mathbf{V}^{-1} \). The homogeneous response is
\[ \mathbf{x}(t)=\mathbf{V}e^{\boldsymbol{\Lambda}t}\mathbf{V}^{-1}\mathbf{x}(0). \]
Even when all desired poles are stable, a poorly conditioned \( \mathbf{V} \) can create large transient amplification. A useful diagnostic is
\[ \kappa_2(\mathbf{V})=\|\mathbf{V}\|_2\|\mathbf{V}^{-1}\|_2. \]
Multi-input freedom can be used to choose eigenvectors that reduce \( \kappa_2(\mathbf{V}) \) or reduce control effort. For the feedback law \( \mathbf{u}=-\mathbf{K}\mathbf{x} \), a common preliminary effort index is
\[ J_K=\|\mathbf{K}\|_F^2=\operatorname{trace}(\mathbf{K}\mathbf{K}^T). \]
A practical MIMO pole-placement design is therefore not complete after eigenvalue verification. Students should also inspect the gain norm, the eigenvector condition number, actuator saturation margins, and the simulated state and control histories.
5. Implementation Labs
The following programs implement the same design example. Python uses
both SciPy's robust routine and a from-scratch null-space construction.
C++ and Java implement the null-space construction using standard
numerical linear algebra libraries. MATLAB uses place and
also gives a Simulink construction path. Wolfram Mathematica shows the
same algebra in symbolic/numeric form.
Chapter24_Lesson5.py
"""
Chapter24_Lesson5.py
Modern Control - Chapter 24, Lesson 5
Design examples for multi-input pole placement.
Libraries:
numpy, scipy, matplotlib
Install:
pip install numpy scipy matplotlib
"""
import numpy as np
from numpy.linalg import inv, matrix_rank, eig
from scipy.linalg import null_space
from scipy.signal import place_poles
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
np.set_printoptions(precision=5, suppress=True)
def controllability_matrix(A: np.ndarray, B: np.ndarray) -> np.ndarray:
"""Kalman controllability matrix [B AB ... A^(n-1)B]."""
n = A.shape[0]
blocks = [B]
Ap = np.eye(n)
for _ in range(1, n):
Ap = Ap @ A
blocks.append(Ap @ B)
return np.hstack(blocks)
def eigenstructure_assignment(A: np.ndarray, B: np.ndarray, poles, seed: int = 24):
"""
Multi-input pole assignment using eigenvector selection.
For u = -Kx, desired pairs (lambda_i, v_i) must satisfy
(A - B K) v_i = lambda_i v_i.
Put f_i = K v_i. Then
[A - lambda_i I, -B] [v_i; f_i] = 0.
After selecting n linearly independent v_i, K = F V^{-1}.
"""
rng = np.random.default_rng(seed)
n, m = B.shape
V = np.zeros((n, n), dtype=float)
F = np.zeros((m, n), dtype=float)
for i, lam in enumerate(poles):
M = np.hstack((A - lam * np.eye(n), -B))
N = null_space(M) # basis for [v; f]
if N.shape[1] == 0:
raise RuntimeError(f"No nullspace found for lambda={lam}.")
# Try deterministic/random combinations until V remains well-conditioned.
best_vec = None
best_score = -np.inf
for trial in range(200):
q = np.ones(N.shape[1]) if trial == 0 else rng.normal(size=N.shape[1])
s = N @ q
v = np.real_if_close(s[:n]).astype(float)
f = np.real_if_close(s[n:]).astype(float)
V_trial = V.copy()
V_trial[:, i] = v
score = matrix_rank(V_trial[:, : i + 1])
norm_score = np.linalg.norm(v)
if score + 1e-4 * norm_score > best_score:
best_score = score + 1e-4 * norm_score
best_vec = (v, f)
if score == i + 1:
best_vec = (v, f)
break
V[:, i], F[:, i] = best_vec
if matrix_rank(V) < n:
raise RuntimeError("Selected eigenvectors are not linearly independent.")
K = F @ inv(V)
return K, V, F
def simulate_closed_loop(A: np.ndarray, B: np.ndarray, K: np.ndarray, x0: np.ndarray):
"""Simulate xdot = (A - B K)x."""
Acl = A - B @ K
def rhs(t, x):
return Acl @ x
sol = solve_ivp(rhs, (0.0, 8.0), x0, max_step=0.01, dense_output=True)
return sol
def main():
# Example: fourth-order plant with two independent actuators.
A = np.array([
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
[-2.0, -5.0, -4.0, -1.0],
])
B = np.array([
[0.0, 0.0],
[1.0, 0.0],
[0.0, 0.0],
[0.0, 1.0],
])
desired_poles = np.array([-1.0, -2.0, -3.0, -4.0])
Ctrb = controllability_matrix(A, B)
print("rank(C) =", matrix_rank(Ctrb), "out of", A.shape[0])
# Library design: SciPy implements robust MIMO algorithms.
scipy_result = place_poles(A, B, desired_poles, method="YT")
K_scipy = scipy_result.gain_matrix
print("\nSciPy gain K:")
print(K_scipy)
print("Closed-loop eigenvalues (SciPy):", np.sort(eig(A - B @ K_scipy)[0]))
# From-scratch eigenstructure design.
K_eig, V, F = eigenstructure_assignment(A, B, desired_poles)
print("\nEigenstructure-assignment gain K:")
print(K_eig)
print("Closed-loop eigenvalues (from scratch):", np.sort(eig(A - B @ K_eig)[0]))
print("cond(V) =", np.linalg.cond(V))
# Time-domain check.
x0 = np.array([1.0, -0.5, 0.8, 0.0])
sol = simulate_closed_loop(A, B, K_eig, x0)
plt.figure(figsize=(8, 5))
for i in range(A.shape[0]):
plt.plot(sol.t, sol.y[i], label=f"x{i+1}")
plt.xlabel("time (s)")
plt.ylabel("state")
plt.title("Closed-loop response with multi-input pole placement")
plt.grid(True)
plt.legend()
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()
Chapter24_Lesson5.cpp
/*
Chapter24_Lesson5.cpp
Modern Control - Chapter 24, Lesson 5
Design examples for multi-input pole placement using Eigen.
Dependency:
Eigen 3.x
Compile example:
g++ -std=c++17 Chapter24_Lesson5.cpp -I /path/to/eigen -O2 -o Chapter24_Lesson5
*/
#include <Eigen/Dense>
#include <Eigen/SVD>
#include <iostream>
#include <random>
#include <vector>
using Eigen::JacobiSVD;
using Eigen::MatrixXd;
using Eigen::VectorXd;
MatrixXd controllabilityMatrix(const MatrixXd& A, const MatrixXd& B) {
const int n = A.rows();
const int m = B.cols();
MatrixXd C(n, n * m);
MatrixXd Ap = MatrixXd::Identity(n, n);
for (int k = 0; k < n; ++k) {
if (k > 0) Ap = Ap * A;
C.block(0, k * m, n, m) = Ap * B;
}
return C;
}
int numericalRank(const MatrixXd& M, double tol = 1e-9) {
JacobiSVD<MatrixXd> svd(M);
int r = 0;
for (int i = 0; i < svd.singularValues().size(); ++i) {
if (svd.singularValues()(i) > tol) ++r;
}
return r;
}
MatrixXd nullspace(const MatrixXd& M, double tol = 1e-9) {
JacobiSVD<MatrixXd> svd(M, Eigen::ComputeFullV);
int rank = 0;
for (int i = 0; i < svd.singularValues().size(); ++i) {
if (svd.singularValues()(i) > tol) ++rank;
}
return svd.matrixV().rightCols(M.cols() - rank);
}
MatrixXd eigenstructurePolePlacement(
const MatrixXd& A,
const MatrixXd& B,
const std::vector<double>& poles
) {
const int n = A.rows();
const int m = B.cols();
MatrixXd V = MatrixXd::Zero(n, n);
MatrixXd F = MatrixXd::Zero(m, n);
std::mt19937 gen(24);
std::normal_distribution<double> normal(0.0, 1.0);
for (int i = 0; i < n; ++i) {
double lambda = poles[i];
MatrixXd M(n, n + m);
M.block(0, 0, n, n) = A - lambda * MatrixXd::Identity(n, n);
M.block(0, n, n, m) = -B;
MatrixXd N = nullspace(M);
if (N.cols() == 0) {
throw std::runtime_error("Empty nullspace for a requested pole.");
}
VectorXd bestV(n), bestF(m);
bool accepted = false;
for (int trial = 0; trial < 300; ++trial) {
VectorXd q(N.cols());
if (trial == 0) {
q.setOnes();
} else {
for (int j = 0; j < q.size(); ++j) q(j) = normal(gen);
}
VectorXd s = N * q;
VectorXd v = s.head(n);
VectorXd f = s.tail(m);
MatrixXd Vtrial = V;
Vtrial.col(i) = v;
if (numericalRank(Vtrial.leftCols(i + 1)) == i + 1) {
bestV = v;
bestF = f;
accepted = true;
break;
}
}
if (!accepted) {
throw std::runtime_error("Could not select independent eigenvector.");
}
V.col(i) = bestV;
F.col(i) = bestF;
}
if (numericalRank(V) < n) {
throw std::runtime_error("Final eigenvector matrix is singular.");
}
return F * V.inverse();
}
int main() {
MatrixXd A(4, 4);
A << 0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
-2,-5,-4,-1;
MatrixXd B(4, 2);
B << 0, 0,
1, 0,
0, 0,
0, 1;
std::vector<double> poles = {-1.0, -2.0, -3.0, -4.0};
MatrixXd C = controllabilityMatrix(A, B);
std::cout << "rank(C) = " << numericalRank(C) << " out of " << A.rows() << "\n\n";
MatrixXd K = eigenstructurePolePlacement(A, B, poles);
std::cout << "K =\n" << K << "\n\n";
Eigen::EigenSolver<MatrixXd> es(A - B * K);
std::cout << "Closed-loop eigenvalues:\n" << es.eigenvalues() << "\n";
return 0;
}
Chapter24_Lesson5.java
/*
Chapter24_Lesson5.java
Modern Control - Chapter 24, Lesson 5
Design examples for multi-input pole placement using EJML.
Dependency:
EJML Simple API
Maven dependency:
org.ejml:ejml-simple:0.43 or newer
*/
import org.ejml.simple.SimpleEVD;
import org.ejml.simple.SimpleMatrix;
import org.ejml.simple.SimpleSVD;
import java.util.Random;
public class Chapter24_Lesson5 {
static SimpleMatrix controllabilityMatrix(SimpleMatrix A, SimpleMatrix B) {
int n = A.numRows();
int m = B.numCols();
SimpleMatrix C = new SimpleMatrix(n, n * m);
SimpleMatrix Ap = SimpleMatrix.identity(n);
for (int k = 0; k < n; k++) {
if (k > 0) Ap = Ap.mult(A);
C.insertIntoThis(0, k * m, Ap.mult(B));
}
return C;
}
static int numericalRank(SimpleMatrix M, double tol) {
SimpleSVD<SimpleMatrix> svd = M.svd();
int r = 0;
for (int i = 0; i < svd.getSingularValues().length; i++) {
if (svd.getSingularValues()[i] > tol) r++;
}
return r;
}
static SimpleMatrix nullspace(SimpleMatrix M, double tol) {
SimpleSVD<SimpleMatrix> svd = M.svd();
int rank = 0;
double[] s = svd.getSingularValues();
for (double value : s) if (value > tol) rank++;
SimpleMatrix V = svd.getV();
return V.extractMatrix(0, V.numRows(), rank, V.numCols());
}
static SimpleMatrix eigenstructurePolePlacement(SimpleMatrix A, SimpleMatrix B, double[] poles) {
int n = A.numRows();
int m = B.numCols();
SimpleMatrix V = new SimpleMatrix(n, n);
SimpleMatrix F = new SimpleMatrix(m, n);
Random rng = new Random(24);
for (int i = 0; i < n; i++) {
double lambda = poles[i];
SimpleMatrix M = new SimpleMatrix(n, n + m);
M.insertIntoThis(0, 0, A.minus(SimpleMatrix.identity(n).scale(lambda)));
M.insertIntoThis(0, n, B.scale(-1.0));
SimpleMatrix N = nullspace(M, 1e-9);
if (N.numCols() == 0) throw new RuntimeException("Empty nullspace.");
boolean accepted = false;
for (int trial = 0; trial < 300; trial++) {
SimpleMatrix q = new SimpleMatrix(N.numCols(), 1);
for (int j = 0; j < q.numRows(); j++) {
q.set(j, 0, trial == 0 ? 1.0 : rng.nextGaussian());
}
SimpleMatrix s = N.mult(q);
SimpleMatrix v = s.extractMatrix(0, n, 0, 1);
SimpleMatrix f = s.extractMatrix(n, n + m, 0, 1);
SimpleMatrix Vtrial = V.copy();
Vtrial.insertIntoThis(0, i, v);
if (numericalRank(Vtrial.extractMatrix(0, n, 0, i + 1), 1e-8) == i + 1) {
V.insertIntoThis(0, i, v);
F.insertIntoThis(0, i, f);
accepted = true;
break;
}
}
if (!accepted) throw new RuntimeException("Could not select independent eigenvector.");
}
return F.mult(V.invert());
}
public static void main(String[] args) {
SimpleMatrix A = new SimpleMatrix(new double[][]{
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1},
{-2, -5, -4, -1}
});
SimpleMatrix B = new SimpleMatrix(new double[][]{
{0, 0},
{1, 0},
{0, 0},
{0, 1}
});
double[] poles = {-1, -2, -3, -4};
SimpleMatrix C = controllabilityMatrix(A, B);
System.out.println("rank(C) = " + numericalRank(C, 1e-9) + " out of " + A.numRows());
SimpleMatrix K = eigenstructurePolePlacement(A, B, poles);
System.out.println("K =");
K.print();
SimpleEVD<SimpleMatrix> evd = A.minus(B.mult(K)).eig();
System.out.println("Closed-loop eigenvalues:");
for (int i = 0; i < evd.getNumberOfEigenvalues(); i++) {
System.out.println(evd.getEigenvalue(i));
}
}
}
Chapter24_Lesson5.m
% Chapter24_Lesson5.m
% Modern Control - Chapter 24, Lesson 5
% Design examples for multi-input pole placement in MATLAB / Simulink.
%
% Required toolbox for place/ss/initial:
% Control System Toolbox
clear; clc; close all;
A = [ 0 1 0 0;
0 0 1 0;
0 0 0 1;
-2 -5 -4 -1 ];
B = [ 0 0;
1 0;
0 0;
0 1 ];
p = [-1 -2 -3 -4];
Co = ctrb(A,B);
fprintf('rank(Co) = %d out of %d\n', rank(Co), size(A,1));
% MATLAB's robust MIMO pole placement.
K_place = place(A,B,p);
disp('K from place(A,B,p):');
disp(K_place);
disp('eig(A-B*K_place):');
disp(eig(A-B*K_place));
% From-scratch eigenstructure-assignment implementation.
[K_eig,V,F] = localEigenstructurePlacement(A,B,p);
disp('K from eigenstructure assignment:');
disp(K_eig);
disp('eig(A-B*K_eig):');
disp(eig(A-B*K_eig));
fprintf('cond(V) = %.3e\n', cond(V));
% Closed-loop simulation from an initial condition.
x0 = [1; -0.5; 0.8; 0];
sys_cl = ss(A-B*K_eig, zeros(4,2), eye(4), zeros(4,2));
t = 0:0.01:8;
initial(sys_cl, x0, t);
grid on;
title('Closed-loop response with multi-input pole placement');
% Simulink construction idea:
% 1) Add a State-Space block with A, B, C=eye(4), D=zeros(4,2).
% 2) Add a Gain block K_eig and set input u = -K_eig*x.
% 3) Feed the state output back through the negative gain into the input port.
% 4) Use Scope blocks for the state vector and control vector.
function [K,V,F] = localEigenstructurePlacement(A,B,p)
n = size(A,1);
m = size(B,2);
V = zeros(n,n);
F = zeros(m,n);
rng(24);
for i = 1:n
lambda = p(i);
M = [A - lambda*eye(n), -B];
N = null(M);
if isempty(N)
error('Empty nullspace for lambda = %g', lambda);
end
accepted = false;
for trial = 1:300
if trial == 1
q = ones(size(N,2),1);
else
q = randn(size(N,2),1);
end
s = N*q;
v = real(s(1:n));
f = real(s(n+1:n+m));
Vtrial = V;
Vtrial(:,i) = v;
if rank(Vtrial(:,1:i)) == i
V(:,i) = v;
F(:,i) = f;
accepted = true;
break;
end
end
if ~accepted
error('Could not select an independent eigenvector.');
end
end
K = F / V;
end
Chapter24_Lesson5.nb
Notebook[{
Cell["Chapter24_Lesson5.nb", "Title"],
Cell["Modern Control - Chapter 24, Lesson 5: Design examples for multi-input pole placement", "Subtitle"],
Cell[BoxData[RowBox[{"ClearAll", "[", "\"Global`*\"", "]"}]], "Input"],
Cell[BoxData[RowBox[{"A", "=", RowBox[{"{ {", RowBox[{RowBox[{"0", ",", "1", ",", "0", ",", "0"}], ",", RowBox[{"0", ",", "0", ",", "1", ",", "0"}], ",", RowBox[{"0", ",", "0", ",", "0", ",", "1"}], ",", RowBox[{RowBox[{"-", "2"}], ",", RowBox[{"-", "5"}], ",", RowBox[{"-", "4"}], ",", RowBox[{"-", "1"}]}]}], "} }"}]}]], "Input"],
Cell[BoxData[RowBox[{"B", "=", RowBox[{"{ {", RowBox[{RowBox[{"0", ",", "0"}], ",", RowBox[{"1", ",", "0"}], ",", RowBox[{"0", ",", "0"}], ",", RowBox[{"0", ",", "1"}]}], "} }"}]}]], "Input"],
Cell[BoxData[RowBox[{"poles", "=", RowBox[{"{", RowBox[{RowBox[{"-", "1"}], ",", RowBox[{"-", "2"}], ",", RowBox[{"-", "3"}], ",", RowBox[{"-", "4"}]}], "}"}]}]], "Input"],
Cell[BoxData[RowBox[{"Co", "=", RowBox[{"ArrayFlatten", "[", RowBox[{"{", RowBox[{"Table", "[", RowBox[{RowBox[{RowBox[{"MatrixPower", "[", RowBox[{"A", ",", "k"}], "]"}], ".", "B"}], ",", RowBox[{"{", RowBox[{"k", ",", "0", ",", RowBox[{RowBox[{"Length", "[", "A", "]"}], "-", "1"}]}], "}"}]}], "]"}], "}"}], "]"}]}]], "Input"],
Cell[BoxData[RowBox[{"MatrixRank", "[", "Co", "]"}]], "Input"],
Cell[BoxData[RowBox[{"eigPlacement", "[", RowBox[{"A_", ",", "B_", ",", "poles_"}], "]", ":=", RowBox[{"Module", "[", RowBox[{RowBox[{"{", RowBox[{"n", ",", "m", ",", "V", ",", "F", ",", "M", ",", "N", ",", "s", ",", "v", ",", "f", ",", "i"}], "}"}], ",", RowBox[{RowBox[{"n", "=", RowBox[{"Length", "[", "A", "]"}]}], ";", RowBox[{"m", "=", RowBox[{"Dimensions", "[", "B", "]"}], "[", RowBox[{"[", "2", "]"}], "]"}], ";", RowBox[{"V", "=", RowBox[{"ConstantArray", "[", RowBox[{"0", ",", RowBox[{"{", RowBox[{"n", ",", "n"}], "}"}]}], "]"}]}], ";", RowBox[{"F", "=", RowBox[{"ConstantArray", "[", RowBox[{"0", ",", RowBox[{"{", RowBox[{"m", ",", "n"}], "}"}]}], "]"}]}], ";", RowBox[{"Do", "[", RowBox[{RowBox[{RowBox[{"M", "=", RowBox[{"ArrayFlatten", "[", RowBox[{"{ {", RowBox[{RowBox[{"A", "-", RowBox[{"poles", "[", RowBox[{"[", "i", "]"}], "]"}], " ", RowBox[{"IdentityMatrix", "[", "n", "]"}]}], ",", RowBox[{"-", "B"}]}], "} }"}], "]"}]}], ";", RowBox[{"N", "=", RowBox[{"NullSpace", "[", "M", "]"}]}], ";", RowBox[{"s", "=", RowBox[{"First", "[", "N", "]"}]}], ";", RowBox[{"v", "=", RowBox[{"Take", "[", RowBox[{"s", ",", "n"}], "]"}]}], ";", RowBox[{"f", "=", RowBox[{"Take", "[", RowBox[{"s", ",", RowBox[{"-", "m"}]}], "]"}]}], ";", RowBox[{RowBox[{"V", "[", RowBox[{"[", RowBox[{"All", ",", "i"}], "]"}], "]"}], "=", "v"}], ";", RowBox[{RowBox[{"F", "[", RowBox[{"[", RowBox[{"All", ",", "i"}], "]"}], "]"}], "=", "f"}]}], ",", RowBox[{"{", RowBox[{"i", ",", "1", ",", "n"}], "}"}]}], "]"}], ";", RowBox[{"F", ".", RowBox[{"Inverse", "[", "V", "]"}]}]}]}], "]"}]}]], "Input"],
Cell[BoxData[RowBox[{"K", "=", RowBox[{"eigPlacement", "[", RowBox[{"A", ",", "B", ",", "poles"}], "]"}]}]], "Input"],
Cell[BoxData[RowBox[{"Eigenvalues", "[", RowBox[{"A", "-", RowBox[{"B", ".", "K"}]}], "]"}]], "Input"],
Cell[BoxData[RowBox[{"MatrixExp", "[", RowBox[{RowBox[{"(", RowBox[{"A", "-", RowBox[{"B", ".", "K"}]}], ")"}], " ", "t"}], "]"}]], "Input"]
}]
6. Problems and Solutions
Problem 1 (Controllability before design): For the plant in Section 3, explain why the desired pole set \( \{-1,-2,-3,-4\} \) can be assigned by state feedback.
Solution: The pole assignment theorem requires the pair \( (\mathbf{A},\mathbf{B}) \) to be controllable for arbitrary pole assignment. The controllability matrix is
\[ \mathcal{C}=\left[\mathbf{B}\;\;\mathbf{A}\mathbf{B}\;\;\mathbf{A}^2\mathbf{B}\;\;\mathbf{A}^3\mathbf{B}\right]. \]
Computing its rank gives \( \operatorname{rank}(\mathcal{C})=4=n \). Therefore all four closed-loop eigenvalues are assignable.
Problem 2 (Eigenstructure equation): Starting from \( \mathbf{u}=-\mathbf{K}\mathbf{x} \), derive the null-space equation used for each desired pole.
Solution: If \( (\lambda_i,\mathbf{v}_i) \) is a desired closed-loop eigenpair, then \( (\mathbf{A}-\mathbf{B}\mathbf{K})\mathbf{v}_i=\lambda_i\mathbf{v}_i \). Let \( \mathbf{f}_i=\mathbf{K}\mathbf{v}_i \). Rearranging gives
\[ (\mathbf{A}-\lambda_i\mathbf{I})\mathbf{v}_i-\mathbf{B}\mathbf{f}_i= \mathbf{0}, \]
which is equivalently
\[ \left[\mathbf{A}-\lambda_i\mathbf{I}\;\; -\mathbf{B}\right] \begin{bmatrix}\mathbf{v}_i\\ \mathbf{f}_i\end{bmatrix}= \mathbf{0}. \]
Problem 3 (Gain reconstruction): Suppose the selected vectors give nonsingular \( \mathbf{V}=[\mathbf{v}_1\;\cdots\;\mathbf{v}_n] \) and \( \mathbf{F}=[\mathbf{f}_1\;\cdots\;\mathbf{f}_n] \). Show that \( \mathbf{K}=\mathbf{F}\mathbf{V}^{-1} \) satisfies \( \mathbf{K}\mathbf{v}_i=\mathbf{f}_i \).
Solution: Since \( \mathbf{V} \) has columns \( \mathbf{v}_i \), the equality \( \mathbf{K}\mathbf{V}=\mathbf{F} \) is equivalent to the column-wise equations \( \mathbf{K}\mathbf{v}_i=\mathbf{f}_i \). Multiplying \( \mathbf{K}\mathbf{V}=\mathbf{F} \) on the right by \( \mathbf{V}^{-1} \) gives \( \mathbf{K}=\mathbf{F}\mathbf{V}^{-1} \).
Problem 4 (Repeated desired poles in MIMO systems): A controllable system has \( m=2 \) inputs. Can a desired pole be repeated four times in a fourth-order system?
Solution: Algebraic multiplicity four is possible for a controllable pair, but the geometric multiplicity of a closed-loop eigenvalue cannot exceed the number of independent input directions available through the eigenstructure equation. Therefore a repeated pole may require Jordan-chain structure rather than four independent eigenvectors. Numerically, exact repeated pole assignment is often ill-conditioned; designers usually separate repeated poles slightly, for example \( -2,-2.1,-2.2,-2.3 \), unless a repeated structure is theoretically required.
Problem 5 (Control effort comparison): Two gains \( \mathbf{K}_1 \) and \( \mathbf{K}_2 \) assign the same poles. Which one is preferable if \( \|\mathbf{K}_1\|_F<\|\mathbf{K}_2\|_F \)?
Solution: The smaller Frobenius norm is often preferable because \( \mathbf{u}=-\mathbf{K}\mathbf{x} \) will tend to generate smaller control signals for comparable state magnitudes. However, gain norm alone is not sufficient. The designer should also inspect \( \kappa_2(\mathbf{V}) \), actuator limits, transient overshoot, and robustness to modeling errors.
7. Summary
Multi-input pole placement begins with controllability but ends with a
design decision. A controllable pair permits arbitrary pole assignment,
while the extra MIMO degrees of freedom determine which eigenvectors,
gains, control effort, and transient behavior are obtained. The
eigenstructure construction provides a transparent proof-based design
method, and numerical routines such as place and
place_poles provide robust practical implementations. In
the next chapter, these same ideas are revisited under design limits:
uncontrollable modes, transmission zeros, effort constraints, and
uncertainty.
8. References
- 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. Nelson.
- Moore, B.C. (1976). On the flexibility offered by state feedback in multivariable systems beyond closed-loop eigenvalue assignment. IEEE Transactions on Automatic Control, 21(5), 689–692.
- Kautsky, J., Nichols, N.K., & Van Dooren, P. (1985). Robust pole assignment in linear state feedback. International Journal of Control, 41(5), 1129–1155.
- Tits, A.L., & Yang, Y. (1996). Globally convergent algorithms for robust pole assignment by state feedback. IEEE Transactions on Automatic Control, 41(10), 1432–1452.
- Varga, A. (2000). Robust and minimum norm pole assignment with periodic state feedback. IEEE Transactions on Automatic Control, 45(5), 1017–1022.
- Datta, B.N., & Saad, Y. (1991). Arnoldi methods for large Sylvester-like observer matrix equations, and an associated algorithm for partial spectrum assignment. Linear Algebra and its Applications, 154–156, 225–244.
- Byers, R., & Nash, S.G. (1989). Approaches to robust pole assignment. International Journal of Control, 49(1), 97–117.