一、专家PID的原理
专家PID 控制主要是利用受控对象和控制规律的知识进行控制,对被控制的对象不需要精确的模型,使用专家经验来对系统模型进行设计控制。对于系统模型的控制专家PID 具有灵活性、适应性和鲁棒性。可根据系统的工作状态及误差情况去灵活的选择相应的控制规律去控制,并根据专家知识和经验,能动性的去调整控制器的参数,适应对象特性及环境的变化,通过利用专家规则,控制系统模型可以在非线性、大偏差下进行可靠地工作。
专家PID 主要由五个控制律组成,通过工作状态及误差去选择相应的控制律去稳定数据,并对参数进行调节达到对控制系统稳定的作用。其控制结构如图 所示。
设定偏差的一个极大值,记为Mmax;设定一个偏差较大的中间值,记为Mmid;设定一个偏差的极小值,记为Mmin。根据以上偏差、偏差增量以及偏差极值的设定,我们分析如下:
(1)如果|e(k)|>Mmax (规则一)
这种情况说明偏差的绝对值已经很大了,不论偏差变化趋势如何,都应该考虑控制器的输入应按最大(或最小)输出,以达到迅速调整偏差的效果,使偏差绝对值以最大的速度减小。同时避免超调,此时相当于开环控制。
(2)当e(k)*∆e(k)>0或者∆e(k)=0时(规则二)
这种情况说明偏差在朝向偏差绝对值增大的方向变化,或者偏差为某一固定值,此时我们再判断偏差的绝对值与偏差的中间值Mmid之间的关系。
(2.1)此时如果|e(k)|>Mmid
说明偏差也较大,可考虑由控制器实施较强的控制作用,以达到扭转偏差绝对值向减小的方向变化,并迅速减小偏差的绝对值。
(2.2)此时如果|e(k)|≤Mmid
说明尽管偏差是向绝对值增大的方向变化,但是偏差绝对值本身并不是很大,可以考虑控制器实施一般的控制作用,只需要扭转偏差的变化趋势,使其向偏差绝对值减小的方向变化即可。
(3)当e(k)*∆e(k)<0且∆e(k)*∆e(k-1)>0或者e(k)=0时
说明偏差的绝对值向减小的方向变化,或者已经达到平衡状态,此时保持控制器输出不变即可。
(4)当e(k)*∆e(k)<0且∆e(k)*∆e(k-1)<0时
说明偏差处于极限状态。
(4.1)如果此时偏差的绝对值较大,|e(k)|>Mmid
可以考虑实施较强控制作用。
(4.2)如果此时偏差绝对值较小,|e(k)|<Mmid
可以考虑实施较弱控制作用。
其中,k1为增益放大系数,k1取大于1的值;k2为增益抑制系数,取大于0而小于1的值。
(5)如果|e(k)|<Mmin
这种情况实际上说明偏差绝对值很小,这种偏差有可能是系统静差引起的,此时必须要引入积分作用,减小系统的稳态误差。
(6)如果u(k)没变过
这种情况说明以上规则均未触发,所以输出不变,否者会(控制量直接为0,震荡)
二、源代码
sp_pid.h
#ifndef INCLUDE_SP_PID_H_
#define INCLUDE_SP_PID_H_
#include "xxxxx底层文件XXXXX.h"
typedef struct
{
float Ref; // 输入:参考输入 Input: Reference input
float Fdb; // 输入:反馈输入 Input: Feedback input
float Err; //偏差
float Err_1; // 变量:误差信号e(k-1) Variable: Error
float Err_2; // 变量:误差信号e(k-2) Variable: Error
float Ts; // 控制周期
float Kp; // 参数:比例增益 Parameter: Proportional gain
float Ki; // 参数:积分增益 Parameter: Integral gain
float Kd; // 参数:微分增益Parameter: Derivative gain
float ErrorAbsMax; /*偏差绝对值最大值*/
float ErrorAbsMid; /*偏差绝对值中位值*/
float ErrorAbsMin; /*偏差绝对值最小值*/
float OutPreSat; // 变量:饱和输出 Variable: Pre-saturated output
float OutMax; // 参数:最大输出 Parameter: Maximum output
float OutMin; // 参数:最小输出 Parameter: Minimum output
float Out; // 输出:SP_PID输出 Output: SP_PID output
void (*init)(); // Pointer to the init funcion
void (*calc)(); // 计算函数指针 Pointer to calculation function
} SP_PID;
typedef SP_PID *SP_PID_handle;
/*-----------------------------------------------------------------------------
默认初始化 Default initalizer for the AW_PID object.
-----------------------------------------------------------------------------*/
#define SP_PID_DEFAULTS { 0, 0, 0, 0, 0, 0,\
0, 0, 0, \
0, 0, 0,\
0, 0, 0, 0, \
(void (*)(unsigned long)) sp_pid_init,\
(void (*)(unsigned long)) sp_pid_calc }
/*------------------------------------------------------------------------------
函数原型 Prototypes for the functions in <SP_PID.c>
------------------------------------------------------------------------------*/
void sp_pid_init(SP_PID_handle);
void sp_pid_calc(SP_PID_handle);
#endif /* INCLUDE_SP_PID_H_ */
sp_pid.c
#include "sp_pid.h"
#include <stdlib.h>
// include the header for PID data structure definition
void sp_pid_init(SP_PID *v)
{
//以下初始化时针对阶跃输入而言
v->ErrorAbsMax = v->Ref * 0.20f;
v->ErrorAbsMid = v->Ref * 0.10f;
v->ErrorAbsMin = v->Ref * 0.05f;
// v->ErrorAbsMax = v->Ref *0.45f;
// v->ErrorAbsMid = v->Ref *0.30f;
// v->ErrorAbsMin = v->Ref *0.1f;
}
void sp_pid_calc(SP_PID *v)
{
float Delta_Err; //e(k)-e(k-1)
float Last_Delta_Err; //e(k-1)-e(k-2)
float uk=0; //本次调节输出值
v->Err = v->Ref - v->Fdb;
Delta_Err = v->Err - v->Err_1;
Last_Delta_Err = v->Err_1 - v->Err_2;
if (fabs(v->Err) >= v->ErrorAbsMax)
{
/*执行规则1*/
if (v->Err > 0)
{
// uk = v->OutMax;
uk = v->Ref / (12+v->Ref) * 6000;
}
if (v->Err < 0)
{
// uk = v->OutMin;
uk = v->Ref / (12+v->Ref) * 6000;
}
}
if ((v->Err * Delta_Err > 0) || (Delta_Err == 0))
{
/*执行规则2*/
if (fabs(v->Err) >= v->ErrorAbsMid)
{
uk = v->Out + 1.5f * (v->Kp * Delta_Err + v->Ki * v->Err + v->Kd * (Delta_Err - Last_Delta_Err));
}
else
{
uk = v->Out + 0.3f * (v->Kp * Delta_Err + v->Ki * v->Err + v->Kd * (Delta_Err - Last_Delta_Err));
}
}
if (((v->Err * Delta_Err < 0) && (Delta_Err * Last_Delta_Err > 0)) || (v->Err == 0))
{
/*执行规则3*/
uk = v->Out;
}
if ((v->Err * Delta_Err < 0) && (Delta_Err * Last_Delta_Err < 0))
{
/*执行规则4*/
if (fabs(v->Err) >= v->ErrorAbsMid)
{
uk = v->Out + 1.5f * v->Kp * v->Err;
}
else
{
uk = v->Out + 0.4f * v->Kp * v->Err;
}
}
if ((fabs(v->Err) <= v->ErrorAbsMin) && (fabs(v->Err) > 0))
{
/*执行规则5*/
uk = v->Out + 0.5f * v->Kp * Delta_Err + 0.3f * v->Ki * v->Err;
}
if(uk==0)//防止触发这五条规则导致输出为0
uk=v->Out;
v->OutPreSat = uk;
// Saturate the output
if (v->OutPreSat > v->OutMax)
v->Out = v->OutMax;
else if (v->OutPreSat < v->OutMin)
v->Out = v->OutMin;
else
v->Out = v->OutPreSat;
v->OutPreSat = v->Out;
v->Err_2 = v->Err_1;
v->Err_1 = v->Err;
}
说明:规则是死的,人是活的
比如规则一,偏差很大 就开环输出
在电机控制,可以直接给最大电压;
但是在电源控制不能这么给,而是输出固定占空比。
在其他领域的控制可能不太一样。
PS;该代码移植来自《智能控制》的专家PID控制
详细可参考我这篇博文专家PID控制Matlab仿真_龙晨天的博客-CSDN博客_专家pid控制matlab
三、调用方法
//声明
SP_PID pid = SP_PID_DEFAULTS;
//初始化
/*增量式PID初始化*/
pid.Kp = 25.4597502;
pid.Ki = 10.053997;
pid.Kd = 15.59500027;
pid.Ts = Ts;
pid.OutMax = 2900;
pid.OutMin = 100;
pid.Ref = VoltageRef2;
//注意更改给定后必须要初始化以下内容(偏差最大值、最小值等等的大小)
pid.init(&pid);
//反馈完调用
pid.Fdb = man;
pid.calc(&pid);
control_uk = pid.Out;