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

目录

概述

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

    作者:mftang

    物联沃分享整理
    物联沃-IOTWORD物联网 » 磁场定向控制 (FOC)模型的C语言实现(STM32G4)

    发表回复