记录本人学习的笔记
开始学习PID感觉他很难,其实并不是的。首先我们需要取了解PID控制电机的基本流程,然后再搭配你学的理论知识。
下面来讲讲具体的实现,首先设置单片机的定时器用来读取到AB相中的脉冲数目(首先注意的是获取脉冲的通道只能是定时器的前两个通道),然后在PID中将你设置的脉冲数目与测得的脉冲数目进行对比。通常来说我们设定的是转速,所以我们需要将转速转换成脉冲数目。举个例子:TT电机13线的使四倍频,一圈有2496个脉冲,我们设定50转每分钟(这里只轮子转速,如果是电机内部转速需要除以你的减速比),然后我们设置定时器10ms读取一次值。那么10ms脉冲数应该为50*2496/6000(一分钟有6000个10ms),因此将设定脉冲与目前测得得脉冲带入PID计算公式算出值,抽象地来说它就是个依据偏差,产生的控制作用。通过计算值产生一个PWM信号。
下面是部分代码,可以看一看,基本上就这些东西。
1.对定时器进行设置
#include "encoder.h"
#include "delay.h"
#include "usart.h"
//PA0
//PA1
void TIM5_Encoder_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0x00; // No prescaling
TIM_TimeBaseStructure.TIM_Period = 65535;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM5, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM5, &TIM_ICInitStructure);
TIM_SetCounter(TIM5,0);
TIM_Cmd(TIM5, ENABLE);
}
2.PID计算部分
这里有两种计算公式就是写法不同,用哪个都可以,PID计算公式用很多看你自己需求。
#include "pid.h"
static double Kp=10;
static double Ki=0;
static double Kd=0;
int shouxie_PID_ZLP(int now , int set)
{
int PWM,Bias,Bias_D;
static int error,last_Bias;
Bias = now - set;
error +=Bias;
Bias_D=Bias-last_Bias;
PWM = Kp*Bias + Ki*error +Kd*Bias_D;
last_Bias = Bias;
return PWM;
}
static double ProportionA=70;
static double IntegralA=25;
static double DerivativeA=35;
int SPEED_PID_A(int now,int set)
{
static int LastError; //Error[-1]
static int PrevError; //Error[-2]
int iError,Output;
iError=set-now;
Output = ((ProportionA * (iError - LastError)) + (IntegralA * iError) + (DerivativeA * (iError - 2*LastError + PrevError)));
PrevError=LastError;
LastError=iError;
return Output;
}
3.对输出进行控制
#include "control.h"
#include "pid.h"
#include "datascope.h"
#include "motor.h"
#include "usart.h"
int para_A;
int Motor_A;
int SetRPMA=100;
#define SetPointA SetRPMA*2496/6000
int myabs(int a){
int temp;
if(a < 0) temp = -a;
else temp = a;
return temp;
}
int Read_Encoder(u8 x)
{
int Encoder_TIM;
switch(x)
{
case 5: Encoder_TIM= (short)TIM5 -> CNT; TIM5 -> CNT=0;break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
int Encoder_A;
int Send_Count,i;
extern int adc_data;
int position_out;
int ac_speed_b;
int bigout_B;
void TIM6_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM6, TIM_IT_Update) != RESET)
{
Encoder_A=Read_Encoder(5); //读取编码器得值
para_A=SPEED_PID_A(Encoder_A,SetPointA);
/*
将设定脉冲与测得脉冲带入到PID控制器中
*/
printf("%d\r\n",Encoder_A);
if((para_A<-3)||(para_A>3))
{
Motor_A +=para_A;
}
if(Motor_A>7000)Motor_A=7000;//限幅
if(Motor_A<-7000)Motor_A=-7000;
if(Motor_A > 0){
MotorA1 = 0;
MotorA2 = 1;
}else{
MotorA1 = 1;
MotorA2 = 0;
}TIM8->CCR1 = myabs(Motor_A);
/*定时器8 我使用得计数周期为一万,CCR1的值为 myabs(Motor_A)*/
}
TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
}
有不懂的或者是我错的可以写在评论区。