stm32(HAL)庫(kù)編碼器電機(jī)pid代碼及利用VOFA+對(duì)Pid波形顯示調(diào)參
基本介紹
PID控制是一種經(jīng)典的反饋控制算法,它通過不斷地調(diào)整輸出來使系統(tǒng)的實(shí)際值與設(shè)定值盡量接近,并保持在設(shè)定值附近。PID控制器由三個(gè)部分組成:比例§、積分(I)和微分(D)。
比例作用(P):比例作用通過測(cè)量實(shí)際值與設(shè)定值之間的偏差,乘以一個(gè)比例系數(shù)來產(chǎn)生輸出。輸出與偏差成正比,用來調(diào)整系統(tǒng)的響應(yīng)速度和穩(wěn)定性。較大的比例系數(shù)會(huì)增加系統(tǒng)的靈敏度,但可能導(dǎo)致過渡振蕩。
積分作用(I):積分作用通過將偏差的累積值乘以一個(gè)積分系數(shù)來產(chǎn)生輸出。積分作用能夠消除系統(tǒng)的靜差,提高系統(tǒng)的穩(wěn)定性和響應(yīng)速度。然而,過大的積分系數(shù)可能導(dǎo)致系統(tǒng)過度響應(yīng)和振蕩。
微分作用(D):微分作用通過測(cè)量偏差變化率,并乘以一個(gè)微分系數(shù)來產(chǎn)生輸出。微分作用能夠預(yù)測(cè)系統(tǒng)的未來趨勢(shì),以防止過沖和振蕩。合適的微分系數(shù)可以提高系統(tǒng)的響應(yīng)速度和穩(wěn)定性,但過大的微分系數(shù)可能導(dǎo)致靈敏度降低。
PID控制器的輸出是比例、積分和微分的加權(quán)和,即輸出 = P * 偏差 + I * 積分值 + D * 變化率。通過調(diào)整比例、積分和微分系數(shù),可以根據(jù)實(shí)際需求優(yōu)化系統(tǒng)的性能。pid的控制規(guī)律:
Kp——比例系數(shù); Ti——積分時(shí)間常數(shù); TD——微分時(shí)間常數(shù)
利用pid控制電機(jī)是我們學(xué)習(xí)pid的基礎(chǔ),以下是我用HAL庫(kù)通過stm32控制編碼器電機(jī)pid代碼及利用VOFA+對(duì)Pid波形顯示調(diào)參
配置與程序部分
以F103為例配置hal,其實(shí)其他版本的也差不多
先基礎(chǔ)配置時(shí)鐘樹,RCC,SYS,這里可以略過了
先配置io輸出引腳,供一個(gè)電機(jī),到下面寫代碼會(huì)define控制引腳高電平低電平,控制電機(jī)正反轉(zhuǎn)
接下來需要配置3個(gè)定時(shí)器,分別用于pwm輸出,編碼器捕獲,和中斷。編碼器捕獲用于識(shí)別脈沖,計(jì)算速度反饋給pid調(diào)速,中斷定時(shí)器里就用于中斷回調(diào)計(jì)算pid。接下來配置定時(shí)器
配置定時(shí)器2打開編碼器捕獲
定時(shí)器3,pwm輸出
定時(shí)器4
我們還要利用usart與上位機(jī)通信顯示波形
這里我用的usart2,usart1和I2c是之前我用來和藍(lán)牙通信的和iled的,可以不管
最后還要去NVIC里設(shè)置中斷優(yōu)先
這里配置就完成了,建立工程
首先我們需要打開定時(shí)器和USART,放在main中,while之前,定時(shí)器初始化之后,為了方便我們之后的函數(shù)都寫在main.c里都可以,首先定時(shí)器就是以下代碼
// 為了方便我們的函數(shù)都寫在main.c里
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL); //開啟編碼器模式
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start(&htim2);
HAL_TIM_Base_Start_IT(&htim4); //開啟定時(shí)器的中斷
HAL_TIM_Base_Start(&htim4);
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_Base_Start(&htim3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
因?yàn)槲覀冃枰肰OFA+來打印波形,要用printf命令(VOFA的特點(diǎn)就是不用協(xié)議有printf就可打印波形非常好用),所以需要在函數(shù)前include <stdio.h>就可
// An highlighted block
#include <stdio.h>
了解上面的pid的控制規(guī)律
然后就寫pid的代碼了
直接把下面的代碼無腦復(fù)制到main前面就行了
// An highlighted block
typedef struct __PID_Increment_Struct//一個(gè)定義結(jié)構(gòu)體
{
float Kp, Ki, Kd; //系數(shù)
float Error_Last1; //上次誤差
float Error_Last2; //上次誤差
float Out_Last; //上次輸出
} PID_Increment_Struct;
void motor1()//正//BIN()是我在main.h里define的,往下可以看到
{
BIN1(0);
BIN2(1);}
void motor0()//反//如果你的反了改0,1即可
{
BIN1(1);
BIN2(0);}
//vofa的FireWater數(shù)據(jù)協(xié)議 換行結(jié)尾 /n或/r/n 逗號(hào)分隔通道
//指定三個(gè)通道
/* USER CODE BEGIN 0 */
#define Encoder_TIM_Handle htim2
#define Motor_MAX_Duty 2000
// PID_Increment_Struct PID_Speed = {80, 5};
float Get_Speed()//計(jì)算速度
{
int16_t zj;
float Speed = 0;
zj = __HAL_TIM_GetCounter(&Encoder_TIM_Handle);
__HAL_TIM_SetCounter(&Encoder_TIM_Handle, 0);
Speed = (float)zj / (4 * 11 * 30) * 100 * 60;//30是減速比,11是基礎(chǔ)脈沖,4是因?yàn)?分頻,可不改,最后算出速度是一分鐘多少轉(zhuǎn)
b=Speed;//提取數(shù)值到外部變量,到時(shí)候放到while中顯示實(shí)時(shí)速度
return Speed;
}
//float Get_Angle()//計(jì)算角度(只速度控制可忽略)
//{
// int16_t zj;
// float angle = 0;
// zj = __HAL_TIM_GetCounter(&Encoder_TIM_Handle);
// angle = (float)zj / (4 * 15 * 34) * 360;
// return angle;
//}
float PID_Increment(PID_Increment_Struct *PID, float Current, float Target)//pid計(jì)算
{
float err, //誤差
out, //輸出
proportion, //比例
differential; //微分
err = (float)Target - (float)Current; //計(jì)算誤差
proportion = (float)err - (float)PID->Error_Last1; //計(jì)算比例項(xiàng)
differential = (float)err - 2 * (float)PID->Error_Last1 + (float)PID->Error_Last2; //計(jì)算微分項(xiàng)
if(err<=40&&err>-40)
out = (float)PID->Out_Last + (float)PID->Kp * proportion+(float)PID->Ki * err+ (float)PID->Kd * differential; //計(jì)算PID
else//如果不if else,調(diào)試的時(shí)候如果單片機(jī)只是5v上電但電機(jī)沒轉(zhuǎn),你接12v電源開電機(jī),電機(jī)速度會(huì)拉滿,這就不好了
out = (float)PID->Out_Last + (float)PID->Kp * proportion+ (float)PID->Kd * differential; //計(jì)算PID
PID->Error_Last2 = PID->Error_Last1; //更新上上次誤差
PID->Error_Last1 = err; //更新誤差
PID->Out_Last = out; //更新上此輸出
return out;
}
void motor(int16_t Speed)
{
if (Speed == 0)
{
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Motor_MAX_Duty+1 );
}
else if (Speed > 0)
{
motor0();
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Speed);
}
else if (Speed < 0)
{
Speed *= -1;
motor1();
__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,Speed);
}
}
PID_Increment_Struct PID_Speed = {20, 0.1, 0};//這個(gè)參數(shù)是我調(diào)的,不同參數(shù)得看不同電機(jī)
//PID_Increment_Struct PID_Angle = {3.1, 0, 0.06};//角度控制的,可忽略
//float angle;
float mb_speed_last;
float aa = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
float Speed = 0;
float set_speed = 0;
float mb_speed;
if (htim == &htim2)
{
}
else if (htim == &htim4)//注釋的都是角度控制的,到時(shí)候可以在深入使用
{
// angle += Get_Angle();
// mb_speed = (int16_t)PID_Increment(&PID_Angle, angle, aa);
// if (PID_Angle.Error_Last1 > 360)
// mb_speed = 300;
// else if (PID_Angle.Error_Last1 < -360)
// mb_speed = -300;
Speed = Get_Speed();
mb_speed = 120;//目標(biāo)速度
set_speed = PID_Increment(&PID_Speed, Speed, mb_speed);
if (set_speed > 2000)
set_speed = 2000;
else if (set_speed < -2000)
set_speed = -2000;
motor(set_speed);
if (set_speed > 100 || set_speed < -100)//死區(qū)控制,改善電機(jī)異響
motor(set_speed);
a=set_speed;//提取數(shù)值到外部變量,到時(shí)候放到while中顯示實(shí)時(shí)占空比
}
}
宏定義電機(jī)輸出變量
#define BIN1(state) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_12,(GPIO_PinState)(state)) //IN1
#define BIN2(state) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_13,(GPIO_PinState)(state)) //IN2
//控制電機(jī)正反的輸出
接下來在while里加入printf命令
printf("%2f\n",b);//顯示實(shí)時(shí)速度,vofa的FireWater數(shù)據(jù)協(xié)議 記得要換行結(jié)尾 \n,不然打印不出來,逗號(hào)分隔通道
下載編譯燒錄就可完成了
vofa+調(diào)試顯示波形調(diào)參
需要下載vofa可從官網(wǎng)下鏈接: link
打開vofa,按圖選擇FireWater,F(xiàn)ireWater是用來顯示波形的協(xié)議,更多可參考官網(wǎng)的使用說明鏈接: link
選擇控件,將波形顯示器加入到主屏幕上,長(zhǎng)按拖動(dòng)即可文章來源:http://www.zghlxwxcb.cn/news/detail-546722.html
x軸默認(rèn)為t軸,y軸選擇io,即變量,單片機(jī)接上usb to ttl 轉(zhuǎn)換器,再接上位機(jī),識(shí)別到串口,打開串口,給電機(jī)上電即可顯示出波形,愉快的調(diào)參。
我最后的結(jié)果是這樣的文章來源地址http://www.zghlxwxcb.cn/news/detail-546722.html
到了這里,關(guān)于stm32(HAL)庫(kù)編碼器電機(jī)pid代碼及利用VOFA+對(duì)Pid波形顯示調(diào)參的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!