How to add a very simple controller to a set of differential equations - matlab

I have a very complicated set of differential equations with several time dependent variables. I have found a simplification of my code online, it's here;
It's in two sections. First
`t_values=linspace(0,10,101);
initial_cond=[1 ; 0 ; 0];
[tv,Yv]=ode45('simplemodel',t_values,initial_cond);
plot(tv,Yv(:,1),'+',tv,Yv(:,2),'x',tv,Yv(:,3),'o');
legend('y1','y2','y3');`
Then another
function Dy = simplemodel(t,y)
Dy=[ a(t)*y(1)+b(t)*y(2); ...
-a(t)*y(3)+b(t)*y(1); ...
a(t)*y(2)] ;
end
function fa=a(t);
fa=cos(t); % or place whatever you want to place for a(t)..
end
function fb=b(t);
fb=sin(t) % or place whatever you want to place for b(t)..
end
I have a problem however, that I want to try to add a PID controller to my a(t) term.. I have tried to do first add a "proportional term" by changing the fb=sin(t) line to fb=-K*y(2)+d+K*int(y(2))+K*diff(y(2)), where K and d are constants. When I substitute this fb line for the one in the code, I get errors. Any ideas guys? Thank you very much

You want to go from a system that is described by
function Dy = simplemodel(t,y)
a = cos(t)
b = sin(t)
Dy=[ a*y(1)+b*y(2); ...
-a*y(3)+b*y(1); ...
a*y(2)] ;
end
to something that has
b = -K*y(2)+d+K*Iy2+K*Dy2
where Dy2 is the derivative and Iy2 the anti-derivative of the second component. The first problem is that
Dy(2) = -a*y(3)+b*y(1)
depends itself on b, which depends on Dy(2) so that you get some feed-back resp. a dependency circle that you have to solve. This can be done by solving
Dy(2) = -a*y(3)+(b1+K*Dy(2))*y(1);
for Dy(2). b1 contains all the other parts of b, so that in total the circle is solved as
b1 = -K*y(2)+d+K*Iy2
Dy(2) = (-a*y(3) + b1*y(1))/(1-K*y(1))
b = b1+K*Dy(2);
This still contains the as-of-yet unaddressed anti-derivative value. In other words, we need a function whose derivative is y(2). As such a function is not present in the system, we have to add an extra component to it,
function Dy = simplemodel(t,y)
a = cos(t)
Dy(4) = y(2) % y(4) = integral(y(2))
b1 = -K*y(2)+d+K*y(4) % missing +K*Dy(2)
Dy(2) = (-a*y(3) + b1*y(1))/(1-K*y(1))
b2 = K*Dy(2);
b = b1+b2;
Dy(1) = a*y(1)+b*y(2)
Dy(3) = a*y(2)
end
You will still need some initial value for the anti-derivative, most likely it will be 0 if the system is thought of being switched on after a state of rest.

Related

Create function to use in ode45

So this is part of a larger project but I am stuck on number two of this section. I rewrote the system to get it in the required form:
dx(1)/dt = x(2)
dx(2)/dt = (-(M+m)/mL))x(4) + 1/(mL)u
dx(3)/dt = x(4)
dx(4)/dt = -(mg/M)x(1) + (1/M)u
After substituting the variables given in the problem I wrote the funcion:
function dx = fun(t,x)
dx = zeros(4,1);
dx(1) = x(2);
dx(2) = -((2+.1)/(.1*.5)).*x(4);
dx(3) = x(4);
dx(4) = -((.1*9.81)/2).*x(1);
end
I am confused on how to implement u(t) = 0 and how to create the theta function.
Any help even if its just pointing me in the right direction would be amazing. Thank you in advance :)
It's easy. You implement theta as another state. This is possible since you know the derivative, which does not even depend on the other states. To be more precise, you should add two more states here. One for theta and one for theta_dot.
dx(5) = x(6) % Theta'
dx(6) = -0.1 % Theta''
And by the way, you can also pass additional variables to your differential equation. You just add more arguments to it
function dx = diffeq(t,x,parameters)
...
end
and create a new function handle where you execute the ODE solver
[T,X] = ode45(#(t,x)diffeq(t,x,parameters),t_span,X0, ode_options);
This is just a hint since you're using magic numbers in your differential equation function.

Pass extra variable parameters to ode15s function (MATLAB)

I'm trying to solve a system of ordinary differential equations in MATLAB.
I have a simple equation:
dy = -k/M *x - c/M *y+ F/M.
This is defined in my ode function test2.m, dependant on the values X and t. I want to trig 'F' with a signal, generated by my custom function squaresignal.m. The output hereof, is the variable u, spanding from 0 to 1, as it is a smooth heaviside function. - Think square wave. The inputs in squaresignal.m, is t and f.
u=squaresignal(t,f)
These values are to be used inside my function test2, in order to enable or disable variable 'F' with the value u==1 (enable). Disable for all other values.
My ode function test2.m reads:
function dX = test2(t ,X, u)
x = X (1) ;
y = X (2) ;
M = 10;
k = 50;
c = 10;
F = 300;
if u == 1
F = F;
else
F = 0,
end
dx = y ;
dy = -k/M *x - c/M *y+ F/M ;
dX = [ dx dy ]';
end
And my runscript reads:
clc
clear all
tstart = 0;
tend = 10;
tsteps = 0.01;
tspan = [0 10];
t = [tstart:tsteps:tend];
f = 2;
u = squaresignal(t,f)
for ii = 1:length(u)
options=odeset('maxstep',tsteps,'outputfcn',#odeplot);
[t,X]=ode15s(#(t,X)test2(t,X,u(ii)),[tstart tend],[0 0],u);
end
figure (1);
plot(t,X(:,1))
figure (2);
plot(t,X(:,2));
However, the for-loop does not seem to do it's magic. I still only get F=0, instead of F=F, at times when u==1. And i know, that u is equal to one at some times, because the output of squaresignal.m is visible to me.
So the real question is this. How do i properly pass my variable u, to my function test2.m, and use it there to trig F? Is it possible that the squaresignal.m should be inside the odefunction test2.m instead?
Here's an example where I pass a variable coeff to the differential equation:
function [T,Q] = main()
t_span = [0 10];
q0 = [0.1; 0.2]; % initial state
ode_options = odeset(); % currently no options... You could add some here
coeff = 0.3; % The parameter we wish to pass to the differential eq.
[T,Q] = ode15s(#(t,q)diffeq(t,q,coeff),t_span,q0, ode_options);
end
function dq = diffeq(t,q,coeff)
% Preallocate vector dq
dq = zeros(length(q),1);
% Update dq:
dq(1) = q(2);
dq(2) = -coeff*sin(q(1));
end
EDIT:
Could this be the problem?
tstart = 0;
tend = 10;
tsteps = 0.01;
tspan = [0 10];
t = [tstart:tsteps:tend];
f = 2;
u = squaresignal(t,f)
Here you create a time vector t which has nothing to do with the time vector returned by the ODE solver! This means that at first we have t[2]=0.01 but once you ran your ODE solver, t[2] can be anything. So yes, if you want to load an external signal source depending on time, then you need to call your squaresignal.m from within the differential equation and pass the solver's current time t! Your code should look like this (note that I'm passing f now as an additional argument to the diffeq):
function dX = test2(t ,X, f)
x = X (1) ;
y = X (2) ;
M = 10;
k = 50;
c = 10;
F = 300;
u = squaresignal(t,f)
if u == 1
F = F;
else
F = 0,
end
dx = y ;
dy = -k/M *x - c/M *y+ F/M ;
dX = [ dx dy ]';
end
Note however that matlab's ODE solvers do not like at all what you're doing here. You are drastically (i.e. non-smoothly) changing the dynamics of your system. What you should do is to use one of the following:
a) events if you want to trigger some behaviour (like termination) depending on the integrated variable x or
b) If you want to trigger the behaviour based on the time t, you should segment your integration into different parts where the differential equation does not vary during one segment. You can then resume your integration by using the current state and time as x0 and t0 for the next run of ode15s. Of course this only works of you're external signal source u is something simple like a step funcion or square wave. In case of the square wave you would only integrate for a timespan during which the wave does not jump. And then exactly at the time of the jump you start another integration with altered differential equations.

How do I integrate a differential equation in MATLAB?

I want to integrate a differential equation dc/dt. Below is the code and the values of the variables.
clear all;
c1=.185;c0=2*10^-6;k3=.1*10^-6;
v1=6;v2=.11;v3=.09*10^-6;
Ca_ER=10*10^-6;Ca_cyto=1.7*10^-6;
p_open3=0.15;c=15*10^-6;
dcdt= (c1*(v1*(p_open3)+v2)*(Ca_ER)-c)-v3*((c)^2)/(c^2+(k3)^2);
I know there is an integral function but I am not sure how to apply for this equation. How do I proceed from here? Please help. The value of initial c, if needed, can be taken as 0.15*10^-6. Also, I need to plot the obtained result versus time. So will get an array of values or just a single value?
the link to the article. the equation i have used comes under Calcium Oscillations section
You could use Euler method to solve this problem to get a rough idea regarding the solution yet not accurate.
clear all
clc
t = 0;
dt = 0.0001;
c1 = 0.185;
c0 = 2*10^-6;
k3 = 0.1*10^-6;
v1 =6;
v2 =.11;
v3 =.09*10^-6;
Ca_ER =10*10^-6;
Ca_cyto =1.7*10^-6;
p_open3 =0.15;
c = 15*10^-6;
%store initial values
C(1) = c;
T(1) = t;
for i = 1:40000
dc = ( (c1*(v1*(p_open3)+v2)*(Ca_ER)-c)- v3*( c^2 /( c^2+(k3)^2) ) );
c = c + dt*dc;
t = t + dt;
%store data
C(i+1) = c;
T(i+1) = t;
end
plot(T,C, 'LineWidth',2)
xlabel('time (sec)')
ylabel('c(t)')
grid on
The result is
You can also use Wolfram which gives same result.

Undefined function or variable

I have a simple function as below:
function dz = statespace(t,z)
dz = A*z + B*k*z
end
My main script is :
clear all;close all;format short;clc;
% step 1 -- input system parameters
% structure data
M1 = 1; M2= 1; M3= 1; %Lumped Mass(Tons)
M= diag([M1,M2,M3]);
k(1)= 980; k(2)= 980; k(3)= 980; %Stiffness Coefficient(KN/M)
K = zeros(3,3);
for i=1:2
K(i,i)=k(i)+k(i+1);
end
K(3,3)=k(3);
for i=1:2
K(i,i+1)=-k(i+1);
end
for i=2:3
K(i,i-1)=-k(i);
end %Stiffness Matrix(KN/M)
c(1)= 1.407; c(2)= 1.407; c(3)= 1.407; %Damping Coefficient(KN.sec/M)
C = zeros(3,3);
for i=1:2
C(i,i)=c(i)+c(i+1);
end
C(3,3)=c(3);
for i=1:2
C(i,i+1)=-c(i+1);
end
for i=2:3
C(i,i-1)=-c(i);
end %Damping Matrix(KN.sec/M)
A = [zeros(3) eye(3);-inv(M)*K -inv(M)*C]
H = [1;0;0]
B = [0;0;0;inv(M)*H]
k = [-1 -1 -1 -1 -1 -1]
t = 0:0.004:10;
[t,z] = ode45(statespace,t);
When I run my main script it comes with following error:
Undefined function or variable 'A'.
Error in statespace (line 2)
dz = A*z + B*k*z
As you can see I defined A in main script. Why this problem happening?
There multiple things wrong with your code. First, you need to supply the values of A and B to your function but as you are invoking it (incorrectly without the # and additional parameter y0 as I commented below) in the toolbox ode45, you have to keep just two parameters so you cannot supply A and B as additional parameters. If you define A and B within your function or share them via global variables you will get further. However, as noted below the definitions don't seem to be correct as A * z and B * k * z don't have the same dimensions. z is a scalar so B * k needs to be same size and shape as A which currently it is not.
Edit from:
As Jubobs suggested change your function's parameters to include A, B and k. Also you don't need t as it is never used in the function. So:
function dz = statespace(A, B, k, z)
dz = A*z + B*k*z
end
As others have pointed out, A, B and k are not defined in the function workspace, so you either need to define them again (ugly, not recommended), declare them as global variables (slightly better, but still not good practice), or pass them as arguments to your function (the better solution). However, because you then want to use the function with ode45, you need to be a bit careful with how you do it:
function dz = statespace(t,z,A,B,k)
dz = A*z + B*k*z
end
and then the call to ode45 would like this:
[t,z] = ode45(#(t,z)statespace(t,z,A,B,k),[0 Tf],z0); % where Tf is your final time and z0 your initial conditions
See Error using ode45 and deval for a similar problem.

Matlab - Unexpected Results from Differential Equation Solver Ode45

I am trying to solve a differential equation with the ode solver ode45 with MATLAB. I have tried using it with other simpler functions and let it plot the function. They all look correct, but when I plug in the function that I need to solve, it fails. The plot starts off at y(0) = 1 but starts decreasing at some point when it should have been an increasing function all the way up to its critical point.
function [xpts,soln] = diffsolver(p1x,p2x,p3x,p1rr,y0)
syms x y
yp = matlabFunction((p3x/p1x) - (p2x/p1x) * y);
[xpts,soln] = ode45(yp,[0 p1rr],y0);
p1x, p2x, and p3x are polynomials and they are passed into this diffsolver function as parameters.
p1rr here is the critical point. The function should diverge after the critical point, so i want to integrate it up to that point.
EDIT: Here is the code that I have before using diffsolver, the above function. I do pade approximation to find the polynomials p1, p2, and p3. Then i find the critical point, which is the root of p1 that is closest to the target (target is specified by user).
I check if the critical point is empty (sometimes there might not be a critical point in some functions). If its not empty, then it uses the above function to solve the differential equation. Then it plots the x- and y- points returned from the above function basically.
function error = padeapprox(m,n,j)
global f df p1 p2 p3 N target
error = 0;
size = m + n + j + 2;
A = zeros(size,size);
for i = 1:m
A((i + 1):size,i) = df(1:(size - i));
end
for i = (m + 1):(m + n + 1)
A((i - m):size,i) = f(1:(size + 1 - i + m));
end
for i = (m + n + 2):size
A(i - (m + n + 1),i) = -1;
end
if det(A) == 0
error = 1;
fprintf('Warning: Matrix is singular.\n');
end
V = -A\df(1:size);
p1 = [1];
for i = 1:m
p1 = [p1; V(i)];
end
p2 = [];
for i = (m + 1):(m + n + 1)
p2 = [p2; V(i)];
end
p3 = [];
for i = (m + n + 2):size
p3 = [p3; V(i)];
end
fx = poly2sym(f(end:-1:1));
dfx = poly2sym(df(end:-1:1));
p1x = poly2sym(p1(end:-1:1));
p2x = poly2sym(p2(end:-1:1));
p3x = poly2sym(p3(end:-1:1));
p3fullx = p1x * dfx + p2x * fx;
p3full = sym2poly(p3fullx); p3full = p3full(end:-1:1);
p1r = roots(p1(end:-1:1));
p1rr = findroots(p1r,target); % findroots eliminates unreal roots and chooses the one closest to the target
if ~isempty(p1rr)
[xpts,soln] = diffsolver(p1x,p2x,p3fullx,p1rr,f(1));
if rcond(A) >= 1e-10
plot(xpts,soln); axis([0 p1rr 0 5]); hold all
end
end
I saw some examples using another function to generate the differential equation but i've tried using the matlabFunction() method with other simpler functions and it seems like it works. Its just that when I try to solve this function, it fails. The solved values start becoming negative when they should all be positive.
I also tried using another solver, dsolve(). But it gives me an implicit solution all the time...
Does anyone have an idea why this is happening? Any advice is appreciated. Thank you!
Since your code seems to work for simpler functions, you could try to increase the accuracy options of the ode45 solver.
This can be achieved by using odeset:
options = odeset('RelTol',1e-10,'AbsTol',1e-10);
[T,Y] = ode45(#function,[tspan],[y0],options);