You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于PySerial与FFT实现Arduino实时数据时域、频域可视化的问题求助

基于PySerial与FFT实现Arduino实时数据时域、频域可视化的问题求助

我想从Arduino读取实时数据,绘制时域图,然后对这些数据做FFT并绘制频域图,但完全不知道该怎么做。现在的问题是没法把从串口获取的数据数组传给FFT,写的freqdom函数估计完全是错的。

我来帮你梳理下问题并给出修正方案,你的核心需求是把Arduino的实时数据同时做时域和FFT频域可视化,现在代码里的问题主要集中在这几个方面:

  • 数据同步问题:你现在的时域、频域函数各自独立读取串口,这会导致两份数据完全不同步,而且串口是流式传输,重复读取必然会丢包,完全没必要。
  • FFT逻辑完全错误:频域函数里的y=np.sin(2*np.pi*dataList1*t)完全没有意义——你既没定义时间轴t,也不需要生成正弦波,应该直接用采集到的原始数据做FFT。而且FFT需要足够多的采样点(比如512个,最好是2的幂次)才能得到有意义的频域结果,你每次只处理一个点,根本无法反映频率特征。
  • 动画执行阻塞:第一个plt.show()会完全阻塞程序,后面的ani2和其他代码根本不会运行,你没法同时看到两个动画窗口。
  • 变量传递混乱:ani2里传递的参数是freqdom_real,但freqdomain函数的形参是dataList1,完全不匹配,而且频域函数里的变量逻辑一团糟。

修正后的完整代码

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import serial
import time

# 配置参数(根据你的Arduino实际情况调整)
SERIAL_PORT = "COM3"
BAUD_RATE = 9600
SAMPLING_RATE = 100  # 要和Arduino的采样频率保持一致,单位Hz
FFT_SAMPLE_COUNT = 512  # FFT采样点数,建议用2的幂次(如256/512/1024)
TIME_WINDOW_LENGTH = 100  # 时域图显示的实时数据点数

# 全局变量:存储实时采集的数据
data_buffer = []
# 初始化串口连接
ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
time.sleep(2)  # 等待Arduino完成串口初始化

# 创建一个包含两个子图的绘图窗口,同时显示时域和频域
fig, (ax_time, ax_freq) = plt.subplots(2, 1, figsize=(10, 8))

def update_plots(frame):
    global data_buffer
    
    # ---------------- 统一采集串口数据 ----------------
    ser.write(b'g')  # 发送指令请求数据
    try:
        # 读取并解析串口数据
        raw_data = ser.readline().decode('ascii').strip()
        data_value = float(raw_data)
        data_buffer.append(data_value)
    except (ValueError, UnicodeDecodeError):
        # 跳过无效数据(比如串口乱码、非数字内容)
        pass

    # ---------------- 更新时域图 ----------------
    # 保持时域图的窗口长度固定
    data_buffer = data_buffer[-TIME_WINDOW_LENGTH:]
    ax_time.clear()
    ax_time.plot(data_buffer)
    ax_time.set_ylim([0, 2000])  # 和你原代码的Y轴范围保持一致
    ax_time.set_title("实时时域波形")
    ax_time.set_ylabel("数据值")
    ax_time.grid(True, alpha=0.3)

    # ---------------- 更新频域图(FFT) ----------------
    ax_freq.clear()
    # 只有当采集到足够的FFT采样点时才计算
    if len(data_buffer) >= FFT_SAMPLE_COUNT:
        # 取最近的N个采样点做FFT
        fft_input = data_buffer[-FFT_SAMPLE_COUNT:]
        
        # 执行FFT并计算幅值谱(做归一化处理)
        fft_result = np.fft.fft(fft_input)
        fft_amplitude = 2 * np.abs(fft_result) / FFT_SAMPLE_COUNT
        
        # 计算对应的频率轴
        freq_axis = np.fft.fftfreq(FFT_SAMPLE_COUNT, 1 / SAMPLING_RATE)
        
        # 只显示正频率部分(符合实际观测习惯)
        positive_freq_mask = freq_axis >= 0
        valid_freq = freq_axis[positive_freq_mask]
        valid_amp = fft_amplitude[positive_freq_mask]
        
        # 绘制频域图
        ax_freq.plot(valid_freq, valid_amp)
        ax_freq.set_ylim([0, np.max(valid_amp)*1.1 if len(valid_amp) > 0 else 1000])
        ax_freq.set_title("实时频域谱(FFT)")
        ax_freq.set_xlabel("频率 (Hz)")
        ax_freq.set_ylabel("幅值")
        ax_freq.grid(True, alpha=0.3)
    else:
        # 数据不足时提示
        ax_freq.text(0.5, 0.5, "正在采集FFT所需数据...", ha='center', va='center', transform=ax_freq.transAxes)
        ax_freq.set_title("实时频域谱(FFT)")

# 创建动画,每100ms更新一次
ani = FuncAnimation(fig, update_plots, interval=100, cache_frame_data=False)

# 确保程序结束时正确关闭串口
try:
    plt.tight_layout()
    plt.show()
finally:
    ser.close()
    print("串口连接已关闭")

关键修正说明

  1. 统一数据采集:只在一个函数里读取串口数据,保证时域和频域用的是同一份实时数据,彻底解决同步问题
  2. 正确的FFT实现:
    • 直接用采集到的原始数据做FFT,不再生成无意义的正弦波
    • 计算了对应实际物理意义的频率轴,频域图的X轴是真实频率
    • 对FFT结果做了幅值归一化,结果更准确
    • 只显示正频率部分,符合实际观测需求
  3. 合并绘图窗口:把时域和频域图放在同一个窗口的两个子图里,避免plt.show()阻塞导致的动画无法运行问题
  4. FFT有效性保障:只有当采集到足够多的采样点时才执行FFT,保证频域结果的参考价值
  5. 安全资源释放:用try...finally确保无论程序正常关闭还是异常退出,串口都会被正确关闭

备注:内容来源于stack exchange,提问作者Nemanja Nenadić

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 14:45:28