You are given a transfer function G(s)=1.81K(s+20)/(0.03338(s^3+10s^2+32s+32)). This system is connected with unity negative feedback . Determine the smallest positive value of K which makes the closed-loop system unstable. Give the answer to 3 d.p.
Correct Answer:
0.531 ± 0.02
This question is set by my lecturer and I have no idea how to do it.
The closed loop system will become unstable as soon as the rlocus function is no longer in the LHP. Either of the two lines when they intersect with 0 on the x-axis. Assume K=1 at first as we are multiplying by the gain.
>> G=tf([1.81 36.2],[0.03338 0.3338 1.0682 1.0682])
G =
1.81 s + 36.2
------------------------------------------
0.03338 s^3 + 0.3338 s^2 + 1.068 s + 1.068
Continuous-time transfer function.
>> rlocus(G)
you should see that when the real axis is 0 the gain is at 0.531.
if we want greater accuracy we can simply use the rlocfind(G,(point that it is 0)
A smart friend of mine showed me that "Routh Array" is the key for this equation.
Expand the polynomial as you have it, therefore you will have this:
VARS TERM VALUE
A s^3 0.03338
B s^2 0.3338
C s 1.06816 + 1.81Κ
D 1.06816 + 36.2Κ
Equate them as A*D=B*C and you will have the term "s^3" on both sides and then you can cancel it out and solve for K.
(0.03338)(1.06816 + 36.2K) s^3 = (0.3338)(1.06816 + 1.81K) s^3
0.0357 + 1.2084K = 0.3566 + 0.6042K
(1.2084 - 0.6042) K = 0.3566-0.0357
K = 0.5311155247
Otherwise use the rlocus function in MATLAB.
Related
I'm given the a(k) matrix and e(n) and I need to compute y(n) from the following eq:
y(n) = sum(k = 1 to 10 )( a(k)*y(n-k) ) + e(n).
I have the a matrix(filter coefficients) and the e matrix (the residual), therefore there is only 1 unknown: y, which is built by the previous 10 samples of y.
An example to this equation:
say e(0) (my first residual sample) = 3
and y(-10) to y(-1) = 0
then y(0), my first sample in the signal y, would just be e(0) = 3:
y(0) = a(1)*y(-1) + a(2)*y(-2) + .... + a(10)*y(-10) + e(0) = e(0) = 3
and if e(1) = 4, and a(1) = 5, then
y(1) = a(1)*y(0) + a(2)*y(-1) + a(3)&y(-2) + ... + a(10)*y(-9) + e(1) = 19
The problem is
I don't know how to do this without loops because, say, y(n) needs y(n-1), so I need to immediately append y(n-1) into my matrix in order to get y(n).
If n (the number of samples) = say, 10,000,000, then using a loop is not ideal.
What I've done so far
I have not implemented anything. The only thing I've done for this particular problem is research on what kind of Matlab functions I could use.
What I need
A Matlab function that, given an equation and or input matrix, computes the next y(n) and automatically appends that to the input matrix, and then computes the next y(n+1), and automatically append that to the input matrix and so on.
If there is anything regarding my approach
That seems like it's on the wrong track, or my question isn't clear enough, of if there is no such matlab function that exists, then I apologize in advance. Thank you for your time.
I would like to solve the following equation: tan(x) = 1/x
What I did:
syms x
eq = tan(x) == 1/x;
sol = solve(eq,x)
But this gives me only one numerical approximation of the solution. After that I read about the following:
[sol, params, conds] = solve(eq, x, 'ReturnConditions', true)
But this tells me that it can't find an explicit solution.
How can I find numerical solutions to this equation within some given range?
I've never liked using solvers "blindly", that is, without some sort of decent initial value selection scheme. In my experience, the values you will find when doing things blindly, will be without context as well. Meaning, you'll often miss solutions, think something is a solution while in reality the solver exploded, etc.
For this particular case, it is important to realize that fzero uses numerical derivatives to find increasingly better approximations. But, derivatives for f(x) = x · tan(x) - 1 get increasingly difficult to accurately compute for increasing x:
As you can see, the larger x becomes, the better f(x) approximates a vertical line; fzero will simply explode! Therefore it is imperative to get an estimate as closely to the solution as possible before even entering fzero.
So, here's a way to get good initial values.
Consider the function
f(x) = x · tan(x) - 1
Knowing that tan(x) has Taylor expansion:
tan(x) ≈ x + (1/3)·x³ + (2/15)·x⁵ + (7/315)·x⁷ + ...
we can use that to approximate the function f(x). Truncating after the second term, we can write:
f(x) ≈ x · (x + (1/3)·x³) - 1
Now, key to realize is that tan(x) repeats with period π. Therefore, it is most useful to consider the family of functions:
fₙ(x) ≈ x · ( (x - n·π) + (1/3)·(x - n·π)³) - 1
Evaluating this for a couple of multiples and collecting terms gives the following generalization:
f₀(x) = x⁴/3 - 0π·x³ + ( 0π² + 1)x² - (0π + (0π³)/3)·x - 1
f₁(x) = x⁴/3 - 1π·x³ + ( 1π² + 1)x² - (1π + (1π³)/3)·x - 1
f₂(x) = x⁴/3 - 2π·x³ + ( 4π² + 1)x² - (2π + (8π³)/3)·x - 1
f₃(x) = x⁴/3 - 3π·x³ + ( 9π² + 1)x² - (3π + (27π³)/3)·x - 1
f₄(x) = x⁴/3 - 4π·x³ + (16π² + 1)x² - (4π + (64π³)/3)·x - 1
⋮
fₙ(x) = x⁴/3 - nπ·x³ + (n²π² + 1)x² - (nπ + (n³π³)/3)·x - 1
Implementing all this in a simple MATLAB test:
% Replace this with the whole number of pi's you want to
% use as offset
n = 5;
% The coefficients of the approximating polynomial for this offset
C = #(npi) [1/3
-npi
npi^2 + 1
-npi - npi^3/3
-1];
% Find the real, positive polynomial roots
R = roots(C(n*pi));
R = R(imag(R)==0);
R = R(R > 0);
% And use these as initial values for fzero()
x_npi = fzero(#(x) x.*tan(x) - 1, R)
In a loop, this can produce the following table:
% Estimate (polynomial) Solution (fzero)
0.889543617524132 0.860333589019380 0·π
3.425836967935954 3.425618459481728 1·π
6.437309348195653 6.437298179171947 2·π
9.529336042900365 9.529334405361963 3·π
12.645287627956868 12.645287223856643
15.771285009691695 15.771284874815882
18.902410011613000 18.902409956860023
22.036496753426441 22.036496727938566 ⋮
25.172446339768143 25.172446326646664
28.309642861751708 28.309642854452012
31.447714641852869 31.447714637546234
34.586424217960058 34.586424215288922 11·π
As you can see, the approximant is basically equal to the solution. Corresponding plot:
To find a numerical solution to a function within some range, you can use fzero like this:
fun = #(x)x*tan(x)-1; % Multiplied by x so fzero has no issue evaluating it at x=0.
range = [0 pi/2];
sol = fzero(fun,range);
The above would return just one solution (0.8603). If you want additional solutions, you will have to call fzero more times. This can be done, for example, in a loop:
fun = #(x)tan(x)-1/x;
RANGE_START = 0;
RANGE_END = 3*pi;
RANGE_STEP = pi/2;
intervals = repelem(RANGE_START:RANGE_STEP:RANGE_END,2);
intervals = reshape(intervals(2:end-1),2,[]).';
sol = NaN(size(intervals,1),1);
for ind1 = 1:numel(sol)
sol(ind1) = fzero(fun, mean(intervals(ind1,:)));
end
sol = sol(~isnan(sol)); % In case you specified more intervals than solutions.
Which gives:
[0.86033358901938;
1.57079632679490; % Wrong
3.42561845948173;
4.71238898038469; % Wrong
6.43729817917195;
7.85398163397449] % Wrong
Note that:
The function is symmetric, and so are its roots. This means you can solve on just the positive interval (for example) and get the negative roots "for free".
Every other entry in sol is wrong because this is where we have asymptotic discontinuities (tan transitions from +Inf to -Inf), which is mistakenly recognized by MATLAB as a solution. So you can just ignore them (i.e. sol = sol(1:2:end);.
Multiply the equation by x and cos(x) to avoid any denominators that can have the value 0,
f(x)=x*sin(x)-cos(x)==0
Consider the normalized function
h(x)=(x*sin(x)-cos(x)) / (abs(x)+1)
For large x this will be increasingly close to sin(x) (or -sin(x) for large negative x). Indeed, plotting this this is already visually true, up to an amplitude factor, for x>pi.
For the first root in [0,pi/2] use the Taylor approximation at x=0 of second degree x^2-(1-0.5x^2)==0 to get x[0]=sqrt(2.0/3) as root approximation, for the higher ones take the sine roots x[n]=n*pi, n=1,2,3,... as initial approximations in the Newton iteration xnext = x - f(x)/f'(x) to get
n initial 1. Newton limit of Newton
0 0.816496580927726 0.863034004302817 0.860333589019380
1 3.141592653589793 3.336084918413964 3.425618459480901
2 6.283185307179586 6.403911810682199 6.437298179171945
3 9.424777960769379 9.512307014150883 9.529334405361963
4 12.566370614359172 12.635021895208379 12.645287223856643
5 15.707963267948966 15.764435036320542 15.771284874815882
6 18.849555921538759 18.897518573777646 18.902409956860023
7 21.991148575128552 22.032830614521892 22.036496727938566
8 25.132741228718345 25.169597069842926 25.172446326646664
9 28.274333882308138 28.307365162331923 28.309642854452012
10 31.415926535897931 31.445852385744583 31.447714637546234
11 34.557519189487721 34.584873343220551 34.586424215288922
Im trying to design a PID compensator with Matlab.
I'm doing the following:
My plant has this transfer function:
plant
( 0.0195 s - 6.5 )/ (1.74e-06 s^2 - 0.003 s - 1)
So, using the PID tune function, specifying the desired crossover frequency:
>> [info,pid_c] = pidtune(plant,'PID',(2E6/6)*2*pi)
info =
Kp + Ki * 1/s
with Kp = 162, Ki = 1.96e+08
Continuous-time PI controller in parallel form.
pid_c =
Stable: 1
CrossoverFrequency: 2.0944e+06
PhaseMargin: 60.0000
But, when I close the loop and analyse the poles of the system I see that there's a pole in the right semi-plane:
>> pid_c = 162 + 1.96E8/s
pid_c =
( 162 s + 1.96e08 ) / s
Continuous-time transfer function.
>> sys=feedback(plant*pid_c,1)
sys =
(3.159 s^2 + 3.821e06 s - 1.274e09 )/ ( 1.74e-06 s^3 + 3.156 s^2 + 3.821e06 s - 1.274e09)
Continuous-time transfer function.
>> pole(sys)
ans =
1.0e+06 *
-0.9071 + 1.1721i
-0.9071 - 1.1721i
0.0003 <==== RSP pole
My intuition says I should get away with this pole by adding a (s+0.0003) to the numerator of the PID:
>> pid_c=pid_c*(s+0.0003)
pid_c =
( 162 s^2 + 1.96e08 s + 5.88e04 )/ s
But it doesnt work at all, and the tranfer function of my closed-loop goes like that:
sys =
(3.159 s^3 + 3.821e06 s^2 - 1.274e09 s - 3.822e05) / (3.159 s^3 + 3.821e06 s^2 - 1.274e09 s - 3.822e05)
and off course thats wrong.
I really appreciate your help.
The problem is because you are not calling pidtune correctly. The first variable returned is the controller, the second the information about closed-loop stability, not the other way round. From the documentation:
[C,info] = pidtune(...) returns the data structure info, which
contains information about closed-loop stability, the selected
open-loop gain crossover frequency, and the actual phase margin.
So I would change the code to:
[pid_c,info] = pidtune(plant,'PID',(2E6/6)*2*pi);
sys=minreal(feedback(pid_c*plant,1));
pole(sys)
The second error you make is that the pole isn't at 0.0003, but at 0.0003 * 1e6. If you are going to rely on what is displayed on the screen, you should at least use format long g to have more significant digits.
Also, note that I have used minreal to compute a minimum realisation of the closed-loop transfer function, as you seem to be having a mix of very large and very small number, not a good combination.
I want to determine the linearized transfer function from a non-linear system made in Simulink. I can see that it should be possible to use the linmod function in Matlab but when I try this
[num,den]=linmod('sys')
I'm not getting the numerator and denominator but instead the state space matrix etc. Can anyone help?
Try the function balred instead: documentation
rsys = balred(sys,ORDERS) computes a reduced-order approximation rsys
of the LTI model sys. The desired order (number of states) for rsys is
specified by ORDERS. You can try multiple orders at once by setting
ORDERS to a vector of integers, in which case rsys is a vector of
reduced-order models. balred uses implicit balancing techniques to
compute the reduced- order approximation rsys.
example:
Q = tf([1 2 3 4 5],[5 4 3 2 1])
Q =
s^4 + 2 s^3 + 3 s^2 + 4 s + 5
-------------------------------
5 s^4 + 4 s^3 + 3 s^2 + 2 s + 1
Q_lin = balred(Q,2)
Q_lin =
3.276 s^2 - 2.06 s + 2.394
--------------------------
s^2 - 0.2757 s + 0.4789
balred(Q,1)
is not working for my example, as there are 2 unstable poles, but it may works for your system.
linmod always returns a state-space representation (see documentation). Use tf to convert your stae-space representation to a transfer function:
Conversion to Transfer Function
tfsys = tf(sys) converts the dynamic
system model sys to transfer function form. The output tfsys is a tf
model object representing sys expressed as a transfer function.
BTW, if you have Simulink Control Design, a better alternative to linmod is linearize.
I am currently trying to implement a machine learning algorithm that involves the logistic loss function in MATLAB. Unfortunately, I am having some trouble due to numerical overflow.
In general, for a given an input s, the value of the logistic function is:
log(1 + exp(s))
and the slope of the logistic loss function is:
exp(s)./(1 + exp(s)) = 1./(1 + exp(-s))
In my algorithm, the value of s = X*beta. Here X is a matrix with N data points and P features per data point (i.e. size(X)=[N,P]) and beta is a vector of P coefficients for each feature such that size(beta)=[P 1].
I am specifically interested in calculating the average value and gradient of the Logistic function for given value of beta.
The average value of the Logistic function w.r.t to a value of beta is:
L = 1/N * sum(log(1+exp(X*beta)),1)
The average value of the slope of the Logistic function w.r.t. to a value of b is:
dL = 1/N * sum((exp(X*beta)./(1+exp(X*beta))' X, 1)'
Note that size(dL) = [P 1].
My issue is that these expressions keep producing numerical overflows. The problem effectively comes from the fact that exp(s)=Inf when s>1000 and exp(s)=0 when s<-1000.
I am looking for a solution such that s can take on any value in floating point arithmetic. Ideally, I would also really appreciate a solution that allows me to evaluate the value and gradient in a vectorized / efficient way.
How about the following approximations:
– For computing L, if s is large, then exp(s) will be much larger than 1:
1 + exp(s) ≅ exp(s)
and consequently
log(1 + exp(s)) ≅ log(exp(s)) = s.
If s is small, then using the Taylor series of exp()
exp(s) ≅ 1 + s
and using the Taylor series of log()
log(1 + exp(s)) ≅ log(2 + s) ≅ log(2) + s / 2.
– For computing dL, for large s
exp(s) ./ (1 + exp(s)) ≅ 1
and for small s
exp(s) ./ (1 + exp(s)) ≅ 1/2 + s / 4.
– The code to compute L could look for example like this:
s = X*beta;
l = log(1+exp(s));
ind = isinf(l);
l(ind) = s(ind);
ind = (l == 0);
l(ind) = log(2) + s(ind) / 2;
L = 1/N * sum(l,1)
I found a good article about this problem.
Cutting through a lot of words, we can simplify the argument to stating that the original expression
log(1 + exp(s))
can be rewritten as
log(exp(s)*(exp(-s) + 1))
= log(exp(s)) + log(exp(-s) + 1)
= s + log(exp(-s) + 1)
This stops overflow from occurring - it doesn't prevent underflow, but by the time that occurs, you have your answer (namely, s). You can't just use this instead of the original, since it will still give you problems. However, we now have the basis for a function that can be written that will be accurate and won't produce over/underflow:
function LL = logistic(s)
if s<0
LL = log(1 + exp(s));
else
LL = s + logistic(-s);
I think this maintains reasonably good accuracy.
EDIT now to the meat of your question - making this vectorized, and allowing the calculation of the slope as well. Let's take these one at a time:
function LL = logisticVec(s)
LL = zeros(size(s));
LL(s<0) = log(1 + exp(s(s<0)));
LL(s>=0) = s(s>=0) + log(1 + exp(-s(s>=0)));
To obtain the average you wanted:
L = logisticVec(X*beta) / N;
The slope is a little bit trickier; note I believe you may have a typo in your expression (missing a multiplication sign).
dL/dbeta = sum(X * exp(X*beta) ./ (1 + exp(X*beta))) / N;
If we divide top and bottom by exp(X*beta) we get
dL = sum(X ./ (exp(-X*beta) + 1)) / N;
Once again, the overflow has gone away and we are left with underflow - but since the underflowed value has 1 added to it, the error this creates is insignificant.