磁场定向控制 (FOC)模型的C语言实现(STM32G4)

发布于:2025-02-27 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

概述

1 磁场定向控制 (FOC)介绍

1.1 FOC控制模型介绍

1.2  模型功能

2 FOC模型的几个重要的转换关系

2.1 Clarke Transform 

2.2  Inverse Clarke Transform

2.3  Park Transform 

2.4 Inverse Park Transform 

3 STM32模拟实现FOC

3.1 FOC算法的C语言实现

3.2 测试代码实现

4 波形验证

5 源代码文件


概述

本文主要介绍磁场定向控制 (FOC)的控制原理框架结构,还简要介绍了模型中使用几个模块的数学实现方法,并使用C语言将这些数学模型实现出来,同时在STM32G4平台上对其做验证。

1 磁场定向控制 (FOC)介绍

1.1 FOC控制模型介绍

磁场定向控制 (FOC),又称矢量控制,是一种控制方法,用于在多种电机类型(包括感应电机、永磁同步电机 (PMSM 和无刷直流 (BLDC) 电机)的全转矩和转速范围内实现良好的控制能力。如果超出额定转速,则使用配合弱磁磁场磁场定向控制。

以下模块图显示了一个磁场定向控制架构,包括以下组件:

1.2  模型功能

设计磁场定向控制的电机控制工程师执行以下任务:

1)为电流回路开发具有两个 PI 控制器的控制器架构
2)为可选的转速外环和位置外环开发 PI 控制器
3)调节所有 PI 控制器的增益以满足性能要求
4)设计用于控制 PWM 的空间矢量调制器

5)如果使用无传感器控制,则设计观测器算法来估计转子位置和速度
6)设计每安培最大转矩或弱磁控制算法,以生成最佳 id_ref 和 iq_ref
7)实现在计算上高效的帕克变换、克拉克变换和帕克逆变换
8)设计故障检测和保护逻辑
9)验证和确认控制器在不同工况下的性能
10)在微控制器或 FPGA 上实现采用定点或浮点的控制器

2 FOC模型的几个重要的转换关系

2.1 Clarke Transform 

Clarke Transform 模块计算 abc 参考系中平衡三相分量的克拉克变换,并输出静止 αβ 参考系中平衡两相正交分量。该模块也可以计算三相分量 ab 和 c 的克拉克变换,并输出分量 αβ 和 0。对于平衡系统,零分量等于零。使用输入数参数以使用两个或三个输入。

使用双输入配置时,该模块接受三相 (abc) 中的两个信号,自动计算第三个信号,并输出 αβ 参考系中的对应分量。例如,该模块接受 a 和 b 输入值或多路复用输入值 abc,其中相位 a 轴与 α 轴对齐。

数学模型如下:

2.2  Inverse Clarke Transform

Inverse Clarke Transform 模块计算静止 αβ 参考系中平衡的两相正交分量的克拉克逆变换,并输出静止 abc 参考系中平衡的三相分量。该模块也可以计算分量 αβ 和 0 的克拉克逆变换,以输出三相分量 ab 和 c。对于平衡系统,零分量等于零。使用输入数参数以使用两个或三个输入。

该模块接受 α-β 轴分量作为输入,并输出对应的三相信号,其中相位 a 轴与 α 轴对齐。

 数学模型:

2.3  Park Transform 

Park Transform 模块计算静止 αβ 参考系中两相正交分量(αβ)或多路复用 αβ0 分量的帕克变换。该模块接受以下输入:

  • 静止参考系中的 α-β 轴分量或多路复用分量 αβ0。使用输入数参数以使用两个或三个输入。

  • 对应变换角度的正弦值和余弦值。

在使用双输入配置时,它会输出旋转 dq 参考系中的正交直轴 (d) 和交轴 (q) 分量。在使用三输入配置时,它输出多路复用分量 dq0

对于平衡系统,零分量等于零。

可以配置模块,使 d 轴或 q 轴在时间 t = 0 处与 α 轴对齐。

下列各图显示在以下情形下 αβ 参考系和旋转 dq 参考系中的 α-β 轴分量:

数学模型:

  当 q 轴与 α 轴对齐时:

 其中:

  • fα 和 fβ 是静止 αβ 参考系中的两相正交分量。

  • fd 和 fq 是旋转 dq 参考系中的直轴和交轴正交分量。

2.4 Inverse Park Transform 

Inverse Park Transform 模块计算正交直轴 (d) 和正交轴 (q) 分量或旋转 dq 参考系中的多路复用 dq0 分量的帕克逆变换。可以对该模块进行配置,使 d 轴或 q 轴在时间 t = 0 处与 α 轴对齐。

该模块接受以下输入:

  • 旋转参考系中的 d-q 轴分量或多路复用分量 dq0。使用输入数参数以使用两个或三个输入。

  • 对应变换角度的正弦值和余弦值。

在使用双输入配置时,它输出静止 αβ 参考系中的两相正交分量。在使用三输入配置时,它输出多路复用分量 αβ0。对于平衡系统,零分量等于零。

下列各图显示在以下情形下的旋转 dq 参考系和 αβ 参考系中的 α-β 轴分量:

当 d 轴与 α 轴对齐时:

当 q 轴与 α 轴对齐时:

其中:

  • fd 和 fq 是旋转 dq 参考系中的直轴和交轴正交分量。

  • fα 和 fβ 是静止 αβ 参考系中的两相正交分量。

3 STM32模拟实现FOC

3.1 FOC算法的C语言实现

1)Clarke变换

2)Clarke逆变换

 3) Park变换

 4) park逆变换

3.2 测试代码实现

代码216行: 设置d轴的值为0.2f

代码217行: 设置q轴的值为0

代码221行: 设置电机DQ轴上运行的角度值范围(0~2π)

代码224行: 实现park逆变换,将D,Q坐标转变至α、β坐标系上

代码229行: 将经过逆park生成的ia,ib,ic,生成svpwm

4 波形验证

1)D,Q坐标系中的值经过逆park变换生成的α、β坐标系上的波形图,α、β的波形相位相差90°

 2)α、β坐标系经过逆Clark生成的ia,ib,ic的波形图,ia,ib,ic三个波形之间相位相差120°

3)将 α、β坐标系和ia,ib,ic的波形图放在同一个图像中进行参照

5 源代码文件

创建foc.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 */

 创建foc.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 */