简体   繁体   English

使用scipy fft和ifft以数字方式求解常微分方程

[英]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 for integration. 另一个问题是除以进行积分时除以零。 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个问题。

  1. You aren't scaling the frequency variable w by fs . 您没有按fs缩放频率变量w
  2. You use fftfreq , which uses the range -0.5 to 0.5. 您使用fftfreq ,它使用范围-0.5到0.5。 You need -π to π. 你需要-π到π。 Actually you only need 0 to π because i(t) is real-valued. 实际上你只需要0到π,因为i(t)是实值的。 In this case you can use rfft and irfft for real-valued signals, which skips computing the negative frequencies. 在这种情况下,您可以使用rfftirfft作为实值信号,跳过计算负频率。

All that said, you may still be disappointed with the result because the DFT uses the periodic extension of your signal. 所有这些,你可能仍然对结果感到失望,因为DFT使用信号的周期性扩展。

Examples 例子

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()

DT0

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()

DT1

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; gC设置为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()

DT2

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.

相关问题 使用SciPy解常微分方程 - Solve ordinary differential equations using SciPy 如何使用scipy odeint求解该微分方程? - How to solve this differential equation using scipy odeint? 使用scipy解决python中的微分方程 - solve differential equation in python using scipy 如何使用scipy.integrate的odeint积分常微分方程? - How can I integrate the Ordinary differential equation using odeint from scipy.integrate? Python:如何解决其中带有积分项的常微分方程 - Python: How to solve an ordinary differential equation with integral term in it 如何使用Theano求解常微分方程? - How do I use Theano to solve an ordinary differential equation? 在python(odeint)中求解具有时间相关系数的常微分方程 - solve ordinary differential equation with time dependent coefficients in python (odeint) 我试图在python中解决基于延迟微分方程和常微分方程的模型,但遇到了几个错误 - I tried to solve delayed differential equation and ordinary differential equation based model in python, but encountered several errors python代码求解以下初值问题常微分方程在区间上使用欧拉法(o 10) - python code to solve the following initial value problem ordinary differential equation using Euler method over the interval (o 10) 常微分方程指标误差 - Ordinary differential equation index error
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM