所需元件
??STM32F103開發板、L298N一個、帶編碼器的直流電機一個(如下圖所示,淘寶上有很多)

系統框圖

??通過系統框圖,我們需要做兩件事,一是要測速,二是要調節,測速目前流行的就是通過編碼器測速,調節器我采用的時PI調節,PI調節器調節的引數少,而且能夠消除靜差,當然用PID調節器也行,
編碼器
??編碼器的結構簡化如下圖:

??在電機轉軸上安裝了一個磁環,在磁環的下方有一個霍爾傳感器,在磁環轉動程序中就在霍爾傳感器的附近產生了變化的磁場,于是霍爾傳感器就輸出了脈沖信號,我所用的這個直流電機是1:48的減速電機,電機轉軸每轉動1圈,編碼器輸出13個脈沖信號,也就是說輸出轉軸轉動1圈,編碼器輸出13x48=624個脈沖,再通過STM32編碼器介面 4 倍頻就是 624x4=2496 個脈沖信號,通過STM32定時器的計數值除以2496就是輸出轉軸轉動的圈數,
??所謂4倍頻,如下圖:

??編碼器中有兩個線路,即A相和B相,我們以A相或B相為例,1個上升沿或者下降沿代表1個脈沖信號,由圖中可知有2個上升沿或2個下降沿,即2個脈沖信號,而所謂的4倍頻,就是把A、B相的上升沿和下降沿都加起來,一共8個,與之前的2個脈沖信號就是4倍,而所以要加起來做成4倍頻,可以提高測量轉速的精度,另外A、B相之間相差90度,從而可以判斷電機的轉向,如果電機正轉,A相比B相先90度,也就是說A相已經上升沿了B還是低電平,
PI調節器
??關于PID演算法,可參考STM32——PID恒溫控制
??這里貼兩張速度曲線圖:
??1、

??2、

??圖1中設定目標值為400,從圖上可以看出超調量還是比較小的,調節時間也比較短,調節的效果還是可以的,圖2中,目標值每隔一段時間增加100,加到400后又設為100,整體的調節效果還是蠻不錯的,(我程式中的PID引數套用的時候可能達不到圖中的效果,這與電機以及編碼器之間的差別有關,可適當在做調節)
主要程式
TIM_Encoder.c
#include "TIM_Encoder.h"
float RPM_1=0; //存盤上一次測速結果
void TIM_Encoder_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定時器4的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB埠時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //埠配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure); //根據設定引數初始化GPIOB
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x0; // 預分頻器
TIM_TimeBaseStructure.TIM_Period = 65535; //設定計數器自動重裝值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //選擇時鐘分頻:不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//使用編碼器模式3
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除TIM的更新標志位
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
//Reset counter
TIM_SetCounter(TIM4,0);
TIM_Cmd(TIM4, ENABLE);
}
int GetTIMCounter(void) //獲取計數值
{
int count=TIM_GetCounter(TIM4);
return count;
}
float GetRPM(int count) //計算轉速
{
// int RPM=count/2496*2000+0.5;//30ms計算一次(pid.T=30),60000ms為1min,也就是1min計算了2000次,2496=13*4*48表示轉動一圈的脈沖數,48表示1:48的減速比
float RPM=count*0.8f+0.5f; //等同于上式,2000/2496約等于0.8
if(RPM>1000) //過濾掉不合理的結果,仍然使用上次的速度,在按鍵設定速度的時候或者在減速為0時會有非常的大的錯誤測速結果,具體原因還未查清 {
{
return RPM_1;
}
RPM_1=RPM; //更新
return RPM;
}
PID.c
#include "PID.h"
PID pid;
//int time=0;
void PID_Init()
{
pid.Sv=400; //用戶設定轉速400
pid.Kp=0.3; //比例
pid.Ki=0.015; //積分
pid.Kd=0; //微分
pid.pwmcycle=100; //pwm周期100us
pid.T=30; //PID計算周期30ms
pid.OUT0=0;
pid.C1ms=0;
pid.SEk=0;
pid.Ek=0;
pid.Ek_1=0;
pid.DelEk=0;
pid.Dout=0;
pid.Iout=0;
pid.Pout=0;
}
void PID_Calc(float data) //pid計算
{
float out;
pid.Pv=data;
pid.Ek=pid.Sv-pid.Pv; //得到當前的偏差值
pid.Pout=pid.Kp*pid.Ek; //比例輸出
pid.SEk+=pid.Ek; //歷史偏差總和
if(pid.SEk<(-50))
{
pid.SEk=(-50);
}
pid.DelEk=pid.Ek-pid.Ek_1; //最近兩次偏差之差
pid.Iout=pid.Ki*pid.SEk; //積分輸出
if(pid.Iout<(-10))
{
pid.Iout=(-10);
}
pid.Dout=pid.Kd*pid.DelEk; //微分輸出
out= pid.Pout+ pid.Iout+ pid.Dout;
if(out>pid.pwmcycle)
{
pid.OUT=pid.pwmcycle;
}
else if(out<=0)
{
pid.OUT=pid.OUT0;
}
else
{
pid.OUT=out+0.5f; //四舍五入
}
pid.Ek_1=pid.Ek; //更新偏差
pid.C1ms=0;
}
工程鏈接
鏈接:https://pan.baidu.com/s/1dSXgPf0gzSvTdjlMHyOZ7w
提取碼:f8h1
??PID調參比較麻煩,這里推薦一個ST官方的軟體StmStdio,這個軟體網上有很多教程,使用也比較簡單,
鏈接:https://pan.baidu.com/s/1etsrBL80rCe_LouNEE1XEg
提取碼:ckve
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/305510.html
標籤:其他
上一篇:實作簡易通訊錄(動態增長版)
