以电机控制状态机为切入点,厘清电机控制的整体逻辑与思路。查漏补缺,反思状态机一些状态设置的目的。
电机库下载链接:https://www.st.com/en/embedded-software/x-cube-mcsdk.html
在ST电机库中,电机控制系统的状态划分为12个,具体的状态机如下图:
特别的,除了ICL WAIT、IDLE、FAULT NOW、FAULT OVER四种状态,其余状态都可以跳转到Stop状态
另外,任意状态下都可以跳转到FAULT NOW状态
下面具体解释每个状态的含义:
IDLE:就绪态,表示电机系统已经准备好工作,等待运行;
OFFSET MEAS:偏置测量状态,在系统进入就绪态后,如果接收到电机启动指令,会先检测系统是否进行偏置校准,如果没有校准,状态机会跳转到偏置检测状态,类似传感器校准,校准完成后状态机继续跳转回就绪态;
CHARGE BOOT CAP:启动电容充电状态,在此状态下,栅极驱动器引导电容充电,更具体的是给栅极驱动电路的自举电容充电。充电时间预先在程序中设置,在此段时间中,系统维持在此状态下等待,结束后状态机跳转到后续的ALIGNMENT或者RUN状态下;
自举电容充电是需要一定时间的。刚启动电机的时候,一般要求先开启下桥臂几十到几百毫秒的时间,确保自举电容先充满电,然后再按照互补输出的方式控制上下桥臂。如果不先开启下桥臂,造成自举电容充电不足,无法维持Vgs的15V压差,上桥臂就会失控,造成上桥臂关闭时无法关闭,或导通时无法导通,或者处于半导通状态,情况比较复杂,易发生过流、过压、过热等现象,造成IGBT损伤或烧毁。由于IGBT是集成在IPM中的,具体是哪种损伤,很难判断。三相交流电机驱动频繁炸机的原因梳理
ALIGNMENT:正交编码器对齐状态,只有系统使用正交编码器时,才会有此状态。在此状态下,正交编码器正确对齐,设置转子的机械角度。对齐完成后,状态机进入WAIT STOP MOTOR状态。(个人理解:正交编码器是增量式的传感器,没法获得电机转子初始机械角度,可能需要在此状态下进行初始位置校准,完成后进入下一状态。)
在实际操作中,欧美厂商习惯于采用给电机的绕组通以小于额定电流的直流电流使电机转子定向的方法来对齐编码器和转子磁极的相位。当电机的绕组通入小于额定电流的直流电流时,在无外力条件下,初级电磁场与磁极永磁场相互作用,会相互吸引并定位至互差0度相位的平衡位置上。伺服电机编码器与转子磁极相位对齐方法
WAIT STOP MOTOR:在正交编码器对齐后,为了确保电机完全静止,系统会在此状态下等待固定时间,此时间也是在程序中预设的。状态结束后根据是否将正交编码器作为主要的位移和速度传感器来选择后续跳转的状态;
START:系统正在执行电机启动程序。当过程完成时,状态机转换到SWITCH OVER状态。这种状态只有在使用无传感器算法来估计转子的位置和转速时才存在。
SWITCH OVER:在这种状态下,系统从电机启动过程中使用的开环操作模式过渡到电机受控制的闭环模式。然后系统进入RUN状态。
RUN:电机正常运行态,在此状态下,状态机不会主动跳转,等待接收到电机停止指令或者电机故障时,状态机跳转。
STOP:电机控制松开。在接收到电机控制停止命令时进入此状态。系统在一段固定且可配置的时间内保持这种状态,之后电机被认为是静止的。然后系统进入IDLE或ICL WAIT状态(如果启用了此功能)。
FAULT NOW:这个状态是在发生故障时从任何其他状态进入的。只要存在一个活跃的故障条件,系统就会保持在这个状态。当所有活跃的故障条件消失时,状态机将转移到FAULT OVER状态。在FAULT NOW状态下,系统通常会停止电机操作,以防止故障进一步恶化,并等待进一步的指令或故障处理。
AULT OVER(故障结束):当不再有任何活跃的故障条件,但至少有一个故障条件曾经活跃过时,系统会进入FAULT OVER状态。系统会在这个状态下保持,直到接收到确认故障(Acknowledge Faults)的命令。接收到该命令后,系统将返回到IDLE(空闲)或ICL WAIT(电流限制等待)状态之一。在FAULT OVER状态下,系统可能会执行一些自检或清理操作,准备恢复到正常操作或等待用户或维护人员的干预。
ICL WAIT:电流限制等待状态是电机控制系统中的一个特定状态,用于处理启动时可能出现的浪涌电流。浪涌电流是什么?
电机库中,状态机在中频任务中进行维护,下面是源码:
/**
* @brief Executes medium frequency periodic Motor Control tasks
*
* This function performs some of the control duties on Motor 1 according to the
* present state of its state machine. In particular, duties requiring a periodic
* execution at a medium frequency rate (such as the speed controller for instance)
* are executed here.
*/
__weak void TSK_MediumFrequencyTaskM1(void)
{
/* USER CODE BEGIN MediumFrequencyTask M1 0 */
/* USER CODE END MediumFrequencyTask M1 0 */
int16_t wAux = 0;
bool IsSpeedReliable = STO_PLL_CalcAvrgMecSpeedUnit(&STO_PLL_M1, &wAux);
PQD_CalcElMotorPower(pMPM[M1]);
if (MCI_GetCurrentFaults(&Mci[M1]) == MC_NO_FAULTS)
{
if (MCI_GetOccurredFaults(&Mci[M1]) == MC_NO_FAULTS)
{
switch (Mci[M1].State)
{
case IDLE:
{
if ((MCI_START == Mci[M1].DirectCommand) || (MCI_MEASURE_OFFSETS == Mci[M1].DirectCommand))
{
/* 空闲状态下,如果接受到启动指令,需要先更新斜坡加速的相关参数 */
RUC_Clear(&RevUpControlM1, MCI_GetImposedMotorDirection(&Mci[M1]));
if (pwmcHandle[M1]->offsetCalibStatus == false)//如果未校准电流
{
(void)PWMC_CurrentReadingCalibr(pwmcHandle[M1], CRC_START);//电流校准初始化
Mci[M1].State = OFFSET_CALIB;//跳转状态到OFFSET_CALIB
}
else//电流已校准且MCI_START == Mci[M1].DirectCommand
{
/* Calibration already done. Enables only TIM channels */
pwmcHandle[M1]->OffCalibrWaitTimeCounter = 1u;
(void)PWMC_CurrentReadingCalibr(pwmcHandle[M1], CRC_EXEC);
R3_1_TurnOnLowSides(pwmcHandle[M1],M1_CHARGE_BOOT_CAP_DUTY_CYCLES);
TSK_SetChargeBootCapDelayM1(M1_CHARGE_BOOT_CAP_TICKS);
Mci[M1].State = CHARGE_BOOT_CAP;
}
}
else
{
/* Nothing to be done, FW stays in IDLE state */
}
break;
}
case OFFSET_CALIB:
{
/* 除了ICL WAIT、IDLE、FAULT NOW、FAULT OVER四种状态,其余状态下接收到停止指令,都要响应 */
if (MCI_STOP == Mci[M1].DirectCommand)
{
TSK_MF_StopProcessing(M1);
}
else
{
if (PWMC_CurrentReadingCalibr(pwmcHandle[M1], CRC_EXEC))//执行校准程序,校准完成返回true,未完成返回false
{
/* 校准完成判断控制指令,如果是校准指令,状态机跳转回空闲态;否则(电机开启指令),状态机跳转到启动电容充电态 */
if (MCI_MEASURE_OFFSETS == Mci[M1].DirectCommand)
{
FOC_Clear(M1);//清零FOC运算中的相关变量(如Id,Iq,PID积分累计等),关闭PWM输出
Mci[M1].DirectCommand = MCI_NO_COMMAND;//覆盖旧指令
Mci[M1].State = IDLE;
}
else
{
R3_1_TurnOnLowSides(pwmcHandle[M1],M1_CHARGE_BOOT_CAP_DUTY_CYCLES);//导通下桥臂,确保自举电容充电完全
TSK_SetChargeBootCapDelayM1(M1_CHARGE_BOOT_CAP_TICKS);//设置自举电容充电时间
Mci[M1].State = CHARGE_BOOT_CAP;//跳转状态到自举电容充电态
}
}
else
{
/* Nothing to be done, FW waits for offset calibration to finish */
}
}
break;
}
case CHARGE_BOOT_CAP:
{
if (MCI_STOP == Mci[M1].DirectCommand)
{
TSK_MF_StopProcessing(M1);
}
else
{
if (TSK_ChargeBootCapDelayHasElapsedM1())//延时完成(自举电容充电完成)
{
R3_1_SwitchOffPWM(pwmcHandle[M1]);//关闭PWM输出
FOCVars[M1].bDriveInput = EXTERNAL;//设置电流参考源为内部或者为外部
STC_SetSpeedSensor( pSTC[M1], &VirtualSpeedSensorM1._Super );//实例化虚拟速度传感器的参数
STO_PLL_Clear(&STO_PLL_M1);//清零观测器的计算值
FOC_Clear( M1 );//清零FOC运算中的相关变量(如Id,Iq,PID积分累计等),关闭PWM输出
Mci[M1].State = START;//状态跳转到START
PWMC_SwitchOnPWM(pwmcHandle[M1]);//开启PWM输出
}
else
{
/* Nothing to be done, FW waits for bootstrap capacitor to charge */
}
}
break;
}
case START:
{
if (MCI_STOP == Mci[M1].DirectCommand)
{
TSK_MF_StopProcessing(M1);
}
else
{
/* 转速上升阶段,机械转速来自虚拟速度传感器 */
int16_t hForcedMecSpeedUnit;
qd_t IqdRef;
bool ObserverConverged;
/* Execute the Rev Up procedure */
if(! RUC_Exec(&RevUpControlM1))
{
/* The time allowed for the startup sequence has expired */
MCI_FaultProcessing(&Mci[M1], MC_START_UP, 0);
}
else
{
/* Execute the torque open loop current start-up ramp:
* Compute the Iq reference current as configured in the Rev Up sequence */
IqdRef.q = STC_CalcTorqueReference(pSTC[M1]);
IqdRef.d = FOCVars[M1].UserIdref;
/* Iqd reference current used by the High Frequency Loop to generate the PWM output */
FOCVars[M1].Iqdref = IqdRef;
}
(void)VSS_CalcAvrgMecSpeedUnit(&VirtualSpeedSensorM1, &hForcedMecSpeedUnit);
/* Check that startup stage where the observer has to be used has been reached */
if (true == RUC_FirstAccelerationStageReached(&RevUpControlM1))
{
ObserverConverged = STO_PLL_IsObserverConverged(&STO_PLL_M1, &hForcedMecSpeedUnit);
STO_SetDirection(&STO_PLL_M1, (int8_t)MCI_GetImposedMotorDirection(&Mci[M1]));
(void)VSS_SetStartTransition(&VirtualSpeedSensorM1, ObserverConverged);
}
else
{
ObserverConverged = false;
}
if (ObserverConverged)
{
qd_t StatorCurrent = MCM_Park(FOCVars[M1].Ialphabeta, SPD_GetElAngle(&STO_PLL_M1._Super));
/* Start switch over ramp. This ramp will transition from the revup to the closed loop FOC */
REMNG_Init(pREMNG[M1]);
(void)REMNG_ExecRamp(pREMNG[M1], FOCVars[M1].Iqdref.q, 0);
(void)REMNG_ExecRamp(pREMNG[M1], StatorCurrent.q, TRANSITION_DURATION);
Mci[M1].State = SWITCH_OVER;
}
}
break;
}
case SWITCH_OVER:
{
if (MCI_STOP == Mci[M1].DirectCommand)
{
TSK_MF_StopProcessing(M1);
}
else
{
bool LoopClosed;
int16_t hForcedMecSpeedUnit;
if (! RUC_Exec(&RevUpControlM1))
{
/* The time allowed for the startup sequence has expired */
MCI_FaultProcessing(&Mci[M1], MC_START_UP, 0);
}
else
{
/* Compute the virtual speed and positions of the rotor.
The function returns true if the virtual speed is in the reliability range */
LoopClosed = VSS_CalcAvrgMecSpeedUnit(&VirtualSpeedSensorM1, &hForcedMecSpeedUnit);
/* Check if the transition ramp has completed. */
bool tempBool;
tempBool = VSS_TransitionEnded(&VirtualSpeedSensorM1);
LoopClosed = LoopClosed || tempBool;
/* If any of the above conditions is true, the loop is considered closed.
The state machine transitions to the RUN state */
if (true == LoopClosed)
{
#if PID_SPEED_INTEGRAL_INIT_DIV == 0
PID_SetIntegralTerm(&PIDSpeedHandle_M1, 0);
#else
PID_SetIntegralTerm(&PIDSpeedHandle_M1,
(((int32_t)FOCVars[M1].Iqdref.q * (int16_t)PID_GetKIDivisor(&PIDSpeedHandle_M1))
/ PID_SPEED_INTEGRAL_INIT_DIV));
#endif
/* USER CODE BEGIN MediumFrequencyTask M1 1 */
/* USER CODE END MediumFrequencyTask M1 1 */
STC_SetSpeedSensor(pSTC[M1], &STO_PLL_M1._Super); /* Observer has converged */
FOC_InitAdditionalMethods(M1);
FOC_CalcCurrRef(M1);
STC_ForceSpeedReferenceToCurrentSpeed(pSTC[M1]); /* Init the reference speed to current speed */
MCI_ExecBufferedCommands(&Mci[M1]); /* Exec the speed ramp after changing of the speed sensor */
Mci[M1].State = RUN;
}
}
}
break;
}
case RUN:
{
if (MCI_STOP == Mci[M1].DirectCommand)
{
TSK_MF_StopProcessing(M1);
}
else
{
/* USER CODE BEGIN MediumFrequencyTask M1 2 */
/* USER CODE END MediumFrequencyTask M1 2 */
MCI_ExecBufferedCommands(&Mci[M1]);
FOC_CalcCurrRef(M1);
if(!IsSpeedReliable)
{
MCI_FaultProcessing(&Mci[M1], MC_SPEED_FDBK, 0);
}
else
{
/* Nothing to do */
}
}
break;
}
case STOP:
{
if (TSK_StopPermanencyTimeHasElapsedM1())
{
STC_SetSpeedSensor(pSTC[M1], &VirtualSpeedSensorM1._Super); /* Sensor-less */
VSS_Clear(&VirtualSpeedSensorM1); /* Reset measured speed in IDLE */
/* USER CODE BEGIN MediumFrequencyTask M1 5 */
/* USER CODE END MediumFrequencyTask M1 5 */
Mci[M1].DirectCommand = MCI_NO_COMMAND;
Mci[M1].State = IDLE;
}
else
{
/* Nothing to do, FW waits for to stop */
}
break;
}
case FAULT_OVER:
{
if (MCI_ACK_FAULTS == Mci[M1].DirectCommand)
{
Mci[M1].DirectCommand = MCI_NO_COMMAND;
Mci[M1].State = IDLE;
}
else
{
/* Nothing to do, FW stays in FAULT_OVER state until acknowledgement */
}
break;
}
case FAULT_NOW:
{
Mci[M1].State = FAULT_OVER;
break;
}
default:
break;
}
}
else
{
Mci[M1].State = FAULT_OVER;
}
}
else
{
Mci[M1].State = FAULT_NOW;
}
/* USER CODE BEGIN MediumFrequencyTask M1 6 */
/* USER CODE END MediumFrequencyTask M1 6 */
}
电机状态机的跳转机制也跟用户指令密切相关。下面我们继续解析Mci[M1]结构体的内容:
typedef struct
{
SpeednTorqCtrl_Handle_t *pSTC; /*!< 用户使用的速度和力矩控制结构体*/
pFOCVars_t pFOCVars; /*!< 用户使用的FOC变量结构体指针,其中包含FOC控制所需的变量*/
PWMC_Handle_t *pPWM; /*!< 用户使用的PWM控制结构体指针*/
MCI_UserCommands_t lastCommand; /*!< 用户上一条控制指令.*/
int16_t hFinalSpeed; /*!< 最后一个ExecSpeedRamp命令的最终速度*/
int16_t hFinalTorque; /*!< 最后一个ExecTorqueRamp命令的最终力矩*/
qd_t Iqdref; /*!< 最后一个SetCurrentReferences指令的电流目标值*/
ScaleParams_t *pScale;
uint16_t hDurationms; /*!< 以毫秒为单位的持续时间(斜坡持续时间),来自最后一个ExecSpeedRamp或者ExecTorqueRamp指令*/
MCI_DirectCommands_t DirectCommand; /*!< 电机控制枚举变量*/
MCI_State_t State; /*!< 电机状态枚举变量*/
uint16_t CurrentFaults; /*!< 当前错误标志,用于记录当前系统是否有错误发生*/
uint16_t PastFaults; /*!< 过去错误标志,用于记录系统是否发生过错误,在用户确认错误后清除,保证系统运行正常*/
MCI_CommandState_t CommandState; /*!< 缓冲指令的状态?(目前还不清除这个状态有什么用处,电机控制指令难道不是覆盖式的吗)*/
MC_ControlMode_t LastModalitySetByUser; /*!< 用户上一次设置的电机控制模式*/
} MCI_Handle_t;