发表于:2008/10/24 17:27:00
#0楼
用c51实现pid算法
关于pid的算法实现,很多书上都讲了。
但是,最近真正要用pid算法的时候,发现书上的代码在我们51上来实现还不是那么容易的事情。简单的说来,就是不能直接调用。仔细分析你可以发现,教材上的、网上现行的pid实现的c语言代码几乎都是用浮点型的数据来做的,可以想象,如果我们的计算使用浮点数据,那我们的51单片机来运行的话会有多痛苦。
所以,本人自己琢磨着弄了一个整型变量来实现了pid算法,由于是用整型数来做的,所以也不是很精确,但是对于很多的使用场合,这个精度也够了。关于系数和采样电压全部是放大10倍处理的。所以精度不是很高,但是也不是那么低,大部分的场合都够用了。实在觉得精度不够,可以再放大10倍或者100倍处理,但是要注意不超出整个数据类型的范围就可以了。
本人做的是带死区控制的pid算法。
具体的参考代码参见下面:
typedef struct pidvalue
{
uint32 ek_uint32[3]; //差值保存,给定和反馈的差值
uint8 ekflag_uint8[3]; //符号,1则对应的ek[i]为负数,0为对应的ek[i]为正数
uint8 kp_uint8;
uint8 ki_uint8;
uint8 kd_uint8;
uint8 b_uint8; //死区电压
uint8 kp; //显示修改的时候用
uint8 ki; //
uint8 kd; //
uint8 b; //
uint16 uk_uint16; //上一时刻的控制电压
}pidvaluestr;
pidvaluestr xdata pid;
/*******************************
**pid = uk + (kp*e(k) - ki*e(k-1) + kd*e(k-2));
********************************/
void pidprocess(void)
{
uint32 idata temp[3]; //
uint32 idata postsum; //正数和
uint32 idata negsum; //负数和
temp[0] = 0;
temp[1] = 0;
temp[2] = 0;
postsum = 0;
negsum = 0;
if( adpool.value_uint16[uinadch] > adpool.value_uint16[ufadch] ) //给定大于反馈,则ek为正数
{
temp[0] = adpool.value_uint16[uinadch] - adpool.value_uint16[ufadch]; //计算ek[0]
if( temp[0] > pid.b_uint8 )
{
//数值移位
pid.ek_uint32[2] = pid.ek_uint32[1];
pid.ek_uint32[1] = pid.ek_uint32[0];
pid.ek_uint32[0] = temp[0];
//符号移位
pid.ekflag_uint8[2] = pid.ekflag_uint8[1];
pid.ekflag_uint8[1] = pid.ekflag_uint8[0];
pid.ekflag_uint8[0] = 0; //当前ek为正数
temp[0] = (uint32)pid.kp_uint8 * pid.ek_uint32[0]; // kp*ek0
temp[1] = (uint32)pid.ki_uint8 * pid.ek_uint32[1]; // ki*ek1
temp[2] = (uint32)pid.kd_uint8 * pid.ek_uint32[2]; // kd*ek2
}
}
else //反馈大于给定
{
temp[0] = adpool.value_uint16[ufadch] - adpool.value_uint16[uinadch]; //计算ek[0]
if( temp[0] > pid.b_uint8 )
{
//数值移位
pid.ek_uint32[2] = pid.ek_uint32[1];
pid.ek_uint32[1] = pid.ek_uint32[0];
pid.ek_uint32[0] = temp[0];
//符号移位
pid.ekflag_uint8[2] = pid.ekflag_uint8[1];
pid.ekflag_uint8[1] = pid.ekflag_uint8[0];
pid.ekflag_uint8[0] = 1; //当前ek为负数
temp[0] = (uint32)pid.kp_uint8 * pid.ek_uint32[0]; // kp*ek0
temp[1] = (uint32)pid.ki_uint8 * pid.ek_uint32[1]; // ki*ek1
temp[2] = (uint32)pid.kd_uint8 * pid.ek_uint32[2]; // kd*ek2
}
}
/*以下部分代码是讲所有的正数项叠加,负数项叠加*/
if(pid.ekflag_uint8[0]==0)
{
postsum += temp[0]; //正数和
}
else
{
negsum += temp[0]; //负数和
} // kp*ek0
if(pid.ekflag_uint8[1]!=0)
{
postsum += temp[1]; //正数和
}
else
{
negsum += temp[1]; //负数和
} // - ki * ek1
if(pid.ekflag_uint8[2]==0)
{
postsum += temp[2]; //正数和
}
else
{
negsum += temp[2]; //负数和
} // kd * ek2
postsum += (uint32)pid.uk_uint16; //
if( postsum > negsum ) // 是否控制量为正数
{
temp[0] = postsum - negsum;
if( temp[0]
----------------------------------------------
此篇文章从博客转发
原文地址: Http://blog.gkong.com/more.asp?id=65373&Name=mahuaqiang
关于pid的算法实现,很多书上都讲了。
但是,最近真正要用pid算法的时候,发现书上的代码在我们51上来实现还不是那么容易的事情。简单的说来,就是不能直接调用。仔细分析你可以发现,教材上的、网上现行的pid实现的c语言代码几乎都是用浮点型的数据来做的,可以想象,如果我们的计算使用浮点数据,那我们的51单片机来运行的话会有多痛苦。
所以,本人自己琢磨着弄了一个整型变量来实现了pid算法,由于是用整型数来做的,所以也不是很精确,但是对于很多的使用场合,这个精度也够了。关于系数和采样电压全部是放大10倍处理的。所以精度不是很高,但是也不是那么低,大部分的场合都够用了。实在觉得精度不够,可以再放大10倍或者100倍处理,但是要注意不超出整个数据类型的范围就可以了。
本人做的是带死区控制的pid算法。
具体的参考代码参见下面:
typedef struct pidvalue
{
uint32 ek_uint32[3]; //差值保存,给定和反馈的差值
uint8 ekflag_uint8[3]; //符号,1则对应的ek[i]为负数,0为对应的ek[i]为正数
uint8 kp_uint8;
uint8 ki_uint8;
uint8 kd_uint8;
uint8 b_uint8; //死区电压
uint8 kp; //显示修改的时候用
uint8 ki; //
uint8 kd; //
uint8 b; //
uint16 uk_uint16; //上一时刻的控制电压
}pidvaluestr;
pidvaluestr xdata pid;
/*******************************
**pid = uk + (kp*e(k) - ki*e(k-1) + kd*e(k-2));
********************************/
void pidprocess(void)
{
uint32 idata temp[3]; //
uint32 idata postsum; //正数和
uint32 idata negsum; //负数和
temp[0] = 0;
temp[1] = 0;
temp[2] = 0;
postsum = 0;
negsum = 0;
if( adpool.value_uint16[uinadch] > adpool.value_uint16[ufadch] ) //给定大于反馈,则ek为正数
{
temp[0] = adpool.value_uint16[uinadch] - adpool.value_uint16[ufadch]; //计算ek[0]
if( temp[0] > pid.b_uint8 )
{
//数值移位
pid.ek_uint32[2] = pid.ek_uint32[1];
pid.ek_uint32[1] = pid.ek_uint32[0];
pid.ek_uint32[0] = temp[0];
//符号移位
pid.ekflag_uint8[2] = pid.ekflag_uint8[1];
pid.ekflag_uint8[1] = pid.ekflag_uint8[0];
pid.ekflag_uint8[0] = 0; //当前ek为正数
temp[0] = (uint32)pid.kp_uint8 * pid.ek_uint32[0]; // kp*ek0
temp[1] = (uint32)pid.ki_uint8 * pid.ek_uint32[1]; // ki*ek1
temp[2] = (uint32)pid.kd_uint8 * pid.ek_uint32[2]; // kd*ek2
}
}
else //反馈大于给定
{
temp[0] = adpool.value_uint16[ufadch] - adpool.value_uint16[uinadch]; //计算ek[0]
if( temp[0] > pid.b_uint8 )
{
//数值移位
pid.ek_uint32[2] = pid.ek_uint32[1];
pid.ek_uint32[1] = pid.ek_uint32[0];
pid.ek_uint32[0] = temp[0];
//符号移位
pid.ekflag_uint8[2] = pid.ekflag_uint8[1];
pid.ekflag_uint8[1] = pid.ekflag_uint8[0];
pid.ekflag_uint8[0] = 1; //当前ek为负数
temp[0] = (uint32)pid.kp_uint8 * pid.ek_uint32[0]; // kp*ek0
temp[1] = (uint32)pid.ki_uint8 * pid.ek_uint32[1]; // ki*ek1
temp[2] = (uint32)pid.kd_uint8 * pid.ek_uint32[2]; // kd*ek2
}
}
/*以下部分代码是讲所有的正数项叠加,负数项叠加*/
if(pid.ekflag_uint8[0]==0)
{
postsum += temp[0]; //正数和
}
else
{
negsum += temp[0]; //负数和
} // kp*ek0
if(pid.ekflag_uint8[1]!=0)
{
postsum += temp[1]; //正数和
}
else
{
negsum += temp[1]; //负数和
} // - ki * ek1
if(pid.ekflag_uint8[2]==0)
{
postsum += temp[2]; //正数和
}
else
{
negsum += temp[2]; //负数和
} // kd * ek2
postsum += (uint32)pid.uk_uint16; //
if( postsum > negsum ) // 是否控制量为正数
{
temp[0] = postsum - negsum;
if( temp[0]
----------------------------------------------
此篇文章从博客转发
原文地址: Http://blog.gkong.com/more.asp?id=65373&Name=mahuaqiang