[英]Using scipy fft and ifft to solve ordinary differential equation numerically
I have an ordinary differential equation
in time domain as follows: 我在时域中有一个
ordinary differential equation
如下:
C*du/dt = -g*u + I
where I = A*t/tau*exp^(1-t/tau)
其中
I = A*t/tau*exp^(1-t/tau)
in the freq domain: 在freq域中:
u(w) = I(w)/(g*(1+C/g*j*w))
j
being the complex number sqrt(-1)
j
是复数sqrt(-1)
hence i can get u(t)
by going into the freq domain using fast Fourier transform ( fft
) and then back using ifft
. 因此我可以通过使用快速傅里叶变换 (
fft
)进入频率域然后使用ifft
返回来获得u(t)
。
the codes: 代码:
t = np.linspace(0.,499.9,5000)
I = q*t*np.exp(1-t/Tau_ca)/Tau_ca
u1 = np.fft.ifft(np.fft.fft(I)/(G_l*(1.+1.j*(C_m/G_l)*np.fft.fftfreq(t.shape[-1]))))
However, when I compare this u(t)
obtained with other methods such as numerical integration of the differential equation or its analytical form, it is not correct. 然而,当我将这个
u(t)
与其他方法(如微分方程的数值积分或其分析形式)进行比较时,它是不正确的。 I have tried and been unsuccessful at figuring out where my mistakes are. 我已经尝试过并且未能成功找出我的错误所在。
please enlighten. 请指教。
The derivative of a sinusoid, or complex exponential, is directly proportional to its frequency, and phase shifted by π/2
. 正弦曲线的导数或复指数与其频率成正比,相移
π/2
。 For a complex exponential the phase shift is equivalent to multiplying by j
. 对于复指数,相移相当于乘以
j
。 For example, d/dt exp(j*Ω*t)
== j*Ω * exp(j*Ω*t)
== Ω * exp(j*π/2) * exp(j*Ω*t)
== Ω * exp(j*(Ω*t + π/2))
. 例如,
d/dt exp(j*Ω*t)
== j*Ω * exp(j*Ω*t)
== Ω * exp(j*π/2) * exp(j*Ω*t)
== Ω * exp(j*(Ω*t + π/2))
。 So if you have the Fourier transform pair u(t) <=> U(Ω)
, then du/dt <=> jΩ * U(Ω)
. 因此,如果你有傅立叶变换对
u(t) <=> U(Ω)
,那么du/dt <=> jΩ * U(Ω)
。 Integration is pretty much the inverse operation, but it may add an impulse if there's a DC component: ∫udt <=> U(Ω) / jΩ + π*U(0)*δ(Ω)
积分几乎是反向操作,但如果存在直流分量,它可能会增加一个脉冲:
∫udt <=> U(Ω) / jΩ + π*U(0)*δ(Ω)
To approximate the derivative using a sampled sequence, you have to scale the discrete-time angular frequency ω (which ranges from 0 to 2π, or -π to π) by the sample rate, fs
. 要使用采样序列逼近导数,必须通过采样率
fs
缩放离散时间角频率ω(范围从0到2π,或-π到π)。 For example, say the discrete-time frequency is π/2 radians/sample, such as the sequence [1, 0, -1, 0, ...]
. 例如,假设离散时间频率是π/ 2弧度/样本,例如序列
[1, 0, -1, 0, ...]
。 In the original signal this corresponds to fs/4
. 在原始信号中,这对应于
fs/4
。 The derivative is d/dt cos(2*π*fs/4*t) == d/dt cos(π/2*fs*t)
== -π/2*fs*sin(π/2*fs*t)
== π/2*fs*cos(π/2*fs*t + π/2)
. 导数是
d/dt cos(2*π*fs/4*t) == d/dt cos(π/2*fs*t)
== -π/2*fs*sin(π/2*fs*t)
== π/2*fs*cos(π/2*fs*t + π/2)
。
You have to sample at an fs
that's more than twice the bandwidth of the signal. 你必须在采样
fs
这就是信号的两倍以上的带宽。 The sampled component at exactly fs/2
is unreliable. 精确
fs/2
的采样分量是不可靠的。 Specifically, with only 2 samples per cycle the amplitude of the fs/2
component alternates the sign of the first sample in the sequence. 具体地,每循环仅有2个样本,
fs/2
分量的幅度与序列中第一个样本的符号交替。 So for a real-valued signal the fs/2
DFT component is real valued, with a phase of either 0 or π radians, ie a*cos(π*fs*t + {0, π})
. 因此对于实值信号,
fs/2
DFT分量是实值的,具有0或π弧度的相位,即a*cos(π*fs*t + {0, π})
。 Given the latter, the derivative of the fs/2
component is -a*π*fs*cos(π*fs*t + {π/2, 3*π/2})
, which is 0 for the sample times t == n/fs
. 给定后者,
fs/2
分量的导数是-a*π*fs*cos(π*fs*t + {π/2, 3*π/2})
,对于采样时间t == n/fs
。
(Regarding the latter, the standard trigonometric interpolation of the DFT uses a cosine, and in that case the derivative will be zero at the sample points. The isn't necessarily true if you sample the signal and its derivative simultaneously. Sampling loses the phase of the fs/2
component relative to the signal, but not relative to its derivative. Depending on the time you start sampling, both the fs/2
component and its derivative may be non-zero at the sample points. If by luck one of them is 0 at the sample times, the other will be at its peak, since they're π/2
radians apart.) (关于后者,DFT的标准三角插值使用余弦,在这种情况下,导数在采样点处将为零。如果您同时对信号及其导数进行采样,则不一定正确。采样失去相位
fs/2
分量相对于信号的影响,但不相对于它的导数。根据你开始采样的时间, fs/2
分量及其导数在样本点可能都不为零。它们在采样时间为0,另一个在峰值处,因为它们相距π/2
弧度。)
Given that the fs/2
DFT component is always real valued for a real-valued signal, when you multiply it by j
in the course of computing the derivative or integral, this introduces an imaginary-valued component in the result. 假设
fs/2
DFT分量对于实值信号总是实数值,当在计算导数或积分的过程中将它乘以j
时,会在结果中引入虚值分量。 There's a simple workaround. 有一个简单的解决方法。 If
N
is even, just zero out the fs/2
component at index N/2
. 如果
N
是偶数,则在索引N/2
处将fs/2
分量归零。 Another problem is division by zero when dividing by jΩ
for integration. 另一个问题是除以
jΩ
进行积分时除以零。 This can be solved by adding a small value to index 0 of the Ω
vector (eg finfo(float64).tiny
is the smallest double precision float). 这可以通过向
Ω
矢量的索引0添加一个小值来解决(例如finfo(float64).tiny
是最小的双精度浮点数)。
Given Ω = fs * ω
, the system shown in the question has the following form in the frequency-domain: 给定
Ω = fs * ω
,问题中显示的系统在频域中具有以下形式:
H(Ω) = 1 / (g + j*Ω*C)
U(Ω) = I(Ω) * H(Ω)
It's a single-pole low-pass filter. 这是一个单极点低通滤波器。 The solution you derived has 2 problems.
您派生的解决方案有2个问题。
w
by fs
. fs
缩放频率变量w
。 fftfreq
, which uses the range -0.5 to 0.5. fftfreq
,它使用范围-0.5到0.5。 You need -π to π. i(t)
is real-valued. i(t)
是实值的。 In this case you can use rfft
and irfft
for real-valued signals, which skips computing the negative frequencies. rfft
和irfft
作为实值信号,跳过计算负频率。 All that said, you may still be disappointed with the result because the DFT uses the periodic extension of your signal. 所有这些,你可能仍然对结果感到失望,因为DFT使用信号的周期性扩展。
First, here's a simple example of a 1 Hz sinusoid (plotted in red) sampled at 1024 samples/s for 2 seconds, and its derivative computed via the DFT (plotted in blue): 首先,这是一个简单的例子,1个正弦曲线(以红色绘制),以1024个样本/秒采样2秒,其衍生物通过DFT计算(以蓝色绘制):
from pylab import *
fs = 1024
t = arange(2*fs, dtype=float64) / fs
N = len(t) // 2 + 1 # non-negative frequencies
w = 2 * pi / len(t) * arange(N)
Omega = w * fs
x0 = cos(2*pi*t) # w0 == 2*pi/fs
X0 = rfft(x0);
# Zero the fs/2 component. It's zero here anyway.
X0[-1] = 0
dx0 = irfft(X0 * 1j*Omega)
plot(t, x0, 'r', t, dx0, 'b')
show()
This is an easy case -- a periodic signal with finite bandwidth. 这是一个简单的案例 - 带宽有限的周期信号。 Just make sure to sample an integer number of periods at a high enough rate to avoid aliasing.
只需确保以足够高的速率采样整数个周期以避免混叠。
The next example is a triangle wave, with a slope of 1 and -1, and a discontinuity in the derivative at the center and edges. 下一个例子是一个三角波,斜率为1和-1,以及中心和边缘的导数的不连续性。 Ideally, the derivative should be a square wave, but computing that perfectly would require infinite bandwidth.
理想情况下,导数应该是方波,但计算完全需要无限带宽。 Instead there's Gibbs ringing around the discontinuity:
相反, 吉布斯在不连续性周围响起 :
t2 = t[:fs]
m = len(t) // (2*len(t2))
x1 = hstack([t2, 1.0 - t2] * m)
X1 = rfft(x1)
X1[-1] = 0
dx1 = irfft(X1 * 1j*Omega)
plot(t, x1, 'r', t, dx1, 'b')
show()
The DFT's implicit periodic extension is problematic if you're solving a non-periodic system. 如果您正在解决非周期性系统,则DFT的隐式周期性扩展是有问题的。 Here's a solution to the system in question using both
odeint
and the DFT ( tau
is set to 0.5s; g
and C
are set for a 1 Hz corner frequency): 这是使用
odeint
和DFT的有问题系统的解决方案( tau
设置为0.5s; g
和C
设置为1 Hz转角频率):
from scipy.integrate import odeint
a = 1.0; tau = 0.5
g = 1.0; C = 1.0 / (2 * pi)
i = lambda t: a * t / tau * exp(1 - t / tau)
f = lambda u, t: (-g*u + i(t)) / C
H = 1 / (g + 1j*Omega*C) # system function
I = rfft(i(t))
I[-1] = 0
U_DFT = I * H
u_dft = irfft(U_DFT)
u_ode = odeint(f, u_dft[0], t)[:,0]
td = t[:-1] + 0.5/fs
subplot('221'); title('odeint u(t)');
plot(t, u_ode)
subplot('222'); title('DFT u(t)');
plot(t, u_dft)
subplot('223'); title('odeint du/dt')
plot(td, diff(u_ode)*fs, 'r',
t, (-g*u_ode + i(t)) / C, 'b')
subplot('224'); title('DFT du/dt')
plot(td, diff(u_dft)*fs, 'r',
t, (-g*u_dft + i(t)) / C, 'b')
show()
The du/dt
graphs overlay the derivative as estimated by diff
(red) versus the calculated value from the differential equation (blue). du/dt
图覆盖由diff
(红色)估计的导数与微分方程(蓝色)的计算值。 They're approximately equal in both cases. 在两种情况下,它们大致相等。 I set the initial value for
odeint
to u_dft[0]
in order to show that it returns the DFT solution for the same initial value. 我将
odeint
的初始值设置为u_dft[0]
,以显示它返回相同初始值的DFT解决方案。 The difference is that the odeint
solution would continue to decay to zero, but the DFT solution is periodic with a 2s period. 不同之处在于
odeint
解决方案将继续衰减为零,但DFT解决方案是周期性的2s周期。 The DFT result will look better in this case if more of i(t) is sampled, since i(t) starts at zero and decays to zero. 如果更多的i(t)被采样,则DFT结果在这种情况下看起来会更好,因为i(t)从零开始并衰减到零。
Zero padding is used with the DFT to perform linear convolution. 零填充与DFT一起使用以执行线性卷积。 Specifically in this case, zero padding of the input would help to separate the transient of the Zero State Response from its steady-state.
特别是在这种情况下,输入的零填充将有助于将零状态响应的瞬态与其稳态分离。 However, more commonly the Laplace or z-transform system functions are used to analyze the ZSR/ZIR.
然而,更常见的是拉普拉斯或z变换系统函数用于分析ZSR / ZIR。
scipy.signal
has tools to analyze LTI systems, including mapping continuous-time to discrete-time in polynomial, zero-pole-gain, and state-space forms. scipy.signal
具有分析LTI系统的工具,包括将连续时间映射到多项式,零极点增益和状态空间形式的离散时间。
John P. Boyd discusses a Chebyshev approximation method for non-periodic functions in Chebyshev and Fourier Spectral Methods (free online at his University of Michigan page). John P. Boyd讨论了Chebyshev 和Fourier光谱方法中非周期函数的Chebyshev近似方法 (在他的密歇根大学页面免费在线阅读)。
You'll probably get more help with a question such as this if you ask on the Signal Processing or Mathematics Stack Exchanges. 如果你在信号处理或数学堆栈交换中询问,你可能会得到更多关于这个问题的帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.