-
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
2019-03-19 17:22:19本资源为ST电机库5.2.0版本,FUL完全开源,有兴趣的同学可以借鉴学习。 -
ST电机库4.3手册
2018-08-19 17:29:42ST电机库4.3版本手册,介绍ST电机库框架和原理,用于无刷直流和永磁电机控制。 -
ST最新电机库5.0.3
2018-06-28 18:34:12ST最新电机库V5.0.3,新的马达驱动库是完全开源的了。厂家提供的代码还是很值得学习的。 -
ST官方5.4电机库11月最新.rar
2019-11-27 16:55:10最新的ST官方电机库5.4资料11月更新 STM32微控制器具有运行矢量控制或FOC模式的行业标准Arm®Cortex®-M内核的性能,广泛用于空调,家用电器,无人机,建筑和工业自动化,医疗和电子行业的高性能驱动器中。 自行车... -
st电机库无感调速,采用OLED显示
2022-05-22 23:22:15还可以通过按键进行菜单切换,显示pid参数和速度值等,无霍尔传感器,使用ST电机库5.3版本,使用0.96寸的屏OLED进行显示,OLED芯片型号为SSD1306,使用4线串行模式读写,有良好的人机交互界面,适合电机调速无感应用 -
调用ST电机库5.0霍尔传感器调速
2018-08-19 17:15:11调用ST电机库5.0程序,采用高霍尔传感器实现无刷直流电机矢量控制 -
调试ST电机库5.20遇到的问题
2018-12-20 11:05:141.使用带霍尔的无刷电机运行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官网PMSM电机库与FreeModbus及位置环控制.zip
2020-08-17 17:13:26ST官网下的,集成MCSDK PMSM电机库、FreeModbus、以及增量式编码器,实现位置环控制,是很好的学习资源。 -
ST电机库api使用——获取电机状态
2021-04-19 14:31:36STM_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。
这………是启动后没转起来报错了。嘿嘿,尴尬。 -
ST官方电机库文件MCSDK-FUL_5.4.4最新版本_20201111.zip
2020-11-13 09:31:27STM32___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轴),实际上物理效果是一样的。
2.Clarke变换
- 已解决:Clarke变换的函数比较容易看明白,问题也不是很大,由于没有涉及到电角度theta,所以结果只是beta轴的结果和常见的结果差一个负号。并且注意ST的Clarke变换中前面×了系数2/3。
- 遗留:程序中第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.不同的坐标系定义
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度。
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度。
// 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官方电机库文件MCSDK-FUL_5.4.4最新版本_20200613114445.rar
2020-06-13 11:48:32ST官方电机库X-CUBE-MCSDK-FUL_5.4.4,最新版本。废了老大的劲申请到的,共享给需要的朋友 -
st官方开源的电机库foc5.0
2018-04-09 10:59:59现在官网下载受到了限制,下载比较麻烦,下载后分享给大家 -
ST电机库5.3.3_FUL版本
2019-03-07 10:04:45ST电机库5.3.3_ful版本,代码开源,X-CUBE-MCSDK-FUL_5.3.3 -
ST MC SDK5.x 电机库软件框架说明.pdf
2020-04-30 10:26:08st电机库说明 -
基于ST电机库调试无刷电机
2021-12-29 17:36:51使用f030r8t6调试st电机库,启动后,上位机显示母校电压正常,但功率超过200W(实际电流只有几十毫安,本人认为是程序里测得的电流过大导致,可能是f030的adc没有注入组吧),最终导致只能听到电流啸叫声,电机不动,... -
ST—FOC4.2电机库全中文指南
2018-05-30 10:46:30STM32-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,操作更方便哦 -
ST官方电机库文件_MCSDK-FUL_5.3.3最新版本
2020-11-12 20:28:37STM32___X-CUBE-MCSDK-FUL_5.3.3.zip电机库完整源代码,PMSM电机代码。 -
ST电机库无感FOC模式接电机前准备工作(1)----PWM波的生成
2021-03-22 10:21:58无感模式接电机准备工作(1)----PWM波1.无感工程的创建2.PWM波的检测3.观测输出波形 1.无感工程的创建 无感工程的基本生成参考博客: 链接: link. 注意其中的PWM处设置: 1是PWM的频率,2是PWM的死区时间。注意:... -
ST电机库 MCSDK-FUL_5.4.4 下载
2020-11-13 21:20:20MCSDK-FUL_5.4.4版本下载地址如下: 链接: https://pan.baidu.com/s/16tH321I8Y_1k9QtET4QoyA 提取码: p1ev 开始学习ST电机库,深深感谢各位无私分享,找了好半天找到了最新版的代码,特此分享! -
ST FOC 电机库
2015-12-13 11:49:39ST的FOC电机库,完整的FOC代码,是初学电机同学的完美好帮手。有它在,很多问题不是问题。 -
基于最新5.4电机库的STM32电机控制应用实战分享.zip (2020培训)
2020-02-07 15:41:09内容包含永磁同步电动机的数学模型、 矢量变换、电流前馈、 MTPA与弱磁控制、SVPWM、电流采样、位置速度检测、观测器、HFI的原理讲解,以及工程操作的建议 -
ST5.0 电机库
2018-09-27 09:29:38en.x-cube-mcsdk-ful.exe 马达驱动库是完全开源的了。厂家提供的代码还是很值得学习的。 -
ST FOC电机库_5.4.0
2019-05-30 11:37:22ST的FOC电机库安装文件,官方原版。版本为5.4.0 。 -
st官方电机库en.x-cube-mcsdk-ful 5.3.3
2019-06-11 11:42:53在ST官网申请的,花了好几天终于通过了,此文档为full版本的foc开源库,包含high frequency injection, MPTA, feed-forward,位置观测等功能。现在分享于大家,请放心下载使用。 -
ST的FOC电机库.rar
2020-07-03 14:00:37STM32电机驱动库从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 最高层调用接口,可以对电机进行控制。...