![image]()
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import FancyArrowPatch, Polygon, Circle
from matplotlib.animation import FuncAnimation
# ===================== 解决中文显示问题 =====================
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei']
plt.rcParams['axes.unicode_minus'] = False
# ===================== SVPWM参数(合成矢量=外接圆半径)=====================
# 6个基本电压矢量的标准角度(弧度)
V_ANGLES = [0, np.pi/3, 2*np.pi/3, np.pi, 4*np.pi/3, 5*np.pi/3]
V_LABELS = ['V4(1,0)', 'V6(0,1)', 'V2(-1,1)', 'V3(-1,0)', 'V1(0,-1)', 'V5(1,-1)']
# 六边形外接圆半径 = 基本矢量最大长度 = 合成矢量固定长度
V_BASE_LEN = 1.0
V_REF_FIXED_LEN = V_BASE_LEN # 合成矢量等于外接圆半径
# 每个扇区60°
SECTOR_STEP = np.pi/3
# ===================== 画布设置(左右双图)=====================
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
# 左侧矢量图设置
ax1.set_aspect('equal')
ax1.set_xlim(-1.5, 1.5)
ax1.set_ylim(-1.5, 1.5)
ax1.set_title('SVPWM 空间矢量调制原理', fontsize=16, pad=20)
ax1.set_xlabel('α 轴 (g)', fontsize=14)
ax1.set_ylabel('β 轴 (h)', fontsize=14)
# 右侧三相正弦波图设置
ax2.set_xlim(0, 2*np.pi)
ax2.set_ylim(-1.2, 1.2)
ax2.set_title('输出三相正弦电压波形', fontsize=16, pad=20)
ax2.set_xlabel('电角度 (rad)', fontsize=14)
ax2.set_ylabel('电压幅值', fontsize=14)
ax2.grid(True, alpha=0.3)
# ===================== 左侧绘制几何图形 =====================
# 1. 蓝色虚线外接圆
outer_circle = Circle((0, 0), V_BASE_LEN, color='#2196F3', fill=False,
linestyle='--', lw=2, alpha=0.8)
ax1.add_patch(outer_circle)
# 2. 黑色实线六边形边界
hex_vertices = [[V_BASE_LEN*np.cos(ang), V_BASE_LEN*np.sin(ang)] for ang in V_ANGLES]
hexagon = Polygon(hex_vertices, edgecolor='black', facecolor='none', lw=2.5)
ax1.add_patch(hexagon)
# 3. 灰色扇区分割虚线
for ang in V_ANGLES:
ax1.plot([0, 1.4*np.cos(ang)], [0, 1.4*np.sin(ang)], 'k--', lw=0.8, alpha=0.6)
# 标注基本矢量
for i in range(6):
x = 1.25 * np.cos(V_ANGLES[i])
y = 1.25 * np.sin(V_ANGLES[i])
ax1.text(x, y, V_LABELS[i], fontsize=12, ha='center', va='center',
bbox=dict(facecolor='white', alpha=0.8, edgecolor='none', pad=2))
# 标注扇区编号(I~VI)
sector_labels = ['I', 'II', 'III', 'IV', 'V', 'VI']
for i in range(6):
ang = i * SECTOR_STEP + SECTOR_STEP/2
x = 0.8 * np.cos(ang)
y = 0.8 * np.sin(ang)
ax1.text(x, y, sector_labels[i], fontsize=20, ha='center', va='center',
bbox=dict(facecolor='white', alpha=0.7, edgecolor='none', pad=4))
# ===================== 左侧初始化矢量 =====================
# 6个基本电压矢量(初始隐藏,最大长度严格限制为V_BASE_LEN)
basic_arrows = []
colors = ['#d62728', '#9467bd', '#1f77b4', '#ff7f0e', '#2ca02c', '#8c564b']
for i in range(6):
arr = FancyArrowPatch((0, 0), (0, 0), color=colors[i],
mutation_scale=14, arrowstyle='->', lw=3)
arr.set_visible(False)
basic_arrows.append(arr)
ax1.add_patch(arr)
# 合成参考矢量Vref(红色粗箭头,长度=外接圆半径,固定不变)
ref_arrow = FancyArrowPatch((0, 0), (0, 0), color='green',
mutation_scale=20, arrowstyle='->', lw=4)
ax1.add_patch(ref_arrow)
# 扇区高亮
sector_highlight = Polygon([[0,0], [0,0], [0,0]],
facecolor='yellow', alpha=0.25, edgecolor='none')
ax1.add_patch(sector_highlight)
# ===================== 右侧初始化三相正弦波 =====================
x_data = np.linspace(0, 2*np.pi, 1000)
# A相(红色)、B相(绿色)、C相(蓝色),相位互差120°
line_a, = ax2.plot(x_data, np.zeros_like(x_data), 'r-', lw=2.5, label='A相')
line_b, = ax2.plot(x_data, np.zeros_like(x_data), 'g-', lw=2.5, label='B相')
line_c, = ax2.plot(x_data, np.zeros_like(x_data), 'b-', lw=2.5, label='C相')
ax2.legend(loc='upper right', fontsize=12)
# ===================== 动画更新函数 =====================
angle = 0.0
def update(frame):
global angle
angle += 0.03
if angle > 2*np.pi:
angle = 0
# ========== 左侧矢量图更新 ==========
# 1. 合成参考矢量(长度=外接圆半径,沿着蓝色虚线圆匀速旋转)
v_ref_x = V_REF_FIXED_LEN * np.cos(angle)
v_ref_y = V_REF_FIXED_LEN * np.sin(angle)
ref_arrow.set_positions((0, 0), (v_ref_x, v_ref_y))
# 2. 判断当前扇区
sector = int(np.floor(angle / SECTOR_STEP)) % 6
# 3. 扇区内角度θ(0~60°)
theta = angle - sector * SECTOR_STEP
# 4. 计算原始占空比
T1 = np.sqrt(3) * np.sin(np.pi/3 - theta)
T2 = np.sqrt(3) * np.sin(theta)
# 5. 占空比归一化,保证T1+T2=1,基本矢量绝不超出六边形顶点
total = T1 + T2
if total > 1.0:
T1 = T1 / total
T2 = T2 / total
# 6. 扇区-矢量对应关系(完全匹配原理图)
sector_vectors = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)]
v1_idx, v2_idx = sector_vectors[sector]
# 7. 隐藏所有基本矢量
for arr in basic_arrows:
arr.set_visible(False)
# 8. 只显示当前扇区的两个基本矢量(方向固定,长度≤V_BASE_LEN)
# 矢量1(最大长度=六边形顶点)
v1_ang = V_ANGLES[v1_idx]
v1_x = T1 * V_BASE_LEN * np.cos(v1_ang)
v1_y = T1 * V_BASE_LEN * np.sin(v1_ang)
basic_arrows[v1_idx].set_positions((0, 0), (v1_x, v1_y))
basic_arrows[v1_idx].set_visible(True)
# 矢量2(最大长度=六边形顶点)
v2_ang = V_ANGLES[v2_idx]
v2_x = T2 * V_BASE_LEN * np.cos(v2_ang)
v2_y = T2 * V_BASE_LEN * np.sin(v2_ang)
basic_arrows[v2_idx].set_positions((0, 0), (v2_x, v2_y))
basic_arrows[v2_idx].set_visible(True)
# 9. 更新扇区高亮
sector_ang1 = sector * SECTOR_STEP
sector_ang2 = (sector + 1) * SECTOR_STEP
highlight_verts = [
[0, 0],
[V_BASE_LEN*np.cos(sector_ang1), V_BASE_LEN*np.sin(sector_ang1)],
[V_BASE_LEN*np.cos(sector_ang2), V_BASE_LEN*np.sin(sector_ang2)]
]
sector_highlight.set_xy(highlight_verts)
# ========== 右侧三相正弦波更新 ==========
# 三相电压相位互差120°,与合成矢量角度完全同步
line_a.set_ydata(0.5*np.sin(x_data + angle))
line_b.set_ydata(0.5*np.sin(x_data + angle - 2*np.pi/3))
line_c.set_ydata(0.5*np.sin(x_data + angle + 2*np.pi/3))
return (*basic_arrows, ref_arrow, sector_highlight, line_a, line_b, line_c)
# ===================== 生成动画 =====================
ani = FuncAnimation(fig, update, frames=np.arange(0, 210), interval=30, blit=True, repeat=True)
ani.save('svpwm_final_with_three_phase.gif', writer='pillow', fps=20, dpi=120)
plt.tight_layout()
plt.show()