手寫(xiě)STM32 FOC記錄-----添加編碼器電機(jī)開(kāi)環(huán)轉(zhuǎn)動(dòng)
1、硬件配置
????????硬件處理:ST官方開(kāi)發(fā)板X(qián)-NUCLEO-IHM16M1,由于電機(jī)驅(qū)動(dòng)使用的是STSPIN830,它在硬件上做了限制,不能使用互補(bǔ)PWM控制電機(jī),需要將MODE 置為高,具體操作是拿掉開(kāi)發(fā)板上的R2 電阻,讓MODE引腳強(qiáng)制置高


2、添加磁編碼器
????????完成以上這些步驟,就可以接上電機(jī),驅(qū)動(dòng)電機(jī)開(kāi)環(huán)轉(zhuǎn)動(dòng)。在此之前,需要先添加編碼器同步電機(jī)轉(zhuǎn)動(dòng)電角度。我使用的電機(jī)是自己手?jǐn)€的,編碼器是磁編碼器,是我從其他電機(jī)上摳下來(lái)的,具體型號(hào)是SC60224,它可以是PWM的輸出方式,使用定時(shí)器的PWM輸入捕獲方式,獲取磁編的角度。編碼器的PWM輸出時(shí)序圖如下:

????????CUBEMX配置TIM3的CH1的PWM輸入捕獲模式,具體方法如下:
????????先配置通道

????????再配置時(shí)間和中斷觸發(fā)方式

????????然后配置輸入PWM輸入IO口為PA6;

????????最后記得打開(kāi)TIM3定時(shí)器中斷,生成代碼。
????????這里簡(jiǎn)單說(shuō)明下PWM輸入捕獲流程:
????????1. PWM信號(hào)由TIMx_CH1進(jìn)入,依據(jù)是上升沿還是是下降沿從而觸發(fā)TI1FP1或TI1FP2。
????????2. 當(dāng)捕捉到第一次上升沿時(shí),觸發(fā)TI1FP1,計(jì)數(shù)器復(fù)位,計(jì)數(shù)值CNT清零。
????????3. 當(dāng)捕捉到下降沿時(shí),觸發(fā)TI1FP2,CNT計(jì)數(shù)值在TIMx_CH2中被記錄到TIMx_CCR2寄存器中。
????????4. 當(dāng)再次捕捉到上升沿時(shí),觸發(fā)TI1FP1,CNT計(jì)數(shù)值在TIMx_CH1中被記錄到TIMx_CCR1寄存器中,同時(shí)計(jì)數(shù)器復(fù)位,計(jì)數(shù)值清零。
????????5. 依據(jù)TIMx_CCR1、TIMx_CCR2的值便可以計(jì)算PWM的周期、頻率、占空比。TIMx_CCR1的值就是周期, TIMxCCR1/TIMxCCR2的值就是占空比。
????生成代碼之后,編寫(xiě)初始化函數(shù)PWM_encoder_init,主函數(shù)調(diào)用


再添加中斷回調(diào)函數(shù),中斷回調(diào)中計(jì)算PWM周期,頻率和占空比。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
????if(htim->Instance==TIM3)
????{
????if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1) ????????????????
????{
????????????pwm_encoder.period=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
????????????pwm_encoder.frq=pwm_encoder.period*0.1f; ?????????????
????????????pwm_encoder.duty=((float)pwm_encoder.high_time/(float)pwm_encoder.period);
????}
????if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2) ????????????????
????{
????????????pwm_encoder.high_time=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2); ?
????} ? ?
??????}
?
}
3、測(cè)試編碼器數(shù)據(jù)
????????編寫(xiě)編碼器角度測(cè)試函數(shù),主函數(shù)調(diào)用,編譯下載,并使用vofa+輸出波形,看看波形是否隨著電機(jī)轉(zhuǎn)動(dòng)而變化:

void test_PWM_encoder(void)
{
????????float theta = 0;
????????DQ_Def test_dq;
????????AlphaBeta_Def test_ab;
????????SVPWM_Def svpwm_out;
????????test_dq.d = 0.0f;
????????test_dq.q = 0.2f;
????????for (theta = 0; theta < 6.2831853f; theta += 0.275f)
?????????{
??????????????pwm_encoder.angle = pwm_encoder.duty * 360.0f; //計(jì)算角度0~360
??????????????pwm_encoder.angle_rad = pwm_encoder.duty*2*PI; //計(jì)算角度,弧度制
?????
??????????????vofa_JustFloat_output(pwm_encoder.angle,pwm_encoder.angle_rad,0.0f,0.0f);
????????????????inverseParkTransform(&test_dq,&test_ab,theta);
????????????????SVPWM(&test_ab,&svpwm_out);? ??//電機(jī)會(huì)轉(zhuǎn)動(dòng)
????????}
}

????????說(shuō)明下,如果theta 是增加則表明電機(jī)正轉(zhuǎn),角度輸出斜率應(yīng)該是正,反之為負(fù)。若對(duì)應(yīng)不上,有一個(gè)簡(jiǎn)單辦法,就是倒一下UVW三相中的任意兩相就可以。
4、電角度零點(diǎn)與機(jī)械角度零點(diǎn)對(duì)應(yīng)
????????首先明確一個(gè)公式,電角度=機(jī)械角度×極對(duì)數(shù)。光有這個(gè)公式還不夠,需要保證FOC電角度零點(diǎn)跟機(jī)械角度零點(diǎn)對(duì)應(yīng),才能保證電機(jī)正常啟動(dòng)。
????????要找機(jī)械角度零點(diǎn)比較簡(jiǎn)單,讀取編碼器的值為0時(shí),就是機(jī)械零點(diǎn),但是尋找電角度零點(diǎn)稍微復(fù)雜點(diǎn),需要的操作步驟是,首先,給定ud= 0.5,uq=0,注意這里是D軸給電流,而Q軸沒(méi)有電流,然后指定theta = 0,該角度給到SVPWM中,此時(shí)電機(jī)會(huì)上勁,停到的位置點(diǎn)即為電角度零點(diǎn),最后,讀取機(jī)械角度零點(diǎn),此時(shí)FOC電角度零點(diǎn)跟機(jī)械角度零點(diǎn)對(duì)應(yīng)上了。具體代碼編寫(xiě)一個(gè)Motor_Align預(yù)定位找零點(diǎn)的函數(shù)如下:

void Motor_Align(float ud) //電機(jī)預(yù)定位,找電角度零點(diǎn)
{
????????float theta = 0;
????????DQ_Def align_dq;
????????AlphaBeta_Def align_ab;
????????SVPWM_Def svpwm_out;
????????align_dq.d = ud;
????????align_dq.q = 0.0f;
????????
????????theta = 0;
????????inverseParkTransform(&align_dq,&align_ab,theta);
????????SVPWM(&align_ab,&svpwm_out); ?//電機(jī)停到電角度零點(diǎn)
????????pwm_encoder.angle_rad_offset = pwm_encoder.duty*2*PI;//獲取當(dāng)前機(jī)械角度
}
????????可以通過(guò)debug的方式找到電角度零點(diǎn)之后,填入pwm_encoder.angle_rad_offset,需要在角度獲取函數(shù)中,算電角度時(shí),減去這個(gè)零點(diǎn)偏差

//獲取編碼器的機(jī)械角度,真實(shí)的物理角度
//獲取電角度,F(xiàn)OC電角度
void Get_PWM_Encoder_Angles(void)
{
??????pwm_encoder.angle = pwm_encoder.duty * 360.0f;
??????pwm_encoder.angle_rad = pwm_encoder.duty*2*PI;
????pwm_encoder.angle_rad_offset = 6.045f; //手動(dòng)找到的零點(diǎn)
????if(pwm_encoder.angle_rad>=pwm_encoder.angle_rad_offset) //減去零點(diǎn)偏置
????{
??????????pwm_encoder.angle_rad = pwm_encoder.angle_rad - pwm_encoder.angle_rad_offset;
????}
????else
????{
?????????pwm_encoder.angle_rad = 2*PI - pwm_encoder.angle_rad_offset + ????????????pwm_encoder.angle_rad;
????}
????????pwm_encoder.electronic_angle = pwm_encoder.angle_rad*MOTOR_POLE;
}
????????當(dāng)然,如果是其他接口的編碼器,比如SPI或這IIC接口,這些編碼器都可以手動(dòng)寫(xiě)零點(diǎn)位置,這樣操作起來(lái)就比較簡(jiǎn)單,找到電角度之后,直接在當(dāng)前位置寫(xiě)磁編碼的零點(diǎn)就好了。
5、電機(jī)開(kāi)環(huán)轉(zhuǎn)動(dòng)
????????FOC電角度準(zhǔn)確了,驅(qū)動(dòng)電機(jī)轉(zhuǎn)動(dòng)就很簡(jiǎn)單了,實(shí)時(shí)將獲取到的電角度傳到SVPWM中,輸出PWM即可驅(qū)動(dòng)電機(jī)轉(zhuǎn)動(dòng)

void ?motor_open_loop_control(float uq) ?//開(kāi)環(huán)控制電機(jī)
{
????????float theta = 0.0f;
????????DQ_Def open_loop_dq;
????????AlphaBeta_Def open_loop_ab;
????????SVPWM_Def svpwm_out;
????????open_loop_dq.d = 0.0f;
????????open_loop_dq.q = uq;
????????Get_PWM_Encoder_Angles();? ?//獲取電角度
????????theta = pwm_encoder.electronic_angle;
????????inverseParkTransform(&open_loop_dq,&open_loop_ab,theta);
????????SVPWM(&open_loop_ab,&svpwm_out);
}
????????在主函數(shù)調(diào)用motor_open_loop_control接口函數(shù),編譯下載到硬件平臺(tái)中,電機(jī)就開(kāi)環(huán)轉(zhuǎn)動(dòng)起來(lái)了。
觀(guān)看視頻效果請(qǐng)轉(zhuǎn)至https://www.bilibili.com/video/BV1WN411i7va/?spm_id_from=333.999.0.0&vd_source=4c57166c6142504cc135c2135bf9844e