自学FOC系列分享--SVPWM和clark逆变换及代码实战
- 电脑硬件
- 2025-08-25 08:24:01

自学FOC系列分享--SVPWM和clark 逆变换 1 说在前面2 回顾clarke 和 park的变换和逆变换2.1 概述2.2 公式说明 3 SVPWM是如何写的3.1 简单说明3.2 重要对比 4、代码实战4.1 代码构成说明4.2 全部代码4.3 测试代码4.4 测试结果4.5 结果分析 总结 1 说在前面
如前一篇文章所述的系统框架图如下: 此处,我将红色部分标红,对这个地方进行一个进一步的描述。
结论是: svpwm的计算过程中,包含了 clarke 逆变换
2 回顾clarke 和 park的变换和逆变换 2.1 概述将电机的控制分为 检测 和 控制两部分
检测: ia ib ic -> iα & iβ : 三相到两相 ---------------------- clarke 变换iα & iβ + θ -> id & iq : 静止=》旋转坐标系 ---------------------park 变换 控制: Iq_in Id_in , id & iq -> PI 控制-> id_target & iq_targetid_target & iq_target -> uα & uβ ---------------------park 逆变换 方案1: uα & uβ + θ -> ua ub uc ---------------------- clarke 逆 变换方案2: uα & uβ + θ -> ta tb tc -----svpwm 变换 2.2 公式说明clark变换
park变换
park逆变换
clarke逆变换
3 SVPWM是如何写的我主要参考这篇文章: blog.csdn.net/qq_35947329/article/details/115483413?spm=1001.2014.3001.5501
3.1 简单说明SVPWM的主要作用就是clark 逆变换,在α-β 轴和 ua ub uc变换。 参考上图:
我们已经得到了Uα 和 Uβ , 我们需要得到ua,ub,uc。ua,ub,uc 又可以通过4 6 2 3 1 5 0 7 这几个矢量来表示4 6 2 3 1 5 0 7 这几个适量又可以通过三相桥的开关状态和开关的时间的长短来表示(请参考文章) 相当于这几个矢量是ua ,ub,uc的另一种表达方式,通过这几个矢量的任意叠加,可得到任意方向矢量为了真切的让大家感受到为什么svpwm就是clarke 逆变换,我们需要实际的计算: 假设Uθ在第一扇区:
假设Uθ 在第二扇区:
以此类推,我们可得到一个特点就是:
如果令:
3.2 重要对比clark 逆变换和 我们计算作用时间时候的公式是相同的
4、代码实战 4.1 代码构成说明使用python脚本,实现开环SPWM和SVPWM代码 代码将详细说明 函数有如下几个:
clark() 和iclark()park() 和ipark()update_theta(): 更新最新的目标角度svpwm() : 具体svpwm的实现iclark2pwm(): 替代svpwm的SPWM 4.2 全部代码 class FOC: def __init__(self): # 初始化变量 self.u_d = 0.0 self.u_q = 0.0 self.u_theta = 0.0 self.u_alpha = 0.0 self.u_beta = 0.0 self.t_a = 0.0 self.t_b = 0.0 self.t_c = 0.0 self.i_a = 0.0 self.i_b = 0.0 self.i_c = 0.0 self.i_alpha = 0.0 self.i_beta = 0.0 self.i_d = 0.0 self.i_q = 0.0 self.sine = 0.0 self.cosine = 0.0 self.u_a=0.0 self.u_b = 0.0 self.u_c = 0.0 def clark(self): """Clarke 变换:将三相电流 (i_a, i_b, i_c) 转换为两相静止坐标系 (i_alpha, i_beta)""" self.i_alpha = self.i_a self.i_beta = (self.i_a + 2 * self.i_b) * 0.57735 # 1/sqrt(3) ≈ 0.57735 def park(self): """Park 变换:将两相静止坐标系 (i_alpha, i_beta) 转换为旋转坐标系 (i_d, i_q)""" self.i_d = self.i_alpha * self.cosine + self.i_beta * self.sine self.i_q = -self.i_alpha * self.sine + self.i_beta * self.cosine def ipark(self): """逆 Park 变换:将旋转坐标系 (u_d, u_q) 转换为两相静止坐标系 (u_alpha, u_beta)""" self.u_alpha = self.u_d * self.cosine - self.u_q * self.sine self.u_beta = self.u_d * self.sine + self.u_q * self.cosine def iclark(self): self.u_a = self.u_alpha self.u_b = -0.5 * self.u_alpha + (np.sqrt(3) / 2) * self.u_beta self.u_c = -0.5 * self.u_alpha - (np.sqrt(3) / 2) * self.u_beta def iclark2pwm(self): self.t_a=self.u_a self.t_b=self.u_b self.t_c=self.u_c def svpwm(self): """空间矢量脉宽调制 (SVPWM):将两相静止坐标系 (u_alpha, u_beta) 转换为三相 PWM 信号 (t_a, t_b, t_c)""" ts = 1.0 # PWM 周期 u1 = ( self.u_beta) u2 = -0.8660254037844386 * self.u_alpha - 0.5 * self.u_beta # -sqrt(3)/2 ≈ -0.866 u3 = 0.8660254037844386 * self.u_alpha - 0.5 * self.u_beta # sqrt(3)/2 ≈ 0.866 # 扇区判断 sector = (u1 > 0.0) + ((u2 > 0.0) << 1) + ((u3 > 0.0) << 2) if sector == 5: t4 = u3 t6 = u1 sum_t = t4 + t6 if sum_t > ts: k = ts / sum_t t4 *= k t6 *= k t7 = (ts - t4 - t6) / 2 self.t_a = t4 + t6 + t7 self.t_b = t6 + t7 self.t_c = t7 elif sector == 1: t2 = -u3 t6 = -u2 sum_t = t2 + t6 if sum_t > ts: k = ts / sum_t t2 *= k t6 *= k t7 = (ts - t2 - t6) / 2 self.t_a = t6 + t7 self.t_b = t2 + t6 + t7 self.t_c = t7 elif sector == 3: t2 = u1 t3 = u2 sum_t = t2 + t3 if sum_t > ts: k = ts / sum_t t2 *= k t3 *= k t7 = (ts - t2 - t3) / 2 self.t_a = t7 self.t_b = t2 + t3 + t7 self.t_c = t3 + t7 elif sector == 2: t1 = -u1 t3 = -u3 sum_t = t1 + t3 if sum_t > ts: k = ts / sum_t t1 *= k t3 *= k t7 = (ts - t1 - t3) / 2 self.t_a = t7 self.t_b = t3+t7 self.t_c = t1 + t3 + t7 elif sector == 6: t1 = u2 t5 = u3 sum_t = t1 + t5 if sum_t > ts: k = ts / sum_t t1 *= k t5 *= k t7 = (ts - t1 - t5) / 2 self.t_a = t5 + t7 self.t_b = t7 self.t_c = t1 + t5 + t7 elif sector == 4: t4 = -u2 t5 = -u1 sum_t = t4 + t5 if sum_t > ts: k = ts / sum_t t4 *= k t5 *= k t7 = (ts - t4 - t5) / 2 self.t_a = t4 + t5 + t7 self.t_b = t7 self.t_c = t5+t7 def update_theta(self, theta): """更新角度 theta,并计算 sine 和 cosine""" self.u_theta = theta self.sine = math.sin(theta) self.cosine = math.cos(theta) 4.3 测试代码 # 示例用法 if __name__ == "__main__": foc = FOC() # 设置输入电流 t_a_wave = []; t_b_wave = []; t_c_wave = []; u_a_wave = []; u_b_wave = []; u_c_wave = []; u_ab_wave = []; u_bc_wave = []; u_ca_wave = []; n_mid_wave = []; # 设置角度 theta for i in np.arange(0,7,0.001): theta = i # 30 度转换为弧度 foc.update_theta(theta) # 设置 d-q 轴电压 foc.u_d = 1 foc.u_q = 0.0 # 执行 Clarke 变换 #foc.clark() #print(f"Clarke 变换结果: i_alpha = {foc.i_alpha}, i_beta = {foc.i_beta}") # 执行 Park 变换 #foc.park() #print(f"Park 变换结果: i_d = {foc.i_d}, i_q = {foc.i_q}") # 执行逆 Park 变换 foc.ipark() print(f"逆 Park 变换结果: u_alpha = {foc.u_alpha}, u_beta = {foc.u_beta}") # 执行 SVPWM, # t_a, t_b, t_c 是实际a , b , c 端电压:端子与母线GND的压差,a、b、c点伏秒电压, foc.svpwm() # foc.iclark() # foc.iclark2pwm() print(f"SVPWM 结果: t_a = {foc.t_a}, t_b = {foc.t_b}, t_c = {foc.t_c}") #执行得到 U_a U_b Uc的实际值 # 其是线电压波形, 三次谐波被消除掉, # a 的相电压 u_a= foc.t_a - 0.5 * (foc.t_b + foc.t_c); u_b = foc.t_b - 0.5 * (foc.t_a + foc.t_c); u_c = -(u_a+u_b) # 线电压计算: u_ab_wave.append(u_a-u_b); u_bc_wave.append(u_b - u_c); u_ca_wave.append(u_c-u_a); n=0.5 * (foc.t_b + foc.t_c); u_a_wave.append(u_a); u_b_wave.append(u_b); u_c_wave.append(u_c); t_a_wave.append(foc.t_a); t_b_wave.append(foc.t_b); t_c_wave.append(foc.t_c); # 绘制散点图 plt.subplot(3,1,1) plt.scatter(range(len(u_ab_wave)), u_ab_wave, color='red', marker='.') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(u_bc_wave)), u_bc_wave, color='green', marker='.') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(u_ca_wave)), u_ca_wave, color='blue', marker='.') # 使用索引作为X轴,数据作为Y轴 plt.title("line voltage") # 添加标题 plt.xlabel("Index") # 添加X轴标签 plt.ylabel("Value") # 添加Y轴标签 plt.grid(True) # 添加网格 #plt.show() # 显示图形 # plt.subplot(3, 1, 2) plt.scatter(range(len(t_a_wave)), t_a_wave, color='red', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(t_b_wave)), t_b_wave, color='green', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(t_c_wave)), t_c_wave, color='blue', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.title("endpoint voltage") # 添加标题 plt.xlabel("Index") # 添加X轴标签 plt.ylabel("Value") # 添加Y轴标签 plt.grid(True) # 添加网格 #plt.show() # 显示图形 # # # # 绘制散点图 plt.subplot(3, 1, 3) plt.scatter(range(len(u_a_wave)), u_a_wave, color='red', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(u_b_wave)), u_b_wave, color='green', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.scatter(range(len(u_c_wave)), u_c_wave, color='blue', marker='o') # 使用索引作为X轴,数据作为Y轴 plt.title("xiang voltage") # 添加标题 plt.xlabel("Index") # 添加X轴标签 plt.ylabel("Value") # 添加Y轴标签 plt.grid(True) # 添加网格 plt.show() # 显示图形 # ## 1.引入库 代码如下(示例): ```c import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import warnings warnings.filterwarnings('ignore') import ssl ssl._create_default_https_context = ssl._create_unverified_context 4.4 测试结果 4.5 结果分析相电压、线电压、端电压的概念: zhuanlan.zhihu /p/448123274
对于控制电机旋转的电流的波形: 正弦 对应的相电压:正弦 对应的线电压为ua-ub: 正弦+ 正弦=》正弦 端电压:a点到母线参考GND的波形=》马鞍形
为什么形成了马鞍形?? 参考这个博主对正格6个扇区分别的计算,可以算出来其是每个扇区分别对应了马鞍形的几个分段 .bilibili /video/BV1wvWoeSErL/?spm_id_from=333.1391.0.0&vd_source=08330a8c02f0c1cbea0eb7eea17e1e8d
那么为什么这个马鞍形代表了端电压? 我的理解如下,实际上,马鞍形的绘制,我是直接使用a b c 三个线圈在一个周期内的导通的时间长度来绘制的,也就是a , b,c 三相电压的平均值,所以相当于a点对于gnd 的电压变化
根据端电压,如何得到相电压呢?: 从向量图角度理解: ua,ub,uc 的端电压知道,对应的方向也是知道的,求得相电压如下: Uoa=Ua-ubcos(60°)-Uccos(60°) Uob=Ub-uacos(60°)-Uccos(60°) Uoc=Uc-uacos(60°)-Ubcos(60°)=-(Uoa+Uob) 如上如果用电流的方式理解大概如下: (1) 电机的三相电流,同一个时刻只有一个流入或者只有一个流出。 如下的计算公式: u_a= foc.t_a - 0.5 * (foc.t_b + foc.t_c); u_b = foc.t_b - 0.5 * (foc.t_a + foc.t_c); u_c = -(u_a+u_b)
用上图的基尔霍夫定律来说明,以a相距离: a流入,a导通了foc.ta的时间, b和c 分别反向抵消了了一部分 这个地方我还是不确定该如何理解?
如何得到线电压呢? 有了端电压Ua Ub Uc ,我们先求Uab Ubc Uca 相减即可
总结
如上我们讲解SVPWM的基本原理,给出了撰写过程,以及我的理解方式
自学FOC系列分享--SVPWM和clark逆变换及代码实战由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“自学FOC系列分享--SVPWM和clark逆变换及代码实战”
上一篇
Netty入门详解