鉴于级联PID在系统中的重要性,是否是误差补偿,比如姿态计算; 或者控制的实现,比如姿态控制、位置控制,都是依靠级联pid。 这里我们首先讨论级联pid。 我们先做一个介绍,然后继续分析、姿态控制、位置计算与控制。 他们的分析也将从原理框图和源代码注释中进行解释。 他们只是把自己平时的一些经验整理出来和大家分享。 也希望高手们能够帮助我飞翔。
本部分涵盖三个部分:
1.pid简介
2.pid参数调整
3.级联pid
4、pid和过滤的关系也是一个很有趣的问题。 一种是从控制的角度来理解,另一种是从过滤的角度来理解。 我对此只了解一点点,所以我在这里简单说几句。 在pid中,i相当于一个低通滤波器。 极端情况下理解:直流信号肯定会继续积分,但是高频噪声的正负叠加被屏蔽了,所以i是低通滤波器。 D是高通滤波器。 同样极端情况下,可以理解为直流信号微分为0,但高频噪声微分有值,因此D是高通滤波器。 我们通常说,如果D太大,很容易放大噪声并引起振动。 相等的。
1.pid简介
在工业应用中,PID及其衍生算法是应用最广泛的算法之一,是当之无愧的通用算法。 如果能掌握PID算法的设计和实现过程,对于普通研发人员来说应该足以应付一般的研发。 问题,而且难能可贵的是,在我接触过的控制算法中,PID控制算法是最简单的,也是最能体现反馈思想的控制算法。 可以说是经典中的经典。 经典不一定复杂。 经典的东西往往是简单的,而且是最简单的。 简单不是原始,简单不是落后,简单到了美。 虽然已经进化出很多智能算法,比如蚁群、神经网络等,如果有兴趣可以看一下刘金坤的《高级PID控制》。 但在实际应用中,串级PID仍然是主要方法,因为它可靠。
我们先看一下PID算法的一般形式:
PID过程再简单不过了。 通过误差信号来控制被控变量,控制器本身就是比例、积分、微分三个环节的和。 这里我们规定(在时间t):
1、输入量为rin(t);
2、输出为rout(t);
3、偏差为err(t)=rin(t)-rout(t);
pid的控制规则为
1.解释反馈控制的原理。 从上面的框图中不难看出,PID控制实际上是一个控制偏差的过程;
2、如果偏差为0,则比例联动不起作用。 比例连杆仅在存在偏差时起作用。
3、积分环节主要用于消除静误差。 所谓静态误差就是系统稳定后输出值与设定值之间的差值。 积分环节实际上就是偏差积累的过程。 将累积误差添加到原始值中。 系统来抵消系统引起的静差。
4、微分信号反映了偏差信号的变化规律或趋势,并根据偏差信号的变化趋势进行提前调整,从而提高了系统的速度。
接下来,PID连续系统将被离散化,以便于在处理器上实现。 让我们再次粘贴连续状态的公式:
假设采样间隔为T,则在第KT时刻:
偏差err(K)=rin(K)-rout(K);
积分环节以求和的形式表示,即err(K)+err(K+1)+……;
差分环节以斜率的形式表示,即[err(K)-err(K-1)]/T;
这导致 PID 的离散表示如下:
则u(K)可表示为:
至于Kp、Ki、Kd这三个参数的具体表达式,我想是很容易推导出来的。 为了节省时间,我不会在这里详细描述它们。
事实上,到目前为止,PID的基本离散表示形式已经发布了。 当前的表达式是位置式PID,另一个表达式是增量式PID,由上面的U表达式可以很容易得到:
所以:
这就是离散PID的增量表达式。 从公式中可以看出,增量表达结果与最近3次的偏差有关,大大提高了系统的稳定性。需要注意的是,最终的输出结果应为
u(K)+增量调整值;
PID离散化过程的基本思想是这样的。 下面是将离散化公式转换成C语言,实现单片机的控制功能。
那么用c语言怎么表达呢? 下面将介绍C语言中pid的一种常见形式,关注它们的演变过程,同时也会使用一些注意事项,比如积分分离等。
位置PID的C语言实现:
第一步:定义PID变量结构体,代码如下:
_pid{
float;//定义设置值
float;//定义实际值
浮动错误; //定义偏差值
float;//定义之前的偏差值
浮动Kp,Ki,Kd; //定义比例、积分、微分系数
float;//定义电压值(控制执行器的变量)
float;//定义积分值
}pid;
将控制算法中需要的参数统一定义在一个结构体中,方便后续使用。
第二部分:初始化变量,代码如下:
空白 (){
(“开始\n”);
pid.=0.0;
pid.=0.0;
pid.err=0.0;
pid.=0.0;
pid.=0.0;
pid.=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
(“结束\n”);
统一初始化变量,特别是Kp、Ki、Kd这三个参数。 调试过程中,可以直接通过调节这三个量来调节所需的控制效果。
第三步:编写控制算法,代码如下:
浮动(浮动速度){
pid.=速度;
pid.err=pid.-pid.;
pid.+=pid.err;//位置pid是积分的不断累加,很容易造成积分饱和,是系统超调。
pid.=pid.Kp*pid.err+pid.Ki*pid.+pid.Kd*(pid.err-pid.);
pid.=pid.err;
pid.=pid.*1.0;
PID。;
注:这里使用的是最基本的算法实现形式。 没有考虑死区问题,没有设置上下限。 这只是公式的直接实现。 这一点将在后面的介绍中逐步完善。
至此,PID的基本实现部分已经初步完成。 这是测试代码:
int main(){
(“开始\n”);
();
整数计数=0;
而(计数{
漂浮速度=(200.0);
("%f\n",速度);
计数++;
0;
增量PID的C语言实现:
实现过程仍然分为四个部分:定义变量、初始化变量、实现控制算法功能、算法测试。
#
#
_pid{
float;//定义设置值
float;//定义实际值
浮动错误; //定义偏差值
float;//定义之前的偏差值
float;//定义顶偏差值
浮动Kp,Ki,Kd; //定义比例、积分、微分系数
}pid;
空白 (){
pid.=0.0;
pid.=0.0;
pid.err=0.0;
pid.=0.0;
pid.=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
浮动(浮动速度){
pid.=速度;
pid.err=pid.-pid.;
float =pid.Kp*(pid.err-pid.)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.+pid.);//只对错误值求和前后三个时间相关,方便计算
pid.+=;
pid.=pid.;
pid.=pid.err;
PID。;
int main(){
();
整数计数=0;
而(计数{
漂浮速度=(200.0);
("%f\n",速度);
计数++;
0;
积分分离PID控制算法的C语言实现:
在普通PID控制中,引入积分环节的目的主要是为了消除静差,提高控制精度。 但在启动、结束或大幅增减设定时,系统输出在短时间内出现较大偏差,会造成PID运算时积分累积,导致控制量超过极限控制量对应执行机构的最大允许动作范围。 这会造成较大的超调甚至振荡,这是绝对不允许的。
为了克服这个问题,引入了积分分离的概念。 其基本思想是:当被控变量偏离设定值较大时,积分效应取消; 当被控量接近给定值时,引入积分控制,消除静差,提高精度。 具体实现代码如下:
pid.Kp=0.2;
pid.Ki=0.04;
pid.Kd=0.2;//初始化过程
if(abs(pid.err)>200)
索引=0;
}别的{
索引=1;
pid.+=pid.err;
pid.=pid.Kp*pid.err+index*pid.Ki*pid.+pid.Kd*(pid.err-pid.);
//算法具体实现过程可以参考上面
对抗积分饱和的PID控制算法的C语言实现:
所谓积分饱和现象是指如果系统在一个方向上存在偏差,PID控制器的输出由于积分效应的不断积累而增大,导致执行器达到极限位置。 如果控制器输出U(k)继续增加,则执行器开度不能再增加。 此时,计算机输出控制量超出正常工作范围,进入饱和区。 一旦系统出现反向偏差,u(k)逐渐退出饱和区。 进入饱和区越深,离开饱和区所需的时间就越长。 在此期间,执行器仍停留在极限位置,没有立即随偏差反转而做出相应变化。 此时系统似乎失控,导致控制性能恶化。 这种现象称为积分饱和或积分失控。 现象。
防止积分饱和的方法之一是反积分饱和法。 该方法的思想是在计算u(k)时首先判断前一时刻的控制变量u(k-1)是否超出了限制范围:如果u(k-1)>umax,则只有负偏差被积累; 如果 u(k-1)
浮动(浮动速度){
整数索引;
pid.=速度;
pid.err=pid.-pid.;
if(pid.>pid.umax)//灰色背景表示执行反积分饱和
if(abs(pid.err)>200)//蓝色标记为积分分离过程
索引=0;
}别的{
索引=1;
if(pid.err {//如果超过上限,要么加负值,要么不加,避免进入饱和区
pid.+=pid.err;
}否则如果(pid.
if(abs(pid.err)>200)//积分分离过程
索引=0;
}别的{
索引=1;
if(pid.err>0)
{//如果超过下限,要么加正值,要么不加,避免进入饱和区。
pid.+=pid.err;
}别的{
if(abs(pid.err)>200)//积分分离过程
索引=0;
}别的{
索引=1;
pid.+=pid.err;
pid.=pid.Kp*pid.err+index*pid.Ki*pid.+pid.Kd*(pid.err-pid.);
pid.=pid.err;
pid.=pid.*1.0;
PID。;
变积分PID控制算法C语言实现:
变积分PID可以看作积分分离PID算法的更通用形式。 在普通PID控制算法中,由于积分系数ki是常数,因此在整个控制过程中积分增量保持不变。 但系统对积分项的要求是,当系统偏差较大时,应削弱甚至消除积分作用,当偏差较小时,应加强积分作用。 如果积分系数太大,会导致超调,甚至积分饱和。 如果太小,静态误差不能在短时间内消除。 因此,需要根据系统的偏差来改变积分速度。
变积分PID的基本思想是试图改变积分项的累积速度,使其与偏差的大小相对应:偏差越大,积分越慢; 偏差越小,积分速度越快。
这里,在积分系数前添加比例值索引:
当abs(错误)时
当180
当abs(err)>200时,index=0;
最终比例环节的比例系数值为ki*index;
浮动(浮动速度){
浮动指数;
pid.=速度;
pid.err=pid.-pid.;
if(abs(pid.err)>200)//变量积分过程
索引=0.0;
}否则 if(abs(pid.err) 索引=1.0;
pid.+=pid.err;
}别的{
索引=(200-abs(pid.err))/20;
pid.+=pid.err;
pid.=pid.Kp*pid.err+index*pid.Ki*pid.+pid.Kd*(pid.err-pid.);
pid.=pid.err;
pid.=pid.*1.0;
PID。;
最后给大家带来专家系统中的控制体验,大家可以自行理解。
反映系统性能的两个参数是系统误差e和误差变化规律ec。
首先,我们指定误差的极限值,假设为Mmax; 指定一个比较大的误差值,假设为Mmid; 指定较小的误差值,假设为Mmin;
e*ec>0 误差沿误差绝对值增大的方向变化(可以理解为速度和加速度)
如果此时abs(e)>Mmid:误差较大,需要强控制
如果此时abs(e)
欧洲经济共同体
如果此时e*err(k-1)>0或e=0:误差的绝对值向减小的方向变化,或者已经达到平衡状态,
此时,只需保持控制器输出不变即可。
如果此时使用e*err(k-1)min,则强控制(调节幅度比较大)。 如果此时误差的绝对值较小,则可以考虑弱控制。
当abs(e)>Mmax时,说明误差的绝对值已经很大。 应考虑控制器的输入应以最大(或最小)输出,以达到快速调节误差的效果,使误差的绝对值最大。 速度降低。
当abs(e) 2.您对pid参数调整有何看法?
1)。 PID调试的一般原理
A。 当输出不振荡时,增大比例增益P。
b. 当输出不振荡时,减小积分时间常数Ti。
C。 当输出不振荡时,增大微分时间常数Td。
(三者中任何一个太大都会导致系统震荡。)
2).一般步骤
A。 确定比例增益P:确定比例增益P时,首先去掉PID的积分项和微分项。 一般情况下Ti=0,Td=0(详见PID参数设置说明),这样PID为纯比例调节。 。 输入设定为系统允许最大值的60%~70%,比例增益P从0逐渐增大,直至系统振荡; 反之,从此时开始逐渐减小比例增益P,直至系统振荡消失,并记录此时比例增益P设置为当前值的60%~70%。 比例增益P调试完成。
b. 确定积分时间常数Ti。 比例增益P确定后,设置较大的积分时间常数Ti初始值,然后逐渐减小Ti,直至系统出现振荡,然后依次逐渐增大Ti,直至系统振荡消失。 记录此时的Ti,并将PID积分时间常数Ti设置为当前值的150%~180%。 积分时间常数Ti调试完成。
C。 确定积分时间常数Td。 积分时间常数Td一般不需要设置,设置为0即可。设置方法与确定P、Ti相同,取无振荡时值的30%。
d. 系统空载和带载联合调节,然后对PID参数进行微调,直至满足要求:理想时刻两波,前高后低4个比例。
3.级联pid介绍
级联pid的内循环和外循环是并行调整的。 这样做的好处是增加系统的稳定性和抗干扰能力。 同时,调整制度缓慢且过度。 请注意,外环是其自身的误差,内环是速度。 例如位置控制的外环是位置,内环是速度。 这是因为位置变化是通过对三个方向的速度进行积分来实现的。 同样,在姿态控制中,外环是角度差,内环是加速度,因为角度的实现是通过角速度的转变来实现的。 都是这样一个过渡的过程。 实际中,如果追求快速反应,也可以直接控制内环或者直接控制姿态。
级联PID是两种PID控制算法,但它们串在一起(更准确地说,它们是嵌套的)。 这样做有什么用呢? 答案是,它增强了系统的抗干扰能力(即增强了稳定性),因为有两个控制器控制飞行器,会比单个控制器控制更多的变量,使飞行器的适应性更强。 画出级联PID原理框图,
串级PID整定的经验是:先整定内环PID,再整定外环P。由于内环靠近输出,所以效果很直接。
内圈P:从小到大,拉四轴越来越困难,你越来越感觉四轴抵抗你的拉动; 当达到比较大的值时,四轴本身就会产生高频振动,这是肉眼可见的。 这时,拉力会快速振荡几次,几秒钟后稳定下来; 如果没有人为干预,它会继续增加,并会自行分化和翻转。
特别说明:当只有内圈P时,四轴会向一个方向缓慢下降。 这是正常现象。 这是系统角速度的静态差。
内环I:从前面提到的PID原理可以看出,积分只是用来消除静差。 所以,我个人觉得没有必要把积分项的系数做得很大,因为这样做会降低系统的稳定性。 从小到大,四轴会保持在一个位置,不再掉落; 如果继续增大I的值,四轴就会变得不稳定,一拉就会自行发散。
特别说明:增大I值,四轴角度设定能力很强,拉起来也比较困难,看起来像钉钉子,但一旦有较强干扰,就会发散。 这是因为积分项太大。 拉积分速度快,补偿量很大,所以很难拉,给人稳定的假象。
内环D:这里的微分项D是标准PID原理下的微分项,即本次误差-上一次误差。 角速度环中的微分就是角加速度。 原来四轴振动比较强,导致陀螺仪的数值变化较大。 此时差速器更容易引入噪声。 因此,这里一般可以适当做一些滑动滤波或者IIR滤波。 从小到大,飞机的性能没有太大变化,只是回中心时更加稳定; 继续增大D值,肉眼即可看到平衡位置的四轴高频振动(或听到电机滋滋声)。 上面已经说过,D项是辅助项,所以如果机架振动较大,则可以忽略D项。
外环P:当所有内环PID整定完成后,飞行器可以稳定在某个位置而不移动。 此时内环P,由小到大,可以清晰地看到飞行器从倾斜位置慢慢回到中心。 用手拉一下,然后松开,它会慢慢回到中心,达到平衡位置; 继续增大P值,使用遥控器给定不同角度,可以看到飞行器的跟踪速度和响应越来越快; 继续增大P值,飞机变得非常灵敏,机动性越来越强,并且有发散的倾向。
4.最后我把关于pid的源码贴给大家。 位置类型非常简单。 请你自己理解一下。 需要注意的是,位置pid很容易导致积分饱和,所以对积分做了很多处理。 例如,在位置控制中,推力的积分量饱和。
__EXPORT float pid_calculate(PID_t *pid, float sp, float val, float val_dot, float dt)
{
if (!isfinite(sp) || !isfinite(val) || !isfinite(val_dot) || !isfinite(dt)) {
return pid->last_output;
}
float i, d;
/* current error value */
float error = sp - val;
/* current error derivative */
if (pid->mode == PID_MODE_DERIVATIV_CALC) {
d = (error - pid->error_previous) / fmaxf(dt, pid->dt_min);
pid->error_previous = error;
} else if (pid->mode == PID_MODE_DERIVATIV_CALC_NO_SP) {
d = (-val - pid->error_previous) / fmaxf(dt, pid->dt_min);
pid->error_previous = -val;
} else if (pid->mode == PID_MODE_DERIVATIV_SET) {
d = -val_dot;
} else {
d = 0.0f;
}
if (!isfinite(d)) {
d = 0.0f;
}
/* calculate PD output */
float output = (error * pid->kp) + (d * pid->kd);
if (pid->ki > SIGMA) {
// Calculate the error integral and check for saturation
i = pid->integral + (error * dt);
/* check for saturation */
if (isfinite(i)) {
if ((pid->output_limit < SIGMA || (fabsf(output + (i * pid->ki)) <= pid->output_limit)) &&
fabsf(i) <= pid->integral_limit) {
/* not saturated, use new integral value */
pid->integral = i;
}
}
/* add I component to output */
output += pid->integral * pid->ki;
}
/* limit output */
if (isfinite(output)) {
if (pid->output_limit > SIGMA) {
if (output > pid->output_limit) {
output = pid->output_limit;
} else if (output < -pid->output_limit) {
output = -pid->output_limit;
}
}
pid->last_output = output;
}
return pid->last_output;
}
__EXPORT void pid_reset_integral(PID_t *pid)
{
pid->integral = 0.0f;
}