Veloris.
返回索引
设计实战 2026-02-14

Python数据可视化:matplotlib画波形图和频谱图,FPGA调试的好帮手

2 分钟
433 words

Python数据可视化:matplotlib画波形图和频谱图,FPGA调试的好帮手

数据可视化是分析和展示数据的重要手段。在FPGA开发中,可视化常用于波形显示、频谱分析、时序图绘制等场景。本篇介绍matplotlib的基本使用。


1. matplotlib简介

pip install matplotlib
import matplotlib.pyplot as plt
import numpy as np

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 设置默认图像大小和DPI
plt.rcParams['figure.figsize'] = [10, 6]
plt.rcParams['figure.dpi'] = 100

# 简单示例
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

plt.plot(x, y)
plt.title('正弦波')
plt.xlabel('x')
plt.ylabel('sin(x)')
plt.show()

2. 基本绑图

import matplotlib.pyplot as plt
import numpy as np

# 创建数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 方式1:快速绑图
plt.plot(x, y1)
plt.show()

# 方式2:面向对象方式(推荐)
fig, ax = plt.subplots()
ax.plot(x, y1, label='sin(x)')
ax.plot(x, y2, label='cos(x)')
ax.set_xlabel('X轴')
ax.set_ylabel('Y轴')
ax.set_title('三角函数')
ax.legend()
ax.grid(True)
plt.show()

# 线条样式
plt.plot(x, y1, 'r-', linewidth=2, label='红色实线')
plt.plot(x, y2, 'b--', linewidth=1.5, label='蓝色虚线')
plt.plot(x, y1+y2, 'g:', label='绿色点线')
plt.plot(x, y1-y2, 'm-.', label='品红点划线')
plt.legend()
plt.show()

# 标记样式
plt.plot(x[::10], y1[::10], 'ro', markersize=8, label='圆点')
plt.plot(x[::10], y2[::10], 'bs', markersize=6, label='方块')
plt.plot(x[::10], (y1+y2)[::10], 'g^', markersize=6, label='三角')
plt.legend()
plt.show()

# 常用颜色:r红 g绿 b蓝 c青 m品红 y黄 k黑 w白
# 常用线型:- 实线 -- 虚线 : 点线 -. 点划线
# 常用标记:o圆 s方 ^三角 *星 +加 x叉 d菱形

# 保存图像
fig, ax = plt.subplots()
ax.plot(x, y1)
fig.savefig('plot.png', dpi=150, bbox_inches='tight')
fig.savefig('plot.pdf')  # 矢量图
fig.savefig('plot.svg')  # SVG格式
plt.close(fig)

3. 图表类型

import matplotlib.pyplot as plt
import numpy as np

# 散点图
x = np.random.randn(100)
y = np.random.randn(100)
colors = np.random.rand(100)
sizes = np.random.rand(100) * 500

plt.scatter(x, y, c=colors, s=sizes, alpha=0.5, cmap='viridis')
plt.colorbar()
plt.title('散点图')
plt.show()

# 柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]

plt.bar(categories, values, color='steelblue', edgecolor='black')
plt.title('柱状图')
plt.ylabel('数值')
plt.show()

# 分组柱状图
x = np.arange(5)
width = 0.35
values1 = [20, 35, 30, 35, 27]
values2 = [25, 32, 34, 20, 25]

fig, ax = plt.subplots()
ax.bar(x - width/2, values1, width, label='组1')
ax.bar(x + width/2, values2, width, label='组2')
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend()
plt.show()

# 直方图
data = np.random.randn(1000)

plt.hist(data, bins=30, density=True, alpha=0.7, color='steelblue', edgecolor='black')
plt.title('直方图')
plt.xlabel('值')
plt.ylabel('频率')
plt.show()

# 饼图
sizes = [30, 25, 20, 15, 10]
labels = ['A', 'B', 'C', 'D', 'E']
explode = (0.1, 0, 0, 0, 0)  # 突出第一块

plt.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%',
        shadow=True, startangle=90)
plt.title('饼图')
plt.axis('equal')
plt.show()

# 箱线图
data = [np.random.randn(100) for _ in range(4)]

plt.boxplot(data, labels=['A', 'B', 'C', 'D'])
plt.title('箱线图')
plt.show()

# 热力图
data = np.random.rand(10, 10)

plt.imshow(data, cmap='hot', interpolation='nearest')
plt.colorbar()
plt.title('热力图')
plt.show()

# 等高线图
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)

plt.contour(X, Y, Z, levels=20, cmap='RdYlBu')
plt.colorbar()
plt.title('等高线图')
plt.show()

# 填充等高线
plt.contourf(X, Y, Z, levels=20, cmap='RdYlBu')
plt.colorbar()
plt.title('填充等高线图')
plt.show()

4. 图表美化

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x)

fig, ax = plt.subplots(figsize=(10, 6))

# 绑制曲线
ax.plot(x, y, 'b-', linewidth=2, label='sin(x)')

# 标题和标签
ax.set_title('正弦波形', fontsize=16, fontweight='bold')
ax.set_xlabel('时间 (s)', fontsize=12)
ax.set_ylabel('幅度 (V)', fontsize=12)

# 坐标轴范围
ax.set_xlim(0, 10)
ax.set_ylim(-1.5, 1.5)

# 刻度
ax.set_xticks(np.arange(0, 11, 2))
ax.set_yticks(np.arange(-1.5, 2, 0.5))

# 网格
ax.grid(True, linestyle='--', alpha=0.7)

# 图例
ax.legend(loc='upper right', fontsize=10)

# 添加文本注释
ax.annotate('最大值', xy=(np.pi/2, 1), xytext=(2, 1.2),
            arrowprops=dict(arrowstyle='->', color='red'),
            fontsize=10, color='red')

# 添加水平/垂直线
ax.axhline(y=0, color='k', linestyle='-', linewidth=0.5)
ax.axvline(x=np.pi, color='r', linestyle='--', linewidth=1, label='π')

# 填充区域
ax.fill_between(x, y, 0, where=(y > 0), alpha=0.3, color='green')
ax.fill_between(x, y, 0, where=(y < 0), alpha=0.3, color='red')

# 边框
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

# 使用样式
print(plt.style.available)  # 查看可用样式

with plt.style.context('seaborn-v0_8-darkgrid'):
    plt.plot(x, y)
    plt.title('使用seaborn样式')
    plt.show()

5. 子图布局

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

# 方式1:subplot
plt.figure(figsize=(12, 8))

plt.subplot(2, 2, 1)
plt.plot(x, np.sin(x))
plt.title('sin(x)')

plt.subplot(2, 2, 2)
plt.plot(x, np.cos(x))
plt.title('cos(x)')

plt.subplot(2, 2, 3)
plt.plot(x, np.tan(x))
plt.title('tan(x)')
plt.ylim(-5, 5)

plt.subplot(2, 2, 4)
plt.plot(x, np.exp(-x/5) * np.sin(x))
plt.title('衰减正弦')

plt.tight_layout()
plt.show()

# 方式2:subplots(推荐)
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('sin(x)')

axes[0, 1].plot(x, np.cos(x))
axes[0, 1].set_title('cos(x)')

axes[1, 0].plot(x, np.tan(x))
axes[1, 0].set_title('tan(x)')
axes[1, 0].set_ylim(-5, 5)

axes[1, 1].plot(x, np.exp(-x/5) * np.sin(x))
axes[1, 1].set_title('衰减正弦')

plt.tight_layout()
plt.show()

# 方式3:GridSpec(灵活布局)
from matplotlib.gridspec import GridSpec

fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)

ax1 = fig.add_subplot(gs[0, :])  # 第一行,跨所有列
ax1.plot(x, np.sin(x))
ax1.set_title('跨列')

ax2 = fig.add_subplot(gs[1:, 0])  # 第2-3行,第一列
ax2.plot(x, np.cos(x))
ax2.set_title('跨行')

ax3 = fig.add_subplot(gs[1, 1:])  # 第二行,第2-3列
ax3.plot(x, np.tan(x))
ax3.set_ylim(-5, 5)

ax4 = fig.add_subplot(gs[2, 1])
ax4.plot(x, x**2)

ax5 = fig.add_subplot(gs[2, 2])
ax5.plot(x, np.sqrt(x))

plt.tight_layout()
plt.show()

# 共享坐标轴
fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)

axes[0].plot(x, np.sin(x))
axes[0].set_ylabel('sin(x)')

axes[1].plot(x, np.cos(x))
axes[1].set_ylabel('cos(x)')
axes[1].set_xlabel('x')

plt.tight_layout()
plt.show()

6. 波形显示

import matplotlib.pyplot as plt
import numpy as np

def plot_waveform(time, signal, title='波形', xlabel='时间', ylabel='幅度'):
    """绑制波形"""
    fig, ax = plt.subplots(figsize=(12, 4))
    ax.plot(time, signal, 'b-', linewidth=0.5)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.grid(True, alpha=0.3)
    plt.tight_layout()
    return fig, ax

def plot_digital_waveform(time, signals, names):
    """绑制数字波形(时序图)"""
    n_signals = len(signals)
    fig, axes = plt.subplots(n_signals, 1, figsize=(12, 2*n_signals), sharex=True)
    
    if n_signals == 1:
        axes = [axes]
    
    for i, (signal, name) in enumerate(zip(signals, names)):
        axes[i].step(time, signal, 'b-', where='post', linewidth=1.5)
        axes[i].set_ylabel(name)
        axes[i].set_ylim(-0.2, 1.2)
        axes[i].set_yticks([0, 1])
        axes[i].grid(True, axis='x', alpha=0.3)
        axes[i].fill_between(time, signal, step='post', alpha=0.3)
    
    axes[-1].set_xlabel('时间')
    plt.tight_layout()
    return fig, axes

# 示例:模拟波形
fs = 10000  # 采样率
t = np.arange(0, 0.1, 1/fs)
signal = np.sin(2*np.pi*100*t) + 0.5*np.sin(2*np.pi*200*t)
signal += 0.1 * np.random.randn(len(t))

plot_waveform(t*1000, signal, '模拟信号', '时间 (ms)', '电压 (V)')
plt.show()

# 示例:数字波形
t = np.arange(0, 100, 1)
clk = (t % 2).astype(int)
data = np.random.randint(0, 2, len(t))
enable = np.zeros(len(t), dtype=int)
enable[20:80] = 1

plot_digital_waveform(t, [clk, data, enable], ['CLK', 'DATA', 'EN'])
plt.show()

# 双Y轴
fig, ax1 = plt.subplots(figsize=(10, 5))

t = np.linspace(0, 10, 1000)
signal1 = np.sin(2*np.pi*0.5*t)
signal2 = 100 * np.cos(2*np.pi*0.3*t) + 500

ax1.plot(t, signal1, 'b-', label='信号1')
ax1.set_xlabel('时间 (s)')
ax1.set_ylabel('信号1 (V)', color='b')
ax1.tick_params(axis='y', labelcolor='b')

ax2 = ax1.twinx()
ax2.plot(t, signal2, 'r-', label='信号2')
ax2.set_ylabel('信号2 (mV)', color='r')
ax2.tick_params(axis='y', labelcolor='r')

fig.legend(loc='upper right', bbox_to_anchor=(0.9, 0.9))
plt.title('双Y轴波形')
plt.tight_layout()
plt.show()

7. 频谱分析图

import matplotlib.pyplot as plt
import numpy as np

def plot_spectrum(signal, fs, title='频谱'):
    """绑制频谱图"""
    n = len(signal)
    fft_result = np.fft.fft(signal)
    freq = np.fft.fftfreq(n, 1/fs)
    
    # 只取正频率
    positive_freq = freq[:n//2]
    magnitude = np.abs(fft_result[:n//2]) * 2 / n
    magnitude_db = 20 * np.log10(magnitude + 1e-10)
    
    fig, axes = plt.subplots(2, 1, figsize=(12, 8))
    
    # 线性幅度
    axes[0].plot(positive_freq, magnitude, 'b-')
    axes[0].set_xlabel('频率 (Hz)')
    axes[0].set_ylabel('幅度')
    axes[0].set_title(f'{title} - 线性')
    axes[0].grid(True, alpha=0.3)
    
    # dB幅度
    axes[1].plot(positive_freq, magnitude_db, 'b-')
    axes[1].set_xlabel('频率 (Hz)')
    axes[1].set_ylabel('幅度 (dB)')
    axes[1].set_title(f'{title} - 对数')
    axes[1].grid(True, alpha=0.3)
    axes[1].set_ylim(bottom=-80)
    
    plt.tight_layout()
    return fig, axes

def plot_spectrogram(signal, fs, title='频谱图'):
    """绑制时频图(频谱图)"""
    fig, ax = plt.subplots(figsize=(12, 6))
    
    Pxx, freqs, bins, im = ax.specgram(signal, NFFT=256, Fs=fs, 
                                        noverlap=128, cmap='viridis')
    
    ax.set_xlabel('时间 (s)')
    ax.set_ylabel('频率 (Hz)')
    ax.set_title(title)
    
    cbar = fig.colorbar(im, ax=ax)
    cbar.set_label('功率 (dB)')
    
    plt.tight_layout()
    return fig, ax

# 示例
fs = 10000
t = np.arange(0, 1, 1/fs)

# 单频信号
signal1 = np.sin(2*np.pi*1000*t)
plot_spectrum(signal1, fs, '1kHz正弦波频谱')
plt.show()

# 多频信号
signal2 = np.sin(2*np.pi*500*t) + 0.5*np.sin(2*np.pi*1500*t) + 0.3*np.sin(2*np.pi*2500*t)
plot_spectrum(signal2, fs, '多频信号频谱')
plt.show()

# 扫频信号
f0, f1 = 100, 2000
signal3 = np.sin(2*np.pi*(f0 + (f1-f0)*t/2)*t)
plot_spectrogram(signal3, fs, '扫频信号时频图')
plt.show()

# 综合显示
def plot_signal_analysis(signal, fs, title='信号分析'):
    """综合信号分析图"""
    n = len(signal)
    t = np.arange(n) / fs
    
    # FFT
    fft_result = np.fft.fft(signal)
    freq = np.fft.fftfreq(n, 1/fs)[:n//2]
    magnitude = np.abs(fft_result[:n//2]) * 2 / n
    
    fig = plt.figure(figsize=(14, 10))
    
    # 时域波形
    ax1 = fig.add_subplot(3, 1, 1)
    ax1.plot(t*1000, signal, 'b-', linewidth=0.5)
    ax1.set_xlabel('时间 (ms)')
    ax1.set_ylabel('幅度')
    ax1.set_title(f'{title} - 时域')
    ax1.grid(True, alpha=0.3)
    
    # 频谱
    ax2 = fig.add_subplot(3, 1, 2)
    ax2.plot(freq, magnitude, 'b-')
    ax2.set_xlabel('频率 (Hz)')
    ax2.set_ylabel('幅度')
    ax2.set_title(f'{title} - 频域')
    ax2.grid(True, alpha=0.3)
    
    # 时频图
    ax3 = fig.add_subplot(3, 1, 3)
    ax3.specgram(signal, NFFT=256, Fs=fs, noverlap=128, cmap='viridis')
    ax3.set_xlabel('时间 (s)')
    ax3.set_ylabel('频率 (Hz)')
    ax3.set_title(f'{title} - 时频')
    
    plt.tight_layout()
    return fig

# 测试
signal = np.sin(2*np.pi*500*t) + 0.5*np.sin(2*np.pi*1200*t)
signal += 0.1 * np.random.randn(len(t))
plot_signal_analysis(signal, fs, '测试信号')
plt.show()

8. 实时绑图

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

# 方式1:使用动画
def realtime_plot_animation():
    """使用FuncAnimation实现实时绑图"""
    fig, ax = plt.subplots()
    line, = ax.plot([], [], 'b-')
    ax.set_xlim(0, 100)
    ax.set_ylim(-1.5, 1.5)
    ax.set_xlabel('采样点')
    ax.set_ylabel('幅度')
    ax.set_title('实时波形')
    ax.grid(True)
    
    xdata, ydata = [], []
    
    def init():
        line.set_data([], [])
        return line,
    
    def update(frame):
        xdata.append(frame)
        ydata.append(np.sin(frame * 0.1) + 0.1 * np.random.randn())
        
        # 保持窗口大小
        if len(xdata) > 100:
            xdata.pop(0)
            ydata.pop(0)
            ax.set_xlim(xdata[0], xdata[-1])
        
        line.set_data(xdata, ydata)
        return line,
    
    ani = FuncAnimation(fig, update, frames=range(500),
                       init_func=init, blit=True, interval=50)
    plt.show()

# 方式2:交互模式
def realtime_plot_interactive():
    """使用交互模式实现实时绑图"""
    plt.ion()  # 开启交互模式
    
    fig, ax = plt.subplots()
    line, = ax.plot([], [], 'b-')
    ax.set_xlim(0, 100)
    ax.set_ylim(-1.5, 1.5)
    ax.set_xlabel('采样点')
    ax.set_ylabel('幅度')
    ax.grid(True)
    
    xdata, ydata = [], []
    
    for i in range(200):
        xdata.append(i)
        ydata.append(np.sin(i * 0.1) + 0.1 * np.random.randn())
        
        if len(xdata) > 100:
            xdata.pop(0)
            ydata.pop(0)
            ax.set_xlim(xdata[0], xdata[-1])
        
        line.set_data(xdata, ydata)
        fig.canvas.draw()
        fig.canvas.flush_events()
        time.sleep(0.05)
    
    plt.ioff()
    plt.show()

# 方式3:使用blitting(更高效)
class RealtimePlotter:
    """高效实时绑图器"""
    
    def __init__(self, max_points=1000):
        self.max_points = max_points
        self.fig, self.ax = plt.subplots()
        self.line, = self.ax.plot([], [], 'b-', animated=True)
        self.ax.set_xlim(0, max_points)
        self.ax.set_ylim(-2, 2)
        self.ax.grid(True)
        
        self.xdata = np.arange(max_points)
        self.ydata = np.zeros(max_points)
        self.idx = 0
        
        self.fig.canvas.draw()
        self.background = self.fig.canvas.copy_from_bbox(self.ax.bbox)
    
    def update(self, new_value):
        self.ydata[self.idx % self.max_points] = new_value
        self.idx += 1
        
        # 滚动显示
        if self.idx >= self.max_points:
            display_data = np.roll(self.ydata, -self.idx % self.max_points)
        else:
            display_data = self.ydata
        
        self.fig.canvas.restore_region(self.background)
        self.line.set_data(self.xdata, display_data)
        self.ax.draw_artist(self.line)
        self.fig.canvas.blit(self.ax.bbox)
        self.fig.canvas.flush_events()

# realtime_plot_animation()

9. 实战案例

案例:FPGA信号分析可视化工具

"""
实战案例:FPGA信号分析可视化工具
"""
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Button, Slider
from dataclasses import dataclass
from typing import Optional

@dataclass
class SignalData:
    """信号数据"""
    time: np.ndarray
    voltage: np.ndarray
    sample_rate: float
    name: str = "Signal"

class SignalViewer:
    """信号查看器"""
    
    def __init__(self):
        self.fig = None
        self.axes = None
        self.signals = []
    
    def add_signal(self, signal: SignalData):
        """添加信号"""
        self.signals.append(signal)
    
    def plot_time_domain(self, ax, signal: SignalData):
        """绑制时域波形"""
        ax.clear()
        ax.plot(signal.time * 1000, signal.voltage, 'b-', linewidth=0.5)
        ax.set_xlabel('时间 (ms)')
        ax.set_ylabel('电压 (V)')
        ax.set_title(f'{signal.name} - 时域')
        ax.grid(True, alpha=0.3)
        
        # 添加统计信息
        stats = f'Min: {signal.voltage.min():.3f}V  Max: {signal.voltage.max():.3f}V  RMS: {np.sqrt(np.mean(signal.voltage**2)):.3f}V'
        ax.text(0.02, 0.98, stats, transform=ax.transAxes, 
                verticalalignment='top', fontsize=9,
                bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
    
    def plot_frequency_domain(self, ax, signal: SignalData):
        """绑制频域"""
        ax.clear()
        n = len(signal.voltage)
        fft_result = np.fft.fft(signal.voltage)
        freq = np.fft.fftfreq(n, 1/signal.sample_rate)[:n//2]
        magnitude_db = 20 * np.log10(np.abs(fft_result[:n//2]) * 2 / n + 1e-10)
        
        ax.plot(freq/1000, magnitude_db, 'b-')
        ax.set_xlabel('频率 (kHz)')
        ax.set_ylabel('幅度 (dB)')
        ax.set_title(f'{signal.name} - 频域')
        ax.grid(True, alpha=0.3)
        ax.set_ylim(bottom=-80)
        
        # 标记主频
        peak_idx = np.argmax(magnitude_db[1:]) + 1
        peak_freq = freq[peak_idx]
        peak_mag = magnitude_db[peak_idx]
        ax.annotate(f'{peak_freq:.0f}Hz\n{peak_mag:.1f}dB',
                   xy=(peak_freq/1000, peak_mag),
                   xytext=(peak_freq/1000 + 0.5, peak_mag + 5),
                   arrowprops=dict(arrowstyle='->', color='red'),
                   fontsize=9, color='red')
    
    def plot_histogram(self, ax, signal: SignalData):
        """绑制直方图"""
        ax.clear()
        ax.hist(signal.voltage, bins=50, density=True, alpha=0.7, 
                color='steelblue', edgecolor='black')
        ax.set_xlabel('电压 (V)')
        ax.set_ylabel('概率密度')
        ax.set_title(f'{signal.name} - 分布')
        ax.grid(True, alpha=0.3)
    
    def plot_spectrogram(self, ax, signal: SignalData):
        """绑制时频图"""
        ax.clear()
        ax.specgram(signal.voltage, NFFT=256, Fs=signal.sample_rate,
                   noverlap=128, cmap='viridis')
        ax.set_xlabel('时间 (s)')
        ax.set_ylabel('频率 (Hz)')
        ax.set_title(f'{signal.name} - 时频')
    
    def show(self, signal: Optional[SignalData] = None):
        """显示分析界面"""
        if signal is None and self.signals:
            signal = self.signals[0]
        
        if signal is None:
            print("没有信号数据")
            return
        
        self.fig = plt.figure(figsize=(14, 10))
        
        # 创建子图
        ax1 = self.fig.add_subplot(2, 2, 1)
        ax2 = self.fig.add_subplot(2, 2, 2)
        ax3 = self.fig.add_subplot(2, 2, 3)
        ax4 = self.fig.add_subplot(2, 2, 4)
        
        self.plot_time_domain(ax1, signal)
        self.plot_frequency_domain(ax2, signal)
        self.plot_histogram(ax3, signal)
        self.plot_spectrogram(ax4, signal)
        
        plt.tight_layout()
        plt.show()
    
    def save_report(self, signal: SignalData, filename: str):
        """保存分析报告"""
        self.fig = plt.figure(figsize=(14, 10))
        
        ax1 = self.fig.add_subplot(2, 2, 1)
        ax2 = self.fig.add_subplot(2, 2, 2)
        ax3 = self.fig.add_subplot(2, 2, 3)
        ax4 = self.fig.add_subplot(2, 2, 4)
        
        self.plot_time_domain(ax1, signal)
        self.plot_frequency_domain(ax2, signal)
        self.plot_histogram(ax3, signal)
        self.plot_spectrogram(ax4, signal)
        
        plt.tight_layout()
        self.fig.savefig(filename, dpi=150, bbox_inches='tight')
        plt.close(self.fig)
        print(f"报告已保存到 {filename}")

# 使用示例
def demo():
    # 生成测试信号
    fs = 10000
    t = np.arange(0, 1, 1/fs)
    
    # 多频信号 + 噪声
    signal = (1.0 * np.sin(2*np.pi*500*t) + 
              0.5 * np.sin(2*np.pi*1200*t) +
              0.3 * np.sin(2*np.pi*2000*t) +
              0.2 * np.random.randn(len(t)))
    
    data = SignalData(
        time=t,
        voltage=signal,
        sample_rate=fs,
        name="测试信号"
    )
    
    viewer = SignalViewer()
    viewer.add_signal(data)
    viewer.show(data)
    # viewer.save_report(data, 'signal_report.png')

demo()

10. 总结

🔑 核心要点

知识点要点
基本绑图plt.plot(), fig, ax = plt.subplots()
图表类型折线图、散点图、柱状图、直方图、饼图
图表美化标题、标签、图例、网格、注释
子图布局subplots(), GridSpec
波形显示时域波形、数字波形、双Y轴
频谱分析FFT频谱、时频图
实时绑图FuncAnimation, 交互模式

✅ 学习检查清单

  • 能绑制基本图表
  • 能美化图表
  • 能创建子图布局
  • 能绑制波形和频谱
  • 了解实时绑图方法

📖 下一步学习

掌握了数据可视化后,让我们学习Vivado工程自动化:


常见问题 FAQ

💬 matplotlib图表中文显示乱码怎么办?

设置字体:plt.rcParams['font.sans-serif'] = ['SimHei'],同时plt.rcParams['axes.unicode_minus'] = False解决负号显示问题。

💬 matplotlib和plotly怎么选?

matplotlib适合静态图表和论文插图,plotly适合交互式图表和Web展示。FPGA波形分析用matplotlib足够,需要交互缩放用plotly。


系列导航

End of file.