具有可变仿真时间步长的电路仿真器。之后如何获得FFT?

信息处理 fft Python
2022-01-31 08:08:10

我知道我不是第一个遇到这个问题的人,但我没有找到正确的答案,所以我希望你能帮助我。

在一个电路模拟器中,我在时域中模拟了我的电路,并将一个信号保存在一个 .txt 文件中(其中包含时间和信号值)。基于该 .txt 文件,我想生成一个 FFT 以查看频域中的信号。

不幸的是,电路模拟器的时间步长可变,这让我很难获得正确的 FFT 值(此时双面或单面是第二优先级)

我设法从我的数据中得到了一些 FFT,但我在验证它的正确性时遇到了困难,因此感谢任何关于此事的帮助。我的模拟文件给了我不同的平均值和 RMS 值,因此造成了混乱。谢谢你。下面给出了我的 .txt 文件的链接(因为我在这里找不到直接上传 txt 文件的方法): https ://file.io/PXuD9sWR

我的 Python 代码:

import numpy as np
from scipy.fftpack import fft
import matplotlib.pyplot as plt
import pandas as pd


buckstruct = pd.read_csv('buck.txt', sep = '\\t', engine='python')
bucktime = buckstruct.iloc[:,0]
bucktime = bucktime.values.tolist()
buckcurrent = buckstruct.iloc[:,1]
buckcurrent = buckcurrent.values.tolist()
bucktime_flt = []
buckcurrent_flt = []

# Filter out the start-up transient. Save only steady state values
for i in range(len(bucktime)):
    if bucktime[i] > 0.002:
        bucktime_flt.append(bucktime[i])
        buckcurrent_flt.append(buckcurrent[i])

plt.plot(bucktime_flt, buckcurrent_flt)
buckfft = fft(buckcurrent_flt)
buckfft_flt = []

# Double the amplitude for harmonics as a first step to converter from two-sided to single-sided FFT
for i in range(len(buckfft)):
    if i == 0:
        buckfft_flt.append(1.0/len(bucktime_flt)*abs(buckfft[i]))
    else:
        buckfft_flt.append(2.0/len(bucktime_flt)*abs(buckfft[i]))

plt.plot(buckfft_flt) 

我的电路模拟器的模拟结果

3个回答

谢谢您的答复。我注册了这个网站,所以不确定我的名字是否相同,但我是 OP。我不太确定我是否完全理解重采样和插值如何帮助我的解决方案。如果我下采样(假设每 100 个样本),我将有一个小 100 倍的 Dataframe 大小,但元素不是均匀分离的,即两个样本之间的采样时间不是恒定的。

如果我之后上采样以在我的 Datafra 中创建空条目,我会得到我的原始 Dataframe 大小。到现在为止还挺好。但是在我的情况下,插值函数似乎不起作用。我是 Pandas 的新手,所以请耐心等待 :)

import math
import numpy as np
from scipy.fftpack import fft
import matplotlib.pyplot as plt
import pandas as pd


buckstruct = pd.read_csv('buck.txt', sep = '\\t', engine='python')
buckstruct.index = pd.to_datetime(buckstruct.index, unit='s')
buckstruct_downsampled = buckstruct.resample('100s').sum()
buckstruct_upsampled = buckstruct_downsampled.resample('10s').sum()
buckstruct_interpolated = buckstruct_upsampled.interpolate()

在此处输入图像描述

EDIT1:以前的贡献合并到这个答案中:

感谢您的回复。是的,它是 LTSpice,感谢您提供的工具。我会看看它。虽然在 Python 中也有一个解决方案会很好。

让我反过来问这个问题,因为我不是 FFT 专家。毕竟,也许不需要这种解决方法。

鉴于我的 .txt 文件中的 RAW 数据样本,即未重新采样、插值或其他任何方式,并且通过简单地运行 FFT,我收到以下 FFT 结果: 在此处输入图像描述

所以,我在 0(x 轴)处有大约 1.75(y 轴)的东西,在 300(x 轴)处有大约 0.5(y 轴)的东西。

300 不是频率,因为我的电子电路的频率是 100kHz。所以我需要找到一种方法将图中的 300 映射到 100kHz 以匹配我的模拟。如果采样率恒定,这相当容易。但是,如果 .txt 中的采样率不均匀,也有可能吗?

谢谢,

EDIT2:问题的解决方案:将非统一数据集转换为统一数据集。

这可以使用 Scipy 的插值函数来实现。下面是一个不需要初始数据集的最小工作示例,但使用来自初始数据集的 10 个连续数据样本。可以看出,采样时间不是恒定的,这使得执行 FFT 和提取频率变得困难。使用插值函数,可以实现恒定的采样时间,生成的数据集可用于 FFT 计算。该过程是首先获得插值函数,然后对于给定的数据长度执行实际插值并将插值数据集用于FFT目的。我希望这个最小的工作示例对其他人有用。谢谢,

在此处输入图像描述

# Minimum working example to show the effectiveness of the interpolation function

import matplotlib.pyplot as plt
from scipy import interpolate
import numpy as np

time_raw = [0.0009999650511607746,
 0.000999965566641282,
 0.0009999660821217893,
 0.0009999672947956213,
 0.0009999682836389868,
 0.0009999696068367931,
 0.0009999715724226046,
 0.000999978044536965,
 0.0009999994033983994,
 0.001]

signal_raw = [1.132772,
 1.132642,
 1.132511,
 1.132205,
 1.1319549999999998,
 1.13162,
 1.131124,
 1.1294879999999998,
 1.124091,
 1.12394]

fxxx = interpolate.interp1d(time_raw, signal_raw)

num = len(time_raw)
xx = np.linspace(time_raw[0], time_raw[-1], num)
yy = fxxx(xx)

plt.figure(1)
plt.plot(time_raw, signal_raw,'bo-', label='Original')
plt.plot(xx,yy,'g.-', label='Interpolated')
plt.ylim([1.120, 1.135])
plt.legend();

然后,使用模拟的实际数据集对初始问题产生的 FFT 与预期的一样。我为这个凌乱的线程道歉。 在此处输入图像描述

看起来您正在使用 LTspice。如果是这样,在LTspice 组中,您会找到一个小的免费实用程序 ,ltsputil它完全可以满足您的需求,也许更多。如果您在理解它的工作原理时遇到问题,小组中也有关于它的用法的问题,尽管它应该很容易。


首先,这个小命令行实用程序可以发挥作用,因此如果您阅读帮助文件ltsputil_help.txt. 在该文件中,搜索Export data from raw file,您将进入相关部分。test.raw下面是一个由 LTspiceXVII 保存在 中的文件的示例用法.TRAN,并且只保存了一个变量V(out)

  1. 第一次运行ltsputil17raw4.exe test.raw tmp.raw这会将 XVII 风格的.raw数据转换为 IV 风格(ltsputil为 LTspiceIV 编写的)。显然,如果.raw文件已经用 LTspiceIV 保存,则不需要此步骤。

  2. 然后通过运行应用相等的间距'ltsputil.exe -eo tmp.raw out.raw 131072 ""这会e限定时间步长,而o重写输出文件(如果存在)。因为test.raw有 145746 点,为了举例说明,我选择了下一个较低的 2 次方,即使 Octave 可以fft很好地处理这个点数。

  3. 运行ltsputil.exe -xo0 out.raw data.txt "%14.6e" "," "" 0 1这将x提取数据,o重写输出(如果存在),并写入0仅 SPICE 数据。其他命令行参数用于数字格式、分隔以及要保存的跟踪。默认情况下,为time变量 (in .TRAN) 或freq(in .AC) 保存第一列,因此01最后表示保存第一列(0, time) 和实际数据,第 2 列 (1, V(out))。

可能有一些剩余.tmp文件,可以安全删除。作为比较,从 LTspice as 导出数据rawdata.txt将保留变量时间步长及其导数(右),与结果(左)相比,如下所示:

补偿

它不是完全线性的,但肯定不是原始的。厚度来自插值发生的小凸起。

至于频率,你必须事先知道总的仿真时间。对于这种情况,它是t=800μs,以及选择的点数N=131072. 这些给出了较低的频率fmin=1t=1.25kHz和上限频率fmax=N2t=81.92MHz. 有了这些,您可以生成适当的linspace()or logspace()

但是,关于这部分,我不太确定,但很可能我做错了,因为使用f = linspace(1250, 81920000, 65536)(一半的点数仅绘制 的前半部分fft)不能正确对齐峰。

由于 OP 已经在 Python 中使用 Pandas,Pandas 支持变量重采样;在此处和 pandas 的重新采样文档中有更多详细信息

https://machinelearningmastery.com/resample-interpolate-time-series-data-python/

对于 OP 的目的,最直接的方法是重新采样到固定速率,然后从该数据中使用标准 FFT 算法来计算 DFT。