电机控制的空间矢量调制 (SVPWM)

目录

概述

1 电机控制的空间矢量调制 (SVPWM)介绍

2 实现原理

2.1 设计要求

2.2 SVPWM 的实现

3 SVPWM的C语言 

3.1 代码文件

3.2 STM32G4平台上验证

 4 源代码文件


概述

本文主要介绍电机控制的空间矢量调制 (SVPWM),空间矢量调制 (SVPWM) 是感应电机和永磁同步电机 (PMSM) 磁场定向控制的常用方法。空间矢量调制负责生成脉宽调制信号以控制逆变器的开关,由此产生所需的调制电压,以所需的速度或转矩驱动电机。空间矢量调制也称为空间矢量脉宽调制 (SVPWM)。文中介绍了该部分内容的实现原理,并实现C语言实现其代码。

1 电机控制的空间矢量调制 (SVPWM)介绍

空间矢量调制 (SVPWM) 是感应电机和永磁同步电机 (PMSM) 磁场定向控制的常用方法。空间矢量调制负责生成脉宽调制信号以控制逆变器的开关,由此产生所需的调制电压,以所需的速度或转矩驱动电机。空间矢量调制也称为空间矢量脉宽调制 (SVPWM)。

2 实现原理

2.1 设计要求

试考虑三相逆变器电机控制的空间矢量调制,该逆变器具有六个开关,如以下等效电路所示。注意,有八种有效的开关配置。

用于电机控制的空间矢量调制 (SVPWM)

电机控制的空间矢量调制 (SVM)

 每种开关配置都会产生特定的电压,施加于电机端子。电压是基本空间矢量,以空间矢量六边形表示其幅值和方向。

通过对开关区间内的基本空间矢量(方向)和零矢量(幅值)作用时间进行调节,可以近似得到空间矢量六边形内任意位置、任意幅值的电压矢量。

例如:  图中,一个脉宽调制 (PWM) 周期内,选择两个相邻空间矢量(图中的 U3 和 U4)分别作用一段时间、在周期其余时间内由零矢量(U7 或 U8)作用,从而得到近似平均参考矢量 Uref。 

通过控制开关序列,即控制脉冲的导通持续时间,就可以在每个 PWM 周期获得具有变化幅值和方向的任何电压矢量。空间矢量调制方法的目标是在每个 PWM 周期生成与参考电压矢量相符的开关序列,以实现连续旋转的空间矢量。

2.2 SVPWM的实现

空间矢量调制方法基于参考电压矢量进行操作,在每个 PWM 周期为逆变器生成适当导通信号,目标是实现连续旋转的空间矢量。

采用空间矢量调制的磁场定向控制架构示意图:

在每个 PWM 周期,以电压矢量作为输入参考,SVM 算法会:

  • 基于参考电压矢量计算开关导通时间
  • 基于导通时间生成马鞍波
  • 基于导通时间为逆变器开关生成适当的导通脉冲

SVPWM算法生成的空间矢量调制电压信号的波形图:

所生成的马鞍波能够最大程度地利用直流总线电压。与正弦脉宽调制 (SPWM) 方法相比,该方法能提供更好的额定电压输出。然后,将生成的导通信号应用于三相逆变器的开关,以所需的速度或转矩驱动电机。

3 SVPWM的C语言 

3.1 代码文件

代码84行: 设置SVPWM的采样周期

代码85~87行: 设置象限的电压值

代码91行: 计算扇区

代码97 ~ 111 行: 实现第5扇区的波形

 代码116 ~ 129 行: 实现第1扇区的波形

 代码130~ 145 行: 实现第3扇区的波形

  代码147~ 162行: 实现第2扇区的波形

  代码165~ 179行: 实现第6扇区的波形

  代码181~ 196行: 实现第4扇区的波形

3.2 STM32G4平台上验证

1)测试代码实现

2)运行代码和查看波形

 查看α,β坐标系上的波形图:

经svm转换后的波形图:

 4 源代码文件

1)  .c文件中的代码

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name        :  foc_ctrl.h
 * Description      :  foc driver base on stm32f446
 ******************************************************************************
 * @attention
 *
* COPYRIGHT:    Copyright (c) 2024  tangminfei2013@126.com

* DATE:         JUL 05th, 2024

 ******************************************************************************
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/

#include "foc_ctrl.h"

#define  SQRT_3           1.7320508f
#define  SQRT_3_DIV_2     0.8660254f
#define  DIV_1            0.5f

FOC_T FOC;
/*****************************************************************************
Clarke变换 输入三相电流,输出alpha,bate电流
Iα = Ia
Iβ = (Ia + 2Ib) / sqrt(3)
******************************************************************************/
void clarkeTransform(Phase_T *abc, AlphaBeta_T *alphaBeta)
{
    alphaBeta->alpha = abc->Ua;
    alphaBeta->beta = (abc->Ua + 2 * abc->Ub) * SQRT_3;
}

/****************************************************************************
Park变换,输入电角度、Ialpha和Ibeta,经过Park变换得到Iq、Id
Id = Iα · cosθ + Iβ · sinθ
Iq = Iα · sinθ + Iβ · cosθ
*****************************************************************************/
void parkTransform(const AlphaBeta_T *alphaBeta, float angle, DQ_T *dq)
{
    float sinAngle = sin(angle);
    float cosAngle = cos(angle);

    dq->d = cosAngle * alphaBeta->alpha + sinAngle * alphaBeta->beta;
    dq->q = -sinAngle * alphaBeta->alpha + cosAngle * alphaBeta->beta;
}

/***************************************************************************
park逆变换,输入Uq、Ud得到Ualpha、Ubeta
Uα = Ud · cosθ - Uq · sinθ
Uβ = Ud · sinθ + Uq · cosθ
****************************************************************************/
void inverseParkTransform(DQ_T *dq, AlphaBeta_T *alphaBeta, float angle)
{
    float cosAngle = cos(angle);
    float sinAngle = sin(angle);

    alphaBeta->alpha = dq->d * cosAngle - dq->q * sinAngle;
    alphaBeta->beta = dq->d * sinAngle + dq->q * cosAngle;
}

/**********************************************************************************************************
Clarke逆变换,输入Ualpha、Ubeta,得到Ua,Ub,Uc
Ua = Uα
Ub = -1/2 * Uα + sqrt(3)/2  * Uβ
Ub = -1/2 * Uα - sqrt(3)/2  * Uβ
**********************************************************************************************************/
 void inverseClarkeTransform(AlphaBeta_T *abVoltage, Phase_T *abc)
 {
     abc->Ua = abVoltage->alpha;
     abc->Ub = -DIV_1 * abVoltage->alpha + SQRT_3_DIV_2 * abVoltage->beta;
     abc->Uc = -DIV_1 * abVoltage->alpha - SQRT_3_DIV_2 * abVoltage->beta;
 }

void SVPWM(SVPWM_T *svpwm, Phase_T *abc)
{
    float sum;
    float k_svpwm;

    // step-1: 设置象限电压值
    svpwm->Ts = 1.0f;        // SVPWM的采样周期
    svpwm->u1 = abc->Ua;
    svpwm->u2 = abc->Ub;
    svpwm->u3 = abc->Uc;
    
    // step2:扇区判断
    // 根据u1、u2和u3的正负情况确定所处的扇区
    svpwm->sector = (svpwm->u1 > 0.0f) + ((svpwm->u2 > 0.0f) << 1) + ((svpwm->u3 > 0.0f) << 2); // N=4*C+2*B+A

    // step3:计算基本矢量电压作用时间(占空比)
    // 根据扇区的不同,计算对应的t_a、t_b和t_c的值,表示生成的三相电压的时间
    switch (svpwm->sector)
    {
        case 5:
            // 扇区5
            svpwm->t4 = svpwm->u3;
            svpwm->t6 = svpwm->u1;
            sum = svpwm->t4 + svpwm->t6;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; //
                svpwm->t4 = k_svpwm * svpwm->t4;
                svpwm->t6 = k_svpwm * svpwm->t6;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t4 - svpwm->t6) / 2;
            svpwm->ta = svpwm->t4 + svpwm->t6 + svpwm->t7;
            svpwm->tb = svpwm->t6 + svpwm->t7;
            svpwm->tc = svpwm->t7;
            break;
            
          case 1:
            // 扇区1
            svpwm->t2 = -svpwm->u3;
            svpwm->t6 = -svpwm->u2;
            sum = svpwm->t2 + svpwm->t6;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; // 计算缩放系数
                svpwm->t2 = k_svpwm * svpwm->t2;
                svpwm->t6 = k_svpwm * svpwm->t6;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t2 - svpwm->t6) / 2;
            svpwm->ta = svpwm->t6 + svpwm->t7;
            svpwm->tb = svpwm->t2 + svpwm->t6 + svpwm->t7;
            svpwm->tc = svpwm->t7;
            break; 
        case 3:
            // 扇区3
            svpwm->t2 = svpwm->u1;
            svpwm->t3 = svpwm->u2;
            sum = svpwm->t2 + svpwm->t3;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; //
                svpwm->t2 = k_svpwm * svpwm->t2;
                svpwm->t3 = k_svpwm * svpwm->t3;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t2 - svpwm->t3) / 2;
            svpwm->ta = svpwm->t7;
            svpwm->tb = svpwm->t2 + svpwm->t3 + svpwm->t7;
            svpwm->tc = svpwm->t3 + svpwm->t7;    
            break;

        case 2:
            // 扇区2
            svpwm->t1 = -svpwm->u1;
            svpwm->t3 = -svpwm->u3;
            sum = svpwm->t1 + svpwm->t3;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; 
                svpwm->t1 = k_svpwm * svpwm->t1;
                svpwm->t3 = k_svpwm * svpwm->t3;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t1 - svpwm->t3) / 2;
            svpwm->ta = svpwm->t7;
            svpwm->tb = svpwm->t3 + svpwm->t7;
            svpwm->tc = svpwm->t1 + svpwm->t3 + svpwm->t7;    
            break;

        case 6:
            // 扇区6
            svpwm->t1 = svpwm->u2;
            svpwm->t5 = svpwm->u3;
            sum = svpwm->t1 + svpwm->t5;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; // 
                svpwm->t1 = k_svpwm * svpwm->t1;
                svpwm->t5 = k_svpwm * svpwm->t5;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t1 - svpwm->t5) / 2;
            svpwm->ta = svpwm->t5 + svpwm->t7;
            svpwm->tb = svpwm->t7;
            svpwm->tc = svpwm->t1 + svpwm->t5 + svpwm->t7;
            break;

        case 4:
            // 扇区4
            svpwm->t4 = -svpwm->u2;
            svpwm->t5 = -svpwm->u1;
            sum = svpwm->t4 + svpwm->t5;
            if (sum > svpwm->Ts)
            {
                k_svpwm = svpwm->Ts / sum; // 
                svpwm->t4 = k_svpwm * svpwm->t4;
                svpwm->t5 = k_svpwm * svpwm->t5;
            }
            svpwm->t7 = (svpwm->Ts - svpwm->t4 - svpwm->t5) / 2;
            svpwm->ta = svpwm->t4 + svpwm->t5 + svpwm->t7;
            svpwm->tb = svpwm->t7;
            svpwm->tc = svpwm->t5 + svpwm->t7;    
            break;

        default:
            break;
    }

    // step4:3路PWM输出
}

void foc_test(void)
{
    int run_cnt = 10;
    float theta = 0;
    float ta,tb,tc;
    
    DQ_T dq_t;
    AlphaBeta_T alphaBeta_t;
    SVPWM_T svpwm_out;
    Phase_T phase_t;

    dq_t.d = 0.2f;
    dq_t.q = 0.0f;

    while( run_cnt--)
    {
        for ( theta = 0; theta < 6.2831853f; theta += 0.275f )
        {
            // 逆Park变换
            inverseParkTransform(&dq_t,&alphaBeta_t,theta);
            // 逆Clark变换
            inverseClarkeTransform(&alphaBeta_t, &phase_t);
            
            // swpwm 转换
            SVPWM( &svpwm_out,&phase_t );
             
            ta = 100.0f*svpwm_out.ta;
            tb = 100.0f*svpwm_out.tb;
            tc = 100.0f*svpwm_out.tc;
            
            printf( "%.4f,%.4f,%.4f,%.4f,%.4f\n", 
                    alphaBeta_t.alpha*100.0f ,
                    alphaBeta_t.beta*100.0f ,ta,tb,tc);
            
//            printf("%.4f,%.4f,%.4f,%.4f,%.4f \n", alphaBeta_t.alpha,alphaBeta_t.beta,
//                                                  phase_t.Ua,phase_t.Ub,phase_t.Uc );
        }
    }
}



/* End of this file */

2)  .h文件中的代码

/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name        :  foc_ctrl.h
 * Description      :  foc driver base on stm32f446
 ******************************************************************************
 * @attention
 *
* COPYRIGHT:    Copyright (c) 2024  tangminfei2013@126.com

* DATE:         JUL 05th, 2024

 ******************************************************************************
 */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#ifndef __FOC_CTRL_H
#define __FOC_CTRL_H

/*****************************************************************************/
/* Includes                                                                  */
/*****************************************************************************/
#include "main.h"
#include "utils_types.h"


#ifdef _cplusplus
extern "C" {
#endif 

typedef struct 
{
    float Ia;  // Phase A current
    float Ib;  // Phase B current
    float Ic;  // Phase C current

    float Ua;  // Phase A Voltage
    float Ub;  // Phase B Voltage
    float Uc;  // Phase C Voltage

} Phase_T;

typedef struct
 {
    float alpha;  // alpha-axis current
    float beta;   // beta-axis current
} AlphaBeta_T;

typedef struct 
{
    float d;  // d-axis current
    float q;  // q-axis current
} DQ_T;


typedef struct
{
    int sector;

    float u1;
    float u2;
    float u3;  

    float ta;
    float tb;
    float tc;

    float Ts;
    float t0;
    float t1;
    float t2;
    float t3;
    float t4;
    float t5;
    float t6;
    float t7;
    
} SVPWM_T;

typedef struct
{
    float U_d;
    float U_q;
    float theta;
    float U_alpha;
    float U_bate;
    Phase_T Phase_Curr;
    AlphaBeta_T  AlphaBeta;
    DQ_T DQ;
} FOC_T;

extern FOC_T FOC;


void foc_test(void);


#ifdef _cplusplus
}
#endif   

#endif    /* __FOC_CTRL_H */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977802.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服务器离线部署DeepSeek

目标 本次部署的目标是在本地服务器上部署DeepSeek。但是该服务不能连接外网&#xff0c;因此只能使用离线部署的方式。为了一次完成部署。现在云服务器上进行尝试。 云服务器部署尝试 云服务器配置 CentOS72080Ti 11GB 安装准备 1、上传iso并配置为本地yum源 安装前先将…

Unity打包APK报错 using a newer Android Gradle plugin to use compileSdk = 35

Unity打包APK报错 using a newer Android Gradle plugin to use compileSdk 35 三个报错信息如下 第一个 WARNING:We recommend using a newer Android Gradle plugin to use compileSdk 35This Android Gradle plugin (7.1.2) was tested up to compileSdk 32This warning…

Ubuntu 22.04安装K8S集群

以下是Ubuntu 22.04安装Kubernetes集群的步骤概要 一、设置主机名与hosts解析 # Master节点执行 sudo hostnamectl set-hostname "k8smaster" # Worker节点执行 sudo hostnamectl set-hostname "k8sworker1"# 所有节点的/etc/hosts中添加&#xff1a; ca…

《AI 大模型 ChatGPT 的传奇》

《AI 大模型 ChatGPT 的传奇》 ——段方 某世界 100 强企业大数据/AI 总设计师 教授 北京大学博士后 助理 &#xff1a;1三6三二四61四五4 1 AI 大模型的概念和特点 1.1 什么是”大模型、多模态“&#xff1f; 1.2 大模型带来了什么&#xff1f; 1.3 大模型为什么能产生质变&am…

期权帮|股指期货多单和空单有什么区别?

锦鲤三三每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 股指期货多单和空单有什么区别&#xff1f; 一、股指期货多单和空单定义与操作方向&#xff1a; &#xff08;1&#xff09;股指期货多单定义&#xff1a;投资者买入股指期货合…

从【人工智能】到【计算机视觉】,【深度学习】引领的未来科技创新与变革

前几天偶然发现了一个超棒的人工智能学习网站&#xff0c;内容通俗易懂&#xff0c;讲解风趣幽默&#xff0c;简直让人欲罢不能。忍不住分享给大家&#xff0c;点击这里立刻跳转&#xff0c;开启你的AI学习之旅吧&#xff01; 前言 – 人工智能教程https://www.captainbed.cn/l…

如何在VMware虚拟机的window10系统中安装网易mumu模拟器

安卓模拟器是可以在电脑的windows环境中运行手机软件的工具,喜欢网游或者是要逆向安卓应用应该都要安装这个模拟器,如果要模拟器正常工作,主机的虚拟化应该开启,也就是要开启vt。在有些情况下,需要把模拟器安装到电脑的虚拟机里,隔离模拟器与主机,这时vt的开启就稍麻烦些…

【Rust中级教程】2.10. API设计原则之受约束性(constrained) Pt.1:对类型进行修改、`#[non_exhaustive]`注解

喜欢的话别忘了点赞、收藏加关注哦&#xff08;加关注即可阅读全文&#xff09;&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 2.10.1. 接口的更改要三思 如果你的接口要做出对用户可见的更改&#xff0c;那么一定要三思…

【阿】(阿联酋)迪拜求职指南(Gulftalent)

https://www.gulftalent.com/resources/dubai-jobs-guide 文章目录 Types of Employers 雇主类型Multinationals 跨国公司Large local firms 大型本地公司Local SMEs 本地中小企Government 政府Assessing your Chances 评估您的机会其他城市&#xff08;阿布扎比和沙迦&#xf…

win11 24h2 远程桌面 频繁断开 已失去连接 2025

一、现象 Windows11自升级2025年2月补丁后版本号为系统版本是26100.3194&#xff0c;远程桌面频繁断开连接&#xff0c;尝试连接&#xff0c;尤其在连接旧的server2012 二、临时解决方案 目前经测试&#xff0c;在组策略中&#xff0c;远程桌面连接客户端&#xff0c;关闭客户…

[算法--前缀和] 二维前缀和

目录 1. 前缀和数组的递推公式: dp[i][j] = dp[i-1][j] + dp[i][j-1] + nums[i][j] - dp[i-1][j-1].2. 前缀和数组需要额外开一行一列.3. 想要快速求任意一个矩形和, 实际上是多个前缀和的拼凑.今天来贴一道模板题 -> 二位前缀和 然后我们来简单总结两个公式: 因为这是一个…

公共数据授权运营模式研究(总体框架、主要模式及发展趋势)

本报告以公共数据运营模式为核心&#xff0c;以释放公共数据价值为目标&#xff0c;深入分析公共数据概念及特征&#xff0c;厘清公共数据运营的内涵及本质&#xff0c;提出纵深分域数据要素市场运营体系的总体思路&#xff0c;构建了一座&#xff08;一个数据底座&#xff09;…

MySQL主从架构

MySQL主从架构 MySQL REPLICATION 在实际生产环境中&#xff0c;如果对数据库的读和写都在一个数据库服务器中操作。无论是在安全性、高可用性&#xff0c;还是高并发等各个方面都是完全不能满足实际需求的&#xff0c;因此&#xff0c;一般来说都是通过主从复制&#xff08;…

C# Combox 绑定数据

1.在界面中添加一个combox 2.将数据绑定到combox List<GrindingType> type new List<GrindingType>();type.Add(new GrindingType { Id 1, Name "Product A", Type new List<string> { "1", "2" } });type.Add(new Grin…

idea 部署 AJ-Report 启动的注意事项

AJ-Report 入门参考&#xff1a; AJ-Report 初学(入门教程) gitee 下载&#xff1a;https://gitee.com/anji-plus/report/releases 根据上面提供的 gitee 下载链接&#xff0c;点击直接下载 最上面的就是最新版本的&#xff0c;旧版本往下拉就可以找到&#xff0c;有三个下载…

【Go | 从0实现简单分布式缓存】-3:分布式节点通信

本文目录 一、通信流程二、peers.go三、http.go四、geecache.go五、测试代码 本文为极客兔兔动手写分布式缓存GeeCache学习笔记。 一、通信流程 在前面一节中&#xff0c;已经为 HTTPPool 实现了服务端功能&#xff0c;通信不仅需要服务端还需要客户端&#xff0c;因此本节来…

Win32/ C++ 简易对话框封装框架(多语言, 通知栏菜单, 拖拽文件处理)

Win32 简易对话框封装简易框架示例 1. 菜单操作: 多语言 2. 通知栏图标菜单 3. 其他操作: 接受拖拽文件等等 CDialogFrame.h #pragma once #include "CWindow/CDialogBase.h" #include "CNSFHeader.h" #include "Win32Utils/CBytesUtils.h" …

如何在 Linux 上安装和配置 Zsh

文章目录 如何在 Linux 上安装和配置 Zsh1. 安装 Zsh1.1 在 Ubuntu/Debian 上安装1.2 在 CentOS/RHEL/Fedora 上安装1.3 在 Arch Linux 上安装1.4 验证 Zsh 安装 2. 设置 Zsh 为默认 Shell2.1 验证默认 shell 3. 配置 Zsh3.1 使用 Oh My Zsh3.1.1 安装 Oh My Zsh3.1.2 启用插件…

Ubuntu搭建esp32环境 配置打开AT指令集 websocket功能

1&#xff0c;搭建前提 环境搭建参考乐鑫官网给的本地编译 ESP-AT 工程方法 因为公司电脑和网络的特殊性&#xff0c;不能正确解析域名&#xff08;仅在浏览器上可以访问&#xff09; &#xff0c;所以这边访问的时候改成了ssh 未了避免使用外网困难的问题&#xff0c;这里用…

网络安全第三次练习

一、实验拓扑 二、实验要求 配置真实DNS服务信息&#xff0c;创建虚拟服务&#xff0c;配置DNS透明代理功能 三、需求分析 1.创建用户并配置认证策略 2.安全策略划分接口 3.ip与策略配置 四、实验步骤 1.划分安全策略接口 2.创建用户并进行策略认证 3.配置安全策略 4.NAT配…