三環控制和PID在電機的應用
前言:
最近用到了大疆的直流無刷(BLDC)減速電機M3508和M2006。做RoboMaster比賽的同學應該對它們很熟悉,這兩款電機質量都不錯,配套電調C620、C610功能強大,應用場景廣泛。當然價格不算低。
我作為第一次接觸電機控制的新手,在搜索PID和三環控制資料的時候常常得到的是一些理論論述,而且千篇一律。雖然PID是較為簡單的控制算法,新人上手難度還是有些大。那么我就徹徹底底地回顧一下搭建最簡單的電機控制算法的流程。提供一個新的理解視角。
本文依托大疆官方M2006電機例程,其與M3508電機配套例程在CAN通信驅動、PID等核心部分基本相似甚至可以直接替換(M2006例程里的一些文件注釋寫的是3508)。最大的不同就是M3508使用了FreeRTOS實時系統,這一點並不方便我們的學習。而2006就是裸機前后台,非常簡單明晰。
例程均是基於Keil 、STM32F429(我使用407)、HAL庫
例程下載:M2006例程、M3508例程
2021/7/23:
第一次接觸這兩款電機或者CAN通信的朋友可以看一下這篇文章:
大疆M3508、M2006必備CAN總線知識與配置方法
0x00 PID到底怎么部署到電機上?
我們都知道PID應用廣泛,效果良好,在網上能搜到大量生動形象的文章。這些文章講述了比例、積分、微分有什么用處和缺陷,什么是死區,並且配有動態曲線圖來展示調參效果。但是舉的例子往往是這樣:一個水缸要固定水位,加水量是輸出,反饋是水位之差。我當時看過好多類似文章之后還是不明白怎么把PID應用於更復雜的系統,比如電機控制。
答案是 拆分 復雜系統,運用多次控制算法層層遞進
比如在電機上,常用的就是三環控制:電流環、速度環、位置環。
三環層層遞進並且具有因果關系。
這個關系非常好理解,通過電磁學知識我們知道電機能轉是因為有電流產生磁場。所以,電流是電機轉動的根本原因,也就是:
中學物理也講過,(角)速度是描述運動的物理量,包括方向、大小。如果希望控制速度,比如定速轉動,那就也要控制電流。再進一步,運動能改變物體位置,如果想確定達到某個位置那就控制速度。然而控制速度歸根結底是控制電流。得出:
得出結論:如果要控制位置,那就要控速度,如果要控速度,就必須控制電流。
位置環的輸出是速度值,速度環的輸出是電流值。而他們的反饋(輸入)都是電機的實時數據:實時速度,實時位置。
就這樣,最末端的電機^ 1反饋出三個值,依次給最外面的位置,中間的速度,里面的電流。形成了三個閉合的環。如下圖
不難發現,很多例子像水缸加水和直升機定高,他們都是直接的位置環,輸出量是加水的速度和向上飛的升力。沒有底層的兩環。
接下來我們以M2006例程為例子看一看怎么實現上述過程。
0x01 剖析例程--多少行代碼能實現電機控速?
先說明,M3508支持PWM和CAN兩種控制方式,其中PWM可以直接控速但是沒有數據反饋。M2006僅支持CAN總線。兩種電機的總線編碼都是從0x200開始,驅動文件
bsp_can.c
和can.c
幾乎一樣可以替換。本文僅介紹PID的部署實現,不涉及CAN通信內容。
總結下來,PID就是一個結構體三個函數。
一個結構體PID_TypeDef
:
typedef struct _PID_TypeDef
{
float target; //目標值
float kp; //比例系數
float ki; //積分系數
float kd; //微分系數
float measure; //測量值
float err; //誤差
float last_err; //上次誤差
float pout; //比例項
float iout; //積分項
float dout; //微分項
float output; //本次輸出
float last_output; //上次輸出
float MaxOutput; //輸出限幅
float IntegralLimit; //積分限幅
float DeadBand; //死區(絕對值)
float Max_Err; //最大誤差
void (*f_param_init) //參數初始化
void (*f_pid_reset) //pid三個參數修改
float (*f_cal_pid) //pid計算
}PID_TypeDef;
注意:為了簡單明晰,我刪去了原文件里一些用不到的參數,最后三個函數指針的參數列表也被刪除。其中不乏很重要的計算周期,但是在簡單的控制下,時間間隔是可以忽略的。
這個結構體的核心就是三個系數,調參調的也就是這三個。
target
目標值和output
輸出值還有measure
反饋值,再就是err
和last_err
兩個誤差。
其他的一些限幅,和死區[^2]無非就是防止問題發生的補丁。
三個函數:f_param_init
、f_pid_reset
、f_cal_pid
名副其實,分別是結構體的參數初始化,三個系數修改、最重要的輸出值計算。
static float pid_calculate(PID_TypeDef* pid, float measure)
{
pid->measure = measure; //目標速度
pid->last_err = pid->err; //更新前一次誤差
pid->err = pid->target - pid->measure; //計算當前誤差
pid->last_output = pid->output;
if((ABS(pid->err) > pid->DeadBand)) //是否進入死區,如果進入則直接跳過,返回上一次的output結果
{
pid->pout = pid->kp * pid->err;
pid->iout += (pid->ki * pid->err); //注意是加等於
pid->dout = pid->kd * (pid->err - pid->last_err);
//積分是否超出限制
if(pid->iout > pid->IntegralLimit)
pid->iout = pid->IntegralLimit;
if(pid->iout < - pid->IntegralLimit)
pid->iout = - pid->IntegralLimit;
//pid輸出和
pid->output = pid->pout + pid->iout + pid->dout;
//限制輸出的大小
if(pid->output>pid->MaxOutput)
{
pid->output = pid->MaxOutput;
}
if(pid->output < -(pid->MaxOutput))
{
pid->output = -(pid->MaxOutput);
}
}
return pid->output;
}
這是計算函數,我們要把實時測量值measure傳入函數,用來更新誤差,產生新的輸出。
measure是電機發來的速度或者位置數據,在CAN中斷函數里自動更新。
在這里我們用誤差之差代替微分,並且對積分項和輸出結果進行了限幅。這些幅度都是自定義的。
得出流程如下:
0x02 一個小例子
M2006例程相對M3508雖然簡單,但還是包括了一些上位機通信控制的代碼。
還是簡單明晰的原則。下面是一個最最簡單的demo框架。
#include "pid.h"
#define NUM_OF_MOTOR 1
PID_TypeDef moto_pid[NUM_OF_MOTOR];
float set_spd;
int main(){
init_all();
for(int i=0;i<NUM_OF_MOTOR;i++){
pid_init(&moto_pid[i]);//把結構體里的函數指針賦值,三個函數
moto_pid[i].f_param_init(&moto_pid[i],PID_Speed,16384,5000,10,0,8000,0,1.5,0.1,0);
//確定結構體內的參數,幅值,死區大小,PID系數
}
for(;;){
get_set_spd_from_USART();//從串口得到設定值set_spd
for(int i=0; i<NUM_OF_MOTOR; i++)
{
motor_pid[i].target = set_spd; //更新目標值
motor_pid[i].f_cal_pid(&motor_pid[i],measure[i]); //PID計算。measure由CAN中斷更新
}
set_moto_current(&hcan1,motor_pid[0].output, //將PID的計算結果通過CAN發送到電機
motor_pid[1].output,
motor_pid[2].output,
motor_pid[3].output);
HAL_Delay(10);//延時10ms控制周期
}
return 0;
}
到此為止,一個簡單的PID電機控制就做好了。如果寫的緊湊一點,代碼可能不超過50行,還是非常簡單的。
注意事項:
我選用的是F407,帶有FPU浮點運算單元的MCU。盡量選擇CM4,這樣浮點運算會快很多。
實際使用的時候要把Keil的option里面target一欄里floating point Hardware選成Single Precision
演示視頻在微信視頻號上:掃碼查看微信文章底部的視頻
0x03 總結
以上就是最簡單的電機控制部署,本着怎么簡單怎么來的原則,希望能幫助朋友們節約一些學習時間。
除了官方例程,我還有自己移植的基於F407的版本,去除例程的無用部分,加入了串口的通信解析,可以比較方便的調參,在線修改速度、pid參數等。有需要的可以加微信公眾號直接問我要。
關注嵌入式、電機控制的朋友也可以添加公眾號,最近會更新有關上位機通信,CAN通信等電機控制相關內容
大一技術新人,如果發現文中錯誤請各位大佬不吝賜教,一定指出,如果有意見或建議同樣歡迎。謝謝。
歡迎轉載,請注明原文鏈接:(https://www.cnblogs.com/huxiaoan/p/14727970.html)