如何在 MCSDK 5.x 中增加絕對位置編碼器

在“AI電堂”微信公眾號回復(fù)“SPI”,獲得示例代碼下載鏈接
當(dāng)前MC SDK5.x中支持的關(guān)于編碼器(Encoder)為增量編碼器,即有A,B,Z信號的編碼器,主要為光編碼器;在市場中有些應(yīng)用(比如無人機云臺,伺服,醫(yī)療)已經(jīng)在廣泛使用磁編碼器,輸出的是絕對位置,而想取得位置信息,或者通過取樣絕對位置傳感的PWM脈寬,或者需要通過通訊方式進(jìn)行獲取,比如SPI,I2C,UART等等;當(dāng)前MC SDK5.x還未對這個部分直接支持,需要通過修改程序?qū)崿F(xiàn)絕對位置編碼器的應(yīng)用,本文使用STM32F303RE-Nucleo控制說明。
絕對位置編碼器使用簡介
當(dāng)導(dǎo)體通電后放入磁場中,由于磁場的作用導(dǎo)體上流動的電荷就會因為受到由磁場感應(yīng)產(chǎn)生的洛倫茲力而發(fā)生流通路徑的偏移,這樣在垂直于電流方向上如果去測量的Y1,Y2點電壓,將產(chǎn)生電勢差,這個就是所說的霍爾效應(yīng);如果使用ADC對Y1,Y2點進(jìn)行測量標(biāo)定即可以反推磁場變化;

打開有絕對位置編碼器的電機可以看到轉(zhuǎn)子上有一個同步旋轉(zhuǎn)的磁石,同時有檢測芯片用于絕對角度輸出,絕對位置編碼器芯片輸出PWM波或者內(nèi)部做通訊輸出,單片機可以讀取相關(guān)信息得到到當(dāng)前轉(zhuǎn)子的角度。
1. 準(zhǔn)備工作
為了方便調(diào)試,首先使用無傳感控制(Sensor-less)將電機閉環(huán)運行起來,如果電機合適,并且使用的是ST的電機Demo板,可以使用ST的Motor Profiler(30KHzPWM)進(jìn)行無傳感電機參數(shù)以及轉(zhuǎn)動參數(shù)識別,可以很快將電機無傳感轉(zhuǎn)動起來;
如果使用客戶自制板子,則按照ST的操作說明,使用MCWorkbench將電機閉環(huán)無傳感轉(zhuǎn)動起來,電機閉環(huán)轉(zhuǎn)動過程可以確保硬件軟件的完備性;注意這邊生成的CubeMx工程文件*.ioc文件,后面我們添加程序會使用到;
為了便于調(diào)試可增加兩路DAC,比如使用STM32F303則直接使用兩路輸出的DAC,如果該芯片上沒有DAC模塊,可以使用TIMER+RC濾波電路模擬輸出DAC;下圖為使用DAC配置,需要配置為Userdefined DAC1/2。

絕對編碼器芯片角度信息讀取
絕對編碼器的輸出可以為PWM波形輸出,也可以為通訊端口輸出,比如UART,SPI,I2C等等,本文以SPI輸出以及PWM輸出為例做說明
2. 角度以及速度標(biāo)定
2.1 電角度標(biāo)定
因為我們讀取的角度為機械角度,在控制過程中我們用到的是電角度,因此還要做一下變換,將機械角度數(shù)據(jù)變?yōu)殡娊嵌葦?shù) 據(jù);使用公式為:

在程序中需要量化為Q16格式后得到電角度范圍[?180°~180°];即[-32768~32767]

2.2 速度計算

3. 編碼器SPI輸出模式
3.1 CubeMx配置
打開MCWorkbench生成的*.ioc文件,配置SPI模塊,這邊我們使用SPI3接收,配置參數(shù)需要參考編碼器芯片的說明,這 邊以AS5048A為例,如下配置,配置PB10作為片選信號CS輸出


3.2 重新生成代碼

3.3 編寫SPI讀取代碼
根據(jù)編碼器芯片的數(shù)據(jù)手冊編寫SPI讀取代碼,調(diào)試代碼以便讀取到角度信息,因為是外設(shè)配置,本文對此部分不做具體說明;可以參考spi_pwm_encoder.c文件,得到角度信息后進(jìn)行機械角度轉(zhuǎn)變?yōu)殡娊嵌鹊挠嬎悖?/p>
#define ENCODER_SPI_MAX 16384?
#define POLES 11?
/*?
* Calculate electrical angle based on motor, data in Q16 format?
* 16384 stand for 360degree(65536)*motor poles?
*/?
SPI_EIAngle = (int16_t)((65536*SPI_Angle_Digital*POLES)/ENCODER_SPI_MAX)%65536;
SPI_Angle_Digital為通過SPI讀取的電角度數(shù)字量,ENCODER_SPI_MAX為最大數(shù)字量輸出,比如14-bit精度的輸出,這個數(shù)據(jù)為16384。
3.4 創(chuàng)建絕對值編碼器的結(jié)構(gòu)體
創(chuàng)建結(jié)構(gòu)體,包含SpeednPosFdbk_Handle_t的通用組件,并且定義私有成員變量,用于絕對位置編碼器的控制以及參量計算,在abs_encoder_pos_fdbk.c中進(jìn)行初始化動作。
/**
?
?* @brief Abs encoder component parameters definition
?
?*/
?
?typedef struct
{
? ? ??
? ? ? SpeednPosFdbk_Handle_t _Super;
? ? ?
? ? ? int16_t Encoder_EIAngle; /* Encoder final electrical angle */? ? ? ? ? ? ? int16_t Encoder_MecAngle; /* Encoder final mechanical angle */? ? ? ? ? ? ?
int16_t Encoder_AngleD_Pre; /* Encoder previous digital angle */? ? ? ? ? ? int16_t Encoder_AngleD_Now; /* Encoder present/now digital angle */? ? ? ? ? int16_t Encoder_Speed_RPM; /* Speed uint in RPM */
? ? ??
? ? ? int16_t Encoder_Average_Speed_RPM; /* Average speed unit in RPM*/ ? ? ? ? ? bool SensorIsReliable; ? ? ?
? ? ? uint8_t mode; ? ? ? ? ? ??
? ? ? int32_t Circle_Counter; /* Count the circle for motro run */? ? ? ? ? ? ? bool Middle_Flag; /* Middle flag, 1--arrived pass middle*/ ? ? ??
} Abs_Encoder_Handle_t;
3.5 絕對值編碼器函數(shù)編寫
參照encoder_speed_pos_fdbk.c文件編寫對應(yīng)的絕對值編碼器的代碼,接口函數(shù)對應(yīng)起來,這樣可以有效的集成擴展, 分別定義:
1) 初始化函數(shù)Abs_Encoder_Init
2) 清除函數(shù)Abs_Encoder_Clear
3) 絕對值編碼器讀取電角度函數(shù)Abs_Encoder_GetElAngle
4) 讀取機械角度函數(shù)Abs_Encoder_GetMecAngle
5) 得到電機平均機械速度函數(shù) Abs_Encoder_GetAvrgMecSpeed01Hz
6) 計算電機平均機械速度函數(shù) Abs_Encoder_CalcAvrgMecSpeed01Hz
3.6 添加絕對位置編碼器代碼
3.6.1 絕對位置編碼器速度計算
參照2.2公式進(jìn)行速度計算
/* Speed_RPM = Deta(angle)*60*f/65536 */?
pHandle->Encoder_Speed_RPM = (int16_t)((int32_t)(pHandle->Encoder_AngleD_Now - pHandle-
>Encoder_AngleD_Pre * 60 * SPEED_LOOP_FREQUENCY_HZ/65536);
3.6.2 添加編碼器初始化代碼
在mc_task.c文件的MCboot()函數(shù)中增加初始化代碼
Abs_Encoder_Init(&Abs_Encoder_M1); // Initial absolute encoder sensor
Abs_Encoder_M1.mode = SENSOR_SPI;
3.6.3 角度以及速度的代碼添加
中頻任務(wù)TSK_MediumFrequencyTaskM1中加入絕對編碼器的角度以及速度計算函數(shù)
Abs_Encoder_CalcAvrgMecSpeed01Hz(&Abs_Encoder_M1, &wAux );
3.7 判斷絕對位置編碼器角度準(zhǔn)確性
使用DAC對角度進(jìn)行判讀,在mc_task.c中TSK_HighFrequencyTask()函數(shù)中增加UserDAC數(shù)據(jù)更新,一個DAC輸出無 傳感觀測器輸出的電角度,另外一路輸出絕對值編碼器計算出的電角度。
/* DAC output*/?
extern UI_Handle_t * pDAC;?
static int16_t temp1;?
static int16_t temp2;?
temp1 = FOCVars[M1].hElAngle;?
temp2 = Abs_Encoder_M1.Encoder_EIAngle;?
DAC_SetUserChannelValue(pDAC, DAC_USER1, temp1);?
DAC_SetUserChannelValue(pDAC, DAC_USER2, temp2);
程序修改完成后編譯下載,讓電機工作在無傳感閉環(huán)模式,示波器連接DAC兩路輸出。
如果出現(xiàn)下面波形,則需要交換其中任意兩條電機線,或者在程序中加入負(fù)號即可;

下面的圖為正確的DAC輸出波形,可以看到黃色為無傳感器觀測器輸出電角度,藍(lán)色為計算得到的絕對位置編碼器輸出計算結(jié)果,兩者基本重合,如果出現(xiàn)吻合度非常差,波形錯開很多情況下,有兩種措施,要么和電機廠商溝通做準(zhǔn)確的零點校準(zhǔn), 要么自行增加角度補償。

3.8 絕對位置角度閉環(huán)程序修改
3.8.1 修改電角度賦值
修改mc_task.c中TSK_MediumFrequencyTaskM1()函數(shù),由無傳感方式獲得電角度改為絕對位置編碼器的轉(zhuǎn)換完成的電角度,并且返回對應(yīng)的速度值;
#if defined(ABS_ENCODER_MODE)? ? ? ? ? ? ? ? ? ? ? ? ?Abs_Encoder_CalcAvrgMecSpeed01Hz(&Abs_Encoder_M1, &wAux );
? ? ?IsSpeedReliable = 1;
#else
? ? ?
?Abs_Encoder_CalcAvrgMecSpeed01Hz(&Abs_Encoder_M1, &wAux );
? ? ?IsSpeedReliable = STO_PLL_CalcAvrgMecSpeedUnit( &STO_PLL_M1, &wAux );?
#endif
3.8.2 屏蔽無傳感電角度計算
修改 mc_task.c 中 TSK_HighFrequencyTask() 函數(shù),只保留MC_FOC_DURATION這個判斷,其他都可以屏蔽掉。
hFOCreturn = FOC_CurrController(M1);?
if(hFOCreturn == MC_FOC_DURATION)?
{
??
? ?STM_FaultProcessing(&STM[M1], MC_FOC_DURATION, 0);?
}
3.8.3 修改速度傳感器為絕對編碼傳感器
修改 mc_task.c 中 TSK_MediumFrequencyTaskM1()函數(shù),傳感器設(shè)定修改為絕對編碼器。
case CLEAR:
?
? FOCVars[M1].bDriveInput = INTERNAL;
?
? STC_SetSpeedSensor( pSTC[M1], &Abs_Encoder_M1._Super );
?
? if ( STM_NextState( &STM[M1], START ) == true )
?
? {
? ?
? ?FOC_Clear( M1 );
? ?
? ?R3_2_SwitchOnPWM( pwmcHandle[M1] );
??
? ?}
?
? ?break;
3.8.4 修改狀態(tài)跳轉(zhuǎn)
因為開始就直接是閉環(huán)運行,因此需要修改mc_task.c中TSK_MediumFrequencyTaskM1()函數(shù),狀態(tài)START下,只需要一條跳轉(zhuǎn)語句,其他刪除或者屏蔽掉。
case START:
? ??
? ?STM_NextState( &STM[M1], START_RUN );
break ;
同樣在狀態(tài)START_RUN下,也只需要一條跳轉(zhuǎn)語句。
case START_RUN:
? ?
? ?STM_NextState( &STM[M1], RUN );?
break ;
3.9 絕對編碼器力
以上修改完成后,重新編譯下載后就可以直接運行電機了,電機需要工作在力矩模式,如果使用GUI進(jìn)行控制,則切換到Torque模式,同時設(shè)定Iqref數(shù)據(jù),500這個數(shù)據(jù)計算來自于電機最大支持電流數(shù)字量(比如5000)的10%,可以逐漸增加這個數(shù)據(jù),直到電機能夠運轉(zhuǎn)起來。

如果程序控制電機運行則可以寫為下面代碼
MC_ProgramTorqueRampMotor1(500,1000);?
MC_StartMotor1();
3.10 絕對編碼器速度閉環(huán)運行電機
速度輸出已經(jīng)在上面的中頻任務(wù)中計算得到,可以在無傳感模式下對編碼器速度輸出做調(diào)試,看輸出速度是否和無傳感匹配,如果匹配后可以轉(zhuǎn)入絕對編碼器的速度閉環(huán)控制

如果程序控制電機運行則可以寫為下面代碼:
MC_ProgramSpeedRampMotor1 (600/6,1000); // 600RPM?
MC_StartMotor1();
3.11 使用絕對編碼器做位置環(huán)
3.11.1 框架說明
以表貼電機為例,一般的FOC電機控制是兩環(huán)控制,速度環(huán)+電流環(huán)的控制方式,速度環(huán)為外環(huán),電流環(huán)為內(nèi)環(huán),Idref = 0(d軸電流參考為0)的控制為常見控制;參考速度環(huán)輸出參考轉(zhuǎn)矩后供后端電流環(huán)路;
如果增加位置環(huán)最簡單和直接的方式即為將速度環(huán)換為位置環(huán),即變?yōu)槲恢铆h(huán)+電流環(huán)的方式:


3.11.2 計算說明
為了方便控制,以及考慮精度問題,角度單位為rad*10000,比如電機轉(zhuǎn)動10圈,也就是10圈后的位置,則該位置的設(shè)定角度為:
? ? ? ? ? ?θset=10?2???10000≈628320
位置環(huán)采用的是線性系數(shù)乘以系數(shù)直接輸出到速度的參考:
? ? ? ? ? ??re??=?????(?????????)
? ? ? ? ? ??????位置環(huán)系數(shù)
? ? ? ? ? ????? ?設(shè)定的位置
? ? ? ? ? ??????當(dāng)前的位置
當(dāng)前位置的角度數(shù)字量計算如下:
? ? ? ? ? ????=???????2??????10000+????
? ? ? ? ??????? –絕對值編碼器轉(zhuǎn)過的整圈數(shù)
? ? ? ? ??????當(dāng)前的機械角度的數(shù)字量
? ? ? ? ????? ?取值3.1416
3.11.3 位置控制改進(jìn)
在三環(huán)控制中,不可避免的會涉及到加速度的計算,正??刂七^程為:加速、勻速、減速、定位過程。

那么會涉及到加速度以及定位階段的控制,這邊我們可以按照加速度與速度差成反比,而定位階段可以直接設(shè)定速度為0,但是實際使用過程中我們會發(fā)現(xiàn)直接直接的速度模式在定位階段力量有限。
方法一:PID 需要特別設(shè)定,更能夠定位到給定位置;
方法二:拋開速度環(huán),直接力矩控制;

3.11.4 增加以下變量或函數(shù)用于位置環(huán)控制
1) 位置環(huán)PID 結(jié)構(gòu)體PID_Handle_t PIDAngleHandle_M1
2) 位置控制的結(jié)構(gòu)體Position_Handle_t
3) 位置角度誤差Position_GetErrorAngle
4) 位置環(huán)速度參考輸出計算Position_CalcSpeedReferrence
5) 位置環(huán)力矩參考輸出計算Position_CalcTorqueReferrence
3.11.5 增加位置控制程序
在mc_task.c的中頻任務(wù)函數(shù)TSK_MediumFrequencyTaskM1中增加位置環(huán)差值數(shù)據(jù)計算,根據(jù)差值計算,當(dāng)差值在閾值之上的話進(jìn)行速度控制,在RUN下調(diào)用下面函數(shù):
/* Get error position/Angle, unit in rad*10000 */
Position_GetErrorAngle(&Abs_Encoder_M1,Target_Angle);?
……?
Position_CalcSpeedReferrence()
如果差值縮小到一定范圍內(nèi),則進(jìn)行力矩控制,在函數(shù)FOC_CalcCurrRef中進(jìn)行調(diào)用。
__weak void FOC_CalcCurrRef(uint8_t bMotor)?
{
? ? ??
? ? ? if(FOCVars[bMotor].bDriveInput == INTERNAL)
? ? ??
? ? ? {
? ? ??
? ? ? #if defined(POSITION_CONTROL)
? ? ? ? ? ??
? ? ? ? ?/* If in position torque mode, Iqref come from Angle PID*/? ? ? ? ? ? ? ? if(Position_M1.Mode_Flag == P_TORQUE_MODE)
? ? ? ? ? ??
? ? ? ? ?{
? ? ? ? ? ? ? ? ??
? ? ? ? ? ? ?FOCVars[bMotor].hTeref = Position_CalcTorqueReferrence();
? ? ? ? ? ? ? ?FOCVars[bMotor].Iqdref.q = FOCVars[bMotor].hTeref;? ? ? ? ? ? ? ? ? }
? ? ? ? ? ??
? ? ? ? ?else
? ? ??
? ? ? #endif
? ? ? ? ? ??
? ? ? ? ?{
? ? ? ? ? ? ? ? ?
? ? ? ? ? ? FOCVars[bMotor].hTeref=STC_CalcTorqueReference(pSTC[bMotor]);? ? ? ? ? ? ? FOCVars[bMotor].Iqdref.q = FOCVars[bMotor].hTeref;? ? ? ? ? ? ? ? ? ? }
? ? ? ?
? ? ? ?}?
}
3.11.6 實際運行效果
位置偏差較大時,電機加速到最大速度,偏差小時將進(jìn)入力矩控制模式,直到目標(biāo)位置,在目標(biāo)位置直接力矩鎖定。
設(shè)定位置角度值在0<- ->15708000(電機轉(zhuǎn)動250圈) 實際速度與參考速度曲線。

3.12 測試說明
spi_pwm_encoder.h中提供了測試宏定義
A,無傳感運行,用于測試絕對值編碼器,如下宏定義
//#define ABS_ENCODER_MODE?
//#define POSITION_CONTROL?
#define ENCODER_SPI_MODE?
//#define ENCODER_PWM_MODE
B,絕對值編碼器,SPI輸出模式的閉環(huán)運行
#define ABS_ENCODER_MODE?
//#define POSITION_CONTROL?
#define ENCODER_SPI_MODE?
//#define ENCODER_PWM_MODE
C,加入位置環(huán)后的三環(huán)運行
#define ABS_ENCODER_MODE?
#define POSITION_CONTROL?
?#define ENCODER_SPI_MODE?
//#define ENCODER_PWM_MODE
4. 編碼器PWM輸出模式
一般編碼器的PWM都是以固定頻率輸出,占空比代表角度,Duty的范圍為0—T,也就是0—360度(機械角度);

4.1 CubeMx配置
編碼器的PWM輸出可以直接使用ST芯片的TIMER PWM捕獲功能獲取PWM波的頻率(用于計算電角度)和占空比(機械角度信息);這邊我們使用TIM2;打開MC workbench生成的*.ioc文件,修改TIM2為PWM捕獲輸入:

將TIM2捕獲輸入通道1配置為Rising Edge,通道2為間接捕獲輸入(實際是TIM2_CH1的內(nèi)部輸出),配置為Falling Edge,同時我們看到PA15(TIM2_CH1)已經(jīng)被配置;通道1為頻率捕獲,通道2為占空比捕獲;

使能TIM2中斷

編輯中斷優(yōu)先級
因為TIM2中斷不能妨礙電機運行,因此需要修改中斷優(yōu)先級為搶占等級為3
4.2 重新生成代碼
在生成代碼的main.c中增加通道使能代碼,TIM2(32-bit)工作在72MHz,如果默認(rèn)編碼器1K PWM,CCR1計數(shù)數(shù)值為 72MHz/1KHz = 72000
/* In PWM mode, use Tim2 PWM capture mode to get frequency and duty cycle,?
* CCR1 value is the frequency of PWM, can be used as calibration value.?
* CCR2 value is for digital angle?
* In this demo, TIM2 work under 72MHz?
*/?
HAL_TIM_Base_Start(&htim2);
HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_1);?
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
4.3 編寫電角度轉(zhuǎn)換讀取代碼
void PWM_Encoder_EIangle(uint32_t data)?
{
? ? ?
? ? ?PWM_Angle_Digital = data;
? ? ?
? ? ?if(PWM_Angle_Digital > ENCODER_PWM_MAX)? ? ? ? ? ??
? ? ? ? ? PWM_Angle_Digital = ENCODER_PWM_MAX;
? ? ?
? ?PWM_EIAngle= (int16_t((65536*PWM_Angle_Digital*POLES)/ENCODER_PWM_MAX)%65536;
}
在TIM2的中斷服務(wù)中讀取CCR2寄存器內(nèi)容,為防止誤動作,這邊CCR1數(shù)據(jù)標(biāo)定為ENCODER_PWM_MAX,可以仿真條件下讀取這個數(shù)據(jù),如果編碼器準(zhǔn)確1KHz PWM輸出這個數(shù)據(jù)為72000,如果為其他數(shù)據(jù)可以進(jìn)行對應(yīng)修改;
#define ENCODER_PWM_MAX 67039 //72000?
void TIM2_IRQHandler(void)?
{
? ?
? ? PWM_Encoder_EIangle(TIM2->CCR2);
? ?
? ? Encoder_EIAngle = PWM_EIAngle;
? ?
? ? Encoder_MecAngle = (int16_t)(PWM_Angle_Digital*65536/ENCODER_PWM_MAX);?
}
4.4 判斷絕對位置編碼器角度準(zhǔn)確性
同3.7所描述
4.5 絕對位置角度閉環(huán)程序修改
同3.8,3.9,3.10的描述
4.6使用絕對編碼器做位置環(huán)
參照 3.11 中的具體操作