Can't fit nonlinear filter equation by lsqcurvefit()
1 view (last 30 days)
Show older comments
Md Nure Alam Dipu
on 19 Jul 2021
Edited: Walter Roberson
on 20 Jul 2021
I want to fit a passive RLC bandstop filter equation to the following data:
f = [5500 6000 6500 7000 7500 8000 8500 9000];
y = [0.998 0.997 0.994 0.988 0.947 0.255 0.956 0.987];
Where f is the frequency and y is the value of transfer function. Here is my code:
fun = @(p, x) abs(j.*(x.*p(2)-1./(x.*p(3)))./(p(1) + j.*(x.*p(2)-1./(x.*p(3))))); % transfer function of RLC bandstop
% x, p(1), p(2), p(3) correspond to w, R, L, C respectively
x = f(1):f(end); % for plotting purpose
x0 = [1 1e-3 1e-6] % starting points
lb = [1 1e-3 1e-6];ub = [100E3 100 1000E-6];
p_fit = lsqcurvefit(fun, x0, f, y, lb, ub) % p_fit is the vector of fitted parameters
semilogx(f, y, 'ro'); hold on;
semilogx(x, fun(p_fit, x), '--m'); grid on; % semilog plot of the transfer function
hold off;
I am not familiar with lsqcurvefit() parameter's starting points. Please, suggest any modification or any other method which could fit for any frequency range.
I have attached the image of actual plot and the transfer function.
0 Comments
Accepted Answer
Walter Roberson
on 20 Jul 2021
Edited: Walter Roberson
on 20 Jul 2021
format long g
f = [5500 6000 6500 7000 7500 8000 8500 9000];
y = [0.998 0.997 0.994 0.988 0.947 0.255 0.956 0.987];
fun = @(p, x) abs(1i.*(x.*p(2)-1./(x.*p(3)))./(p(1) + 1i.*(x.*p(2)-1./(x.*p(3))))); % transfer function of RLC bandstop
% x, p(1), p(2), p(3) correspond to w, R, L, C respectively
x = f(1):f(end); % for plotting purpose
x0 = [1 1e-3 1e-6] % starting points
lb = [1 1e-3 1e-6];ub = [100E3 100 1000E-6];
ub = [1 0.02 2e-5];
x0 = [1 0.0038733109633344873895993294337111 4e-6]
[p_fit, fval_fit] = lsqcurvefit(fun, x0, f, y, lb, ub) % p_fit is the vector of fitted parameters
semilogx(f, y, 'ro', 'displayname', 'original'); hold on;
semilogx(x, fun(p_fit, x), '--m', 'displayname', 'lsq'); grid on; % semilog plot of the transfer function
E = @(p) sum((fun(p,f)-y).^2);
[Pfc, fval_fc] = fmincon(E, x0, [], [], [], [], lb, ub)
[Pfs, fval_fs] = fminsearch(E, x0)
semilogx(x, fun(Pfs,x), '--g', 'displayname', 'fminsearch');
semilogx(x, fun(Pfc,x), ':b', 'displayname', 'fmincon');
hold off
legend show
6 Comments
Walter Roberson
on 20 Jul 2021
Edited: Walter Roberson
on 20 Jul 2021
format long g
f = [5500 6000 6500 7000 7500 8000 8500 9000];
y = [0.998 0.997 0.994 0.988 0.947 0.255 0.956 0.987];
fun = @(p, x) abs(1i.*(x.*p(2)-1./(x.*p(3)))./(p(1) + 1i.*(x.*p(2)-1./(x.*p(3))))); % transfer function of RLC bandstop
% x, p(1), p(2), p(3) correspond to w, R, L, C respectively
x = f(1):f(end); % for plotting purpose
x0 = [1 1e-3 1e-6] % starting points
lb = [1 1e-3 1e-6];ub = [100E3 100 1000E-6];
ub = [1 0.02 2e-5];
%x0 = [1 0.0038733109633344873895993294337111 4e-6]
E = @(P) sum((fun(P,f)-y).^2);
syms P [1 3]
E2 = simplify(E([1, P(2), 4e-6]))
dE2 = diff(E2,P(2))
fplot(E2, [0.003 0.004])
fplot(E2, [0.0038 0.004])
E2f = matlabFunction(E2);
[bestP2, fval_E2] = fminsearch(E2f,50)
x0 = [1, bestP2, 4E-6]
[p_fit, fval_fit] = lsqcurvefit(fun, x0, f, y, lb, ub) % p_fit is the vector of fitted parameters
semilogx(f, y, 'ro', 'displayname', 'original');
hold on;
semilogx(x, fun(p_fit, x), '--m', 'displayname', 'lsq'); grid on; % semilog plot of the transfer function
[p_fit2, fval_fit2] = lsqcurvefit(fun, x0, f, y, lb, ub)
[Pfc, fval_fc] = fmincon(E, x0, [], [], [], [], lb, ub)
[Pfs, fval_fs] = fminsearch(E, x0)
semilogx(x, fun(Pfs,x), '--g', 'displayname', 'fminsearch');
semilogx(x, fun(Pfc,x), ':b', 'displayname', 'fmincon');
hold off
legend show
The result of [bestP2, fval_E2] = fminsearch(E2f,50) shows that you do not need to start the search from anywhere close by in order to hit one of the two best regions. The one found, the second of the two dips in the w, gives an even better fit (using fminsearch) than using the first of the w dips.
Walter Roberson
on 20 Jul 2021
Edited: Walter Roberson
on 20 Jul 2021
The following was not able to find a good solution... but
format long g
f = [80 190 216 224 234 243 254 314]*1e3
y = [999 984 897 679 205 801 938 993]*1e-3
fun = @(p, x) abs(1i.*(x.*p(2)-1./(x.*p(3)))./(p(1) + 1i.*(x.*p(2)-1./(x.*p(3))))); % transfer function of RLC bandstop
% x, p(1), p(2), p(3) correspond to w, R, L, C respectively
x = f(1):f(end); % for plotting purpose
lb = [1 1e-3 1e-6]; %ub = [100E3 100 1000E-6];
ub = [1 0.02 2e-5];
%x0 = [1 0.0038733109633344873895993294337111 4e-6]
E = @(P) sum((fun(P,f)-y).^2);
syms P [1 3]
E2 = simplify(E([1, P(2), 4e-6]))
dE2 = diff(E2,P(2))
%fplot(E2, [0.003 0.004])
%fplot(E2, [0.0038 0.004])
E2f = matlabFunction(E2);
[bestP2, fval_E2] = fminsearch(E2f,50)
x0 = [1, bestP2, 4E-6]
Notice that search came out with a negative P(2), which is out of range.
[p_fit, fval_fit] = lsqcurvefit(fun, x0, f, y, lb, ub) % p_fit is the vector of fitted parameters
Those search coordinates are in range, but the result is not great
semilogx(f, y, 'ro', 'displayname', 'original');
hold on;
semilogx(x, fun(p_fit, x), '--m', 'displayname', 'lsq'); grid on; % semilog plot of the transfer function
[Pfc, fval_fc] = fmincon(E, x0, [], [], [], [], lb, ub)
Again within range, but the results are not great
[Pfs, fval_fs] = fminsearch(E, x0)
and that one is out of range again
%semilogx(x, fun(Pfs,x), '--g', 'displayname', 'fminsearch');
semilogx(x, fun(Pfc,x), ':b', 'displayname', 'fmincon');
hold off
legend show
I did some grid searching, brute force. The locations I found varied a lot depending how fine a grid I used. The best I found so far was
63535.7188297698 0.710929969491426 0.001
but there was a lot of variability -- such as near 1000 0.01 1e-3 was only marginally more.
In short: there is no good fit.
More Answers (0)
See Also
Categories
Find more on Linear Least Squares in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!