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报警。