导航:首页 > 操作系统 > pid算法单片机

pid算法单片机

发布时间:2025-07-02 19:47:51

‘壹’ 单片机PID控制问题

首先弄清楚PID是一种控制算法!!!

1,“如果用单片机恒温可以使温度到达预定值就停止加热,低了就加热,用一个温度传感器反馈,这样算是一个自动控制吗”你这是控制系统,但是效果会非常差,尤其是对于温度控制这种大惯性系统,达到预定值就停止加热,但是由于惯性,温度肯定会继续上升,电炉烧水的时候,水开了,断电之后水还要沸腾一定时间的(沸腾是很消耗能量的,由此可见如果是加热的话温度上升更严重,你也可以自己用温度计试试看);“低了就加热”是同样的道理。如果系统对控制精度有要求,你这样做肯定达不到要求。PID是一种控制算法,相对于其他控制算法来说算是最简单的了。PID能够做到在温度快要达到设定值的时候降低加热功率,让温度上升速度变慢,最终稳定在设定值。如果用你的直接控制,温度会在设定值上下振荡,永远不会停在设定值。
2,一般的控制系统都需要加反馈,以构成闭环控制系统,相对的还有开环控制系统。开环控制系统,举个例子,就是你加热的时候事先计算好大约需要多少热量,然后考虑一下环境影响,计算出加热时间,然后控制加热系统按照你这个时间加热。你觉得这样的系统能够稳定工作吗?环境稍稍有变动就挂了!开环控制系统的特点就是很容易受到环境的影响;闭环控制系统就稳定很多,你用1L水可用,2L水也行,500W电能用,1000W电炉也能用,这就是闭环的优点。
因此,大多数的控制系统都是闭环的,开环很少单独使用,即使用到了也是有闭环的。开环其实也是有优点的,开环在控制系统里面叫做前馈(跟反馈对应的),比如你的系统里面电源电压上升了,加热速度肯定会变快,如果你对电源电压采样,将采样的结果输入到闭环里面,对闭环做一个轻微的修正,控制的精度会更好,这就是开环的优势,它是超前的,能够预知结果(根据地源电压提高就能知道需要降低输出功率了)。

说完这些,你应该明白了,反馈是必需的(前馈也可以要,但是不是必需的),PID不能被取代(除非你用其它更复杂的控制算法)。

‘贰’ 单片机pid算法控制步进电机的电路图和程序

//P1.1(T0):Count They Distance
//P0.4:Tx
//P0.5:Rx
#include <C8051F310.h> //SFR declarations
#include <stdio.h> //Standard I/O definition file
#include <math.h> //Math library file
#include <Intrins.h>
#include <absacc.h>

unsigned int j,i;
char a=0;
unsigned int t=0;

//sbit led=P0^2;
//P0.0(PWM0):给定左轮速度.
sbit vls=P0^4; //P0.4(GPIO):给定左轮方向.
sbit vlf=P0^6; //P0.6(T0) :反馈左轮速度.
sbit dlf=P1^0; //P1.0(GPIO):反馈左轮方向.

//P0.2(PWM0):给定右轮速度.
sbit vrs=P0^5; //P0.5(GPIO):给定右轮方向.
sbit vrf=P0^7; //P0.7(T0) :反馈右轮速度.
sbit drf=P1^1; //P1.1(GPIO):反馈右轮方向.

int ol; //左轮给定值
int len;
int len_1,len_2;
int lyn_1,lyn_2;
int vl1,vl2; //反馈左轮速度值(取样周期内的方波数)
int lfz; //运算后赋给PWM的值

int lyn,lynn;
int lun=0,lun_1=0; //偏差校正值 即校正PWM输出
int lunp,luni,lund; //PID 校正值

int or; //右轮给定值
int ren;
int ren_1,ren_2;
int ryn_1,ryn_2;
int vr1,vr2; //反馈右轮速度值(取样周期内的方波数)
int rfz; //运算后赋给PWM的值

int ryn,rynn;
int run=0,run_1=0; //偏差校正值 即校正PWM输出
int runp,runi,rund; //PID 校正值

float kp=2.0; //比例系数1.8
float kd=0.2; //微分系数0.4
float lki; //积分系数

void pio_init(void);
void sys_init(void);
void t01_init(void);
void TIME3_INT(void);
void PID(void);
void interrupt_init(void);
void delay(unsigned int x);
void pwm1_1(void);

void main(void)
{
PCA0MD &= ~0x40; //关闭
pio_init(); //P11为测距输入端
sys_init();
t01_init();
pwm1_1();
TIME3_INT();
interrupt_init();

vls=1;vrs=0;
while(1)
{

ol=50;
or=50;
delay(1000);

ol=100;
or=100;
delay(1000);

ol=-50;
or=50;
delay(1000);

}

}

void PID(void)
{
/****************左轮PID调节******************/
if(dlf==1)
{
lyn=(vl2*256+vl1); //dlf是左轮反馈方向,0表示向前 vl=TL0
}
else
{
lyn=-(vl2*256+vl1); //dlf=1表示是向后退,速度应该为负值
}

len=ol-lyn; //误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(len)<8)//30
{
lki=1.4; //ki值的确定1.4
}
else
{
lki=0.05; //积分系数:如果 | 给定值-反馈值 | 太大
} //则就可以不引入积分,或者引入的很小0.05

lunp=kp*(len-len_1); //比例校正
luni=lki*len; //积分校正
lund=kd*(len-2*len_1+len_2); //微分校正

lun=lunp+luni+lund+lun_1; //总校正

/*************新旧数据更新*************************/
len_2=len_1;
len_1=len; //len:当前取样周期内出现的速度偏差;len_1:上次取样周期内出现的速度偏差
lun_1=lun; //lun:当前取样周期内得出的PWM校正值;lun_1:上次取样周期内得出的PWM校正值
/*************新旧数据更新*************************/

if(lun>255)
{
lun=255; //正速度
}
if(lun<-255)
{
lun=-255; //负速度
}
if(lun<0)

{
vls=1;
PCA0CPH0=-lun;
}

if(lun>=0)
{
vls=0;
PCA0CPH0=lun;
}

/****************右轮PID调节******************/
if(drf==0)
{
ryn=(vr2*256+vr1); //drf是右轮反馈方向,0表示向前 vl=TL0
}
else
{
ryn=-(vr2*256+vr1); //dlf=1表示是向后退,速度应该为负值
}

ren=or-ryn; //误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(ren)<8)//30
{
lki=1.4; //ki值的确定1.4
}
else
{
lki=0.05; //积分系数:如果 | 给定值-反馈值 | 太大
} //则就可以不引入积分,或者引入的很小0.05

runp=kp*(ren-ren_1); //比例校正
runi=lki*ren; //积分校正
rund=kd*(ren-2*ren_1+ren_2); //微分校正

run=runp+runi+rund+run_1; //总校正

/*************新旧数据更新*************************/
ren_2=ren_1;
ren_1=ren; //len:当前取样周期内出现的速度偏差;len_1:上次取样周期内出现的速度偏差
run_1=run; //lun:当前取样周期内得出的PWM校正值;lun_1:上次取样周期内得出的PWM校正值
/*************新旧数据更新*************************/

if(run>255)
{
run=255; //正速度
}
if(run<-255)
{
run=-255; //负速度
}
if(run<0)

{
vrs=1;
PCA0CPH1=-run;
}

if(run>=0)
{
vrs=0;
PCA0CPH1=run;
}
//因为这里的PCA0CPH0越大,对应的电机速度越小,所以要255来减一下
}

void pio_init(void)
{
XBR0=0x00; //0000 0001
XBR1=0x72; //0111 0010 时能弱上拉 T0T1连接到脚口P06、P07 CEX0、CEX1连接到脚口P00、P01

P0MDIN=0xff; //模拟(0);数字(1) 1111 0011
P0MDOUT=0xc3;//开漏(0);推挽(1) 1111 1111
P0SKIP=0x3c; //0011 1100

P1MDIN=0xff; //1111 1111
P1MDOUT=0xfc;//
P1SKIP=0x00; //1111 1111

}

void sys_init(void) //12MHz
{
OSCICL=0x43;
OSCICN=0xc2;
CLKSEL=0x00;

}

void pwm1_1(void) //PWM的初始化
{
PCA0MD=0x08; //PCA时钟为12分频

PCA0CPL0=200; //左轮
PCA0CPM0=0x42; //设置左轮为8位PWM输出
PCA0CPH0=200;

PCA0CPL1=200; //平衡校正
PCA0CPM1=0x42; //设置为8位PWM输出
PCA0CPH1=200;

PCA0CN=0x40; //允许PCA工作
}

void t01_init(void)
{
TCON=0x50; //计数器1、2允许
TMOD=0x55; //定时器1、2采用16位计数功能
CKCON=0x00;

TH1=0x00; //用于采集左轮的速度
TL1=0x00;

TH0=0x00; //用于采集右轮的速度
TL0=0x00;
}

void TIME3_INT(void)
{
TMR3CN = 0x00; //定时器3为16位自动重载
CKCON &= ~0x40;

TMR3RLL = 0xff;
TMR3RLH = 0xd7;
TMR3L = 0xff;
TMR3H = 0xd7;

TMR3CN |= 0x04;
}

void T3_ISR() interrupt 14 //定时器3中断服务程序
{
//led=~led;
EA=0;
TCON &=~0x50; //关闭计数器0、1

vl1=TL0; //取左轮速度值
vl2=TH0;

vr1=TL1; //取右轮速度值
vr2=TH1;

TH1=0x00;
TL1=0x00;

TH0=0x00;
TL0=0x00;

PID(); //PID处理

TMR3CN &=~0x80; //清中断标志位
TCON |=0x50; //重新开计数器0、1
EA=1;
}
void interrupt_init(void)
{ IE=0x80;
IP=0x00;
EIE1|=0x80;
EIP1|=0x80;

}

void delay(unsigned int m) //延时程序
{
for(i=0;i<2000;i++)
{
for(j=0;j<m;j++){_nop_(); _nop_();}
}
}

‘叁’ 单片机如何写PID程序

PID算法有公式,直接套用就可以了。
公式在这个链接说的很明白了。
http://ke..com/view/1759810.htm
1、如果加入D抖动的特别厉害,试试只用PI控制。
2、还有PID参数都是一步一步调出来的,我建议你做个上位机,就是个简单的VB串口程序,用来设置PID参数
3、然后在单片机这边弄个串口接收程序,这里就是个简单的串口程序,人人都会,把接收到的PID存储在缓冲区里。
4、然后单片机程序直接调用。单片机带EEPROM的话,当接收到改变的PID参数时,存储这些参数。去STC官网下你的单片机资料,上面有EEPROM测试程序,直接套用。
上面纯属个人建议,都容易实现,不过有点费时。

‘肆’ 单片机实现pid控制中 pid是一种算法吗

1.可以直接套用PID公式,无论增量还是绝对的。PID算法是根据误差来控制的算法,不依赖系统的模型,故不用算系统的传递函数。有的书提到传递函数,一般是用于理论建模仿真,从而直接用Matlab一类的仿真软件进行PID参数调试。得到的参数可以为实际应用提供一定参考价值。
2.PID参数整定有一套原则。首先要了解各个参数的作用。具体的整定方法,随便找本自控原理的书都会提到,我不太记得了,大致是有一个倍数关系。但实际操作,一般不会是用这个数,是需要根据系统的反应,改变各个参数来试的。尽信书不如无书啊~
另外,不同系统的参数肯定不一样。就算同一个系统,稍微有一些改变,可能最好的那组参数就会变化。因此衍生了很多先进PID算法,如神经PID、专家PID、模糊PID等等。

‘伍’ 8位单片机PID控制PWM的算法如何实现,C语言计算

PID控制在8位单片机中仍然有广泛的应用,比如温度控制,利用比例、积分、微分补偿来做恒温补偿控制,当然由于有这些数学处理,用C语言相对方便一些,以下是一个具体的实例。

#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[6])

{

unsigned char n,a,m;

for(n=0;n<6;n++)

{

// k=disp_num1[n];

for(a=0;a<8;a++)

{

clk=0;

m=(disp_num1[n]&1);

disp_num1[n]=disp_num1[n]>>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[6];

unsigned int k,k1;


k=high_time;

k=k%1000;

k1=k/100;

if(k1==0)

disp_num[0]=0;

else

disp_num[0]=0x60;

k=k%100;

disp_num[1]=number[k/10];

disp_num[2]=number[k%10];

k=temper;

k=k%100;

disp_num[3]=number[k/10];

disp_num[4]=number[k%10]+1;

disp_num[5]=number[s/10];

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;

}

}

‘陆’ 请问用单片机通过PWM调节占空比软启动电动机,需不需要PID算法

在使用单片机通过PWM调节占空比来软启动电动机时,是否需要采用PID算法取决于具体应用场景。如果系统对速度反馈有需求,并且需要实现较为精确的速度控制,那么使用PID算法会是一个不错的选择。PID算法的核心在于通过反馈机制来调节输出,以达到期望的速度。这里提供一个简化的PID程序示例,用于参考:

c
int error, derror;
error = acon - temp;
pida.sumer = error - pida.er1;
derror = error - 2 * (pida.er1) + pida.er2;
pida.er2 = pida.er1;
pida.er1 = error;
ad += ((pida.pp * pida.sumer) + (pida.pi * error) + (pida.pd * derror));

在这个程序中,`acon` 和 `temp` 分别代表设定值和当前测量值,`pida` 是一个结构体,包含了PID控制所需的参数,如比例增益(pp)、积分增益(pi)和微分增益(pd),以及一些临时变量。程序通过计算误差、误差的变化率以及积分误差,来调整输出信号的大小,从而实现对速度的精确控制。

然而,如果对速度控制的精度要求不高,或者系统无法提供速度反馈,那么可以考虑使用其他方法,比如直接设定一个逐步增加的PWM占空比,来实现软启动。这种方式简单易行,但可能无法达到PID算法所能实现的精确控制效果。

综上所述,是否采用PID算法需要根据实际情况来决定。在具体应用中,可以先尝试使用较为简单的控制方法,然后再逐步引入更复杂的控制策略,以优化系统性能。

阅读全文

与pid算法单片机相关的资料

热点内容
pythonwebmodule 浏览:327
空调压缩机不停了 浏览:114
python序列怎么取 浏览:198
线上数据库加密怎么查询 浏览:793
js中数据加密 浏览:469
穴pdf 浏览:547
阿里云服务器云数据库还需要吗 浏览:145
在程序设计中常用的算法有哪些 浏览:976
为什么苏州公积金app一直维护 浏览:804
有ip地址但是dhcp服务器 浏览:445
三星手机加密中断怎么回事 浏览:537
训练模型init源码 浏览:839
程序编译是谁的功能 浏览:504
qq收藏怎样设置加密 浏览:290
服务器的视频怎么保存 浏览:349
下载暗黑2压缩包解压后无法启动 浏览:745
安卓手机删除了的照片怎么找回来 浏览:349
安卓文件夹显示多图 浏览:886
文件夹内变目录 浏览:861
欧盟程序员培训 浏览:186