# FOC voltage space vector animation (SVPWM-style)
# Save as e.g. foc_space_vector.py and run: python foc_space_vector.py
# Requirements: numpy, matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Parameters
V_mag = 1.0 # reference vector magnitude
f = 1.0 # electrical frequency (Hz)
omega = 2 * np.pi * f
fs = 60 # animation frames per second
duration = 6 # seconds
frames = int(duration * fs)
t_vals = np.linspace(0, duration, frames)
# Clarke inverse (alpha-beta -> a,b,c)
def alpha_beta_to_abc(alpha, beta):
va = alpha
vb = -0.5 * alpha + np.sqrt(3)/2 * beta
vc = -0.5 * alpha - np.sqrt(3)/2 * beta
return va, vb, vc
# phase axis unit vectors in alpha-beta plane
u_a = np.array([1.0, 0.0])
u_b = np.array([np.cos(-2*np.pi/3), np.sin(-2*np.pi/3)])
u_c = np.array([np.cos(2*np.pi/3), np.sin(2*np.pi/3)])
# Set up figure
fig = plt.figure(figsize=(8, 6))
ax_ab = fig.add_subplot(2, 1, 1)
ax_traces = fig.add_subplot(2, 1, 2)
# Alpha-beta plane limits
R = 1.2 * V_mag
ax_ab.set_xlim(-R, R)
ax_ab.set_ylim(-R, R)
ax_ab.set_aspect('equal', 'box')
ax_ab.set_title('Voltage Space Vector (α-β plane)')
ax_ab.set_xlabel('α')
ax_ab.set_ylabel('β')
ax_ab.grid(True)
# Draw hexagon (SVPWM switching states approximate)
angles = np.linspace(np.pi/6, np.pi/6 + 2*np.pi, 7)
hex_x = V_mag * np.cos(angles)
hex_y = V_mag * np.sin(angles)
ax_ab.plot(hex_x, hex_y, 'k--', alpha=0.6)
# draw phase axes
for u, label in zip((u_a, u_b, u_c), ('A', 'B', 'C')):
ax_ab.arrow(0, 0, 0.9*R*u[0], 0.9*R*u[1], head_width=0.04*R, head_length=0.06*R, fc='gray', ec='gray', linestyle=':')
ax_ab.text(1.02*R*u[0], 1.02*R*u[1], label, color='gray', fontsize=10, ha='center', va='center')
# reference vector arrow
ref_arrow = ax_ab.arrow(0, 0, 0, 0, head_width=0.04*R, head_length=0.06*R, color='r')
# projections to phase axes (lines)
proj_lines = [
ax_ab.plot([0, 0], [0, 0], 'r--', linewidth=1)[0],
ax_ab.plot([0, 0], [0, 0], 'g--', linewidth=1)[0],
ax_ab.plot([0, 0], [0, 0], 'b--', linewidth=1)[0],
]
# moving dots on hexagon/circle
dot_ref, = ax_ab.plot([0], [0], 'ro')
# Time traces for phase voltages
ax_traces.set_title('Phase voltages (a,b,c)')
ax_traces.set_xlabel('Time (s)')
ax_traces.set_ylabel('Voltage (pu)')
ax_traces.set_xlim(0, duration)
ax_traces.set_ylim(-1.2*V_mag, 1.2*V_mag)
ax_traces.grid(True)
line_va, = ax_traces.plot([], [], 'r-', label='va')
line_vb, = ax_traces.plot([], [], 'g-', label='vb')
line_vc, = ax_traces.plot([], [], 'b-', label='vc')
ax_traces.legend(loc='upper right')
# storage for traces
t_history = []
va_history = []
vb_history = []
vc_history = []
def init():
global ref_arrow
ref_arrow.remove()
ref_arrow = ax_ab.arrow(0, 0, 0, 0, head_width=0.04*R, head_length=0.06*R, color='r')
dot_ref.set_data([], [])
for l in proj_lines:
l.set_data([], [])
line_va.set_data([], [])
line_vb.set_data([], [])
line_vc.set_data([], [])
t_history.clear()
va_history.clear()
vb_history.clear()
vc_history.clear()
return [ref_arrow, dot_ref, *proj_lines, line_va, line_vb, line_vc]
def update(frame):
t = t_vals[frame]
theta = omega * t
# reference vector in alpha-beta
alpha = V_mag * np.cos(theta)
beta = V_mag * np.sin(theta)
# phase voltages from Clarke inverse
va, vb, vc = alpha_beta_to_abc(alpha, beta)
# update ref arrow (remove and redraw because matplotlib arrow isn't easily updated)
global ref_arrow
try:
ref_arrow.remove()
except Exception:
pass
ref_arrow = ax_ab.arrow(0, 0, alpha, beta, head_width=0.04*R, head_length=0.06*R, color='r')
dot_ref.set_data(alpha, beta)
# projection lines onto phase axes (from origin to projection point)
for u, line, color, vall in zip((u_a, u_b, u_c), proj_lines, ('r', 'g', 'b'), (va, vb, vc)):
proj_point = vall * u # projection magnitude times unit axis
line.set_data([0, proj_point[0]], [0, proj_point[1]])
line.set_color(color)
# update traces
t_history.append(t)
va_history.append(va)
vb_history.append(vb)
vc_history.append(vc)
# keep only recent window to match xlim
line_va.set_data(t_history, va_history)
line_vb.set_data(t_history, vb_history)
line_vc.set_data(t_history, vc_history)
return [ref_arrow, dot_ref, *proj_lines, line_va, line_vb, line_vc]
ani = animation.FuncAnimation(fig, update, frames=frames, init_func=init,
blit=False, interval=1000/fs, repeat=True)
plt.tight_layout()
plt.show()
将上面代码完善