标准的PID处理例程
/*====================================================================================================这是从网上找来的一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID
参数必须由具体对象通过实验确定。由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算,
而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算,可
大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余
数补偿。这个程序只是一般常用pid算法的基本架构,没有包含输入输出处理部分。
=====================================================================================================*/
#include <string.h>
#include <stdio.h>
/*====================================================================================================
PID Function
The PID (比例、积分、微分) function is used in mainly
control applications. PIDCalc performs one iteration of the PID
algorithm.
While the PID function works, main is just a dummy program showing
a typical usage.
=====================================================================================================*/
typedef struct PID {
doubleSetPoint; //设定目标 Desired Value
doubleProportion; //比例常数 Proportional Const
doubleIntegral; //积分常数 Integral Const
doubleDerivative; //微分常数 Derivative Const
doubleLastError; //Error[-1]
doublePrevError; //Error[-2]
doubleSumError; //Sums of Errors
} PID;
/*====================================================================================================
PID计算部分
=====================================================================================================*/
double PIDCalc( PID *pp, double NextPoint )
{
doubledError,
Error;
Error = pp->SetPoint -NextPoint; // 偏差
pp->SumError += Error; // 积分
dError = pp->LastError - pp->PrevError; // 当前微分
pp->PrevError = pp->LastError;
pp->LastError = Error;
return (pp->Proportion * Error // 比例项
+ pp->Integral * pp->SumError // 积分项
+ pp->Derivative * dError // 微分项
);
}
/*====================================================================================================
Initialize PID Structure
=====================================================================================================*/
void PIDInit (PID *pp)
{
memset ( pp,0,sizeof(PID));
}
/*====================================================================================================
Main Program
=====================================================================================================*/
double sensor (void) //Dummy Sensor Function
{
return 100.0;
}
void actuator(double rDelta) //Dummy Actuator Function
{}
void main(void)
{
PID sPID; //PID Control Structure
double rOut; //PID Response (Output)
double rIn; //PID Feedback (Input)
PIDInit ( &sPID ); //Initialize Structure
sPID.Proportion = 0.5; //Set PID Coefficients
sPID.Integral = 0.5;
sPID.Derivative = 0.0;
sPID.SetPoint = 100.0; //Set PID Setpoint
for (;;) { //Mock Up of PID Processing
rIn = sensor (); //Read Input
rOut = PIDCalc ( &sPID,rIn ); //Perform PID Interation
actuator ( rOut ); //Effect Needed Changes
}
}
请问大伙这个什么意思
doubleSetPoint; //设定目标 Desired Value 不懂,./emotion/em021.gif Desired Value 期望的值
就是你这个pid要达到的设定目标
假设你这个是液位控制,要稳定在1.00m 则这个就是1.00米.
假设你这个是温度控制,要稳定在52.3℃,则这个就是52.3℃.
假设你这个是转速控制,要稳定在128rpm,则这个就是128rpm. #include<reg51.h>
#include<intrins.h>
#include<math.h>
#include<string.h>
struct PID {
unsigned int SetPoint; // 设定目标 Desired Value
unsigned int Proportion; // 比例常数 Proportional Const
unsigned int Integral; // 积分常数 Integral Const
unsigned int Derivative; // 微分常数 Derivative Const
unsigned int LastError; // Error[-1]
unsigned int PrevError; // Error[-2]
unsigned int SumError; // Sums of Errors
};
struct PID spid; // PID Control Structure
unsigned int rout; // PID Response (Output)
unsigned int rin; // PID Feedback (Input)
sbit data1=P1^0;
sbit clk=P1^1;
sbit plus=P2^0;
sbit subs=P2^1;
sbit stop=P2^2;
sbit output=P3^4;
sbit DQ=P3^3;
unsigned char flag,flag_1=0;
unsigned char high_time,low_time,count=0;//占空比调节参数
unsigned char set_temper=35;
unsigned char temper;
unsigned char i;
unsigned char j=0;
unsigned int s;
/***********************************************************
延时子程序,延时时间以12M晶振为准,延时时间为30us×time
***********************************************************/
void delay(unsigned char time)
{
unsigned char m,n;
for(n=0;n<time;n++)
for(m=0;m<2;m++){}
}
/***********************************************************
写一位数据子程序
***********************************************************/
void write_bit(unsigned char bitval)
{
EA=0;
DQ=0; /*拉低DQ以开始一个写时序*/
if(bitval==1)
{
_nop_();
DQ=1; /*如要写1,则将总线置高*/
}
delay(5); /*延时90us供DA18B20采样*/
DQ=1; /*释放DQ总线*/
_nop_();
_nop_();
EA=1;
}
/***********************************************************
写一字节数据子程序
***********************************************************/
void write_byte(unsigned char val)
{
unsigned char i;
unsigned char temp;
EA=0;
TR0=0;
for(i=0;i<8;i++) /*写一字节数据,一次写一位*/
{
temp=val>>i; /*移位操作,将本次要写的位移到最低位*/
temp=temp&1;
write_bit(temp); /*向总线写该位*/
}
delay(7); /*延时120us后*/
// TR0=1;
EA=1;
}
/***********************************************************
读一位数据子程序
***********************************************************/
unsigned char read_bit()
{
unsigned char i,value_bit;
EA=0;
DQ=0; /*拉低DQ,开始读时序*/
_nop_();
_nop_();
DQ=1; /*释放总线*/
for(i=0;i<2;i++){}
value_bit=DQ;
EA=1;
return(value_bit);
}
/***********************************************************
读一字节数据子程序
***********************************************************/
unsigned char read_byte()
{
unsigned char i,value=0;
EA=0;
for(i=0;i<8;i++)
{
if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/
value|=0x01<<i;
delay(4); /*延时80us以完成此次都时序,之后再读下一数据*/
}
EA=1;
return(value);
}
/***********************************************************
复位子程序
***********************************************************/
unsigned char reset()
{
unsigned char presence;
EA=0;
DQ=0; /*拉低DQ总线开始复位*/
delay(30); /*保持低电平480us*/
DQ=1; /*释放总线*/
delay(3);
presence=DQ; /*获取应答信号*/
delay(28); /*延时以完成整个时序*/
EA=1;
return(presence); /*返回应答信号,有芯片应答返回0,无芯片则返回1*/
}
/***********************************************************
获取温度子程序
***********************************************************/
void get_temper()
{
unsigned char i,j;
do
{
i=reset(); /*复位*/
}while(i!=0); /*1为无反馈信号*/
i=0xcc; /*发送设备定位命令*/
write_byte(i);
i=0x44; /*发送开始转换命令*/
write_byte(i);
delay(180); /*延时*/
do
{
i=reset(); /*复位*/
}while(i!=0);
i=0xcc; /*设备定位*/
write_byte(i);
i=0xbe; /*读出缓冲区内容*/
write_byte(i);
j=read_byte();
i=read_byte();
i=(i<<4)&0x7f;
s=(unsigned int)(j&0x0f); //得到小数部分
s=(s*100)/16;
j=j>>4;
temper=i|j; /*获取的温度放在temper中*/
}
/*====================================================================================================
Initialize PID Structure
=====================================================================================================*/
void PIDInit (struct PID *pp)
{
memset ( pp,0,sizeof(struct PID)); //全部初始化为0
}
/*====================================================================================================
PID计算部分
=====================================================================================================*/
unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )
{
unsigned int dError,Error;
Error = pp->SetPoint - NextPoint; // 偏差
pp->SumError += Error; // 积分
dError = pp->LastError - pp->PrevError; // 当前微分
pp->PrevError = pp->LastError;
pp->LastError = Error;
return (pp->Proportion * Error // 比例项
+ pp->Integral * pp->SumError // 积分项
+ pp->Derivative * dError); // 微分项
}
/***********************************************************
温度比较处理子程序
***********************************************************/
void compare_temper()
{
unsigned char i;
if(set_temper>temper) //是否设置的温度大于实际温度
{
if(set_temper-temper>1) //设置的温度比实际的温度是否是大于1度
{
high_time=100; //如果是,则全速加热
low_time=0;
}
else //如果是在1度范围内,则运行PID计算
{
for(i=0;i<10;i++)
{
get_temper(); //获取温度
rin = s; // Read Input
rout = PIDCalc ( &spid,rin ); // Perform PID Interation
}
if (high_time<=100)
high_time=(unsigned char)(rout/800);
else
high_time=100;
low_time= (100-high_time);
}
}
else if(set_temper<=temper)
{
if(temper-set_temper>0)
{
high_time=0;
low_time=100;
}
else
{
for(i=0;i<10;i++)
{
get_temper();
rin = s; // Read Input
rout = PIDCalc ( &spid,rin ); // Perform PID Interation
}
if (high_time<100)
high_time=(unsigned char)(rout/10000);
else
high_time=0;
low_time= (100-high_time);
}
}
// else
// {}
}
/*****************************************************
T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
******************************************************/
void serve_T0() interrupt 1 using 1
{
if(++count<=(high_time))
output=1;
else if(count<=100)
{
output=0;
}
else
count=0;
TH0=0x2f;
TL0=0xe0;
}
/*****************************************************
串行口中断服务程序,用于上位机通讯
******************************************************/
void serve_sio() interrupt 4 using 2
{
/* EA=0;
RI=0;
i=SBUF;
if(i==2)
{
while(RI==0){}
RI=0;
set_temper=SBUF;
SBUF=0x02;
while(TI==0){}
TI=0;
}
else if(i==3)
{
TI=0;
SBUF=temper;
while(TI==0){}
TI=0;
}
EA=1; */
}
void disp_1(unsigned char disp_num1)
{
unsigned char n,a,m;
for(n=0;n<6;n++)
{
// k=disp_num1;
for(a=0;a<8;a++)
{
clk=0;
m=(disp_num1&1);
disp_num1=disp_num1>>1;
if(m==1)
data1=1;
else
data1=0;
_nop_();
clk=1;
_nop_();
}
}
}
/*****************************************************
显示子程序
功能:将占空比温度转化为单个字符,显示占空比和测得到的温度
******************************************************/
void display()
{
unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};
unsigned char disp_num;
unsigned int k,k1;
k=high_time;
k=k%1000;
k1=k/100;
if(k1==0)
disp_num=0;
else
disp_num=0x60;
k=k%100;
disp_num=number;
disp_num=number;
k=temper;
k=k%100;
disp_num=number;
disp_num=number+1;
disp_num=number;
disp_1(disp_num);
}
/***********************************************************
主程序
***********************************************************/
void main()
{
unsigned char z;
unsigned char a,b,flag_2=1,count1=0;
unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2};
TMOD=0x21;
TH0=0x2f;
TL0=0x40;
SCON=0x50;
PCON=0x00;
TH1=0xfd;
TL1=0xfd;
PS=1;
EA=1;
EX1=0;
ET0=1;
ES=1;
TR0=1;
TR1=1;
high_time=50;
low_time=50;
PIDInit ( &spid ); // Initialize Structure
spid.Proportion = 10; // Set PID Coefficients比例常数 Proportional Const
spid.Integral = 8; //积分常数 Integral Const
spid.Derivative =6; //微分常数 Derivative Const
spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value
while(1)
{
if(plus==0)
{
EA=0;
for(a=0;a<5;a++)
for(b=0;b<102;b++){}
if(plus==0)
{
set_temper++;
flag=0;
}
}
else if(subs==0)
{
for(a=0;a<5;a++)
for(b=0;a<102;b++){}
if(subs==0)
{
set_temper--;
flag=0;
}
}
else if(stop==0)
{
for(a=0;a<5;a++)
for(b=0;b<102;b++){}
if(stop==0)
{
flag=0;
break;
}
EA=1;
}
get_temper();
b=temper;
if(flag_2==1)
a=b;
if((abs(a-b))>5)
temper=a;
else
temper=b;
a=temper;
flag_2=0;
if(++count1>30)
{
display();
count1=0;
}
compare_temper();
}
TR0=0;
z=1;
while(1)
{
EA=0;
if(stop==0)
{
for(a=0;a<5;a++)
for(b=0;b<102;b++){}
if(stop==0)
disp_1(phil);
// break;
}
EA=1;
}
}
这个程序spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value是什么意思,上面的eet_temper=35;难道这个spid.SetPoint = 100是指35-34=1度的温差扩大100倍? 在第345行.
spid.Proportion = 10; // Set PID Coefficients比例常数 Proportional Const
spid.Integral = 8; //积分常数 Integral Const
spid.Derivative =6; //微分常数 Derivative Const
spid.SetPoint = 100; // Set PID Setpoint 设定目标 Desired Value
设定的目标是100
比例的系数是10
也就是 比例项是 误差乘10 第一次搞这个呢,谢谢楼上的 还没搞过这个呢,谢谢楼主
呵呵,不要被雷倒.我做过闭环的控制,也说不好理论上算什么方法,就自己瞎想的,但是有效. 记号 主函数里 PIDInit ( &sPID ); //Initialize Structure
sPID.Proportion = 0.5; //Set PID Coefficients
sPID既然是指向结构的指针,其访问应该是sPID->Proportion吧? 好帖收藏 还有就是flag那几个变量有什么用? 这个帖子对四轴的算法编写有一定的作用。 好东西,谢谢分享 mark! mark!! mark!! 我觉得PID对于这种容易受扰动的四翼飞行器来讲,是不易保持平衡与稳定的 mark mark PID记号 mark 这个算法我转发过一次,但是在430上没有调试好,当时是大连理工的一老师发给我的 mark 有用 感觉积分误差没有设定积分时间的长短,将之前的所有偏差累加是否不对啊 做个标志,还会再来!! 记号 PID记号估计以后用得着,谢谢楼主分享。 MARK mark 非常不错 做个标记,呵呵 MARK mark 也做个标记,可能后面要用到。。。 PID计算部分有误吧?
积分应乘以采样时间dt,微分应处以dt。 做个记号
赞
顶 请问一下闭环的电子燃油控制pid算法怎么应用啊 PID控制广泛存在于自然界的一切基本控制过程中,PID函数写出来却很短小。
但要应用在实际控制中,更重要的是根据现实物理模型,确定合适的参数。这个不是COPY两行代码就能学会的。
比如很多现实物理系统的积分时间,经常会长到做单片机设计的人无法想象的地步 好好学习先,做个记号 请问一下很菜的问题,是否是每一次调用PID子程式的周期都要求是一样的.即假设每一次调用PID子程式的时间周期都要求是5Ms,如果每次的调用时间周期不同,就会使控制出差更大的误差.是不是这样. 谢谢分享 记号 PID算法 呵呵 老帖子,只要是经典,就值得标记,呵呵,方便下次查阅哦 mark mark 加班 PID算法,做温控可能用的上! 引用:只要是经典,就值得标记,呵呵,方便下次查阅哦 记号 记号 mark good 打个记号 记号 【楼主位】 sail_007
> /*====================================================================================================
> 这是从网上找来的一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID
> 参数必须由具体对象通过实验确定。由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算,
> 而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算,可
这个程序跟描述不一致啊,程序中的变量用的全是double,PIDCalc中还是浮点乘法运算 mark 标记,标记,回去好好看 MARK 学习PID中。。。。。。 接3楼的程序有以下疑问啊:
1、你们说spid.SetPoint = 100;表示的是想要的达到的期望温度即设定温度,这个程序里unsigned char set_temper=35;应该是设定温度吧即期望温度啊!!!
2. s=(unsigned int)(j&0x0f); //得到小数部分
s=(s*100)/16;这个应该是温度的小数部分放大一百倍,再把这个值作为nextpiont的输入rin = s; // Read Input
rout = PIDCalc ( &spid,rin ); // Perform PID Interation
那这个rout的具体含义是什么!!!!!! MARK mark是什么意思啊??? mark! mark 记号 呵呵,学习中,谢谢分享 好帖,mark,谢谢啊。 标定以下 mark!以后好好学习 PID 是很基础也很常用的 mark mark PID记号 mark mark mark 标记 还在学习中 PID 例程 马克到此一游,谢谢 mark 受教了
非常感謝 mark mark 真的滿經典的
受益良多
非常感謝分享 mark 记号,学习 记号 飘过。。。 记号 支持! PID现在还用不到,以后不知有没有机会 mark mark mark PID mark 看完后,感觉明白了好多,感谢楼主! ----- pid mark