convolution integral with ode45

124 visualizaciones (últimos 30 días)
Angie
Angie el 8 de Jun. de 2019
Comentada: Paul hace alrededor de 1 hora
Hi guys, can someone help me solve this equation of motion using ode45 or any other way? I dont know how to approach the convolution integral. Thank you!

Respuesta aceptada

Star Strider
Star Strider el 8 de Jun. de 2019
Try this:
odefcn = @(t,y,c,F,k,m,w) [y(2); -(k.*y(1) - F.*cos(t.*w) + integral(@(tau)c(t - tau).*y(2).*tau, 0, t))./m];
c = @(t) exp(-t.^2); % Make Something Up
[F,k,m,w] = deal(rand,rand,rand,rand); % Replace With Correct Values
tspan = [0 10];
ics = [1; 1];
[t,y] = ode45(@(t,y)odefcn(t,y,c,F,k,m,w), tspan, ics);
figure
plot(t, y)
hl = legend('$u(t)$', '$\dot{u}(t)$');
set(hl,'Interpreter','latex')
Noite that ‘c’ actually has to be defined as a function in order for this to work. Supply the correct one.
I derived it with the help of the Symbolic Math Toolbox.
  8 comentarios
JINGYU KANG
JINGYU KANG el 12 de Sept. de 2023
Star Strider's answer solves the different differential equation. That is,
where the desired equation to solve is as below
One can verify that by letting c(t) = 1 with k = 0, F = 0 for simplicity
David Goodmanson
David Goodmanson el 11 de Dic. de 2025 a las 1:16
Editada: David Goodmanson el 11 de Dic. de 2025 a las 7:35
Whether you include a factor of tau or not, I believe both of these solutions are incorrect, and every solution along these lines will be incorrect. The term is
Int{0,t} c(t-tau) u'(tau) dtau % u' instead of udot
i.e. a convolution that depends on the past history of u' from 0 to t. Replacing u'(tau) with u'(t) says that u' is a constant factor in the integrand (which it isn't). This gives
Int{0,t)} c(t-tau) u'(t) dtau = u'(t) Int{0,t)} c(t-tau) dtau.
If you attemp to fix this up by inserting tau or any function f(tau) inside the integral, and then do the integration you get
u'(t) Int{0,t} c(t-tau) f(tau) dtau = u'(t) g(t)
for some function g, which is a pointlike expression that does not involve past history of u' at all.
I have never unaccepted someone else's answer but would encourage Star Strider to take a hard look at that answer.

Iniciar sesión para comentar.

Más respuestas (2)

Paul
Paul el 7 de Dic. de 2025 a las 3:05
Editada: Paul el 11 de Dic. de 2025 a las 2:18
If c(t) has a Laplace transform then we can take advantage of the convolution property and convert to the s-domain, solve for U(s), and then convert back to the t-domain. For simple c(t) we can actually get a closed form expression for u(t), though a numerical or vpa solution is more likely going to be needed.
Define the differential equation
syms m k F omega t tau real
syms u(t) c(t)
du(t) = diff(u(t),t);
z(t) = int(c(t-tau)*diff(u(tau),tau),tau,0,t);
eqn = m*diff(u(t),t,2) + z(t) + k*u(t) == F*cos(omega*t)
eqn = 
Take the Laplace transform
Leqn = laplace(eqn)
Leqn = 
Sub in U(s) and C(s)
syms U(s) C(s)
Leqn = subs(Leqn,laplace([u(t),c(t)],t,s),[U(s),C(s)])
Leqn = 
Solve for U(s)
Leqn = isolate(Leqn,U(s))
Leqn = 
At this point we need the expresion for C(s). Assume a simple form of c(t) and sub C(s) into the expression for U(s)
c(t) = exp(-t);
Leqn = subs(Leqn,C,laplace(c(t),t,s))
Leqn = 
Simplify the expression for U(s)
[num,den] = numden(rhs(Leqn));
Leqn = lhs(Leqn) == num/den
Leqn = 
Choose some arbitrary parameters for illustration and sub in
mval = 1; kval = 2; Fval = 3; omegaval = 4;
Leqn = subs(Leqn,[m,k,F,omega,u(0),du(0)],[mval,kval,Fval,omegaval,5,6])
Leqn = 
If we only want a numerical evaluation of the solution, we can just use impulse
figure
[num,den] = numden(rhs(Leqn));
impulse(tf(double(coeffs(num,'all')),double(coeffs(den,'all'))),0:.01:20);grid
Get the solution for u(t)
u(t) = ilaplace(rhs(Leqn),s,t)
u(t) = 
u(t) = rewrite(rewrite(u(t),'expandsum'),'expandroot');
The solution includes some terms in i = sqrt(-1), but we know the solution has to be real. It proved too difficult to simplify u(t) into an expression with only real terms.
Derivative of u(t)
du(t) = diff(u(t),t);
Plot the real and imaginary parts of u(t) to verify the latter is zero, and plot the derivative of u(t)
figure;
fplot([real(u(t)),imag(u(t)),real(du(t))],[0,20]);grid
legend('u(t)','imag(u(t))','du(t)');
Note that u(0) and du(0) satisfy the initial conditions assumed above.
Now verify the solution satisfies the differential equation.
z(t) actually has a closed form expression
z(t) = int(c(t-tau)*du(tau),tau,0,t)
z(t) = 
Subtract the rhs of the differential equation from the lhs and sub in the parameters
check = subs(lhs(eqn) - rhs(eqn),[m,k,F,omega],[mval,kval,Fval,omegaval]);
Sub in the expressions for u(t) and c(t), note we could just set u(t) = real(u(t)) and z(t) = real(z(t)).
check = subs(check);
Check the result at some points in time, the answer should be zero
vpa(subs(check,t,0:10)).'
ans = 
  5 comentarios
Paul
Paul hace alrededor de 10 horas
Editada: Paul hace alrededor de 9 horas
Here is a solution using the fixed-step RK4 method. At each minor step (evaluation of ki), the derivative function stores and uses only those values of udot that are evaluated at previous major steps of the solution and the current value of udot at each minor step. The convolution in the derivative calculation is computed with integral (admittedly might be overkill) and assumes linear interpolation in the udot variable.
Still don't see how this can adapted to the stock ode solvers (i.e. ode45, et. al.)
m = 1;
k = 2;
F = 3;
wF = 4;
a = 1; % c = exp(-a*t)
h = 1e-2;
tspan = 0:h:20;
u = nan(numel(tspan),2);
icond = [5,6];
deriv = @(t,u,rk) fun(t,u,m,k,a,F,wF,rk);
u(1,:) = icond;
for ii = 2:numel(tspan)
n = ii - 1;
tn = tspan(n);
un = u(n,:);
k1 = deriv(tn ,un ,1);
k2 = deriv(tn+h/2,un + h*k1/2,2);
k3 = deriv(tn+h/2,un + h*k2/2,3);
k4 = deriv(tn+h ,un + h*k3 ,4);
u(ii,:) = un + h/6*(k1 + 2*k2 + 2*k3 + k4);
end
% Clear the persistent variables for subsequent run.
% Or take other action inside fun to initialize on first call.
clear fun
figure
plot(tspan,u),grid
function udot = fun(t,u,m,k,a,F,wf,rk)
persistent thist udothist
if rk == 1
thist = [thist, t];
udothist = [udothist, u(2)];
end
c = @(t) exp(-a*t);
if rk == 1
udotfun = @(tt) interp1(thist,udothist,tt);
else
udotfun = @(tt) interp1([thist,t],[udothist,u(2)],tt);
end
if t == 0
z = 0;
else
z = integral(@(tau) c(t-tau).*udotfun(tau),0,t);
end
udot(1) = u(2);
udot(2) = (F*cos(wf*t) - k*u(1) - z)/m;
end
Paul
Paul hace alrededor de 1 hora
Solution using ode45 with an OutputFcn as insightfully suggested by @Torsten, a 2-element tspan, and the 2-argument output form. In this formulation, the elements of udot are stored in the derivative function at time steps determined by Refine (default is Refine = 4 for ode45).
If tspan is specified with more than two elements (all else the same) then the points in tspan need to be spaced close enough together (whatever that means) to ensure an accurate solution because the values of udot will be stored in the derivative function only at the values in tspan.
Futher investigation needed if using the single output form of ode45, in which case Refine "does not apply," which I assume means it's effectively Refine = 1 as far as the OutputFcn is concerned.
The code could be improved by using a preallocation-fill scheme for the persistent variables instead of growing them dynamically.
m = 1;
k = 2;
F = 3;
w = 4;
a = 1; % c = exp(-a*t)
c = @(t) exp(-a*t);
icond = [5;6];
tspan = [0,20];
[t,x] = ode45(@(t,x) fun(t,x,m,c,k,F,w), tspan, icond,odeset('OutputFcn',@OutputFcn,'OutputSel',2));
figure
plot(t,x(:,1))
[y,ty] = impulse(tf([5,11,94,179,176],conv([1,0,16],[1,1,3,2])),tspan(2));
hold on
plot(ty,y)
legend('ode45','impulse')
function xdot = fun(t,x,m,c,k,F,w,flag,tsol,udotsol)
persistent thist udothist
if nargin == 10
if strcmp(flag,'init')
thist = tsol(1);
udothist = udotsol;
elseif isempty(flag)
thist = [thist,tsol];
udothist = [udothist,udotsol];
elseif strcmp(flag,'done')
thist = [];
udothist = [];
end
return
end
if numel(thist) <= 1
z = 0;
else
udotfun = @(tt) interp1([thist,t],[udothist,x(2)],tt);
z = integral(@(tau) c(t-tau).*udotfun(tau),0,t);
end
xdot = zeros(2,1);
xdot(1) = x(2);
xdot(2) = (F*cos(w*t) - k*x(1) - z)/m;
end
function status = OutputFcn(tsol,udotsol,flag)
status = 0;
fun([],[],[],[],[],[],[],flag,tsol,udotsol)
end

Iniciar sesión para comentar.


David Goodmanson
David Goodmanson hace alrededor de 14 horas
Editada: David Goodmanson hace alrededor de 13 horas
Hi Paul, Torsten et. al.
The code below saves the history of udot in ode45 and then does the convolution. I used delt = 1e-2 as Matt did and the plot is pretty similar. Taking delt down to 1e-4 does not change things very much.
A more accurate method might be available and I will add it to this answer if I can get it to work.
One thing I don't like about the ode solvers is that for e.g a second order ode you get back y and y' but not y'' which you have calculated at great expense in the odefun but can't get to. With this method you could save up the calculated y'' values. The code below just used the saved stuff internally, so accss wasn't a problem. But to get the y'' vales out I can't think of a way other than using a global variable, which is not great, but it would be possible to have a wrapper function that contains the global and then passes the values out from there, so you wouln't have to have a global mucking up the command window.
m = 1;
k = 2;
F = 3;
wF = 4;
a = 1; % c = exp(-a*t)
delt = 1e-2; % step size for the convolution integration
tspan = [0 20];
icond = [5;6];
[t u] = ode45(@(t,u) fun(t,u,m,k,a,delt,F,wF,tspan,icond), tspan, icond);
figure(3); grid on
plot(t,u)
function udot = fun(t,u,m,k,a,delt,F,wF,tspan,icond)
persistent udotstore
persistent tstore
if t == tspan(1)
udotstore = icond(2);
tstore = tspan(1);
G = 0;
else
udotstore = [udotstore u(2)];
tstore = [tstore t];
% sort the times, toss out near-duplcate times
[t1 ind] = sort(tstore);
udot1 = udotstore(ind);
fin = find(diff(t1)<1e-7); % judgment call here
t1(fin) = [];
udot1(fin) = [];
% set up equally spaced time array, do the convolution G
t2 = t1(1):delt:t1(end);
udot2 = interp1(t1,udot1,t2);
c = exp(-a*t2);
G = trapz(t2,flip(c).*(udot2));
end
udot = [0 1; (-k/m) 0]*u -(G/m)*[0;1] + (F/m)*cos(wF*t)*[0;1];
end
  4 comentarios
Torsten
Torsten hace alrededor de 10 horas
Editada: Torsten hace alrededor de 10 horas
As said: not the ODE function, but an OutputFcn function should be used to store the values for u.
OutputFcn is called for increasing values of t and only after a basic integration steps has successfully finished (thus when u really contains the solution of the ODE at time t).
Paul
Paul hace alrededor de 2 horas
Editada: Paul hace alrededor de 2 horas
I didn't see this comment before I prepared and submitted mine. I see that we both have the same fundamental concern (I think).
I did not think of using an OutputFcn.
I think the derivative function can include additional arguments, which won't be used by the ode solver, but can be used in a call from the OutputFcn to pass data from the OutputFcn to the derivative function for the derivative function to store persistently. Don't see why that wouldn't work.
Or the state data could be stored persistently in the OutputFcn and it could be called from the derivative function with an optional second argument and specific value of flag or a fourth argument to return the stored state data. I think that should work too.

Iniciar sesión para comentar.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by