基于单片机的电机转速PID控制
0、引言&实验器材
2020的国庆节一个人呆在实验室有点无聊,翻了翻还未填上的坑,发现对PID的理解一直停留在纸面上,不曾在现实中的系统中实际操作过(倒立摆和小四轴这些DIY入门训练都还没完整的做过,实在不甘心),于是从零搭建实验平台,以便深入理解PID算法,最后便有了此篇博客,仅作为记录本人学习之用,如有错误,还望指出,谢谢。
由于使用的串口助手没有曲线绘制功能,故暂无实际效果图(PID参数效果通过时间计算比较)
以下为本次所使用的材料
类目 | 型号 |
---|---|
MCU | N76e003 |
电机驱动 | TB6612 |
电机 | 单霍尔编码器测速电机 |
电源 | 30V2A可调电源(10V) |
串口模块 | CH340C |
示波器 | 普源 DS5062ME |
编译环境 | Keil4 |
1、PID算法
假设已对闭环控制有了最基本的认识(如无,可参考这篇文章),参考下图1可以得知,Setpoint为设定值(你所期望系统能达到的值),设定值和反馈值经过计算后得到当前Error(误差),后Error经过PID算法计算得到U,参考下图2,U经过执行机构(如电机)处理、输出,再通过测量元件(如编码器)得到反馈值,再与设定值进行计算,得到下一轮的误差,如此往复,直到设定值与反馈值一致。(位置型)
由上可知PID算法是一种通过对误差值的计算,控制执行机构达到设定值的一种算法。
其中Kp:比例调节、Ki:积分调节、Kd:微分调节,PID算法的公式如下式1所示
需要注意的是,以下所使用的各个计算公式,均是对PID算法进行离散化处理后得到的。
1.1、Kp:比例调节
比例调节的计算:Kp*Error
由上式可以看出,在比例调节中,输入与输出的信号成比例关系,但如果仅有P调节,系统将可能出现稳态误差。
关于稳态误差:控制系统的输出响应在过渡过程结束后的变化形态称为稳态。稳态误差为期望的稳态输出量与实际的稳态输出量之差。
比例调节作用:是按比例反应系统的偏差,系统一旦出现了偏差,比例调节立即产生调节作用用以减少偏差。比例作用大,可以加快调节,减少误差,但是过大的比例,使系统的稳定性下降,甚至造成系统的不稳定。
1.2、Ki:积分调节
积分调节的计算:Ki*(Error1+Error2+Error3+…+Error k)
简单来说,积分调节的计算过程就将积分系数*误差的累加值
积分调节是为了对稳态误差进行处理,同时提供系统精度,作用原理和效果可参考上方动图。
1.2.1、积分饱和现象:简单来说可理解为Error过大时,积分调节得到的数值过大,所以需要对最大输出值进行限制,防止出现积分饱和的发生。
1.2.2、为方便积分调节原理的理解,可假设系统出现稳态误差时,Error恒定不变,此时Kp与Kd调节均失效,但Ki并不受稳态误差影响,依然累积误差值,从而使PID调节不陷于稳态误差中。
为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。积分调节作用:是使系统消除稳态误差,提高无差度。因为有误差,积分调节就进行,直至无差,积分调节停止,积分调节输出一常值。积分作用的强弱取决与积分时间常数Ti, Ti越小,积分作用就越强。反之Ti大则积分作用弱,加入积分调节可使系统稳定性下降,动态响应变慢。积分作用常与另两种调节规律结合,组成PI调节器或PID调节器。
1.3、Kd:微分调节
微分调节的计算:Kd*(Erorr2-Erorr1)
其中Erorr1为上次的误差,Erorr2为本次的误差。
微分调节可以理解为对被控对象所产生的“阻尼”,当误差变化过快时,微分调节将出现较大“负值”(与误差变化方向相反值),抑制误差继续上升/下降(拉平曲线,参考动图)。
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入 “比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。微分调节作用:微分作用反映系统偏差信号的变化率,具有预见性,能预见偏差变化的趋势,因此能产生超前的控制作用,在偏差还没有形成之前,已被微分调节作用消除。因此,可以改善系统的动态性能。在微分时间选择合适情况下,可以减少超调,减少调节时间。微分作用对噪声干扰有放大作用,因此过强的加微分调节,对系统抗干扰不利。此外,微分反应的是变化率,而当输入没有变化时,微分作用输出为零。微分作用不能单独使用,需要与另外两种调节规律相结合,组成PD或PID控制器。
2、运行逻辑
2.1、计算转速
单片机通过使用输入捕获功能(上升沿触发),计算得到电机周期T,再通过T=1/f计算当前电机转动频率。
(备注:其中输入捕获采样6次以消除初值可能不稳的情况,由于串口中断优先级高于输入捕获优先级,所以人为将输入捕获设置为最高优先级,防止系统bug)
有关输入N76E003捕获的博文见此
初始化
P00_Input_Mode;//将P00设置为输入模式
P00 = 1;
set_ENF0;//打开0通道噪声滤波
TIMER2_CAP0_Capture_Mode;//采用CAP0组捕获信道
// TIMER2_CAP1_Capture_Mode;
// TIMER2_CAP2_Capture_Mode;
// IC3_P00_CAP0_BothEdge_Capture;//设定P00通过CAP0通道,双边沿触发
// IC3_P00_CAP0_FallingEdge_Capture;//下降沿触发
IC3_P00_CAP0_RisingEdge_Capture;//上升沿触发,得到的是周期T=1/f
set_ECAP;//使能输入捕获中断(位于拓展中断中)
set_TR2; //定时器2计数使能
set_T2DIV1;//16分频,通过PWM计算、测试(10ms)
set_PCAP;
set_PCAPH;//设定输入捕获为最高中断优先级
计算频率
while(1)
{
if(i>=6)//由于周期会发生变化,该方法会造成一定不等延时
{
//周期转换,单位us---->ms
time=10000/(temp[5]/100);//转换为频率
Send_Data_To_UART0(time);//测试PID参数速度用if
i=0;
// PID(Ev,time);
}
}
输入捕获中断
void Capture_ISR (void) interrupt 12
{
clr_CAPF0; //清除CAP0 通道中断标志
temp[i]=(C0H*256)+C0L;//将高八位低八位合并
i++;
clr_TF2;//清除定时器2溢出标志
}
2.2、数据收发
单片机在收到PC端下发的设定值后,将其放入PID函数中进行计算,后变为PWM占空比的脉宽,通过电机驱动器,调节电机转速。
(备注:为了保证串口中断不被更高优先级的输入捕获打断,所以串口接收中断时会关闭输入捕获,接收完成后再打开输入捕获中断使能。)
初始化
//----------串口1、串口0配置----------------
InitialUART0_Timer1(9600);
//在不配置SM1,SM2寄存器的情况下,默认工作在模式0(半双工)
IE = 0x90;//允许总中断中断,串口0中断
串口接收中断
/****************串口0中断服务程序***************************/
void Uart0_test() interrupt 4
{
if(RI)//串口0接收中断标志(有数据时,硬件置1)
{
clr_ECAP;//关闭输入捕获中断
RI=0;//软件置0
TI=0;//同时打开,需要对TI也置低
UART0_RX_BUF[UART0_RX_STA]=SBUF-48;//从SBUF缓存中读取接收到的数据
UART0_RX_STA++ ; //计算接收长度
//进行数据校验,通过RX_BUF&RX_STA
if(UART0_RX_STA==3)
{
//收到期望值
Ev=UART0_RX_BUF[0]*100+UART0_RX_BUF[1]*10+UART0_RX_BUF[2];
UART0_RX_STA=0;//清空接收计数器
uFlag=1;
}
}
if(uFlag)//接收完成进入
{
// start=1;//测试PID参数速度用
// PID(Ev,time);
uFlag=0;//清除接收完成标志位
set_ECAP;//开启输入捕获中断
}
}
2.3、PID计算&变频
当串口中断接收到新的设定值后,Err=设定值-当前电机频率,后通过PID算法进行计算,得到的值为PWM高电平的时间(脉宽),再放入PWM中,通过电机驱动器控制电机变频,再通过测量与电机轴同轴转动的霍尔编码器所产生的脉冲信号(输入捕获),计算当前频率,再更新Err,同时将前一个Err进行累积和赋值给Err1,再放入PID计算,如此往复。(待补图)
如需了解PWM中断及在运行中变频,可参考N76E003 PWM中断及设定新的占空比
本程序使用的是位置式PID。
初始化
//-----------------------------产生1KHz占空比50%的PWM---------------------------------------------
P03_PushPull_Mode;
PWM5_P03_OUTPUT_ENABLE;//使能PWM5,通过P03引脚输出
clr_PWMTYP;//边沿对齐模式
clr_PWMMOD0;//设置为独立输出模式
clr_PWMMOD1;
PWM_CLOCK_DIV_8;//8分频模式16MHz/8
PWMPH = 0x07;//1999
PWMPL = 0xcf;
set_SFRPAGE;//PWM4 and PWM5 duty seting is in SFP page 1
PWM5H = 0x03;//999
PWM5L = 0xe7;
clr_SFRPAGE;
set_LOAD;//载入周期和占空比
set_PWMRUN;//开始输出PWM
PID函数&重装PWM值
#define Kp 3.0
#define Ki 0.9
#define Kd 4.1
int Err=0,Err1=0;//PID过程中使用的算法
uint Ev=100;//期望值
int ErrAdd;//存放误差累积
// SHz为设定值,EHz为当前值(time
void PID(uint SHz,uint EHz)
{
Err1=Err;//获得上次的值
Err=SHz-EHz;//计算得到当前差值
if(Err>1||Err<-1)//由于实际系统中存在各类干扰,所以设定阈值为±1,提高系统稳定性
{
ErrAdd+=Err;//累积误差值
PWM_Value=1000+((Kp*Err)+(Ki*ErrAdd)+(Kd*(Err-Err1)));//PID计算
//防止积分饱和,由于PWM_Value为无符号整数型,为负值时会大于2000。
if(PWM_Value>2000)PWM_Value=1999;
//--------PWM重装初值---------------
clr_LOAD;//关闭载入之前的值
set_SFRPAGE;//PWM4 and PWM5 duty seting is in SFP page 1
PWM_Value-=1;
PWM5H= PWM_Value/256;//得到高八位
PWM5L= PWM_Value%256;//得到第八位
clr_SFRPAGE;
set_LOAD;//设定完成,开始载入
}
}
3、程序代码&电路
#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_define.h"
#define uint unsigned int
#define uchar unsigned char
#define Kp 3.0
#define Ki 0.9
#define Kd 4.1
//#define TH0_INIT 67
uint time;//频率
uint temp[10];//存放输入捕获值
int i=0;
uint PWM_Value;//需要改变的占空比值
uchar UART0_RX_BUF[3];//串口0,串口1数据接收缓冲区
uchar UART0_RX_STA=0;//串口0,串口1接收计数器
uchar uFlag=0;//串口中断指令接收完成标志
int Err=0,Err1=0;//PID过程中使用的算法
uint Ev=100;//期望值
int ErrAdd;//存放误差累积
char start=0;
/*
SHz为设定值,EHz为当前值(time
*/
void PID(uint SHz,uint EHz)
{
Err1=Err;//获得上次的值
Err=SHz-EHz;//计算得到当前差值
if(Err>1||Err<-1)
// if(SHz!=EHz)
{
ErrAdd+=Err;
PWM_Value=1000+((Kp*Err)+(Ki*ErrAdd)+(Kd*(Err-Err1)));//PID计算
if(PWM_Value>2000)PWM_Value=1999;
//--------PWM重装初值---------------
clr_LOAD;//关闭载入之前的值
set_SFRPAGE;//PWM4 and PWM5 duty seting is in SFP page 1
PWM_Value-=1;
PWM5H= PWM_Value/256;//得到高八位
PWM5L= PWM_Value%256;//得到第八位
clr_SFRPAGE;
set_LOAD;//设定完成,开始载入
}
}
/*
通过输入捕获得到的周期,计算当前频率,采样6次发送中间值
由于串口中断优先级高于输入捕获,所以人为将输入捕获设置为最高优先级,防止系统bug
串口中断中会关闭输入捕获,接收完成后再打开输入捕获
PWM需要在运行中变频。
PID函数是对参数进行设定,使用位置式(直接赋值)
电压10V
*/
void main (void)
{
Set_All_GPIO_Quasi_Mode;//所有IO设置为双向模式
P03_PushPull_Mode;
P00_Input_Mode;//将P00设置为输入模式
P00 = 1;
set_ENF0;//打开0通道噪声滤波
TIMER2_CAP0_Capture_Mode;//采用CAP0组捕获信道
// TIMER2_CAP1_Capture_Mode;
// TIMER2_CAP2_Capture_Mode;
// IC3_P00_CAP0_BothEdge_Capture;//设定P00通过CAP0通道,双边沿触发
// IC3_P00_CAP0_FallingEdge_Capture;//下降沿触发
IC3_P00_CAP0_RisingEdge_Capture;//上升沿触发,得到的是周期T=1/f
set_ECAP;//使能输入捕获中断(位于拓展中断中)
set_TR2; //定时器2计数使能
set_T2DIV1;//16分频,通过PWM计算、测试(10ms)
set_PCAP;
set_PCAPH;//设定输入捕获为最高中断优先级
// set_EA;
//-----------------------------产生1KHz占空比50%的PWM---------------------------------------------
PWM5_P03_OUTPUT_ENABLE;//使能PWM5,通过P03引脚输出
clr_PWMTYP;//边沿对齐模式
clr_PWMMOD0;//设置为独立输出模式
clr_PWMMOD1;
PWM_CLOCK_DIV_8;//16分频模式
PWMPH = 0x07;//00
PWMPL = 0xcf;//0f
set_SFRPAGE;//PWM4 and PWM5 duty seting is in SFP page 1
PWM5H = 0x03;//00
PWM5L = 0xe7;//08
clr_SFRPAGE;
set_LOAD;//载入周期和占空比
set_PWMRUN;//开始输出PWM
//----------串口1、串口0配置----------------
InitialUART0_Timer1(9600);
//在不配置SM1,SM2寄存器的情况下,默认工作在模式0(半双工)
IE = 0x90;//允许总中断中断,串口0中断
//---------输入你自己的代码-------------------
while(1)
{
if(i>=6)//由于周期会发生变化,该方法会造成一定不等延时
{
//周期转换,单位us---->ms
time=10000/(temp[5]/100);//转换为频率
if(start)Send_Data_To_UART0(time);//测试PID参数速度用if
i=0;
PID(Ev,time);
}
}
}
/****************串口0中断服务程序*****************/
void Uart0_test() interrupt 4
{
if(RI)//串口0接收中断标志(有数据时,硬件置1)
{
clr_ECAP;//关闭输入捕获中断
RI=0;//软件置0
TI=0;//同时打开,需要对TI也置低
UART0_RX_BUF[UART0_RX_STA]=SBUF-48;//从SBUF缓存中读取接收到的数据
UART0_RX_STA++ ; //计算接收长度
//进行数据校验,通过RX_BUF&RX_STA
if(UART0_RX_STA==3)
{
//收到期望值
Ev=UART0_RX_BUF[0]*100+UART0_RX_BUF[1]*10+UART0_RX_BUF[2];
UART0_RX_STA=0;//清空接收计数器
uFlag=1;
}
}
if(uFlag)//接收完成进入
{
start=1;//测试PID参数速度用
// PID(Ev,time);
uFlag=0;//清除接收完成标志位
set_ECAP;//开启输入捕获中断
}
}
/****************输入捕获中断服务程序*****************/
void Capture_ISR (void) interrupt 12
{
clr_CAPF0; //清除CAP0 通道中断标志
temp[i]=(C0H*256)+C0L;
i++;
clr_TF2;//清除定时器2溢出标志
}
4、调参思路
5、参考资料
简易PID算法的快速扫盲(超详细+过程推导+C语言程序)
PID(比例积分微分)介绍
本文地址:https://blog.csdn.net/u014798590/article/details/108986793
下一篇: QML之侧滑抽屉(菜单)