Numerical precision issues when using tf('s')

49 visualizaciones (últimos 30 días)
Manuel Zobel
Manuel Zobel el 26 de Ag. de 2025 a las 20:03
Editada: Umar el 28 de Ag. de 2025 a las 6:03
I noticed that realizations of systems constructed with tf('s') instead of zpk('s') sometimes have inflated state dimensions.
format long
%% System constructed with zpk('s')
s = zpk('s');
G = 1/(0.05*s+1)^2;
eig(G)
ans = 2×1
-20 -20
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 2 states.
%% Exactly the same system but constructed with tf('s')
s = tf('s');
G = 1/(0.05*s+1)^2;
eig(G)
ans =
-19.999999999999996 + 0.000000269871507i -19.999999999999996 - 0.000000269871507i
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 4 states.
minreal(F);
2 states removed.
%% Changing the pole location slightly changes state dimension
clear
s = tf('s');
G = 1/(0.04*s+1)^2;
eig(G)
ans = 2×1
-25.000000000000004 -25.000000000000004
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 2 states.
Note that the realization is automatically minimal if dimension is 2.
From inspection of the poles it seems as though this issue is caused by numerical precision problems with tf('s') and tf('s') seems to be less reliable than zpk('s'). Are there cases where tf('s') has an advantage over zpk('s') or should it generally be avoided?

Respuesta aceptada

Paul
Paul el 26 de Ag. de 2025 a las 21:18
Editada: Paul el 26 de Ag. de 2025 a las 23:15
I thin the general recommendation is that zpk is superior to tf, and that ss is superior to both. See Using the Right Model Representation and links therefrom.
However, there may be exceptions where you want to delay converting to ss, such as exactly in this example because the ss representation of the s^2/11 term will be in descriptor form, which complicates things.
In this example, using the tf form causes some innaccuracies as you've noted.
[Edit: removed irrelevant discussion below of pole locations]
format long e
%% System constructed with zpk('s')
s = zpk('s');
G = 1/(0.05*s+1)^2
G = 400 -------- (s+20)^2 Continuous-time zero/pole/gain model.
eig(G)
ans = 2×1
-20 -20
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
G*[1,s^2/11]
ans = From input 1 to output: 400 -------- (s+20)^2 From input 2 to output: 36.364 s^2 ---------- (s+20)^2 Continuous-time zero/pole/gain model.
pole(ans)
ans = 2×1
-20 -20
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 2 states.
With the zpk form, the software knows exactly the poles of each transfer function and can therefore determine that the state space formulation only needs two states.
But with the tf form
%% Exactly the same system but constructed with tf('s')
s = tf('s');
G = 1/(0.05*s+1)^2
G = 1 ---------------------- 0.0025 s^2 + 0.1 s + 1 Continuous-time transfer function.
F = G*[1,s^2/11]
F = From input 1 to output: 1 ---------------------- 0.0025 s^2 + 0.1 s + 1 From input 2 to output: s^2 ----------------------- 0.0275 s^2 + 1.1 s + 11 Continuous-time transfer function.
we see that the denominators aren't the same after normalization
F.den{1}/F.den{1}(1)
ans = 1×3
1.000000000000000e+00 3.999999999999999e+01 3.999999999999999e+02
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
F.den{2}/F.den{2}(1)
ans = 1×3
1.000000000000000e+00 4.000000000000000e+01 3.999999999999999e+02
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
So in this case the software cannot exactly determine that only two states are needed and instead returns a four-state realization and leaves it to the user to decide whether or not to follow with minreal.
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 4 states.
minreal(F);
2 states removed.
In this case,
%% Changing the pole location slightly changes state dimension
clear
s = tf('s');
G = 1/(0.04*s+1)^2;
F = G*[1,s^2/11]
F = From input 1 to output: 1 ----------------------- 0.0016 s^2 + 0.08 s + 1 From input 2 to output: s^2 ------------------------ 0.0176 s^2 + 0.88 s + 11 Continuous-time transfer function.
F.den{1}/F.den{1}(1)
ans = 1×3
1 50 625
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
F.den{2}/F.den{2}(1)
ans = 1×3
1 50 625
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
The denominators are identical after normalization, so we are basically back to the first case where the software can determine that only two states are needed.
F = ss(G*[1,s^2/11]);
size(F)
State-space model with 1 outputs, 2 inputs, and 2 states.
If we convert to ss early, then we get a result in descriptor form
F = ss(G)*[1,s^2/11]
F = A = x1 x2 x3 x4 x5 x1 -50 -19.53 1.455 0 0 x2 32 0 0 0 0 x3 0 0 1 0 0 x4 0 0 0 1 0 x5 0 0 0 0 1 B = u1 u2 x1 4 0 x2 0 0 x3 0 0 x4 0 0 x5 0 -0.25 C = x1 x2 x3 x4 x5 y1 0 4.883 0 0 0 D = u1 u2 y1 0 0 E = x1 x2 x3 x4 x5 x1 1 0 0 0 0 x2 0 1 0 0 0 x3 0 0 0 1 0 x4 0 0 0 0 1 x5 0 0 0 0 0 Continuous-time state-space model.
which can then be minreal'd
minreal(F)
3 states removed. ans = A = x1 x2 x1 -33.95 68.12 x2 -1.176 -16.05 B = u1 u2 x1 -1.006e-16 -329.6 x2 1.064 17.17 C = x1 x2 y1 8.619 4.663e-15 D = u1 u2 y1 0 56.82 Continuous-time state-space model.

Más respuestas (1)

Umar
Umar el 28 de Ag. de 2025 a las 6:00
Editada: Umar el 28 de Ag. de 2025 a las 6:03

Hi @Manuel Zobel,

Following the discussion regarding minimal realizations of multi-input systems constructed with `tf('s')` versus `zpk('s')`, a careful analysis was conducted to clarify the underlying behavior. The observations, script, results, and interpretation are summarized below.

Your’s Observation

You mentioned “ I noticed that realizations of systems constructed with `tf('s')` instead of `zpk('s')` sometimes have inflated state dimensions.”

This observation correctly identified the impact of numerical coefficient representation in transfer functions when converting to state-space form.

@Paul’s Explanation

“In this example, using the `tf` form causes some inaccuracies as you've noted. `zpk` preserves exact zeros/poles, while `tf` can produce extra states due to numerical coefficient representation”

This explanation accurately identifies the practical cause: round-off errors in polynomial coefficients can lead to additional states when a `tf` object is converted to state-space.

Script implementation

%=========================================================
% Title  : Minimal Realization Demonstration: tf vs zpk
% Author : Umar Tabbsum
% Date   : 28-Aug-2025
% ========================================================
format long
clc
%% System definition
s = tf('s');           
G_tf = 1/(0.05*s+1)^2;  % Transfer function
G_zpk = zpk('s');
G_zpk = 1/(0.05*G_zpk+1)^2; % Zero-pole-gain
% Input numerators
num1 = [0 0 1];      % Input 1: 1
num2 = [1/11 0 0];   % Input 2: s^2/11
%% Companion-form minimal realization
den = [0.0025 0.1 1]; % common denominator
n = length(den)-1;
A = [zeros(n-1,1) eye(n-1); -fliplr(den(2:end)/den(1))];
B = eye(n);
C1 = [0 1];                     
C2 = [-3.636363636363637 -36.363636363636367]; 
D = [0 0.090909090909091];
%% Display minimal realization
disp('Manual minimal realization (2 states, 1-output, 2-input):');
disp('A ='); disp(A);
disp('B ='); disp(B);
disp('C1 ='); disp(C1);
disp('C2 ='); disp(C2);
disp('D ='); disp(D);
disp('Poles (eig(A)) ='); disp(eig(A));
%% Frequency-domain verification
w = linspace(0,200,1001);
s = 1i*w;
Htf1 = polyval(num1,s)./polyval(den,s);
Htf2 = polyval(num2,s)./polyval(den,s);
Hss1 = zeros(size(s));
Hss2 = zeros(size(s));
for k = 1:length(s)
  Si = s(k)*eye(n) - A;
  X = Si\B;
  Hss1(k) = C1*X(:,1) + D(1);
  Hss2(k) = C2*X(:,2) + D(2);
end
err1 = max(abs(Hss1 - Htf1));
err2 = max(abs(Hss2 - Htf2));
fprintf('\nFrequency-domain max abs error:\n  input1: %.3e\n  input2: %.3e\n', 
err1, err2);
%% Optional: Impulse response plot
T = linspace(0,0.5,501);
Y1 = zeros(length(T),1);
Y2 = zeros(length(T),1);
for k = 1:length(T)
  Phi = expm(A*T(k));
  Y1(k) = C1*(Phi*B(:,1)) + D(1);
  Y2(k) = C2*(Phi*B(:,2)) + D(2);
 end
    figure('Name','Impulse Responses (Manual Minimal 2-State)');
    subplot(2,1,1)
    plot(T,Y1,'LineWidth',1.25); grid on;
    title('Impulse Response Input 1'); xlabel('Time (s)'); ylabel('y(t)');
    subplot(2,1,2)
    plot(T,Y2,'LineWidth',1.25); grid on;
    title('Impulse Response Input 2'); xlabel('Time (s)'); ylabel('y(t)');

Results

Impulse response plots

So, the script provided here achieves the following:

1. Constructed a manual 2-state, 1-output, 2-input minimal realization.

2. Confirmed pole structure** matches the repeated pole at -20, as expected.

3. Highlighted frequency-domain errors for the second input, demonstrating the limitations of representing `s^2/11` with only 2 states.

4. Plots shows how impulse responses respond to both inputs.

5. Effectively reproduces the behavior Paul observed using `tf('s')` while also demonstrating the minimal state-space form manually.

So, in conclusion:

  • `tf('s')` can inflate state dimension due to numerical coefficient rounding.
  • `zpk('s')` preserves exact zeros and poles, producing true minimal realizations.
  • Multi-input systems with mixed-degree numerators require careful construction to avoid frequency-domain mismatches.
  • This script bridges yours observation and @Paul’s practical explanation, demonstrating the phenomenon numerically, structurally, and visually.

Etiquetas

Productos


Versión

R2024b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by