How to animate solution of ode with ode45 solver? - matlab

I'm trying to access the current solution the ode45 provides. All the examples I've seen, they solve the ode and then store the solution for further manipulation. While this is an alternative solution, but I need to access the solution at each iteration so I can plot them for animation purposes. This is the code for simple pendulum.
clear
clc
theta0 = [pi/2; 0];
tspan = [0 10];
[t, theta] = ode45(#odeFun, tspan, theta0);
plot(t, theta(:,1));
function dydx = odeFun(t, theta)
g = 9.8;
l = 1;
dydx = zeros(2, 1);
dydx(1) = theta(2);
dydx(2) = -g/l*theta(1);
end
My goal is achieve the following
clear
clc
theta0 = [pi/2; 0];
t=0;
while t < 10
[t, theta] = ode45(#odeFun, ..., ...);
plot(t,theta)
drawnow
t = t + 0.1;
end
[t, theta] = ode45(#odeFun, tspan, theta0);
plot(t, theta(:,1));
function dydx = odeFun(t, theta)
g = 9.8;
l = 1;
dydx = zeros(2, 1);
dydx(1) = theta(2);
dydx(2) = -g/l*theta(1);
end

You can use the ode output function option. The ODE solver calls the output function after each successful time step. Here is a very simple example of using an output function:
clear
clc
close all
theta0 = [pi/2; 0];
tspan = [0 10];
options = odeset('OutputFcn',#plotfun,'MaxStep',0.0001);
axes; hold on
[t, theta] = ode45(#odeFun, tspan, theta0, options);
function status = plotfun(t,y,flag)
plot(t, y(1,:),'k-',t,y(2,:),'m-');
drawnow
status = 0;
end
function dydx = odeFun(t, theta)
g = 9.8;
l = 1;
dydx = zeros(2, 1);
dydx(1) = theta(2);
dydx(2) = -g/l*theta(1);
end
The maximum integration step is set to 0.0001 so that the solution graphs are not drawn immediately.
Alternatively, you can calculate the solution by dividing the time interval into parts and using the end point of the previous part as the initial point for the next part:
clear
clc
close all
axes; hold on
theta0 = [pi/2; 0];
tstart=0; dt= 0.1;
options= odeset('MaxStep',0.0001);
while tstart < 10
[t, theta] = ode45(#odeFun,[tstart tstart+dt],theta0,options);
plot(t,theta(:,1),'k-',t,theta(:,2),'m-');
drawnow
tstart = tstart + dt;
theta0= theta(end,:);
end
function dydx = odeFun(t, theta)
g = 9.8;
l = 1;
dydx = zeros(2, 1);
dydx(1) = theta(2);
dydx(2) = -g/l*theta(1);
end
And of course you can combine both approaches.

Related

How to simulate a system output with a sine wave input?

I wish to simulate the output of a certain gear system I have. How the gear system looks isn't particularly important to the problem, I managed to get the differential equation needed from the mechanical system.
Here is the code I have
% parameters
N2 = 90;
N1 = 36;
Jn1 = 0.5;
Jn2 = 0.8;
J2 = 2;
D = 8;
K = 5;
J = (N2/N1)^2 * Jn1 + Jn2 + J2;
% define the system
sys = ss([0 1; -K/J -D/J], [0; N2/(N1*J)], [1 0], 0);
% initial state: (position, velocity) [rad; rad/s]
x0 = [0; 0];
% define the time span
t = linspace(0, 15, 10000)';
% define the input step
T1 = zeros(length(t), 1);
T1(t>=0) = 1;
% compute the system step response at once
theta1 = lsim(sys, T1, t, x0);
% compute the system response as aggregate of the forced and unforced
% temporal evolutions
theta2 = lsim(sys, T1, t, [0; 0]) + initial(sys, x0, t);
% plot results
figure('color', 'white');
hold on;
yyaxis left;
plot(t, T1, '-.', 'linewidth', 2);
ylabel('[N]');
yyaxis right;
plot(t, theta1, 'linewidth', 3);
plot(t, theta2, 'k--');
xlabel('t [s]');
ylabel('[rad]');
grid minor;
legend({'$T_1$', '$\theta_1$', '$\theta_2$'}, 'Interpreter', 'latex',...
'location', 'southeast');
hold off;
This should work in generating a graph that shows the positions, my outputs, for a Heaviside/step input. My question is, how would I do this for a sine wave input. I figure I should have sin(w*t) instead of (t>=0), where w is my pulse frequency. Still, I can't seem to make this work. Any help would be really appreciated! :)
Here is the solution to my problem :)
function x = integrate(t, u, x0)
% parameters
N2 = 90;
N1 = 36;
Jn1 = 0.5;
Jn2 = 0.8;
J2 = 2;
D = 8;
K = 5;
J = (N2/N1)^2 * Jn1 + Jn2 + J2;
% integrate the differential equation
[t, x] = ode23(#fun, t, x0);
% plot results
figure('color', 'white');
% plot position
yyaxis left;
plot(t, x(:, 1));
ylabel('$x$ [rad]', 'Interpreter', 'latex');
% plot velocity
yyaxis right;
plot(t, x(:, 2));
ylabel('$\dot{x}$ [rad/s]', 'Interpreter', 'latex');
grid minor;
xlabel('$t$ [s]', 'Interpreter', 'latex');
function g = fun(t, x)
g = zeros(2, 1);
g(1) = x(2);
g(2) = (-K/J)*x(1) + (-D/J)*x(2) + (N2/(N1*J)*u(t));
end
end
Now we can use an anonymous function for example:
t = linspace(0, 120, 10000)';
x0 = [0.1; 0];
x = integrate(t, #(t)(sin(1.5*t)), x0);
Test Run
These are the output results I currently get on MATLAB R2019b. As Luis' comment has suggested I have also declared a sinusoid as T1 to serve as the input. Currently not sure if this result is the expected output.
Code Snippet:
t = linspace(0, 15, 10000)';
f = 0.1;
phi = 0;
T1 = sin(2*pi*f*t + phi);
f → Frequency of sinusoidal input (0.1Hz in this example).
phi → Phase offset of sinusoidal input/initial phase (0 in this example).
t → Time vector dictating the samples of the sinusoid.
0 → Start time (0 seconds in this example).
15 → End time (15 seconds in this example).
10000 → Number of samples between the start time (0s) and end time (15s).
Implementation in Script:
% parameters
N2 = 90;
N1 = 36;
Jn1 = 0.5;
Jn2 = 0.8;
J2 = 2;
D = 8;
K = 5;
J = (N2/N1)^2 * Jn1 + Jn2 + J2;
% define the system
sys = ss([0 1; -K/J -D/J], [0; N2/(N1*J)], [1 0], 0);
% initial state: (position, velocity) [rad; rad/s]
x0 = [0; 0];
% define the time span
t = linspace(0, 15, 10000)';
% define the input step
T1 = zeros(length(t), 1);
T1(t>=0) = 1;
f = 0.1; %Sinusoid frequency = 0.1Hz%
phi = 0; %Phase = 0%
T1 = sin(2*pi*f*t + phi);
% compute the system step response at once
theta1 = lsim(sys, T1, t, x0);
% compute the system response as aggregate of the forced and unforced
% temporal evolutions
theta2 = lsim(sys, T1, t, [0; 0]) + initial(sys, x0, t);
% plot results
figure('color', 'white');
hold on;
yyaxis left;
plot(t, T1, '-.', 'linewidth', 2);
ylabel('[N]');
yyaxis right;
plot(t, theta1, 'linewidth', 3);
plot(t, theta2, 'k--');
xlabel('t [s]');
ylabel('[rad]');
grid minor;
legend({'$T_1$', '$\theta_1$', '$\theta_2$'}, 'Interpreter', 'latex',...
'location', 'southeast');
hold off;

Bifurcation diagram of discrete SIR model in MATLAB

I have a problem with my MATLAB code to display a figure of a Bifurcation diagram of discrete SIR model.
My model is:
S(n+1) = S(n) - h*(0.01+beta*S(n)*I(n)+d*S(n)-gamma*R(n))
I(n+1) = I(n) + h*beta*S(n)*I(n)-h*(d+r)*I(n)
R(n+1) = R(n) + h*(r*I(n)-gamma*R(n));
I tried out the code below but it keeps MATLAB busy for almost 30 mins and showed up no figure.
MATLAB code:
close all;
clear all;
clc;
%Model parameters
beta = 1/300;
gamma = 1/100;
D = 30; % Simulate for D days
N_t = floor(D*24/0.1); % Corresponding no of hours
d = 0.001;
r = 0.07;
%Time parameters
dt = 0.01;
N = 10000;
%Set-up figure and axes
figure;
ax(1) = subplot(2,1,1);
hold on
xlabel ('h');
ylabel ('S');
ax(2) = subplot(2,1,2);
hold on
xlabel ('h');
ylabel ('I');
%Main loop
for h = 2:0.01:3
S = zeros(N,1);
I = zeros(N,1);
R = zeros(N,1);
S(1) = 8;
I(1) = 5;
R(1) = 0;
for n = 1:N_t
S(n+1) = S(n) - h*(0.01+beta*S(n)*I(n)+d*S(n)-gamma*R(n));
I(n+1) = I(n) + h*beta*S(n)*I(n)-h*(d+r)*I(n);
R(n+1) = R(n) + h*(r*I(n)-gamma*R(n));
end
plot(ax(1),h,S,'color','blue','marker','.');
plot(ax(2),h,I,'color','blue','marker','.');
end
Any suggestions?
It was very slow because you were plotting a single value of h versus a vector S which had 7200 points. I assumed that you only want to plot the last value of S versus h. So replacing S with S(end) in the plot command changes everything. You really didn't need to use hold and it's better call plot once for each axis, so here is how I would do it:
beta = 1/300;
gamma = 1/100;
D = 30; % Simulate for D days
N_t = floor(D*24/0.1); % Corresponding no of hours
d = 0.001;
r = 0.07;
%%Time parameters
dt = 0.01;
N = 10000;
%%Main loop
h = 2:0.01:3;
S_end = zeros(size(h));
I_end = zeros(size(h));
for idx = 1:length(h)
S = zeros(N_t,1);
I = zeros(N_t,1);
R = zeros(N_t,1);
S(1) = 8;
I(1) = 5;
R(1) = 0;
for n=1:(N_t - 1)
S(n+1) = S(n) - h(idx)*(0.01+beta*S(n)*I(n)+d*S(n)-gamma*R(n));
I(n+1) = I(n) + h(idx)*beta*S(n)*I(n) - h(idx)*(d+r)*I(n);
R(n+1) = R(n) + h(idx)*(r*I(n)-gamma*R(n));
end
S_end(idx) = S(end);
I_end(idx) = I(end);
end
figure(1)
subplot(2,1,1);
plot(h,S_end,'color','blue','marker','.');
xlabel ('h');
ylabel ('S');
subplot(2,1,2);
plot(h,I_end,'color','blue','marker','.');xlabel ('h');
xlabel ('h');
ylabel ('I');
This now runs in 0.2 seconds on my computer.

How to get the roots of these equations with flexible parameters by fsolve function?

I write two mfiles to solve the roots t and p of two equations. There is a flexible parameter n used in equation which changes from 1 to 100. Now the code can only solve the roots as n = 100 for 100 time instead of 1 to 100. How to correct it?
file 1:
function q=CSMA(x)
m=5;
W=32;
p=x(1);
t=x(2);
for n = 1:100;
q(1)=(1-2*p)*(1+W)+p*W*(1-(2*p)^m)-2*(1-2*p)/t;
q(2)=(1-(1-t)^(n-1))-p;
end
end
file 2:
N = 100;
the_roots = zeros(1, N);
for n = 1:N
y = fsolve('CSMA', [0.1, 0.1], optimset('Display', 'off'));
p = y(1);
t = y(2);
the_roots(n)= t;
end
figure;
plot(the_roots, 'b-');
You need to pass the variable n to your function as a parameter. You could for example change your CSMA function like this:
function q=CSMA(x,n)
m=5;
W=32;
p=x(1);
t=x(2);
q(1)=(1-2*p)*(1+W)+p*W*(1-(2*p)^m)-2*(1-2*p)/t;
q(2)=(1-(1-t)^(n-1))-p;
end
And then the optimization could look like this using a function handle:
N = 100;
the_roots = zeros(1, N);
for n = 1:N
f = #(x) CSMA(x,n);
y = fsolve(f, [0.1, 0.1], optimset('Display', 'off'));
p = y(1);
t = y(2);
the_roots(n)= t;
end
figure;
plot(the_roots, 'b-');
The plotted output looks like this:

How to plot pendulum motion for Elastic Pendulum with Spring in Matlab

I have a code that creates the correct xy plot for elastic pendulum with spring. I would like to show an animation of the elastic spring pendulum on an xy plot as the system marches forward in time. How can this be done?
Here is my simulation code:
clc
clear all;
%Define parameters
global M K L g;
M = 1;
K = 25.6;
L = 1;
g = 9.8;
% define initial values for theta, thetad, del, deld
theta_0 = 0;
thetad_0 = .5;
del_0 = 1;
deld_0 = 0;
initialValues = [theta_0, thetad_0, del_0, deld_0];
% Set a timespan
t_initial = 0;
t_final = 36;
dt = .1;
N = (t_final - t_initial)/dt;
timeSpan = linspace(t_final, t_initial, N);
% Run ode45 to get z (theta, thetad, del, deld)
[t, z] = ode45(#OdeFunHndlSpngPdlmSym, timeSpan, initialValues);
% initialize empty column vectors for theta, thetad, del, deld
M_loop = zeros(N, 1);
L_loop = zeros(N, 1);
theta = zeros(N, 1);
thetad = zeros(N, 1);
del = zeros(N, 1);
deld = zeros(N, 1);
T = zeros(N, 1);
x = zeros(N, 1);
y = zeros(N, 1);
% Assign values for variables (theta, thetad, del, deld)
for i = 1:N
M_loop(i) = M;
L_loop(i) = L;
theta(i) = z(i, 1);
thetad(i) = z(i, 2);
del(i) = z(i, 3);
deld(i) = z(i, 4);
T(i) = (M*(thetad(i)^2*(L + del(i))^2 + deld(i)^2))/2;
V(i) = (K*del(i)^2)/2 + M*g*(L - cos(theta(i))*(L + del(i)));
E(i) = T(i) + V(i);
x(i) = (L + del(i))*sin(theta(i));
y(i) = -(L + del(i))*cos(theta(i));
end
figure(1)
plot(x, y,'r');
title('XY Plot');
xlabel('x position');
ylabel('y position');
Here is my function code:
function dz = OdeFunHndlSpngPdlmSym(~, z)
% Define Global Parameters
global M K L g
% Take output from SymDevFElSpringPdlm.m file for fy1 and fy2 and
% substitute into z2 and z4 respectively
%fy1=thetadd=z(2)= -(M*g*sin(z1)*(L + z3) + M*z2*z4*(2*L + 2*z3))/(M*(L + z3)^2)
%fy2=deldd=z(4)=((M*(2*L + 2*z3)*z2^2)/2 - K*z3 + M*g*cos(z1))/M
% return column vector [thetad; thetadd; deld; deldd]
dz = [z(2);
-(M*g*sin(z(1))*(L + z(3)) + M*z(2)*z(4)*(2*L + 2*z(3)))/(M*(L + z(3))^2);
z(4);
((M*(2*L + 2*z(3))*z(2)^2)/2 - K*z(3) + M*g*cos(z(1)))/M];
You can "simulate" animation in a plot with a continous updating FOR loop and assignig graphic object to variables.
Something like (I assume only to use x,y as arrays function of time array t)
%In order to block the axis and preventing continuous zoom, choose proper axes limit
x_lim = 100; %these values depends on you, these are examples
y_lim = 100;
axis equal
axis([-x_lim x_lim -y_lim y_lim]); %now x and y axes are fixed from -100 to 100
ax = gca;
for i=1:length(t)
if i > 1
delete(P);
delete(L);
end
P = plot(x(i),y(i)); %draw the point
hold on
L = quiver(0,0,x(i),y(i)); %draw a vector from 0,0 to the point
hold on
%other drawings
drawnow
pause(0.1) %pause in seconds needed to simulate animaton.
end
"Hold on" instruction after every plot instruction.
This is only a basic animation, of course.

Solving 2nd order differential equation with boundary condition z(inf) = 0

How can I solve a 2nd order differential equation with boundary condition like z(inf)?
2(x+0.1)·z'' + 2.355·z' - 0.71·z = 0
z(0) = 1
z(inf) = 0
z'(0) = -4.805
I can't understand where the boundary value z(inf) is to be used in ode45() function.
I used in the following condition [z(0) z'(0) z(inf)], but this does not give accurate output.
function [T, Y]=test()
% some random x function
x = #(t) t;
t=[0 :.01 :7];
% integrate numerically
[T, Y] = ode45(#linearized, t, [1 -4.805 0]);
% plot the result
plot(T, Y(:,1))
% linearized ode
function dy = linearized(t,y)
dy = zeros(3,1);
dy(1) = y(2);
dy(2) = y(3);
dy(3) = (-2.355*y(2)+0.71*y(1))/((2*x(t))+0.2);
end
end
please help me to solve this differential equation.
You seem to have a fairly advanced problem on your hands, but very limited knowledge of MATLAB and/or ODE theory. I'm happy to explain more if you want, but that should be in chat (I'll invite you) or via personal e-mail (my last name AT the most popular mail service from Google DOT com)
Now that you've clarified a few things and explained the whole problem, things are a bit more clear and I was able to come up with a reasonable solution. I think the following is at least in the general direction of what you'd need to do:
function [tSpan, Y2, Y3] = test
%%# Parameters
%# Time parameters
tMax = 1e3;
tSpan = 0 : 0.01 : 7;
%# Initial values
y02 = [1 -4.805]; %# second-order ODE
y03 = [0 0 4.8403]; %# third-order ODE
%# Optimization options
opts = optimset(...
'display', 'off',...
'TolFun' , 1e-5,...
'TolX' , 1e-5);
%%# Main procedure
%# Find X so that z2(t,X) -> 0 for t -> inf
sol2 = fminsearch(#obj2, 0.9879680932400429, opts);
%# Plug this solution into the original
%# NOTE: we need dense output, which is done via deval()
Z = ode45(#(t,y) linearized2(t,y,sol2), [0 tMax], y02);
%# plot the result
Y2 = deval(Z,tSpan,1);
plot(tSpan, Y2, 'b');
%# Find X so that z3(t,X) -> 1 for t -> inf
sol3 = fminsearch(#obj3, 1.215435887288112, opts);
%# Plug this solution into the original
[~, Y3] = ode45(#(t,y) linearized3(t,y,sol3), tSpan, y03);
%# plot the result
hold on, plot(tSpan, Y3(:,1), 'r');
%# Finish plots
legend('Second order ODE', 'Third order ODE')
xlabel('T [s]')
ylabel('Function value [-]');
%%# Helper functions
%# Function to optimize X for the second-order ODE
function val = obj2(X)
[~, y] = ode45(#(t,y) linearized2(t,y,X), [0 tMax], y02);
val = abs(y(end,1));
end
%# linearized second-order ODE with parameter X
function dy = linearized2(t,y,X)
dy = [
y(2)
(-2.355*y(2) + 0.71*y(1))/2/(X*t + 0.1)
];
end
%# Function to optimize X for the third-order ODE
function val = obj3(X3)
[~, y] = ode45(#(t,y) linearized3(t,y,X3), [0 tMax], y03);
val = abs(y(end,2) - 1);
end
%# linearized third-order ODE with parameters X and Z
function dy = linearized3(t,y,X)
zt = deval(Z, t, 1);
dy = [
y(2)
y(3)
(-1 -0.1*zt + y(2) -2.5*y(3))/2/(X*t + 0.1)
];
end
end
As in my comment above, I think you're confusing a couple of things. I suspect this is what is requested:
function [T,Y] = test
tMax = 1e3;
function val = obj(X)
[~, y] = ode45(#(t,y) linearized(t,y,X), [0 tMax], [1 -4.805]);
val = abs(y(end,1));
end
% linearized ode with parameter X
function dy = linearized(t,y,X)
dy = [
y(2)
(-2.355*y(2) + 0.71*y(1))/2/(X*t + 0.1)
];
end
% Find X so that z(t,X) -> 0 for t -> inf
sol = fminsearch(#obj, 0.9879);
% Plug this ssolution into the original
[T, Y] = ode45(#(t,y) linearized(t,y,sol), [0 tMax], [1 -4.805]);
% plot the result
plot(T, Y(:,1));
end
but I can't get it to converge anymore for tMax beyond 1000 seconds. You may be running into the limits of ode45 capabilities w.r.t. accuracy (switch to ode113), or your initial value is not accurate enough, etc.