PID在编程中怎么应用啊

PID在编程中怎么应用啊,第1张

给你个Step 7写的PID控制的FC模块。带"_IN"与带"_OUT"的变量,如果前缀是一样的,要求连接同一个变量。

FUNCTION FC1 : VOID

VAR_INPUT

Run:BOOL; //True-运行,False-停止

Auto:BOOL; //True-自动,False-手动

ISW:BOOL; //True-积分有效,False-积分无效

DSW:BOOL; //True-微分有效,False-微分无效

SetMV:REAL; //手动时的开度设定值

SVSW:REAL; //当设定值低于SVSW时,开度为零

PV:REAL; //测量值

SV:REAL; //设定值

DeadBand:REAL; //死区大小

PBW:REAL; //比例带大小

IW:REAL; //积分带大小

DW:REAL; //微分带大小

dErr_IN:REAL; //误差累积

LastPV_IN:REAL; //上一控制周期的测量值

END_VAR

VAR_OUTPUT

MV:REAL; //输出开度

dErr_OUT:REAL; //误差累积

LastPV_OUT:REAL;//上一控制周期的测量值

END_VAR

VAR

Err:REAL; //误差

dErr:REAL; //误差累积

PBH:REAL; //比例带上限

PBL:REAL; //比例带下限

PVC:REAL; //测量值在一个控制周期内的变化率,即测量值变化速率

P:REAL; //比例项

I:REAL; //积分项

D:REAL; //微分项

END_VAR

IF Run=1 THEN

IF Auto=1 THEN

IF SV>=SVSW THEN

Err:=SV-PV;

PBH:=SV+PBW;

PBL:=SV-PBW;

IF PV<PBL THEN

MV:=1;

ELSIF PV>PBH THEN

MV:=0;

ELSE

P:=(PBH-PV)/(PBH-PBL); //计算比例项

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////以下为积分项的计算//////////////////////////////////////////////////////////////

IF ISW=1 THEN

dErr:=dErr_IN;

IF (PV<(SV-DeadBand)) OR (PV>(SV+DeadBand)) THEN

IF (dErr+Err)<(0-IW) THEN

dErr:=0-IW;

ELSIF (dErr+Err)>IW THEN

dErr:=IW;

ELSE

dErr:=dErr+Err;

END_IF;

END_IF;

I:=dErr/IW;

dErr_OUT:=dErr;

ELSE

I:=0;

END_IF;

/////////////////////////////////////////////以上为积分项的计算//////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////以下为微分项的计算//////////////////////////////////////////////////////////////

IF DSW=1 THEN

PVC:=LastPV_IN-PV;

D:=PVC/DW;

LastPV_OUT:=PV;

ELSE

D:=0;

END_IF;

/////////////////////////////////////////////以上为微分项的计算//////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

IF (P+I+D)>1 THEN

MV:=1;

ELSIF (P+I+D)<0 THEN

MV:=0;

ELSE

MV:=P+I+D;

END_IF;

END_IF;

ELSE

MV:=0;

END_IF;

ELSE

MV:=SetMV;

END_IF;

ELSE

MV:=0;

END_IF;

END_FUNCTION

这个应该是通过串口发送数据信息的,发送和接收在一根信号线上,手上没有现成的程序,你看看这个在其他网上的行不行,

最好根据手册自己写

#include <avr/ioh>

#include <util/delayh>

void InitUart0(void)

{

UCSR0A = 0x02; // 设置为倍速模式

UBRR0H = 0;

UBRR0L = 1;

UCSR0B = (1<<RXEN)|(1<<TXEN);// 接收器与发送器使能

UCSR0C = (3<<UCSZ0);

DDRE &= ~_BV(PE0); // 初始化RX 端口默认方向为输入

PORTE &= ~_BV(PE0); // 初始化RX 端口默认状态为高阻

DDRE |= _BV(PE1); // 初始化TX 端口默认方向为输出

PORTE |= _BV(PE1); // 初始化TX 端口默认状态为高电平

DDRA |= _BV(PA0); // 初始化使能端口状态方向为输出

PORTA &= ~_BV(PA0); // 初始化使能端口状态为RX 状态

DDRA |= _BV(PA1); // 初始化使能端口状态方向为输出

PORTA |= _BV(PA1); // 初始化使能端口状态方为TX 状态

}

void SendUart0Byte(unsigned char data)

{

while ( !( UCSR0A & (1<<UDRE)) );// 等待发送缓冲器为空

UDR0 = data;/ 将数据放入缓冲器,发送数据/

}

void SetServoLimit(unsigned char id, unsigned short int cw_limit, unsigned short int ccw_limit)

{

unsigned short int temp_ccw = 0; // 临时速度,用于进行方向判别

unsigned short int temp_cw = 0;

unsigned char temp_ccw_h = 0; // 待发送数据h 位

unsigned char temp_ccw_l = 0; // 待发送数据l 位

unsigned char temp_cw_h = 0;

unsigned char temp_cw_l = 0;

unsigned char temp_sum = 0; // 校验和寄存变量

if (ccw_limit > 1023)

{

temp_ccw = 1023; // 限制速度值在可用范围内

}

else

{

temp_ccw = ccw_limit;

}

if (cw_limit > 1023)

{

temp_cw = 1023;

}

else

{

temp_cw = cw_limit;

}

temp_ccw_h = (unsigned char)(temp_ccw >> 8);

temp_ccw_l = (unsigned char)temp_ccw; // 将16bit 数据拆为2个8bit 数据

temp_cw_h = (unsigned char)(temp_cw >> 8);

temp_cw_l = (unsigned char)temp_cw; // 将16bit 数据拆为2个8bit 数据

PORTA &= ~_BV(PA1);

PORTA |= _BV(PA0); // 使总线处于主机发送状态

UCSR0A |= (1<<TXC0); // 清除UART0写完成标志

SendUart0Byte(0xFF); // 发送启动符号0xFF

SendUart0Byte(0xFF); // 发送启动符号0xFF

SendUart0Byte(id); // 发送id

SendUart0Byte(7); // 发送数据长度为参数长度+2,参数长度为3

SendUart0Byte(0x03); // 命令数据为“WRITE DATA”

SendUart0Byte(0x06); // 舵机控制寄存器首地址

SendUart0Byte(temp_cw_l); // 发送顺时针位置限制低位

SendUart0Byte(temp_cw_h); // 发送顺时针位置限制高位

SendUart0Byte(temp_ccw_l); // 发送逆时针位置限制低位

SendUart0Byte(temp_ccw_h); // 发送逆时针位置限制高位

temp_sum = id + 7 + 0x03 + 0x06 + temp_cw_l + temp_cw_h + temp_ccw_l + temp_ccw_h;

temp_sum = ~temp_sum; // 计算校验和

SendUart0Byte(temp_sum); // 发送校验和

while ( !( UCSR0A & (1<<TXC0)) ) // 等待发送完成

{ // (Waiting for finishing sending)

;

}

PORTA |= _BV(PA1);

PORTA &= ~_BV(PA0); // 使总线处于主机接收状态

_delay_ms(2); //送完成后,总线会被从机占用,反馈应答数据,所以进行延时

}

void SetServoPosition(unsigned char id, unsigned short int position, unsigned short int

velocity)

{

unsigned short int temp_velocity = 0; // 临时速度,用于进行方向判别

unsigned short int temp_position = 0;

unsigned char temp_velocity_h = 0; // 待发送数据h 位

unsigned char temp_velocity_l = 0; // 待发送数据l 位

unsigned char temp_position_h = 0;

unsigned char temp_position_l = 0;

unsigned char temp_sum = 0; // 校验和寄存变量

if (velocity > 1023)

{

temp_velocity = 1023; // 限制速度值在可用范围内

}

else

{

temp_velocity = velocity;

}

if (position > 1023)

{

temp_position = 1023;

}

else

{

temp_position = position;

}

temp_velocity_h = (unsigned char)(temp_velocity >> 8);

// 将16bit 数据拆为2个8bit 数据

temp_velocity_l = (unsigned char)temp_velocity;

temp_position_h = (unsigned char)(temp_position >> 8);

// 将16bit 数据拆为2个8bit 数据

temp_position_l = (unsigned char)temp_position;

PORTA &= ~_BV(PA1);

PORTA |= _BV(PA0); // 使总线处于主机发送状态

UCSR0A |= (1<<TXC0); // 清除UART0写完成标志

SendUart0Byte(0xFF); // 发送启动符号0xFF

SendUart0Byte(0xFF);

SendUart0Byte(id); // 发送id

SendUart0Byte(7); // 发送数据长度为参数长度+2,参数长度为3

SendUart0Byte(0x03); // 命令数据为“WRITE DATA”

SendUart0Byte(0x1E); // 舵机控制寄存器首地址

SendUart0Byte(temp_position_l); // 发送速度数据低位

SendUart0Byte(temp_position_h); // 发送速度数据高位

SendUart0Byte(temp_velocity_l); //发送位置低字节

SendUart0Byte(temp_velocity_h); // 发送位置高字节

temp_sum = id + 7 + 0x03 + 0x1E + temp_position_l + temp_position_h + temp_velocity_l +

temp_velocity_h;

temp_sum = ~temp_sum; // 计算校验和

SendUart0Byte(temp_sum); // 发送校验和 (Send the checksum)

while ( !( UCSR0A & (1<<TXC0)) ) // 等待发送完成

{ // (Waiting for finishing sending)

;

}

PORTA |= _BV(PA1);

PORTA &= ~_BV(PA0); // 使总线处于主机接收状态

_delay_ms(2); // 发送完成后,总线会被从机占用,反馈应答数据,所以进行延时

}

void SetServoVelocity(unsigned char id, signed short int velocity)

{

unsigned char temp_sign = 0; // 临时符号,用于进行方向判别

unsigned short int temp_velocity = 0; // 临时速度,用于进行方向判别

unsigned char temp_value_h = 0; // 待发送数据h 位

unsigned char temp_value_l = 0; // 待发送数据l 位

unsigned char temp_sum = 0; // 校验和寄存变量

if (velocity < 0)

{

temp_velocity = -velocity; // 如果为负数,则取绝对值

temp_sign = 1; // 设置负数符号标志

}

else

{

temp_velocity = velocity;

temp_sign = 0; // 设置正数符号标志

}

if (temp_velocity > 1023)

{

temp_velocity = 1023; // 限制速度值在可用范围内

}

temp_velocity |= (temp_sign << 10);

temp_value_h = (unsigned char)(temp_velocity >> 8);

// 将16bit 数据拆为2个8bit 数据

temp_value_l = (unsigned char)temp_velocity;

PORTA &= ~_BV(PA1);

PORTA |= _BV(PA0); // 使总线处于主机发送状态

UCSR0A |= (1<<TXC0); // 清除UART0写完成标志

SendUart0Byte(0xFF); // 发送启动符号0xFF

SendUart0Byte(0xFF); // 发送启动符号0xFF

SendUart0Byte(id); // 发送id

SendUart0Byte(5); // 发送数据长度为参数长度+2,参数长度为3

SendUart0Byte(0x03); // 命令数据为“WRITE DATA”

SendUart0Byte(0x20); // 舵机控制寄存器首地址

SendUart0Byte(temp_value_l); // 发送速度数据低位

SendUart0Byte(temp_value_h); // 发送速度数据高位

temp_sum = id + 5 + 0x03 + 0x20 + temp_value_l + temp_value_h;

temp_sum = ~temp_sum; // 计算校验和

SendUart0Byte(temp_sum); // 发送校验和

while ( !( UCSR0A & (1<<TXC0)) ) // 等待发送完成

{

;

}

PORTA |= _BV(PA1);

PORTA &= ~_BV(PA0); // 使总线处于主机接收状态

_delay_ms(2); // 发送完成后,总线会被从机占用,反馈应答数据,所以进行延时

}

int main(void)

{

InitUart0();

SetServoLimit(2,0,1023);

while(1)

{

_delay_ms(1000); //延时1s

SetServoPosition(2, 1000, 500); //控制舵机以500的速度运动到1000的位置

_delay_ms(1000); //延时1s

SetServoPosition(2, 200, 100); //控制舵机以100的速度运动到200的位置

}

}

不知道你的pid 表是控制什么的,我们用来控制蒸汽薄膜阀动作来控制温度的,而且一般表都有pid 自诊定,表自身能计算出适合的pid 值。我的经验是,p值最重要,一般p值越小,控制的动作反应越快,I 值和D 值只是帮助控制的效果更好。

和你说下在我们设备的一个经验值里,P=3,I=60,D=90,希望对你有所帮助。很多的控制也都是慢慢试验出来的pid 值。因为各种应用场合千差万别,不好根据公式计算出pid 值。

以下摘自网络:

PID控制方式的具体流程是计算误差和温度的变化速度进行PID计算,先以P参数和误差计算出基础输出量,在根据误差的累积值和I参数计算出修正量,最终找出控制点和温度设定点之间的平衡状态,最后在通过温度的变化速率与D参数控制温度的变化速度以防止温度的剧烈变化。进行整定时先进行P调节,使I和D作用无效,观察温度变化曲线,若变化曲线多次出现波形则应该放大比例(P)参数,若变化曲线非常平缓,则应该缩小比例(P)参数。比例(P)参数设定好后,设定积分(I)参数,积分(I)正好与P参数相反,曲线平缓则需要放大积分(I),出现多次波形则需要缩小积分(I)。比例(P)和积分(I)都设定好以后设定微分(D)参数,微分(D)参数与比例(P)参数的设定方法是一样的。

舵机是一种位置伺服的驱动器,主要是由外壳、电路板、无核心马达、齿轮与位置检测器所构成。其工作原理是由接收机或者单片机发出信号给舵机,其内部有一个基准电路,产生周期为20ms,宽度为15ms 的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。经由电路板上的IC 判断转动方向,再驱动无核心马达开始转动,透过减速齿轮将动力传至摆臂,同时由位置检测器送回信号,判断是否已经到达定位。适用于那些需要角度不断变化并可以保持的控制系统。当电机转速一定时,通过级联减速齿轮带动电位器旋转,使得电压差为0,电机停止转动。一般舵机旋转的角度范围是0 度到180 度。

可以通过计算得到一个参考值然后调整

但是车模的安装间隙太大,计算很难精确,所以仍然需要大量的参数调试

经典的PID就是积分,微分,比例三个系数啊,看看自动控制的书,上面介绍很清楚的,也有算法的,至于系数怎么调,有计算的办法,包括车模的负载,电机的驱动力,摩擦等,但是太麻烦啦,试凑法也很快,只要你能根据现象来及时调整参数。

以上就是关于PID在编程中怎么应用啊全部的内容,包括:PID在编程中怎么应用啊、求stm32控制四自由度舵机的程序、摄像头组舵机PID算法的偏差ek要怎么求等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zz/9333906.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-27
下一篇 2023-04-27

发表评论

登录后才能评论

评论列表(0条)

保存