Python实现复数离散傅里叶变换制作本轮动画的正确方法
我最近在尝试做更有趣的Python项目,打算开发一个程序,读取存储x、y坐标的文本文件后生成傅里叶本轮动画。我已经实现了可根据给定圆心、半径、频率、相位绘制本轮的函数,数学部分的处理思路是对对应x-y坐标的复数列表做离散傅里叶变换。
初始代码实现
第一版DFT代码
def dft(vals): transformed=[] N=len(vals) for k in range(0,N): coeff=0 upper_freq=0 if k>N/2 : b=-k else: b=k for n in range(0,N): coeff+=(vals[n]*(np.e**(-2*np.pi*1j*k*n*1/N))) transformed.append([coeff,b]) return transformed
该代码输出数值对列表,第一个元素为系数,第二个为频率。
参数提取代码
随后我通过如下循环从列表中提取对应本轮的半径、相位、频率参数:
for item in weights: complex_no=item[0] rad=abs(complex_no)*2/(len(weights)) final_freq=item[1]/len(weights) phase=math.atan(complex_no.imag/complex_no.real) if final_freq != 0: rad_freq.append([rad,final_freq,phase])
问题表现
这段代码处理编程生成的正方形坐标时运行正常,但输入更复杂的路径坐标时,生成的本轮动画完全出错。因此我想咨询第一段代码是否是正确的复数DFT实现(我对负频率的处理部分尤其不确定),以及我从DFT输出中提取半径/相位/频率参数的方式是否正确?

上图:程序正确绘制的正方形
上图:预期路径
上图:实际输出路径
代码更新情况
我已经更新了DFT代码:
def dft_1(vals): transformed=[] coeff=0 N=len(vals) if N % 2==0: for k in range(int((-N/2) +1),int(N/2 +1)): coeff=0 for n in range(0,N): coeff+=(vals[n]*(np.exp(-2*np.pi*1j*k*n*1/N))) transformed.append([coeff,k]) else: for k in range(int(-(N-1)/2),int((N-1)/2)): for n in range(0,N): coeff+=(vals[n]*(np.exp(-2*np.pi*1j*k*n*1/N))) transformed.append([coeff,k]) return transformed
但修改后效果没有明显改善,因此我怀疑问题出在绘制/动画代码的其他部分。
问题排查结论
1. 初始版负频率映射错误
初始版DFT中,当k>N/2时,正确的负频率应为k-N而非-k,例如N=8时k=5对应的负频率是-3不是-5,错误的频率映射会导致本轮旋转速度完全不符合预期。
2. 相位计算逻辑缺陷
使用math.atan(complex_no.imag/complex_no.real)计算相位存在两个问题:一是无法自动识别复数所在象限,实部为负时会得到完全相反的相位值;二是实部为0时会触发除零异常。建议直接使用cmath.phase(complex_no)或np.angle(complex_no)处理相位计算。
3. 幅值归一化逻辑错误
半径的归一化系数不需要统一乘2:直流分量(频率为0)的幅值应为abs(coeff)/N,仅非直流分量需要额外乘2,统一乘2会导致直流分量偏移,复杂路径下会出现整体路径错位的问题。
4. 更新版DFT遍历范围问题
奇数长度序列的DFT遍历中,range的左闭右开特性会导致你漏掉最高正频率点,傅里叶分量不全自然无法正确还原完整路径。
内容的提问来源于stack exchange,提问作者Christopher Mattocks

