速度闭环控制器设计
在过程控制中,按偏差的比例(P)、积分(I)和微分(D)进行控制的PID控制器(亦称PID调节器)是应用最为广泛的一种自动控制器。它具有原理简单,易于实现,适用面广,控制参数相互独立,参数的选定比较简单等优点;而且在理论上可以证明,对于过程控制的典型对象──“一阶滞后+纯滞后”与“二阶滞后+纯滞后”的控制对象,PID控制器是一种最优控制。PID调节规律是连续系统动态品质校正的一种有效方法,它的参数整定方式简便,结构改变灵活
图1. PID控制系统原理图
离散化公式:
当|e(t)<=β时
当|e(t)|>β时,q0 = Kp(1+Td/T);q1 = -Kp(1+2Td/T);q2 = Kp Td /T;u(t) = u(t-1) + Δu(t)
参数调整一般步骤:
1、确定比例增益。
P确定比例增益P时,首先去掉PID的积分项和微分项,一般是令Ti=0、Td=0,PID为纯比例调节。输入设定为系统允许的最大值的60%70%,由0逐渐加大比例增益P,直至系统出现振荡。再反过来,从此时的比例增益P逐渐减小,直至系统振荡消失,记录此时的比例增益P,设定PID的比例增益P为当前值的60%70%。比例增益P调试完成。
2、确定积分时间常数Ti。比例增益P确定后,设定一个较大的积分时间常数Ti的初值,然后逐渐减小Ti,直至系统出现振荡,之后在反过来,逐渐加大Ti,直至系统振荡消失。记录此时的Ti,设定PID的积分时间常数Ti为当前值的150%~180%。积分时间常数Ti调试完成。
3、确定微分时间常数Td。微分时间常数Td一般不用设定,为0即可。若要设定,与确定P和Ti的方法相同,取不振荡时的30%。
4、系统空载、带载联调,再对PID参数进行微调,直至满足要求。
PID控制器参数整定的方法:
1、理论计算整定法。它主要是依据系统的数学模型,经过理论计算确定控制器参数。这种方法所得到的计算数据未必可以直接用,还必须通过工程实际进行调整和修改。
2、工程整定方法,它主要依赖工程经验,直接在控制系统的试验中进行,且方法简单、易于掌握,在工程实际中被广泛采用。PID控制器参数的工程整定方法,主要有临界比例法、反应曲线法和衰减法。三种方法各有其特点,其共同点都是通过试验,然后按照工程经验公式对控制器参数进行整定。现在一般采用的是临界比例法。利用该方法进行PID控制器参数的整定步骤如下:
(1)首先预选择一个足够短的采样周期让系统工作。
(2)仅加入比例控制环节,直到系统对输入的阶跃响应出现临界振荡,记下这时的比例放大系数和临界振荡周期。
(3)在一定的控制度下通过公式计算得到PID控制器的参数。
控制器主要由两个数据结构类型:
typedef struct
{
float de ; //各个电机当前误差值
float fe ; //各个电机误差积分
float de1; //各个电机历史误差1
float de2; //各个电机历史误差2
int out; //各个通道最终PWM输出
}PIDDateBaseTypedef; //pid数据结构类型
typedef
struct
{
float kp; //比例权重
float ki; //积分权重
float kd; //微分权重
}PIDParamBaseTypedef; //pid参数结构类型
控制器的执行由定时器中断分频器发起,实现过程部分代码:
#define
MOTOR_OUTPUT_MAX 999
#define
MOTOR_SPEED_PID_KP 2.400
#define
MOTOR_SPEED_PID_KI 1.000
#define
MOTOR_SPEED_PID_KD 0.000
#define
PI 3.1415926535897932384626433832795
const
PIDParamBaseTypedef PARAM_PID = {//电机pid速度控制参数
.kp = MOTOR_SPEED_PID_KP, //比例权重
.ki = MOTOR_SPEED_PID_KI, //积分权重
.kd = MOTOR_SPEED_PID_KD, //微分权重
};
float
g_TargetSpeed[4] = {0,0,0,0};//目的速度
float
g_MotorSpeed[4] = {0,0,0,0};//当前速度
//速度值更新
void
updataSpeed(void)
{
int16_t etr[4];
//获得编码器值
etr[0] = TIM1->CNT;//M1
TIM1->CNT = 0;
etr[1] = TIM4->CNT;//M2
TIM4->CNT = 0;
etr[2] = TIM3->CNT;//M3
TIM3->CNT = 0;
etr[3] = TIM2->CNT;//M4
TIM2->CNT = 0;
for(int i = 0;i < 4;i++)
{
float etrtemp = 0;
//编码器脉冲计算轮角速度
//360度常量 2*80 = 160t
etrtemp = ((float)(etr[i] %
30000)) / (2.0 * 80.0);
//角速度计算轮线速度
etrtemp = etrtemp * (66 * PI);
//写入线速度到全局
g_MotorSpeed[i] = etrtemp * 10.0 *
0.25;
}
}
//设定目的速度
void
SetTargetSpeed(int16_t s1,int16_t s2,int16_t s3,int16_t s4)
{
g_TargetSpeed[0] = s1;
g_TargetSpeed[1] = s2;
g_TargetSpeed[2] = s3;
g_TargetSpeed[3] = s4;
}
//上传速度值
void
sendUpMotorSpeed(void)
{
uint8_t str[500];
//sprintf((char
*)str,"%03.2f,%03.2f,%03.2f,%03.2f\r\n",g_MotorSpeed[0],g_MotorSpeed[1],g_MotorSpeed[2],g_MotorSpeed[3]);
//分速度的合成
float Vx = (((g_MotorSpeed[0] +
g_MotorSpeed[2]) / 2) +
((g_MotorSpeed[1] + g_MotorSpeed[3]) / 2) ) /
2;
float Vw = ((((g_MotorSpeed[0] -
g_MotorSpeed[2]) / 2) * 0.891749586 / 187.833) +
(((g_MotorSpeed[1] - g_MotorSpeed[3]) / 2) *
0.891749586 / 187.833)) / 2;
uint8_t Syx,Syw;
Syx = (Vx >= 0) ? 'A':'B';
Syw = (Vw >= 0) ? 'A':'B';
Vx = (Vx >= 0) ? Vx : -Vx;
Vw = (Vw >= 0) ? Vw : -Vw;
uint16_t vxi = Vx ;
uint16_t vwi = Vw * 1000;
sprintf((char
*)str,"<S%c%03d,%c%04d>\r\n",Syx,vxi,Syw,vwi);
if(g_MotorSpeed[0] != 0)
{
USART_SendString(USART3,str);
}
USART_SendString(USART1,str);
}
/**
* @brief
MotorSpeedPidCtrl.
*
Out = Kp[e(k)]+ki∑e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
*
其中积分项和微分项受大扰动误差阀值范围生效标志位控制
* @retval None
*/
void
MotorSpeedPidCtrl(void)
{
static PIDDateBaseTypedef pid[4] = {
[0].de = 0, [1].de = 0, [2].de = 0, [3].de = 0,
[0].fe = 0, [1].fe = 0,
[2].fe = 0, [3].fe =
0,
[0].de1 = 0, [1].de1 = 0,
[2].de1 = 0, [3].de1 = 0,
[0].de2 = 0, [1].de2 = 0,
[2].de2 = 0, [3].de2 = 0,
[0].out = 0, [1].out = 0,
[2].out = 0, [3].out = 0,
};
//进行PID控制计算
for(int i = 0;i < 4;i++)
{
//计算当前误差并移动历史误差
pid[i].de2 = pid[i].de1;
pid[i].de1 = pid[i].de;
pid[i].de = g_TargetSpeed[i] -
g_MotorSpeed[i];
pid[i].fe += pid[i].de;
//pid[i]控制器核心方程
pid[i].out = PARAM_PID.kp * pid[i].de +
PARAM_PID.ki
* pid[i].fe +
PARAM_PID.kd
* ( pid[i].de - 2 * pid[i].de1 + pid[i].de2) * (pid[i].de < 100);
//输出限制幅度
pid[i].out = (pid[i].out >
MOTOR_OUTPUT_MAX) ? MOTOR_OUTPUT_MAX : pid[i].out;
pid[i].out = (pid[i].out
<-MOTOR_OUTPUT_MAX) ?-MOTOR_OUTPUT_MAX : pid[i].out;
//输出死区限制
}
//输出到电机控制
setMotorPWM(pid[0].out,pid[1].out,pid[2].out,pid[3].out);
//uint8_t str[50];
//sprintf((char
*)str,"%d,%d,%d,%d\r\n",pid[0].out,pid[1].out,pid[2].out,pid[3].out);
//USART_SendString(USART1,str);
}
//pid控制器执行周期分频器
void
PID_Divider(void)
{
static uint32_t div = 0;
div++;
if(div >= 100)
{
div = 0;
//更新当前速度
updataSpeed();
//上传速度信息
sendUpMotorSpeed();
//执行电机PID控制器
MotorSpeedPidCtrl();
}
}