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

news/2025/2/25 23:51:43

目录

概述

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 */


http://www.niftyadmin.cn/n/5866975.html

相关文章

【计算机网络】传输层协议(UDP TCP)

目录 1. 端口号 端口号的划分 2. UDP UDP协议格式 在系统中的描述 缓冲区 使用注意事项 3. TCP 缓冲区 TCP协议格式 标记位 面向字节流 确认应答机制 流量控制 超时重传 连接管理 滑动窗口 延迟应答 捎带应答 快重传 拥塞控制 粘包问题 TIME_WAIT状态 总结 1. 端口…

w227springboot旅游管理系统设计与实现

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;原创团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文…

【Unity】鱼群效果模拟

鱼群效果模拟 文章目录 鱼群效果模拟Boid算法实现方式version1_CPUversion2_GPUversion3_Multilaterationversion4_Bitonic_Sorting &#xff08;GPU友好&#xff09;version5_Skinning &#xff08;TODO&#xff09; 细节项优化项参考链接 Boid算法 Boid算法是一种模拟群体行…

ASP.NET Core Clean Architecture

文章目录 项目地址一、项目主体1. CQRS1.1 Repository数据库接口1.2 GetEventDetail 完整的Query流程1.3 创建CreateEventCommand并使用validation 2. EFcore层2.1 BaseRepository2.2 CategoryRepository2.3 OrderRepository 3. Email/Excel导出3.1 Email1. IEmail接口层2. Ema…

C++的allactor

https://zhuanlan.zhihu.com/p/693267319 1 双层内存配置器 SGI设计了两层的配置器&#xff0c;也就是第一级配置器和第二级配置器。同时为了自由选择&#xff0c;STL又规定了 __USE_MALLOC 宏&#xff0c;如果它存在则直接调用第一级配置器&#xff0c;不然则直接调用第二级配…

华为数通 HCIP-Datacom H12-831 新题

2024年 HCIP-Datacom&#xff08;H12-831&#xff09;变题后的新题&#xff0c;完整题库请扫描上方二维码&#xff0c;新题在持续更新中。 某台IS-IS路由器自己生成的LSP信息如图所示&#xff0c;从LSP信息中不能推断出以下哪一结论? A&#xff1a;该路由器某一个接口的IPv6地…

本地VSCode远程连wsl2中的C++环境的开发配置指南

请参考上一遍文章&#xff1a;在windows上安装wsl2&#xff0c;在wsl2中配置C开发环境-CSDN博客

量子计算如何改变加密技术:颠覆与变革的前沿

量子计算如何改变加密技术:颠覆与变革的前沿 大家好,我是Echo_Wish,一名专注于人工智能和Python的自媒体创作者。今天,我们来探讨一个前沿且引人深思的话题——量子计算如何改变加密技术。随着量子计算的快速发展,传统的加密技术面临前所未有的挑战和机遇。本文将详细介绍…