np.angle 返回的相位不准确

信息处理 fft 离散信号 信号分析 Python 阶段
2022-02-10 11:14:19
  • 我正在生成 2 个正弦波,第一个具有基频 = 50 Hz,幅度 = 10,相位 = 0,第二个具有基频 = 100 Hz,幅度 = 5 和相位 = np.pi/6(即 30 度) .
  • 然后我将它们相加,并对添加的信号执行 FFT。
  • 我使用 np.abs() 计算 50 Hz 和 100 Hz 的信号幅度
  • 我分别使用 np.angle() 计算 50 Hz 和 100 Hz 的信号相位
  • 这是我得到的结果

“幅度_50赫兹”:9.997827675356993,“相位50赫兹”:-89.0677734968239,“幅度_150赫兹”:4.990392258900833,“相位150赫兹”:-57.231981462145704,


返回的幅度分别非常接近 10 和 5。但相位不是 0 和 30 度。

我也尝试了其他方法,例如 math.atan2 和 cmath.phase,它提供了类似的结果。

我想了解我的相位计算出了什么问题。我的代码如下。

def sine_wave(amplitude1: Union[int, float], amplitude2: Union[int, float], phase1: float, phase2: float,
          duration: Union[int, float],fund_freq_1: int, fund_freq_2: int, samp_freq: int) -> dict:


# generating the time domain signal

t = np.linspace(0, duration, int(samp_freq * duration))
wave1 = amplitude1 * np.sin((2 * np.pi * fund_freq_1 * t)+phase1)
wave2 = amplitude2 * np.sin((2 * np.pi * fund_freq_2 * t)+phase2)
combined_wave = np.add(wave1, wave2)
N = combined_wave.size
T = 1/samp_freq

# DFT
f = np.fft.fftfreq(N, 1 / samp_freq)
fft = np.fft.fft(combined_wave)

index_one = np.where(np.isclose(f, fund_freq_1))
magnitude_one = np.mean(np.abs(fft[index_one]) * (2 / N))
phase_one = degrees(np.angle(fft[index_one]))
# phase_one = atan2(fft[index_one].imag, fft[index_one].real)
# phase_one = degrees(phase(fft[index_one]))

index_two = np.where(np.isclose(f, fund_freq_2))
magnitude_two = np.mean(np.abs(fft[index_two]) * (2 / N))
phase_two = degrees(np.angle(fft[index_two]))
# phase_two = atan2(fft[index_two].imag, fft[index_one].real)
# phase_two = degrees(phase(fft[index_two]))

return {'magnitude_{} Hz'.format(fund_freq_1): magnitude_one,
        'phase_{} HZ'.format(fund_freq_1): phase_one,
        'magnitude_{} Hz'.format(fund_freq_2): magnitude_two,
        'phase_{} HZ'.format(fund_freq_2): phase_two}

代码可以这样运行

sine_wave(amplitude1=10, amplitude2=5, phase1=0, phase2=np.pi/6, duration=0.1, fund_freq_1=50, fund_freq_2=150, samp_freq=10000)

还绘制了幅度和相位 在此处输入图像描述

100 Hz 时的幅度

50 Hz 时的幅度

进一步的怀疑

与我昨天讨论的类似,我正在解决工作中的另一个问题。

为了解决这个问题,我正在创建测试正弦波(N=2000 个样本或数据点)并对这些正弦波执行相同的过程。

*** 第一波 = 基频 = 50,采样频率 = 10000,持续时间 = 0.2,幅度 = 10,相位 = 0 度

  • 第二波 = 基频 = 50,采样频率 = 10000,持续时间 = 0.2,幅度 = 10,相位 = 120 度
  • 第三波 = 基频 = 50,采样频率 = 10000,持续时间 = 0.2,幅度 = 10,相位 = 240 度**

我使用此功能生成波浪。

def generate_test(amplitude1: Union[int, float], amplitude2: Union[int, float], amplitude3: Union[int, float],
                  phase1: float, phase2: float, phase3: float, duration: Union[int, float], fund_freq_1: int,
                  fund_freq_2: int, fund_freq_3: int, samp_freq: int) -> tuple:
    """
    :param amplitude1: Amplitude of the first sine wave
    :param amplitude2: Amplitude of the second sine wave
    :param amplitude3: Amplitude of the third sine wave
    :param phase1: Phase of the first sine wave in radians
    :param phase2: Phase of the second sine wave in radians
    :param phase3: Phase of the third sine wave in radians
    :param duration: Duration of each sine waves
    :param fund_freq_1: Fundamental frequency the first sine wave
    :param fund_freq_2: Fundamental frequency the second sine wave
    :param fund_freq_3: Fundamental frequency the third sine wave
    :param samp_freq: Sampling frequency of each sine waves
    :return: 3 sines waves
    :rtype: tuple of 3 numpy arrays
    """
    t = np.arange(0, duration * samp_freq) / samp_freq
    wave1 = amplitude1 * np.sin((2 * np.pi * fund_freq_1 * t) + phase1)
    wave2 = amplitude2 * np.sin((2 * np.pi * fund_freq_2 * t) + phase2)
    wave3 = amplitude3 * np.sin((2 * np.pi * fund_freq_3 * t) + phase3)

    return wave1, wave2, wave3

主要任务:

1. Generate 3 sine waves using the 'generate_test' function. Each with 120 degrees phase shift
    * first wave = fundamental frequency = 50, sampling frequency = 10000, duration = 0.2,  amplitude = 10, phase = 0 degrees
    * second wave = fundamental frequency = 50, sampling frequency = 10000, duration = 0.2, amplitude = 10, phase = 120 degrees
    * third wave = fundamental frequency = 50, sampling frequency = 10000, duration = 0.2, amplitude = 10, phase = 240 degrees 
2. I divide each wave into exactly two halves, let's call these halves as the before half and after half. (2000 samples or data points divided into two signals with 1000 data points each)
2. From each of the halves I drop the first and last 50 data points (50 (first fifty) + 50 (last fifty) = 100)
3. Now i have 3 before halves and 3 after halves
4. From each of the 3 before and 3 after halves I need to calculate the amplitude and phase at 50 Hz using FFT .
5. Calculate FFT
6. Find indices corresponding to 50 Hz. I get 2 indices here, because the frequency bins are not centered at 50 Hz
7. Use np.abs to calculate the absolute value of the FFT component then take a mean of it
8. Use np.angle to calculate the phase of the FFT component, convert it to degrees and then take the mean of it. 

代码如下

def test_amplitude_phase_algo(amplitude1: Union[int, float], amplitude2: Union[int, float],
                              amplitude3: Union[int, float],
                              phase1: float, phase2: float, phase3: float, duration: Union[int, float],
                              fund_freq_1: int,
                              fund_freq_2: int, fund_freq_3: int, samp_freq: int):

    test_data = plot_and_return(amplitude1, amplitude2, amplitude3, phase1, phase2, phase3, duration, fund_freq_1,
                                fund_freq_2, fund_freq_3, samp_freq)
    T = 1 / samp_freq
    result_df = pd.DataFrame()

    for col in test_data.columns:
        prevail = test_data[col][50:950]
        trail = test_data[col][1050:1950]

        N = prevail.size

        # Prevailing samples
        prevail_frequencies = np.fft.fftfreq(N, 1 / samp_freq)
        prevail_fft = np.fft.fft(prevail)
        prevail_index = np.where(np.isclose(prevail_frequencies, 50, atol=1 / (T * N)))

        prevail_amplitude = np.mean(np.abs(prevail_fft[prevail_index]) * (2 / N))
        prevail_phase = degrees(np.mean(np.angle(prevail_fft[prevail_index])))

        # Trailing samples
        trailing_frequencies = np.fft.fftfreq(N, 1 / samp_freq)
        trailing_fft = np.fft.fft(trail)
        trail_index = list(np.where(np.isclose(trailing_frequencies, 50, atol=1 / (T * N)))[0])

        trail_amplitude = np.mean(np.abs(trailing_fft[trail_index]) * (2 / N))
        trail_phase = degrees(np.mean(np.angle(trailing_fft[trail_index])))

        result_array = np.array((prevail_amplitude, trail_amplitude, prevail_phase, trail_phase))
        temp_df = pd.DataFrame(result_array).T
        result_df = result_df.append(temp_df)

    i0 = result_df.mean(axis=0)
    result_df = result_df.append(i0, ignore_index=True)
    result_df.columns = ['amplitude_before', 'amplitude_after', 'phase_before', 'phase_after']
    result_df.index = ['w1', 'w2', 'w3', 'avg_w']

    return result_df

这是我得到的答案:

    amplitude_before  amplitude_after  phase_before  phase_after
w1          6.346514         6.346514     -0.011246    -0.011246
w2          6.383717         6.383717    -60.070936   -60.070936
w3          6.383184         6.383184     60.082026    60.082026
avg_w       6.371139         6.371139     -0.000052    -0.000052

我的问题仅涉及信号的测量相位(我将在另一个问题中讨论幅度)。

正如昨天的答案中提到的,我的角度应该偏离-90度。如果我在合成角度上加上 90 度,那么我的角度将是

    phase_before  phase_after
w1      ~90             ~90
w2      ~30             ~30
W3      ~150            ~150
avg_w   ~90             ~90

阶段应该是

    phase_before  phase_after
w1      ~0              ~0
w2      ~120            ~120
W3      ~240            ~240
avg_w   ~120            ~120
2个回答

有几件事正在发生:

  • 频率的复数表示使得实部对应于余弦分量,虚部对应于正弦分量。因此,0 的复相位对应于余弦波,而不是正弦波。这就是为什么根据三角恒等式 sin(x) = cos(x − π/2),计算的相位与您预期的相差约 90 度。低频波的相位应为 -90 度,高频波的相位应为 30 - 90 = -60 度。

  • 时间t = np.linspace(0, duration, int(samp_freq * duration))并没有以预期的采样率精确间隔。这可以解释不准确的原因。生成时间的更好方法是类似np.arange(N) / samp_freq.

  • 就像 Cris 建议的那样,在光谱分析中通常应用一个 窗口函数是一个好主意。

您似乎在窗口开始附近的计算阶段,其中可能存在圆形不连续性。有(至少)两种方法可以解决这个问题。

一种是使用窗口和 FFT 或 DFT 长度,它们是任何正弦输入周期的精确整数倍。

第二种方法是通过 FFTShift 从数据窗口的中间测量相位。您稍后可以使用信号窗口中心的频率和相位估计来计算原始输入信号中任何其他所需位置的相位。

为了使合成或示例输入数据变得容易,您可能希望在所需相位从信号窗口的中间开始生成正弦曲线,并在两端工作。然后在 FFT 之前进行 fftshift 以将生成的 FFT 相位重新参考到该中间点。