精华内容
下载资源
问答
  • ST 电机库 电机位置环7天冲刺开发
    千次阅读
    2020-08-13 21:28:23

    *直流无刷电机驱动可以说是现代机器人的灵魂之一,这个系列的主题,主要是对于ST电机库,相关工具的使用教学及解读
    之所以说是冲刺开发,则主要是为了给这次长达10个月的驱动开发做一个总结。
    首先列明我使用的平台,以及现阶段的进度,供大家参考:
    主控板使用STM32F302RB NUCLEO官方板,驱动使用融创芯城设计的 IHM08m1V3版本,可支持比官方还大的驱动电流
    电机使用极飞的植保电机,KV值在90左右,绝对值编码器使用巨磁阻编码器TLE5012。
    现阶段开发进度为:编写好了TLE5012的驱动程序,使用单线SPI即可驱动,具体程序请参考以下博客:
    https://blog.csdn.net/qq_41544116/article/details/106690157
    并不需要反复的使能和失能配置,写的非常简练易用。
    其余资料,列举几篇站内的文章,也是我进行下一步位置闭环移植的主要依据:
    https://blog.csdn.net/zhanglifu3601881/article/details/103512318
    https://blog.csdn.net/zhanglifu3601881/article/details/103646498
    这个作者的其余文章也值得一看,写的很有内容
    但现在遇到的问题是:电机库太过于庞大和复杂,有些函数调用层数很多,不容易一下就看懂
    目前我的办法也只有参考教程逐步解读而已。若网友有高见还望指点一二。

    此篇教程主要意图在于,使用ST的电机库实现一个相对比较适用的电机伺服驱动器,实现速度,位置,力矩的三闭环。
    其实还有另外一种方案是移植arduino的simple foc库做这个项目,这个视时间紧张程度,如果做的话,我会另开一帖。
    首先实现程序的稳定运行,之后再行设计电路板,做背贴式驱动,便于集成到各种产品上。

    但要是说使用ST的芯片及方案实现简单的航模用无感FOC电调,则用ST的工具则相对简单,这个应用之后会单做教程。

    DAY1:开始行动*

    更多相关内容
  • ST电机库开源代码

    2018-12-02 20:12:37
    这个是ST电机库元代码,请在MotorControl\MCSDK\MCLib\Any\Src寻找源文件,里面包含电机的基本算法
  • 本资源为ST电机库5.2.0版本,FUL完全开源,有兴趣的同学可以借鉴学习。
  • ST电机库4.3手册

    2018-08-19 17:29:42
    ST电机库4.3版本手册,介绍ST电机库框架和原理,用于无刷直流和永磁电机控制。
  • ST最新电机库5.0.3

    2018-06-28 18:34:12
    ST最新电机库V5.0.3,新的马达驱动库是完全开源的了。厂家提供的代码还是很值得学习的。
  • 最新的ST官方电机库5.4资料11月更新 STM32微控制器具有运行矢量控制或FOC模式的行业标准Arm®Cortex®-M内核的性能,广泛用于空调,家用电器,无人机,建筑和工业自动化,医疗和电子行业的高性能驱动器中。 自行车...
  • 还可以通过按键进行菜单切换,显示pid参数和速度值等,无霍尔传感器,使用ST电机库5.3版本,使用0.96寸的屏OLED进行显示,OLED芯片型号为SSD1306,使用4线串行模式读写,有良好的人机交互界面,适合电机调速无感应用
  • 调用ST电机库5.0程序,采用高霍尔传感器实现无刷直流电机矢量控制
  • 调试ST电机库5.20遇到的问题

    千次阅读 2018-12-20 11:05:14
    1.使用带霍尔的无刷电机运行ST MotorControl Workbench 5.2.0生成的例程。霍尔状态经常出现0或者7,是因为霍尔信号干扰太大了吗?正常的霍尔状态为1-6,一出现0或7电机就不能转了。Iq的目标值变成6067(为标称最大...

    1.使用带霍尔的无刷电机运行ST MotorControl Workbench 5.2.0生成的例程。霍尔状态经常出现0或者7,是因为霍尔信号干扰太大了吗?正常的霍尔状态为1-6,一出现0或7电机就不能转了。Iq的目标值变成6067(为标称最大电流值,这个值是用标称电流3.8A转换的)。Iq的目标值是由于速度PI控制器输出的。当霍尔状态为0或7,程序将pHandle->SensorIsReliable = false; 认为此时速度传感器不可靠,将测到的速度值设成0了。由于0和设定的目标速度相差很大,同时电机不能转速度一直是0,速度PI控制器输出的Iq的目标值立马就超过了标称值6067.

    下图为用STMStudio读到的霍尔状态

    用示波器测到到霍尔波形

    阅读ST电机库中hall_speed_pos_fdbk.c源码中的void * HALL_TIMx_CC_IRQHandler( void * pHandleVoid )函数。

    void * HALL_TIMx_CC_IRQHandler( void * pHandleVoid )
    {
      HALL_Handle_t * pHandle = ( HALL_Handle_t * ) pHandleVoid;
      TIM_TypeDef * TIMx = pHandle->TIMx;
      uint8_t bPrevHallState;
      uint32_t wCaptBuf;
      uint16_t hPrscBuf;
      uint16_t hHighSpeedCapture;
    
      if ( pHandle->SensorIsReliable )
      {
        /* A capture event generated this interrupt */
        bPrevHallState = pHandle->HallState;
    
        if ( pHandle->SensorPlacement == DEGREES_120 )
        {
          pHandle->HallState  = LL_GPIO_IsInputPinSet( pHandle->H3Port, pHandle->H3Pin ) << 2
                                | LL_GPIO_IsInputPinSet( pHandle->H2Port, pHandle->H2Pin ) << 1
                                | LL_GPIO_IsInputPinSet( pHandle->H1Port, pHandle->H1Pin );
        }
        else
        {
          pHandle->HallState  = ( LL_GPIO_IsInputPinSet( pHandle->H2Port, pHandle->H2Pin ) ^ 1 ) << 2
                                | LL_GPIO_IsInputPinSet( pHandle->H3Port, pHandle->H3Pin ) << 1
                                | LL_GPIO_IsInputPinSet( pHandle->H1Port, pHandle->H1Pin );
        }
    
        switch ( pHandle->HallState )
        {
          case STATE_5:
            if ( bPrevHallState == STATE_4 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = pHandle->PhaseShift;
            }
            else if ( bPrevHallState == STATE_1 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift + S16_60_PHASE_SHIFT );
            }
            else
            {
            }
            break;
    
          case STATE_1:
            if ( bPrevHallState == STATE_5 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = pHandle->PhaseShift + S16_60_PHASE_SHIFT;
            }
            else if ( bPrevHallState == STATE_3 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift + S16_120_PHASE_SHIFT );
            }
            else
            {
            }
            break;
    
          case STATE_3:
            if ( bPrevHallState == STATE_1 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift + S16_120_PHASE_SHIFT );
            }
            else if ( bPrevHallState == STATE_2 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift + S16_120_PHASE_SHIFT +
                                                      S16_60_PHASE_SHIFT );
            }
            else
            {
            }
    
            break;
    
          case STATE_2:
            if ( bPrevHallState == STATE_3 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift + S16_120_PHASE_SHIFT
                                                      + S16_60_PHASE_SHIFT );
            }
            else if ( bPrevHallState == STATE_6 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift - S16_120_PHASE_SHIFT );
            }
            else
            {
            }
            break;
    
          case STATE_6:
            if ( bPrevHallState == STATE_2 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift - S16_120_PHASE_SHIFT );
            }
            else if ( bPrevHallState == STATE_4 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift - S16_60_PHASE_SHIFT );
            }
            else
            {
            }
            break;
    
          case STATE_4:
            if ( bPrevHallState == STATE_6 )
            {
              pHandle->Direction = POSITIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift - S16_60_PHASE_SHIFT );
            }
            else if ( bPrevHallState == STATE_5 )
            {
              pHandle->Direction = NEGATIVE;
              pHandle->MeasuredElAngle = ( int16_t )( pHandle->PhaseShift );
            }
            else
            {
            }
            break;
    
          default:
            /* Bad hall sensor configutarion so update the speed reliability */
            //pHandle->SensorIsReliable = false;
    
            break;
        }
    
    
    #ifdef HALL_MTPA
        {
          pHandle->_Super.hElAngle = pHandle->MeasuredElAngle;
        }
    #endif
    
        /* Discard first capture */
        if ( pHandle->FirstCapt == 0u )
        {
          pHandle->FirstCapt++;
          LL_TIM_IC_GetCaptureCH1( TIMx );
        }
        else
        {
          /* used to validate the average speed measurement */
          if ( pHandle->BufferFilled < pHandle->SpeedBufferSize )
          {
            pHandle->BufferFilled++;
          }
    
          /* Store the latest speed acquisition */
          hHighSpeedCapture = LL_TIM_IC_GetCaptureCH1( TIMx );
          wCaptBuf = ( uint32_t )hHighSpeedCapture;
          hPrscBuf =  LL_TIM_GetPrescaler ( TIMx );
    
          /* Add the numbers of overflow to the counter */
          wCaptBuf += ( uint32_t )pHandle->OVFCounter * 0x10000uL;
    
          if ( pHandle->OVFCounter != 0u )
          {
            /* Adjust the capture using prescaler */
            uint16_t hAux;
            hAux = hPrscBuf + 1u;
            wCaptBuf *= hAux;
    
            if ( pHandle->RatioInc )
            {
              pHandle->RatioInc = false;  /* Previous capture caused overflow */
              /* Don't change prescaler (delay due to preload/update mechanism) */
            }
            else
            {
              if ( LL_TIM_GetPrescaler ( TIMx ) < pHandle->HALLMaxRatio ) /* Avoid OVF w/ very low freq */
              {
                LL_TIM_SetPrescaler ( TIMx, LL_TIM_GetPrescaler ( TIMx ) + 1 ); /* To avoid OVF during speed decrease */
                pHandle->RatioInc = true;   /* new prsc value updated at next capture only */
              }
            }
          }
          else
          {
            /* If prsc preload reduced in last capture, store current register + 1 */
            if ( pHandle->RatioDec ) /* and don't decrease it again */
            {
              /* Adjust the capture using prescaler */
              uint16_t hAux;
              hAux = hPrscBuf + 2u;
              wCaptBuf *= hAux;
    
              pHandle->RatioDec = false;
            }
            else  /* If prescaler was not modified on previous capture */
            {
              /* Adjust the capture using prescaler */
              uint16_t hAux = hPrscBuf + 1u;
              wCaptBuf *= hAux;
    
              if ( hHighSpeedCapture < LOW_RES_THRESHOLD ) /* If capture range correct */
              {
                if ( LL_TIM_GetPrescaler ( TIMx ) > 0u ) /* or prescaler cannot be further reduced */
                {
                  LL_TIM_SetPrescaler ( TIMx, LL_TIM_GetPrescaler ( TIMx ) - 1 ); /* Increase accuracy by decreasing prsc */
                  /* Avoid decrementing again in next capt.(register preload delay) */
                  pHandle->RatioDec = true;
                }
              }
            }
          }
    
    #if 0
          /* Store into the buffer */
          /* Null Speed is detected, erase the buffer */
          if ( wCaptBuf > pHandle->MaxPeriod )
          {
            uint8_t bIndex;
            for ( bIndex = 0u; bIndex < pHandle->SpeedBufferSize; bIndex++ )
            {
              pHandle->SensorSpeed[bIndex]  = 0;
            }
            pHandle->BufferFilled = 0 ;
            pHandle->SpeedFIFOSetIdx = 1;
            pHandle->SpeedFIFOGetIdx = 0;
            /* Indicate new speed acquisitions */
            pHandle->NewSpeedAcquisition = 1;
            pHandle->ElSpeedSum = 0;
          }
          /* Filtering to fast speed... could be a glitch  ? */
          /* the HALL_MAX_PSEUDO_SPEED is temporary in the buffer, and never included in average computation*/
          else
    #endif
            if ( wCaptBuf < pHandle->MinPeriod )
            {
              pHandle->CurrentSpeed = HALL_MAX_PSEUDO_SPEED;
              pHandle->NewSpeedAcquisition = 0;
            }
            else
            {
              pHandle->ElSpeedSum -= pHandle->SensorSpeed[pHandle->SpeedFIFOIdx]; /* value we gonna removed from the accumulator */
              if ( wCaptBuf >= pHandle->MaxPeriod )
              {
                pHandle->SensorSpeed[pHandle->SpeedFIFOIdx]  = 0;
              }
              else
              {
                pHandle->SensorSpeed[pHandle->SpeedFIFOIdx] = ( int16_t ) ( pHandle->PseudoFreqConv / wCaptBuf );
                pHandle->SensorSpeed[pHandle->SpeedFIFOIdx] *= pHandle->Direction;
                pHandle->ElSpeedSum += pHandle->SensorSpeed[pHandle->SpeedFIFOIdx];
              }
              /* Update pointers to speed buffer */
              pHandle->CurrentSpeed = pHandle->SensorSpeed[pHandle->SpeedFIFOIdx];
              pHandle->SpeedFIFOIdx++;
              if ( pHandle->SpeedFIFOIdx == pHandle->SpeedBufferSize )
              {
                pHandle->SpeedFIFOIdx = 0u;
              }
              /* Indicate new speed acquisitions */
              pHandle->NewSpeedAcquisition = 1;
            }
          /* Reset the number of overflow occurred */
          pHandle->OVFCounter = 0u;
        }
      }
      return MC_NULL;
    }

     其中pHandle->HallState是通过读取三个霍尔传感器的引脚电平得到的。当出现0或7时,程序设置传感器不可靠pHandle->SensorIsReliable = false;,并将速度设成0,不再进行速度读取。当我把这行注释掉,电机可以运行,不会再停下来。但是会出现异音的情况。我猜是因为霍尔的位置检测有错误时,电角度的位置和方向信息都不会更新,而这些信息在后面的FOC控制中会用到,所以导致转子转动不连贯,出现了震动。

    2.B-Emf constant这三个参数只有在使用观测器时才会用到。RS、LS在计算电流环KP KI时用到了。

    上图中的wc为带宽系数

    在使用霍尔控制,没有用观测器时,ST电机库生成的代码只有parameters_conversion.h中的下面这段代码使用了这三个参数。

    /************************* OBSERVER + PLL PARAMETERS **************************/
    #define MAX_BEMF_VOLTAGE  (uint16_t)((MAX_APPLICATION_SPEED * 1.2 *\
                               MOTOR_VOLTAGE_CONSTANT*SQRT_2)/(1000u*SQRT_3))
    
    /*max phase voltage, 0-peak Volts*/
    #define MAX_VOLTAGE (int16_t)((MCU_SUPPLY_VOLTAGE/2)/BUS_ADC_CONV_RATIO) 
    
    #define MAX_CURRENT (MCU_SUPPLY_VOLTAGE/(2*RSHUNT*AMPLIFICATION_GAIN))
    
    #define C1 (int32_t)((((int16_t)F1)*RS)/(LS*TF_REGULATION_RATE))
    #define C2 (int32_t) GAIN1
    #define C3 (int32_t)((((int16_t)F1)*MAX_BEMF_VOLTAGE)/(LS*MAX_CURRENT*TF_REGULATION_RATE))
    #define C4 (int32_t) GAIN2
    #define C5 (int32_t)((((int16_t)F1)*MAX_VOLTAGE)/(LS*MAX_CURRENT*TF_REGULATION_RATE))
    
    #define PERCENTAGE_FACTOR    (uint16_t)(VARIANCE_THRESHOLD*128u)      
    #define OBS_MINIMUM_SPEED        (uint16_t) (OBS_MINIMUM_SPEED_RPM/6u)
    #define HFI_MINIMUM_SPEED        (uint16_t) (HFI_MINIMUM_SPEED_RPM/6u)
    
    /*********************** OBSERVER + CORDIC PARAMETERS *************************/
    #define CORD_C1 (int32_t)((((int16_t)CORD_F1)*RS)/(LS*TF_REGULATION_RATE))
    #define CORD_C2 (int32_t) CORD_GAIN1
    #define CORD_C3 (int32_t)((((int16_t)CORD_F1)*MAX_BEMF_VOLTAGE)/(LS*MAX_CURRENT\
                                                               *TF_REGULATION_RATE))
    #define CORD_C4 (int32_t) CORD_GAIN2
    #define CORD_C5 (int32_t)((((int16_t)CORD_F1)*MAX_VOLTAGE)/(LS*MAX_CURRENT*\
                                                              TF_REGULATION_RATE))
    #define CORD_PERCENTAGE_FACTOR    (uint16_t)(CORD_VARIANCE_THRESHOLD*128u)      
    #define CORD_MINIMUM_SPEED        (uint16_t) (CORD_MINIMUM_SPEED_RPM/6u)
    

    3.仅使用编码器作为位置反馈时,效果比仅使用霍尔的时候要好。测得的速度也要稳定。如果启动电机,电机不转并且电流一直上升,可能是因为编码器AB相的接线反了。另外启动电机前需要进行Encoder Align 操作。

    4.仅使用编码器作为位置反馈时,我发现测得的速度变化时都是以6的倍数来变化的。并且当速度过小,电机可能转不起来。另外当电机速度很小,力矩会比较小。力矩增大需要速度PI控制器的误差积分慢慢增加来提高输出力矩。

    5.我发现st电机库中采样a,b相电流后没有进行数字滤波就直接使用了。用DAC的debug功能在示波器上看到相电流的正弦波形上会有杂波。另外,当设定好Iq和Id参考值后,Iq和Id对应的PI控制器输出相应的Vq和Vd,经过反Park变换后送到SVPWM模块生成相应的PWM波形控制6个MOS管。

    这个过程中测量得到的Iq和Id一直在参考线上波动。如下图:

    6.只使用编码盘作为速度反馈时,电机在低速时不能正常运行,有时会反转成最大速度。另外电机在100rpm这个转速运行时,会突然变速,有时会导致under voltage报警。

    关注公众号《首飞》回复“机器人”获取精心推荐的C/C++,Python,Docker,Qt,ROS1/2,机器人学等机器人行业常用技术资料。

    展开全文
  • ST官网下的,集成MCSDK PMSM电机库、FreeModbus、以及增量式编码器,实现位置环控制,是很好的学习资源。
  • STM_GetState()这个函数位于state_machine.c中,函数的作用就是返回当前的电机状态。 MC_GetSTMStateMotor1(void)这个函数位于mc_api.c中。作用相同。 2.函数的使用 首先定义一个state_t的变量去存储电机状态,...

    ST 的FOC电机库如何获取电机运行状态:

    1.STM_GetState()/MC_GetSTMStateMotor1(void);:

    STM_GetState()这个函数位于state_machine.c中,函数的作用就是返回当前的电机状态。
    在这里插入图片描述
    MC_GetSTMStateMotor1(void)这个函数位于mc_api.c中。作用相同。
    在这里插入图片描述

    2.函数的使用

    首先定义一个state_t的变量去存储电机状态,其次要在主函数包含STM_Handle_t这个句柄。否则会报错。
    记得写在函数begin和end之间。
    在这里插入图片描述
    在想获取电机状态的地方调用上述任意函数:
    在这里插入图片描述

    3.运行结果

    将变量添加到watch中进行debug。
    在这里插入图片描述
    在这里插入图片描述
    这………是启动后没转起来报错了。嘿嘿,尴尬。

    展开全文
  • STM32___X-CUBE-MCSDK-FUL_5.4.4.zip电机库完整源代码,PMSM电机代码。
  • ST电机库的FOC部分解读笔记

    千次阅读 2021-10-24 20:55:35
    ​ 如下图所示为ST培训资料的PPT中进行定义的坐标系,图中可以看出和常见的坐标系定义不同的主要有两点: ST定义的beta轴朝下,而常见的定义beta轴是朝上的。这个定义和欧洲人习惯定义y轴朝下有关,这个只会影响...

    1.前言

    ​ 如下图所示为ST培训资料的PPT中进行定义的坐标系,图中可以看出和常见的坐标系定义不同的主要有两点:

    • ST定义的beta轴朝下,而常见的定义beta轴是朝上的。这个定义和欧洲人习惯定义y轴朝下有关,这个只会影响Clarke变换,让beta轴的结果和常见的结果差一个负号;
    • ST定义的电角度theta是q轴和alpha轴的夹角,而常见的定义是d轴和alpha轴的夹角。这个会导致Park变换的结果和常见的结果完全相反(交换dq轴),实际上物理效果是一样的。

    image-20210920221819006

    2.Clarke变换

    1. 已解决:Clarke变换的函数比较容易看明白,问题也不是很大,由于没有涉及到电角度theta,所以结果只是beta轴的结果和常见的结果差一个负号。并且注意ST的Clarke变换中前面×了系数2/3。
    2. 遗留:程序中第41行的问题,为什么要在最小值的地方限幅修改?此时其实并没有溢出,而且这个改动值也很小。
    // Clarke变换问题不是很大,主要注意两点:
    // 1.Clark变换中前面×了系数2/3
    // 2.建立的坐标系alpha和常见的一样,但是beta坐标系是朝下的,所以和一般的结果差一个负号
    alphabeta_t mc_clark(ab_t input)
    {
    	alphabeta_t output;
    
    	int32_t a_divSQRT3_tmp, b_divSQRT3_tmp ;
    	int32_t wbeta_tmp;
    	int16_t hbeta_tmp;
    
    	/* qIalpha = qIas*/
    	output.alpha = input.a;  // 注意这里的clarke变换×了系数2/3
    
    	a_divSQRT3_tmp = divSQRT_3 * ( int32_t )input.a;
    
    	b_divSQRT3_tmp = divSQRT_3 * ( int32_t )input.b;
    
    	/*qIbeta = -(2*qIbs+qIas)/sqrt(3)*/   // 这里是负的,也就是beta轴是朝下的
    	wbeta_tmp = ( -( a_divSQRT3_tmp ) - ( b_divSQRT3_tmp ) -
    	( b_divSQRT3_tmp ) ) >> 15;  
    	// 这里>>15是因为上面是两个Q15格式的数据进行运算,结果是Q30格式,需要重新转换为Q15格式
    
    
    	/* Check saturation of Ibeta */
    	if ( wbeta_tmp > INT16_MAX )  // INT16_MAX = 32767
    	{
    		hbeta_tmp = INT16_MAX;
    	}
    	else if ( wbeta_tmp < ( -32768 ) )
    	{
    		hbeta_tmp = ( -32768 );
    	}
    	else
    	{
    		hbeta_tmp = ( int16_t )( wbeta_tmp );
    	}
    
    	output.beta = hbeta_tmp;
    
    	// 这里的限幅是干什么的?把-32768限幅成-32767?
    	if ( output.beta == ( int16_t )( -32768 ) )
    	{
    		output.beta = -32767;
    	}
    
    	return ( output );
    }
    

    3.Park和Rev-Park变换

    3.1.不同的坐标系定义

    image-20210920230326658

    ​ Park(Rev-Park)变换应该是ST的电机库比较难理解的一点,因为程序的运算结果和常见的推导结果完全相反(交换了dq轴的结果)。这里和1.前言中说的ST的坐标定义有关。有关坐标定义,可以参照 FOC中坐标系和电角度的定义

    ​ 如果按照这个坐标系定义,又会出现一个难以理解的问题,因为常见的电角度定义为d轴和alpha轴的夹角,为什么这里定义为q轴和alpha轴的夹角也可以呢?这样结果不会出错吗?

    ​ 实际上,解答这个问题还是要从FOC的核心思想上来解答。FOC使用Clarke变换和Park变换是为了什么?就是为了生成超前转子(d轴,注意不论电角度怎么定义,dq轴定义总是不变的)90度的磁场,或者说超前转子90度的电流。那么电角度theta有什么用呢?其实它仅仅是为了指示当前转子在什么方向,从而生成一个超前转子90度的磁场,因为abc轴和alpha/beta轴都是固定的,而只有知道要生成的磁场相对这些固定的坐标轴在什么位置,才能进行在旋转坐标轴和固定坐标轴之间相互转换。所以说,电角度可以随便定义,它可以是q轴和alpha的夹角,也可以是d轴和alpha的夹角,甚至可以是d轴和(alpha轴与beta轴)的角平分线的夹角,因为电角度存在的意义只是为了指示当前转子在什么方向,然后生成一个超前转子90度的磁场,利用电角度,就可以把这个磁场分解到固定的坐标轴上去,然后控制PWM的生成。

    ​ 那么为什么常见的定义是d轴或者q轴和alpha轴的夹角呢?其实很简单,就是为了Park和反Park变换的方便,因为此时只需要在dq轴和alpha/beta轴之间相互投影即可,而投影的时候×的系数就是theta的三角函数值。由此看来,这个夹角定义成d轴或者q轴和beta轴的夹角也是可以的,它同样方便计算。但是如果定义成和其他轴(比如alpha和beta角平分线)的夹角,那么投影计算的时候就不好算了。

    ​ 总之记住:**所有的变换最终结果都是为了生成超前转子90度的磁场,而电角度只是为了提供转子的位置,方便磁场在各个坐标系之间进行变换。**这也是FOC的核心思想!

    3.2.为什么给-90度电角度就能进行电角度预校准?

    参考解答

    ​ 为什么要进行电角度校准呢?也很简单,因为电机初始停在任意的一个位置,此时磁编码器是有一个读数的(而且极大的概率不是0)。但是从上面常见的坐标系的定义可以直到,电角度的零度是q轴或者d轴与alpha轴对齐的时候定义为电角度0度,此时编码器读出来的角度再×极对数就是偏置电角度。后面读取电角度的时候,只需要把编码器实际读出来的机械角度转换为电角度,然后再减去极对数,就是真正的电角度了。这里也可以发现,由于P对极的电机在初始对齐的时候可以有P个位置,也就对应有P个偏置电机度,实际上使用哪个偏置电角度都可以。(详见另一篇笔记)

    ​ 为什么给-90度电角度就能进行电角度预校准?答案还是和FOC的核心思想有关,那就是FOC控制要生成一个超前转子90度的磁场

    3.2.1.电角度定义为d轴和alpha轴的夹角

    ​ 如下图所示,电角度定义为d轴和alpha轴的夹角,此时为了校准电角度,也就是找到电角度0度的位置,那么要让d轴和alpha轴对齐,这个时候的电角度就是0度。为了让d轴和alpha轴对齐,那么就要生成alpha轴方向的磁场。由于是使用FOC控制,其核心就是控制生成一个超前转子90度的磁场,那么此时虚拟的转子应该在滞后alpha轴90度的方向,也就是在校准电角度的时候角度的反馈不是编码器来的,而是人为给定的,因为目的就是要生成一个想要的固定方向的磁场,从而吸引转子到这个方向,从而对齐电角度0度。所以此时虚拟的转子在图示位置,d轴也在这个位置,那么电角度是d轴和alpha轴的夹角,由图可知就是-90度。

    image-20210920233735851

    3.2.2.电角度定义为q轴和alpha轴的夹角

    ​ 如下图所示,电角度定义为q轴和alpha轴的夹角,此时为了校准电角度,也就是找到电角度0度的位置,那么要让q轴和alpha轴对齐,这个时候的电角度就是0度。为了让q轴和alpha轴对齐,那么就要生成beta轴方向的磁场。由于是使用FOC控制,其核心就是控制生成一个超前转子90度的磁场,那么此时虚拟的转子应该在滞后beta轴90度的方向,也就是在校准电角度的时候角度的反馈不是编码器来的,而是人为给定的,因为目的就是要生成一个想要的固定方向的磁场,从而吸引转子到这个方向,从而对齐电角度0度。所以此时虚拟的转子在图示位置,d轴也在这个位置,此时的q轴在生成的FOC磁场的方向,那么电角度是q轴和alpha轴的夹角,由图可知电角度就是-90度。

    image-20210920232855816
    // Park变化问题比较大,目前还没有弄明白
    // 解决!终于明白了这里的Park变换为什么不对了!
    // 坐标系的定义:https://www.cnblogs.com/neriq/p/14800876.html
    qd_t mc_park(alphabeta_t input, int16_t theta)
    {
    	qd_t output;
    	int32_t d_tmp_1, d_tmp_2, q_tmp_1, q_tmp_2;
    	trig_components local_vector_components;
    	int32_t wqd_tmp;
    	int16_t hqd_tmp;
    
    	// 计算输入的角度的sin和cos值
    	local_vector_components = mc_trig_functions(theta);
    
    	/*No overflow guaranteed  无溢出保证*/
    	q_tmp_1 = input.alpha * ( int32_t )local_vector_components.h_cos;
    
    	/*No overflow guaranteed  无溢出保证*/
    	q_tmp_2 = input.beta * ( int32_t )local_vector_components.h_sin;
    
    	/*Iq component in Q1.15 Format */
    	wqd_tmp = ( q_tmp_1 - q_tmp_2 ) >> 15;  // 这里就是在算iq
    	// 注意这里>>15为还是因为前面的结果是两个Q15格式的运算,结果是Q30格式,需要重新转换为Q15格式
    	
    	/* Check saturation of Iq */
    	if ( wqd_tmp > INT16_MAX )
    	{
    		hqd_tmp = INT16_MAX;
    	}
    	else if ( wqd_tmp < ( -32768 ) )
    	{
    		hqd_tmp = ( -32768 );
    	}
    	else
    	{
    		hqd_tmp = ( int16_t )( wqd_tmp );
    	}
    
    	output.q = hqd_tmp;
    
    	if ( output.q == ( int16_t )( -32768 ) )
    	{
    		output.q = -32767;
    	}
    
    	/*No overflow guaranteed*/
    	d_tmp_1 = input.alpha * ( int32_t )local_vector_components.h_sin;
    
    	/*No overflow guaranteed*/
    	d_tmp_2 = input.beta * ( int32_t )local_vector_components.h_cos;
    
    	wqd_tmp = ( d_tmp_1 + d_tmp_2 ) >> 15;
    
    	/* Check saturation of Id */
    	if ( wqd_tmp > INT16_MAX )
    	{
    		hqd_tmp = INT16_MAX;
    	}
    	else if ( wqd_tmp < ( -32768 ) )
    	{
    		hqd_tmp = ( -32768 );
    	}
    	else
    	{
    		hqd_tmp = ( int16_t )( wqd_tmp );
    	}
    
    	output.d = hqd_tmp;
    
    	if ( output.d == ( int16_t )( -32768 ) )
    	{
    		output.d = -32767;
    	}
    
    	return (output);
    }
    	
    /**
      * @brief 反park变换:将同步旋转坐标系下的vqd,变换为静止坐标系的v_alpha,v_beta
      * @param volt_input: vqd
      * @param theta: 电角度值
      */
    alphabeta_t mc_rev_park(qd_t volt_input, int16_t theta)
    {
    	int32_t q_v_alpha_tmp1, q_v_alpha_tmp2, q_v_beta_tmp1, q_v_beta_tmp2;
    	trig_components local_vector_components;
    	alphabeta_t volt_output;
    	
    	local_vector_components = mc_trig_functions(theta);
    
    	q_v_alpha_tmp1 = volt_input.q * ( int32_t )local_vector_components.h_cos;
    	q_v_alpha_tmp2 = volt_input.d * ( int32_t )local_vector_components.h_sin;
    	
    	volt_output.alpha = ( int16_t )( ( ( q_v_alpha_tmp1 ) + ( q_v_alpha_tmp2 ) ) >> 15 );
    	
    
    	q_v_beta_tmp1 = volt_input.q * ( int32_t )local_vector_components.h_sin;
    	q_v_beta_tmp2 = volt_input.d * ( int32_t )local_vector_components.h_cos;
    	
    	volt_output.beta = ( int16_t )( ( q_v_beta_tmp2 - q_v_beta_tmp1 ) >> 15 );
    	
    	return (volt_output);
    }
    
    展开全文
  • ST官方电机库X-CUBE-MCSDK-FUL_5.4.4,最新版本。废了老大的劲申请到的,共享给需要的朋友
  • 现在官网下载受到了限制,下载比较麻烦,下载后分享给大家
  • ST电机库5.3.3_FUL版本

    2019-03-07 10:04:45
    ST电机库5.3.3_ful版本,代码开源,X-CUBE-MCSDK-FUL_5.3.3
  • st电机库说明
  • 使用f030r8t6调试st电机库,启动后,上位机显示母校电压正常,但功率超过200W(实际电流只有几十毫安,本人认为是程序里测得的电流过大导致,可能是f030的adc没有注入组吧),最终导致只能听到电流啸叫声,电机不动,...
  • STM32-PMSM-SDK-V4.2-使用指南中文版,FOC:高动态性能 高频注入法(HFI)无传感器算法 同时控制2个马达
  • ST电机库5.4.5FUL版本(ST MC SDK)

    千次阅读 2021-01-14 12:04:07
    最新ST官方FUL版SDK: 链接:https://pan.baidu.com/s/1WjquhlR88EgCdQguyt_eFg 提取码:9rgi 复制这段内容后打开百度网盘手机App,操作更方便哦
  • STM32___X-CUBE-MCSDK-FUL_5.3.3.zip电机库完整源代码,PMSM电机代码。
  • 无感模式接电机准备工作(1)----PWM波1.无感工程的创建2.PWM波的检测3.观测输出波形 1.无感工程的创建 无感工程的基本生成参考博客: 链接: link. 注意其中的PWM处设置: 1是PWM的频率,2是PWM的死区时间。注意:...
  • ST电机库 MCSDK-FUL_5.4.4 下载

    千次阅读 2020-11-13 21:20:20
    MCSDK-FUL_5.4.4版本下载地址如下: 链接: https://pan.baidu.com/s/16tH321I8Y_1k9QtET4QoyA 提取码: p1ev 开始学习ST电机库,深深感谢各位无私分享,找了好半天找到了最新版的代码,特此分享!
  • ST FOC 电机库

    2015-12-13 11:49:39
    ST的FOC电机库,完整的FOC代码,是初学电机同学的完美好帮手。有它在,很多问题不是问题。
  • 内容包含永磁同步电动机的数学模型、 矢量变换、电流前馈、 MTPA与弱磁控制、SVPWM、电流采样、位置速度检测、观测器、HFI的原理讲解,以及工程操作的建议
  • ST5.0 电机库

    2018-09-27 09:29:38
    en.x-cube-mcsdk-ful.exe 马达驱动是完全开源的了。厂家提供的代码还是很值得学习的。
  • ST FOC电机库_5.4.0

    2019-05-30 11:37:22
    ST的FOC电机库安装文件,官方原版。版本为5.4.0 。
  • ST官网申请的,花了好几天终于通过了,此文档为full版本的foc开源,包含high frequency injection, MPTA, feed-forward,位置观测等功能。现在分享于大家,请放心下载使用。
  • ST的FOC电机库.rar

    2020-07-03 14:00:37
    STM32电机驱动从2版本到5版本,包含ACIM感应电机驱动,直流无刷电机驱动,永磁同步电机驱动
  • ST 电机驱动源码学习

    千次阅读 2020-03-26 15:01:57
    版本:ST MC SDK\tVer.5.4.0 ...MCI_Handle_t 对接ST电机驱动的结构体 mc_interface MCT_Handle_t 电机内部参数调试结构体 包括PID参数 FOC等参数 2.mc_api.c 最高层调用接口,可以对电机进行控制。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,276
精华内容 510
关键字:

st电机库

友情链接: Izhikevch模型1.zip