State Space Model with PID controller unstable in Julia compared to Matlab

I have the following code in Matlab that creates a stable system using a simple PID controller.

%% Define system
sys = ss(A,B,C,D);

%% New state space
sys_new = sys([1379],13);

%% Define s domain
Kp = 0;
Ki = 0;   % No integrator
Kd = 278.7369;
Tf = 0;
sys_C = pid(Kp,Ki,Kd,Tf);

%% Controller design
sys_cl = feedback(sys_new,sys_C);
step(sys_cl)

which produces the following step response
image

The Julia version (or at least as close as i can make it is

using ControlSystems

sys = ss(A,B,C,D)
sys_new = sminreal(sys[1379,13])
Kp = 0
Ki = 0
Kd = 278.7369
Tf = 0
sys_C = pid(Kp,Ki,Kd; Tf=0.001, form=:parallel, state_space=true) 
# notice the Tf has to be set to be non-zero here but not in the matlab version
sys_cl = feedback(sys_new,sys_C);

y,t = step(sys_cl);
plot(t,y[1,:])

This produces nonsense
image

The main difference I see in the Matlab version and the Julia version is that in the Julia version is that the PID controller needs to be converted into state_space and that requires the Tf be set to something non-zero which causes the step response to go bad. Indeed setting Tf=0.001 in the matlab version produces a similar looking response.
image

My question is how can I make the julia and matlab version agree?

Hard to say without being able to reproduce your problem.

Try any of

step(balance_statespace(sys_cl)[1])

or

step(c2d(sys_cl, 1e-2))

Unfortunately neither of those solultions worked and produced a similar issue

Here is a BSON file containing the ABCD matrices.
https://file.io/yXCBg7jmnl4x

Even with all computations performed using big floats, I still get that the unstable pole in the plant remains unstable after closing the loop:

using ControlSystems, BSON, LinearAlgebra

function LinearAlgebra.eigvals!(R::Matrix{BigFloat}, A::Matrix{BigFloat}; sortby::Nothing)
    eigvals(A)
end

data = BSON.load("/tmp/state_space.bson")
A,B,C,D = getindex.(Ref(data), [:A, :B, :C, :D])

sys = ss(big.(A),big.(B),big.(C),big.(D))
# sys = ss(A,B,C,D)
sys_new = sminreal(sys[1379,13])
Kp = 0
Ki = 0
Kd = big(278.7369)
Tf = 0
sys_C = pid(Kp,Ki,Kd; Tf=1e-16, form=:parallel, state_space=true) 
# notice the Tf has to be set to be non-zero here but not in the matlab version

L = sys_new*sys_C
sys_cl = feedback(sys_new, sys_C)
isstable(sys_cl)
julia> isstable(sys_cl)
false

are you sure that the matlab results are correct?

are you sure that the matlab results are correct?

Welcome to my life…

I gave the matlab code in my first post and the step response flattens out unlike the julia version. What I don’t understand is how matlab is adding the feedback to the state-space model if the pid controller is in s-function form and not in state-space form. Is matlab taking the state-space system and turning it into a transfer function form?

According to my experience Matlab and Simulink often give completely wrong results without any warning when trying to solve control problems.

1 Like

I can’t run matlab, you can check what type sys_cl has, I’d expect them to convert to a descriptor system.

You can directly compute the poles of the closed loop system, that will tell you if you have any unstable ones left, before computing the step response.

The output type for sys_cl is a state_space system. Interestingly if I try to recreate the state_space model from the sys_cl ABCD I get an unstable system. For example

%% matlab
sys_cp = ss(sys_cl.A, sys_cl.B, sys_cl.C,sys_cl.D)
step(sys_cl)

image

and I get the same response in Julia. Matlab is doing something behind the scenes and its making me insane

Are you doing the same thing in MATLAB and Julia? Above is what you do in MATLAB (unless you have a typo) – I don’t see any minimal realization there. Contrast that to what you do in Julia:

sys_new = sminreal(sys[1379,13])

So – in Julia, do you pick out row 1379 and column 13 of a (huge) input-output system, and do sminreal on that system? What are you doing in MATLAB?

[Sorry – my MATLAB CST competence is somewhat rusty…]

Does the closed-loop system in matlab have an E matrix as well?

Yeah It does have a E matrix but its just the Identity matrix so I wouldn’t expect it to change anything. That being said if I do

%% matlab
sys_cp = ss(sys_cl.A, sys_cl.B, sys_cl.C,sys_cl.D)
sys_cp.E = sys_cl.E
step(sys_cl) # this gives the expected response.

image

According to the matlab docs E is

E — Matrix for implicit state-space models [] (default) | Nx-by-Nx matrix
Matrix for implicit or descriptor state-space models, specified as a Nx-by-Nx matrix. E is empty by default, meaning that the state equation is explicit. To specify an implicit state equation E dx/dt = Ax + Bu, set this property to a square matrix of the same size as A. See dss for more information about creating descriptor state-space models.

That seems very strange, are you sure it’s actually identity, or is one or a few elements of the diagonal non-unit?

Is it possible to make the A, B, C and D matrices still available to latecomers? The link no longer works.

actually yeah , your right, its not identity its near identity. I still don’t know how they calculate E thought.

@zdenek_hurak
Here is a new bson file with ABCD and E matrices
https://file.io/SitLMMRnHHZG

DescriptorSystems.jl allows system with non-identity E matrices. Still, it seems strange that the statespace system where the pid controller is filtered with an extremely large bandwidth wouldn’t yield the same result. I wonder if mstlab performs some model reduction in any of the steps.

Does the original system also have a non-unit E matrix? If so, it’s not strange that you get a different result when no including it.

No the original system without feedback control does not use E. Somehow adding PID feedback causes the combined system to have a non-identity E matrix i.e. a Descriptor System. How this happens? I’m not sure but the change does appear to make the system more numerically stable.

I am still puzzled by the very model of the system. The full model sys is of order 163 (the matrix A is 163x163). What is then the point in doing this?

Both in Matlab and in Julia I get (unsurprisingly) an error.

It’s from a larger system we have coded in matlab and we trying to simplify the problem by only considering a single input and output.

A pure derivative term, s, is not a proper system, it cannot be represented as a statespace system. This is why you have to add the low-pass filter in the pid constructor in order to be able to convert it to statespace. Non-proper systems can, however, be represented as descriptor systems:

julia> s = rtf('s')
RationalTransferFunction{Int64, :s, Polynomials.Polynomial{Int64, :s}, 0.0}
s

Continuous-time rational transfer function model.


julia> dss(s)
DescriptorStateSpace{Float64, Matrix{Float64}, Matrix{Float64}}

State matrix A:
2×2 Matrix{Float64}:
 1.0  0.0
 0.0  1.0

Descriptor matrix E:
2×2 Matrix{Float64}:
 0.0  1.0
 0.0  0.0

Input matrix B:
2×1 Matrix{Float64}:
 0.0
 1.0

Output matrix C:
1×2 Matrix{Float64}:
 -1.0  0.0

Feedthrough matrix D:
1×1 Matrix{Float64}:
 0.0

Continuous-time state-space model.

I performed all computations involved in your problem using DescriptorSystems.jl and still got an unstable system, so if I were you I would double check the matlab results one more time.