convolution integral with ode45

114 visualizzazioni (ultimi 30 giorni)
Angie
Angie il 8 Giu 2019
Modificato: Paul circa 6 ore fa
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!

Risposta accettata

Star Strider
Star Strider il 8 Giu 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 Commenti
JINGYU KANG
JINGYU KANG il 12 Set 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 il 11 Dic 2025 alle 1:16
Modificato: David Goodmanson circa 23 ore fa
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.

Accedi per commentare.

Più risposte (2)

Paul
Paul il 7 Dic 2025 alle 3:05
Modificato: Paul il 11 Dic 2025 alle 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 = 
  4 Commenti
Paul
Paul il 12 Dic 2025 alle 18:13
Modificato: Paul circa 7 ore fa
yes, trapz would be another way to compute the convolution integral. I suppose one could even use integral.
No, I can't think of way to use ode45 (et. al.) (I've tried). I just don't see a way around the need to properly maintain the history of xdot(t) in those solvers. Would be very interested if anyone else can.
Paul
Paul circa 7 ore fa
Modificato: Paul circa 6 ore fa
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

Accedi per commentare.


David Goodmanson
David Goodmanson circa 11 ore fa
Modificato: David Goodmanson circa 11 ore fa
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
  3 Commenti
Paul
Paul circa 8 ore fa
Modificato: Paul circa 7 ore fa
Hi David,
"I used delt = 1e-2 as Matt did ..."
Was there another solution provided by someone named Matt? I don't see it in this thread.
I'm afraid I have an issue with this approach at a fundamental level, even though the result seems to pass the eye test for the example problem.
As I undersand it, the code is storing all values of u(2) ( = udot) on every derivative evaluation by the ode45 solver and treating each of those values (as long as they are separated by 1e-7) as actual points in the solution of the underlying ode. However, they are not. As I know that you know, these solvers make a lot of calls to the derivative function for the full state vector and then the outputs of the derivative function are combined to form the full step of the state. At most, only one of those derivative evaluations is actually evaluated at a valid point in the solution of the ode, e.g., the K1 term in the classical fixed step RK4 algorithm.
To put it more concretely, consider the K2 and K3 terms in the RK4 algorithms. They are both evaluated at the same time, but they will have different values (neither of which are an actual point in the solution). Which one would should be used in the convolution?
And that's just for a fixed step solver. I'm sure this issue becomes more convoluted (sorry, couldn't resist) with these adaptive solvers.
To illustrate, I changed the model to a standard, second order model with c = 0.1, but used your code to store the udot history in the derivative function and then plot it at the end and show the final value of udot1. We see that:
@fun is called twice with t = tspan(end)
The values of udot1 at those times are not the same; so at least one of them is incorrect (as compared the actual solution).
The stored history values of udot in @fun do not match the actual solution output from ode45. The stored values have little bits and bobs that deviate from the actual solution.
In the problem as originally posed, we don't really know what happens because the effect that causes these deviations in this fixed-coefficient problem actually influences the solution in the original problem as posed. I'm not sure I've stated that clearly, hopefully the gist is clear.
The issues that I've raised and illustrated here are what I was refering to when I said " .. properly maintain the history of xdot(t)." (emphasis added). At least my opinion of "properly".
Having said all of this, and giving it a bit more thought, I think there may be a path forward for a fixed-step, multi-step solver, such as RK4, that assuages my concerns. I might come back later with an update to my answer. I believe the approach I'm thinking of could be used if one were to write one's own adaptive step solver, but I don't see how it could be used with the built-in ode suite of functions.
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);
ans = "udot1 at tspan = 20 is -2.9486"
ans = "udot1 at tspan = 20 is -3.0678"
["u(2) at tspan = " + tspan(end) " is: " + u(end,2)]
ans = 1×2 string array
"u(2) at tspan = 20" " is: -3.0678"
hold on
plot(t,u(:,2))
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];
c = 0.1;
udot = [0 1; (-k/m) -c/m]*u + (F/m)*cos(wF*t)*[0;1];
if t == tspan(end)
figure
plot(t1,udot1)
["udot1 at tspan = " + tspan(end) + " is " + udot1(end)]
end
end
Torsten
Torsten circa 8 ore fa
Modificato: Torsten circa 8 ore fa
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).

Accedi per commentare.

Community Treasure Hunt

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

Start Hunting!

Translated by