Solving 7 equations of first order equations ode45 Matlab - matlab

I am trying to solve a system with seven differential equations. And I am having a hard time grapsing the ode45 solver.
These are the equations:
ω2_dot = -0.75 ω1 ω3
ω1_dot = 0.75 ω2 ω3 + 0.2
ω3_dot = 0
q1_dot = 1/2(ω1q4 + ω2q3 - ω3q2)
q2_dot = 1/2(ω2q4 + ω3q1 - ω1q3)
q3_dot = 1/2(ω3q4 + ω1q2 - ω2q2)
q4_dot = -1/2(ω1q1 + ω2q2 + ω3q3)
The initial values are listed in the inital_val in the same order. Here is what I have so far:
inital_val = [1 -1 2 0 0 0 1];
timespan = [0:0.05:3.95];
[result t] = ode45(#soe,timespan,inital_val);
function [results,t]=soe(inital_val, timespan)
omega1_dot = -0.75*omega2*omega3;
omega2_dot = 0.75*omega2*omega3+0.2;
omega3_dot = 0;
q1_dot = (1/2)*(q4*omega1+omega2*q3-omega3*q2);
q2_dot = (1/2)*(q4*omega2+omega3*q1-omega1*q3);
q3_dot = (1/2)*(q4*omega3+omega1*q2-omega2*q2);
q4_dot = (1/2)*(q1*omega1+q2*omega2+q3*omega3);
results = [omega1 omega2 omega3 q1 q2 q3 q4];
end
However, it gives me this error message, which makes sense but I then don't know how to fix it:
Unrecognized function or variable 'omega2'.
Error in ode45_part>soe (line 10)
omega1_dot = -0.75*omega2*omega3;
Error in odearguments (line 90)
f0 = feval(ode,t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ode45 (line 115)
odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin);
Error in ode45_part (line 7)
[result t] = ode45(#soe,timespan,inital_val);
Any help would be much appreciated

There are two fundamental problems with your code. First problem is you don't have the correct signature of the derivative function needed for use with ode45( ). Second problem is that blindly integrating quaternion elements this way will lead to non-unit quaternions. I don't know of an easy way to enforce this restriction when using ode45( ).
Let's tackle the easy problem first, the derivative function signature. Also your use of variables inside this function isn't correct. It needs to be like this:
function dy = soe(t,y) % Required signature is (time,state_vector)
% Pick off the named variables from the state vector y
omega1 = y(1);
omega2 = y(2);
omega3 = y(3);
q1 = y(4);
q2 = y(5);
q3 = y(6);
q4 = y(7);
% Calculate the variable derivatives
omega1_dot = -0.75*omega2*omega3; % IS THIS A TYPO? Should it be omega1*omega3?
omega2_dot = 0.75*omega2*omega3+0.2;
omega3_dot = 0;
q1_dot = (1/2)*(q4*omega1+omega2*q3-omega3*q2);
q2_dot = (1/2)*(q4*omega2+omega3*q1-omega1*q3);
q3_dot = (1/2)*(q4*omega3+omega1*q2-omega2*q2);
q4_dot = (1/2)*(q1*omega1+q2*omega2+q3*omega3);
% Group variable derivatives into a single column vector
dy = [omega1dot; omega2dot; omega3dot; q1_dot; q2_dot; q3_dot; q4_dot];
end
Also your angular rate initial values seem completely unreasonable for a rotation problem. They will be interpreted as [1 -1 2] radians/second. I suspect you wanted degrees/second for this, so you will need to scale down those values.
The second problem, that of keeping the quaternion elements forming a unit quaternion, is not going to be easy to overcome using ode45( ). All of the little integration errors will creep in driving the quaternion magnitude away from 1. Any subsequent use of this quaternion in your other code will have problems because of this. My only advice in this is to maybe abandon ode45( ) altogether and write your own custom integrator (e.g., RK4) so that at each step you can re-normalize the quaternion elements.

I am not sure what your inital_val is supposed to represent, but here you can at least run the following snippet and fix/change accordingly.
clear; clc;
y0 = [1 -1 2 0 0 0 1];
tspan = [0:0.05:3.95];
[t, y] = ode45(#(t, y) odefcn(t, y), tspan, y0);
function dydt = odefcn(t, y)
dydt = zeros(7, 1);
dydt(2) = -0.75 * y(1) * y(3);
dydt(1) = 0.75 * y(2) * y(3) + 0.2;
dydt(3) = 0;
dydt(4) = 1 / 2 * (y(1) * y(7) + y(2) * y(6) - y(3) * y(5));
dydt(5) = 1 / 2 * (y(2) * y(7) + y(3) * y(4) - y(1) * y(6));
dydt(6) = 1 / 2 * (y(3) * y(7) + y(1) * y(5) - y(2) * y(5));
dydt(7) = -1 / 2 * (y(1) * y(4) + y(2) * y(5) + y(3) * y(6));
end
Note that you need dydt = zeros(7, 1) because otherwise the code creates a column vector. Without it, you may run into problems.

Related

MATLAB error using fzero function to solve the loop

My following code generates the plot of V and D values in figure 1. In the graph, the parabolas and straigh lines intersect, and I need to find the roots from the plot from a loop. So I tried to use fzero function, but the error appeared:
Operands to the logical AND (&&) and OR (||) operators must be convertible to logical scalar values. Use the ANY or ALL functions to reduce operands to logical scalar values.
Error in fzero (line 326)
elseif ~isfinite(fx) || ~isreal(fx)
Error in HW1 (line 35)
x=fzero(fun,1);
My code is:
clear all; close all
W = 10000; %[N]
S = 40; %[m^2]
AR = 7;
cd0 = 0.01;
k = 1 / pi / AR;
clalpha = 2*pi;
Tsl=800;
figure(1);hold on; xlabel('V');ylabel('D')
for h=0:1:8;
i=0;
for alpha = 1:0.25:12
i=i+1;
rho(i)=1.2*exp(-h/10.4);
cl(i) = clalpha * alpha * pi/180;
V(i) = sqrt(2*W/rho(i)/S/cl(i));
L(i) = 0.5 * rho(i) * V(i) * V(i) * S * cl(i);
cd(i) = cd0 + k * cl(i) * cl(i);
D(i) = 0.5 * rho(i) * V(i) * V(i) * S * cd(i);
clcd(i) = cl(i)/cd(i);
p(i) = D(i)*V(i);
ang(i) = alpha;
T(i)=Tsl*(rho(i)/1.2).^0.75;
end
figure(1); plot(V,D); hold on
plot(V,T);
end
fun = #(V) 0.5*V.*V.*rho.*S.*cd-T;
x=fzero(fun,1);
Probably, I should not use the fzero function, but the task is to find the roots of V from a plot (figure 1). There are parabolas and straight lines respectively.
From the documentation for fzero(fun,x)
fun: Function to solve, specified as a handle to a scalar-valued function or the name of such a function. fun accepts a scalar x and returns a scalar fun(x).
Your function does not return a scalar value for a scalar input, it always returns a vector which is not valid for a function which is being used with fzero.
1.- Your codes doesn't plot V and D: Your code plots D(V) and T(V)
2.- T is completely flat, despite taking part in the inner for loop calculations with T(i)=Tsl*(rho(i)/1.2).^0.75; as it had to be somehow modified.
But in fact it remains constant for all samples of V, constant temperature (°C ?), and for all laps of the outer for loop sweeping variable h within [0:1:8].
The produced T(V) functions are the flat lines.
3.- Then you try building a 3rd function f that you put as if f(V) only but in fact it's f(V,T) with the right hand side of the function with a numerical expression, without a symbolic expression, the symbolic expression that fzero expects to attempt zero solving.
In MATLAB Zero finding has to be done either symbolically or numerically.
A symbolic zero-finding function like fzero doesn't work with numerical expressions like the ones you have calculated throughout the 2 loops for h and for alpha .
Examples of function expressions solvable by fzero :
3.1.-
fun = #(x)sin(cosh(x));
x0 = 1;
options = optimset('PlotFcns',{#optimplotx,#optimplotfval});
x = fzero(fun,x0,options)
3.2.-
fun = #sin; % function
x0 = 3; % initial point
x = fzero(fun,x0)
3.3.- put the following 3 lines in a separate file, call this file f.m
function y = f(x)
y = x.^3 - 2*x - 5;
and solve
fun = #f; % function
x0 = 2; % initial point
z = fzero(fun,x0)
3.4.- fzeros can solve parametrically
myfun = #(x,c) cos(c*x); % parameterized function
c = 2; % parameter
fun = #(x) myfun(x,c); % function of x alone
x = fzero(fun,0.1)
4.- So since you have already done all the numerical calculations and no symbolic expression is supplied, it's reasonable to solve numerically, not symbolically.
To this purpose there's a really useful function called intersections.m written by Douglas Schwarz available here
clear all; close all;clc
W = 10000; %[N]
S = 40; %[m^2]
AR = 7;
cd0 = 0.01;
k = 1 / pi / AR;
clalpha = 2*pi;
Tsl=800;
figure(1);
ax1=gca
hold(ax1,'on');xlabel(ax1,'V');ylabel(ax1,'D');grid(ax1,'on');
title(ax1,'1st graph');
reczeros={}
for h=0:1:8;
i=0;
for alpha = 1:0.25:12
i=i+1;
rho(i)=1.2*exp(-h/10.4);
cl(i) = clalpha * alpha * pi/180;
V(i) = sqrt(2*W/rho(i)/S/cl(i));
L(i) = 0.5 * rho(i) * V(i) * V(i) * S * cl(i);
cd(i) = cd0 + k * cl(i) * cl(i);
D(i) = 0.5 * rho(i) * V(i) * V(i) * S * cd(i);
clcd(i) = cl(i)/cd(i);
p(i) = D(i)*V(i);
ang(i) = alpha;
T(i)=Tsl*(rho(i)/1.2).^0.75;
end
plot(ax1,V,D); hold(ax1,'on');
plot(ax1,V,T);
[x0,y0]=intersections(V,D,V,T,'robust');
reczeros=[reczeros [x0 y0]];
for k1=1:1:numel(x0)
plot(ax1,x0,y0,'r*');hold(ax1,'on')
end
end
Because at each pair D(V) T(V) there may be no roots, 1 root or more than 1 root, it makes sense to use a cell, reczeros, to store whatever roots obtained.
To read obtained roots in let's say laps 3 and 5:
reczeros{3}
=
55.8850 692.5504
reczeros{5}
=
23.3517 599.5325
55.8657 599.5325
5.- And now the 2nd graph, the function that is defined in a different way as done in the double for loop:
P = 0.5*V.*V.*rho.*S.*cd-T;
figure(2);
ax2=gca
hold(ax2,'on');xlabel(ax2,'V');ylabel(ax2,'P');grid(ax2,'on');
title(ax2,'2nd graph')
plot(ax2,V,P)
plot(ax2,V,T)
[x0,y0]=intersections(V,T,V,P,'robust');
for k1=1:1:numel(x0)
plot(ax2,x0,y0,'r*');hold(ax2,'on')
end
format short
V0=x0
P0=y0
V0 =
86.9993
P0 =
449.2990

Using ode45 in matlab to solve a system of odes with a condition

Bit of a novice here...
I'm trying to a solve a system of 6 odes but with a condition that if (x(1)<=0.1) then x(6)=x(6)/2.0. How do I put that into the code?
Thanks!
This is slightly modified basic Example 1 from http://www.mathworks.com/help/matlab/ref/ode45.html
First make a new Matlab function and call it rigid.m. You can put any code inside, but try this:
function dy = rigid(t,y)
dy = zeros(6,1); % a column vector
dy(1) = y(2) * y(3);
dy(2) = -y(1) * y(3);
dy(3) = -0.51 * y(1) * y(2);
dy(4) = y(5) * y(6);
dy(5) = -y(4) * y(6);
dy(6) = -0.51 * y(4) * y(5);
if(dy(6)<0.5)
dy(6)=dy(6)/2;
end
end
Now run these three lines:
options = odeset('RelTol',1e-4,'AbsTol',1e-4);
[T,Y] = ode45(#rigid,[0 12],[0 1 1,0 1 1],options);
plot(T,Y(:,1),'-',T,Y(:,2),'-.',T,Y(:,3),'.',T,Y(:,4),'-',T,Y(:,5),'-.',T,Y(:,6),'.');
And it is done. Matlab solvers should handle quite well the discontinuity in the derivative, but it will depend on your problem. Anyway in this case if dy(6)<0.5 then it is halved.

`ode45` and tspan error Attempted to access

I'm using ode45 to solve second order differential equation. the time span is determined based on how many numbers in txt file, therefore, the time span is defined as follows
i = 1;
t(i) = 0;
dt = 0.1;
numel(theta_d)
while ( i < numel(theta_d) )
i = i + 1;
t(i) = t(i-1) + dt;
end
Now the time elements should not exceed the size of txt (i.e. numel(theta_d)). In main.m, I have
x0 = [0; 0];
options= odeset('Reltol',dt,'Stats','on');
[t, x] = ode45('ODESolver', t, x0, options);
and ODESolver.m header is
function dx = ODESolver(t, x)
If I run the code, I'm getting this error
Attempted to access theta_d(56); index out of bounds because numel(theta_d)=55.
Error in ODESolver (line 29)
theta_dDot = ( theta_d(i) - theta_dPrev ) / dt;
Why the ode45 is not being fixed with the time span?
Edit: this is the entire code
main.m
clear all
clc
global error theta_d dt;
error = 0;
theta_d = load('trajectory.txt');
i = 1;
t(i) = 0;
dt = 0.1;
numel(theta_d)
while ( i < numel(theta_d) )
i = i + 1;
t(i) = t(i-1) + dt;
end
x0 = [pi/4; 0];
options= odeset('Reltol',dt,'Stats','on');
[t, x] = ode45(#ODESolver, t, x0, options);
%e = x(:,1) - theta_d; % Error theta
plot(t, x(:,2), 'r', 'LineWidth', 2);
title('Tracking Problem','Interpreter','LaTex');
xlabel('time (sec)');
ylabel('$\dot{\theta}(t)$', 'Interpreter','LaTex');
grid on
and ODESolver.m
function dx = ODESolver(t, x)
persistent i theta_dPrev
if isempty(i)
i = 1;
theta_dPrev = 0;
end
global error theta_d dt ;
dx = zeros(2,1);
%Parameters:
m = 0.5; % mass (Kg)
d = 0.0023e-6; % viscous friction coefficient
L = 1; % arm length (m)
I = 1/3*m*L^2; % inertia seen at the rotation axis. (Kg.m^2)
g = 9.81; % acceleration due to gravity m/s^2
% PID tuning
Kp = 5;
Kd = 1.9;
Ki = 0.02;
% theta_d first derivative
theta_dDot = ( theta_d(i) - theta_dPrev ) / dt;
theta_dPrev = theta_d(i);
% u: joint torque
u = Kp*(theta_d(i) - x(1)) + Kd*( theta_dDot - x(2)) + Ki*error;
error = error + (theta_dDot - x(1));
dx(1) = x(2);
dx(2) = 1/I*(u - d*x(2) - m*g*L*sin(x(1)));
i = i + 1;
end
and this is the error
Attempted to access theta_d(56); index out of bounds because numel(theta_d)=55.
Error in ODESolver (line 28)
theta_dDot = ( theta_d(i) - theta_dPrev ) / dt;
Error in ode45 (line 261)
f(:,2) = feval(odeFcn,t+hA(1),y+f*hB(:,1),odeArgs{:});
Error in main (line 21)
[t, x] = ode45(#ODESolver, t, x0, options);
The problem here is because you have data at discrete time points, but ode45 needs to be able to calculate the derivative at any time point in your time range. Once it solves the problem, it will interpolate the results back onto your desired time points. So it will calculate the derivative many times more than at just the time points you specified, thus your i counter will not work at all.
Since you have discrete data, the only way to proceed with ode45 is to interpolate theta_d to any time t. You have a list of values theta_d corresponding to times 0:dt:(dt*(numel(theta_d)-1)), so to interpolate to a particular time t, use interp1(0:dt:(dt*(numel(theta_d)-1)),theta_d,t), and I turned this into an anonymous function to give the interpolated value of theta_p at a given time t
Then your derivative function will look like
function dx = ODESolver(t, x,thetaI)
dx = zeros(2,1);
%Parameters:
m = 0.5; % mass (Kg)
d = 0.0023e-6; % viscous friction coefficient
L = 1; % arm length (m)
I = 1/3*m*L^2; % inertia seen at the rotation axis. (Kg.m^2)
g = 9.81; % acceleration due to gravity m/s^2
% PID tuning
Kp = 5;
Kd = 1.9;
Ki = 0.02;
% theta_d first derivative
dt=1e-4;
theta_dDot = (thetaI(t) - theta(I-dt)) / dt;
%// Note thetaI(t) is the interpolated theta_d values at time t
% u: joint torque
u = Kp*(thetaI(t) - x(1)) + Kd*( theta_dDot - x(2)) + Ki*error;
error = error + (theta_dDot - x(1));
dx=[x(2); 1/I*(u - d*x(2) - m*g*L*sin(x(1)))];
end
and you will have to define thetaI=#(t) interp1(0:dt:(dt*(numel(theta_d)-1)),theta_d,t); before calling ode45 using [t, x] = ode45(#(t,x) ODESolver(t,x,thetaI, t, x0, options);.
I removed a few things from ODESolver and changed how the derivative was computed.
Note I can't test this, but it should get you on the way.

Plotting solutions to differential equations, but not with respect to time in MATLAB

My question today is related to this previous question. I am following this research paper. I am trying to duplicate figure 8 located on page 20. I have a screenshot:
I'm confused on how to plot the left figure this in MATLAB because now a instead of having time varying we have the treatment varying. Here's what I have from the previous question:
function dX = CompetitionModel(~,X)
bs = 8e-3;
bl = 4e-3;
bh = 6.4e-3;
N = sum(X);
K = 1e8;
m1 = 2e-5;
m2 = 9e-9;
p = 5e-13;
I = 1e-3;
T = 1e-3; % Treatment
a = 0;
dX = [X(1) * (bs * (1 - N/K) - I - T - m1) - p * X(1) * (X(2) + X(3));
X(2) * (bl * (1 - N/K) - I - a*T - m2) + m1 * X(1) + p * X(2) * (X(1) - X(3));
X(3) * (bh * (1 - N/K) - I - a*T) + m2 * X(2) + p * X(3) * (X(1) + X(2))];
end
To plot my equations in the previous question, I typed the following in the command window:
>> [t,Y] = ode45(#CompetitionModel, [0 4.5e4], [1e4 0 0]);
>> plot(t,X(:,1), t,X(:,2), t,X(:,3))
In my function file, I have Treatment already defined. I'm guessing that it shouldn't be anymore. So what can I do so that I have Treatment varying instead of time? I hope my question makes sense.
You still solve the equation in regards to the time - but solely plot the value at the time t = 1 month.
To vary the treatment you need an additional loop around the ode45 call and pass the current treatment-value to the function dX
for treatment = 10^-4:10^-5:10^-3
[t,Y] = ode45(#CompetitionModel, [0 4.5e4], [1e4 0 0], [] , treatment);
plot(treatment,Y(end,1), 'x')
plot(treatment,Y(end,2), 'kx')
plot(treatment,Y(end,3), 'rx')
hold on
end
the function dX now has to be changed to accept the treatment input:
function dX = CompetitionModel(~,X, T)
Finally, comment your old treatment assignment in the function dX: %T = 1e-3; % Treatment

Matlab ode45 basic setup

I'm trying to simulate the Morris-Lecar model for neurons with ode45.
I am having trouble initializing the ode45 call, and documentation hasn't been able to help me. I understand that I have to call the ode45 through a function, and call that function from my main script.
I have a limited grasp of ODEs in general, and seem to have trouble understanding the syntax required to initialize the ODE45-call.
Also, I am indicated to use a time range for a variable 'pulse', but there is no input for a time range (which seems to be a variable, not fixed) in the function that takes in input from the main script and sends that with the other function to the ode45 function. The function that feeds into the ode45 has a time input as well, but again I cannot figure out how I can input a time range. The instructions are very clear that the function used in the main script does not take in any time variables.
It would be very appreciated if you could point out any glaring errors I have made in the initialization.
the current (below) version's error code is as following:
Error using ODEequation (line 89)
Not enough input arguments.
Error in odearguments (line 88)
f0 = feval(ode,t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ode45 (line 114)
[neq, tspan, ntspan, next, t0, tfinal, tdir, y0, f0, odeArgs, odeFcn, ...
Error in ODEquestion (line 32)
[t Vm]=ode45(#ODEequation,[-20 20],[-30, 0.1]',[],constants, stim_on, stim_off, amp);
Error in YoonS_Lab3_EBME308_Fall2012 (line 355)
[t Vm] = ODEquestion(20,100,30)
I think this goes back to my non-existing and yet needed time input.
The problem involves
Cm * dVm / dt = -Gm(Vm-Vrest) - Gca Minf (Vm - Eca) - Gk w(Vm - Ek) + pulse(t)
dw/dt = (wInf - w) / Tau-w;
wInf = (1+tanh(Vm/30)) / 2;
mInf = (1+tanh(Vm+1)) / 2;
Tau-w = 5/ (cosh(Vm/60));
Cm = membrane leakage capacticance;
Gm = membrane leakage conductance;
Vm = membrane voltage;
Vrest = membrane voltage # neuron resting
Gca = max Ca conductance through membrane
Gk = max K conductance through membrane;
mInf refers = P ( Ca ion channel open )
wInf refers = P ( K ion channel open )
Tau-w = rate which K channels respond to change in membrane voltage
Eca = reversal potential of Ca
Ek = reversal potential of K
pulse(t) = stimulus applied to neuron
pulse(t) = A (stim-on <= t <= stim-off) or 0 (else);
as the variables.
I have created a function that gets sent to the ode45, as below.
function dy = ODEequation(t, Vm, w, constants, stim_on, stim_off, amp)
wInf = (1 + tan(Vm / 30)) / 2
mInf = (1 + tan((Vm + 1)/ 15)) / 2
tauW = 5/ (cosh(Vm/60))
pulse = amp * ((stim_on < t ) - ( t >= stim_off));
dy(1) = y(1) * ((-constants(2) - constants(4) * constants(9) - constants(5) * y(2)) + (constants(2) * constants(3) + constants(6) * constants(4) * constants(9) + constants(5) * y(2) * constants(7) + constants(11))) / constants(1) ;
dy(2) = = ( constants(8) - y(2) )/constants(10)
dy = dy'
and the function that passes that is as below
function [t Vm] = ODEquestion(stim_on, stim_off, amp)
%i)
Cm = 1;
Gm = 0.5;
Vrest = -50;
Gca = 1.1;
Gk = 2;
Eca = 100;
Ek = -70;
%ii)
Vm(1) = -30;
w(1) = 0.1;
%iii)
wInf = (1 + tan(Vm / 30)) / 2
mInf = (1 + tan((Vm + 1)/ 15)) / 2
tauW = 5/ (cosh(Vm/60))
IC1 = Vm(1) % = -30
IC2 = w(1) % = 0.1
pulse = amp %* ((stim_on < t ) - ( t >= stim_off));
constants = [Cm , Gm, Vrest, Gca, Gk, Eca, Ek, wInf, mInf, tauW, pulse];
[t Vm]=ode45(#ODEequation,[-20 20],[-30, 0.1]',[],constants, stim_on, stim_off, amp);
From help ode45:
ODE45 Solve non-stiff differential equations, medium order method.
[TOUT,YOUT] = ODE45(ODEFUN,TSPAN,Y0) with TSPAN = [T0 TFINAL] integrates
the system of differential equations y' = f(t,y) from time T0 to TFINAL
with initial conditions Y0. ODEFUN is a function handle. For a scalar T
and a vector Y, ODEFUN(T,Y) must return a column vector corresponding
to f(t,y).
So the function ODEFUN expects only two inputs (t and y), whereas your function expects 7 inputs.
You can solve this by following instructions found on this site, or this question:
wrapper = #(t,Vm) ODEequation(t, Vm, w, constants, stim_on, stim_off, amp);
[t Vm]=ode45(wrapper, [-20 20],[-30, 0.1]',[],constants, stim_on, stim_off, amp);
e.g., by creating a small wrapper function that passes all the constants while forwarding the variables inserted by ode45.