自学FOC系列分享--SVPWM和clark 逆变换
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_target
- id_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是如何写的
我主要参考这篇文章:
https://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 结果分析
相电压、线电压、端电压的概念:
https://zhuanlan.zhihu.com/p/448123274
对于控制电机旋转的电流的波形: 正弦
对应的相电压:正弦
对应的线电压为ua-ub: 正弦+ 正弦=》正弦
端电压:a点到母线参考GND的波形=》马鞍形
为什么形成了马鞍形??
参考这个博主对正格6个扇区分别的计算,可以算出来其是每个扇区分别对应了马鞍形的几个分段
https://www.bilibili.com/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的基本原理,给出了撰写过程,以及我的理解方式