一、 PID算法简介
PID(Proportional-Integral-Derivative)控制算法是一种经典的反馈控制方法,广泛应用于自动控制系统,例如温度控制、速度控制、位置控制等。
PID控制算法的核心包含三个部分:比例项(P),积分项(I),和微分项(D)。它们分别对系统的当前误差、过去误差的积累和误差的变化率进行响应。PID控制器的输出由这三部分的加权和组成。
PID控制算法通过结合比例、积分和微分三种控制作用,对被控对象的偏差进行精确控制,以达到期望的控制效果。其基本原理可以概括为:
- 比例控制(P):根据偏差的大小成比例地调整输出,以减小偏差。比例控制能迅速反映偏差,但无法消除静差(即系统稳定时的偏差)。
- 积分控制(I):对偏差进行积分,以消除系统的稳态误差。积分控制作用与偏差的存在时间有关,只要系统存在偏差,积分控制就会不断起作用,直至偏差消除。
- 微分控制(D):根据偏差的变化趋势(即偏差的变化率)进行超前控制,以抑制偏差的进一步变化。微分控制有助于减小系统的超调和振荡,提高系统的动态性能。
二、两种变换公式
三、C语言算法代码
1.加入mpu6050的PID算法
(1)主函数里的逻辑:
// 自己定义的中间参数
int Balance_Pwm, Velocity_Pwm, Turn_Pwm; // PID计算的PWM值
int Motor1, Motor2; // 左右电机PWM值
int Encoder_left, Encoder_right; // 检测速度
float Movement = 0; // 速度调节
int Contrl_Turn = 64; // 转向调节变量
// mpu6050数据采集
void mpu6050_data(void)
{
static struct mpu6050_data Last_Data;
if (mpu_dmp_get_data() != 0)
OutMpu = Last_Data;
else
Last_Data = OutMpu;
}
// 平衡小车PID算法
void PID_test(void)
{
Encoder_left = Read_Encoder(1); // 读取编码器值(当作小车当前前进的速度)
Encoder_right = -Read_Encoder(2); // 读取编码器值(当作小车当前前进的速度)
// 1、确定直立环PWM
Balance_Pwm = Vertical_Ring_PD(OutMpu.pitch, OutMpu.gyro_x);
// 2、确定速度环PWM
Velocity_Pwm = Vertical_speed_PI(Encoder_left, Encoder_right, OutMpu.pitch, Movement);
// 3、确定转向环PWM
Turn_Pwm = Vertical_turn_PD(Contrl_Turn, OutMpu.gyro_z);
// 4、确定最终左右电机的PWM
Motor1 = Balance_Pwm + Velocity_Pwm + Turn_Pwm;
Motor2 = Balance_Pwm + Velocity_Pwm - Turn_Pwm;
PWM_Limiting(&Motor1, &Motor2); // PWM限幅函数
// 5、设置电机
Set_PWM(Motor1, Motor2);
}
(2)PID算法封装:
#include "math.h"
#include "stdlib.h"
#include "stm32f4xx_hal.h"
#include "contrl.h"
int Dead_Zone = 1200; // 电机死区
// PID调节参数
struct pid_arg PID = {
.Balance_Kp = 200,
.Balance_Kd = 1,
.Velocity_Kp = -56,
.Velocity_Ki = -0.28,
.Turn_Kp = 18,
.Turn_Kd = 0.18,
};
/**************************************************************************************************************
*函数名:Read_Encoder()
*功能:读取编码器值(当作小车当前前进的速度)
*形参:(u8 TIMX):x为编码器1或者2
*返回值:无
*************************************************************************************************************/
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch (TIMX)
{
case 1:
Encoder_TIM = (short)TIM1->CNT;
TIM1->CNT = 0;
break;
case 2:
Encoder_TIM = (short)TIM2->CNT;
TIM2->CNT = 0;
break;
default:
Encoder_TIM = 0;
}
return Encoder_TIM;
}
/**************************************************************************************************************
*函数名:Vertical_Ring_PD()
*功能:直立环PD控制
*形参:(float Angle):x轴的角度/(float Gyro):x轴的角速度
*返回值:经过PID转换之后的PWM值
**************************************************************************************************************/
// 直立环的PD
int Vertical_Ring_PD(float Angle, float Gyro)
{
float Bias;
int balance;
Bias = Angle - Mechanical_balance;
balance = PID.Balance_Kp * Bias + Gyro * PID.Balance_Kd;
return balance;
}
/**************************************************************************************************************
*函数名:Vertical_speed_PI()
*功能;速度环PI控制
*形参:(int encoder_left):左轮编码器值/(int encoder_right):编码器右轮的值/(float Angle):x轴角度值
*返回值:
**************************************************************************************************************/
int Vertical_speed_PI(int encoder_left, int encoder_right, float Angle, float Movement)
{
static float Velocity, Encoder_Least, Encoder;
static float Encoder_Integral;
Encoder_Least = (encoder_left + encoder_right) - 0; // 获取最新速度偏差=测量速度(左右编码器之和)-目标速度(此处为零)
Encoder *= 0.8f; // 一阶低通滤波器 ,上次的速度占85%
Encoder += Encoder_Least * 0.2f; // 一阶低通滤波器, 本次的速度占15%
Encoder_Integral += Encoder; // 积分出位移 积分时间:10ms
Encoder_Integral = Encoder_Integral - Movement;
if (Encoder_Integral > 10000)
Encoder_Integral = 10000; // 积分限幅
if (Encoder_Integral < -10000)
Encoder_Integral = -10000; // 积分限幅
Velocity = Encoder * PID.Velocity_Kp + Encoder_Integral * PID.Velocity_Ki; // 速度控制
if (Turn_off(Angle) == 1)
Encoder_Integral = 0; // 电机关闭后清除积分
return Velocity;
}
/**************************************************************************************************************
*函数名:Vertical_turn_PD()
*功能:转向环PD
*形参:无 CCD小于64左转、CCD大于64右转。 yaw = z轴陀螺仪数值
*返回值:无
***************************************************************************************************************/
int Vertical_turn_PD(u8 CCD, short yaw)
{
float Turn;
float Bias;
Bias = CCD - 64;
Turn = -Bias * PID.Turn_Kp - yaw * PID.Turn_Kd;
return Turn;
}
/**************************************************************************************************************
*函数名:PWM_Limiting()
*功能:PWM限幅函数
*形参:无
*返回值:无
***************************************************************************************************************/
void PWM_Limiting(int *motor1, int *motor2)
{
int Amplitude = 5800;
if (*motor1 < -Amplitude)
*motor1 = -Amplitude;
if (*motor1 > Amplitude)
*motor1 = Amplitude;
if (*motor2 < -Amplitude)
*motor2 = -Amplitude;
if (*motor2 > Amplitude)
*motor2 = Amplitude;
}
/**************************************************************************************************************
*函数名:Turn_off()
*功能:关闭电机
*形参:(const float Angle):x轴角度值
*返回值:1:小车当前处于停止状态/0:小车当前处于正常状态
***************************************************************************************************************/
u8 FS_state;
u8 Turn_off(const float Angle)
{
u8 temp;
if (fabs(Angle) > 80)
{
FS_state = 1;
temp = 1;
AIN2(0), AIN1(0);
BIN1(0), BIN2(0);
}
else
temp = 0;
FS_state = 0;
return temp;
}
/**************************************************************************************************************
*函数名:Set_PWM()
*功能:输出PWM控制电机
*形参;(int motor1):电机1对应的PWM值/(int motor2):电机2对应的PWM值
*返回值:无
*************************************************************************************************************/
void Set_PWM(int motor1, int motor2)
{
if (motor1 > 0)
AIN2(1), AIN1(0);
else
AIN2(0), AIN1(1);
PWMA = Dead_Zone + (abs(motor1)) * 1.17;
if (motor2 > 0)
BIN1(1), BIN2(0);
else
BIN1(0), BIN2(1);
PWMB = Dead_Zone + (abs(motor2)) * 1.17;
// printf("PWMA = %d\n",PWMA);
// printf("PWMB = %d\n",PWMB);
}
2.纯PID算法
(1)PID.c
#include "pid.h"
//定义一个结构体类型变量
tPid pidMotor1Speed;
//给结构体类型变量赋初值
void PID_init()
{
pidMotor1Speed.actual_val=0.0;
pidMotor1Speed.target_val=0.00;
pidMotor1Speed.err=0.0;
pidMotor1Speed.err_last=0.0;
pidMotor1Speed.err_sum=0.0;
pidMotor1Speed.Kp=0;
pidMotor1Speed.Ki=0;
pidMotor1Speed.Kd=0;
}
//比例p调节控制函数
float P_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值
//比例控制调节 输出=Kp*当前误差
pid->actual_val = pid->Kp*pid->err;
return pid->actual_val;
}
//比例P 积分I 控制函数
float PI_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;//当前误差=目标值-真实值
pid->err_sum += pid->err;//误差累计值 = 当前误差累计和
//使用PI控制 输出=Kp*当前误差+Ki*误差累计值
pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum;
return pid->actual_val;
}
// PID控制函数
float PID_realize(tPid * pid,float actual_val)
{
pid->actual_val = actual_val;//传递真实值
pid->err = pid->target_val - pid->actual_val;当前误差=目标值-真实值
pid->err_sum += pid->err;//误差累计值 = 当前误差累计和
//使用PID控制 输出 = Kp*当前误差 + Ki*误差累计值 + Kd*(当前误差-上次误差)
pid->actual_val = pid->Kp*pid->err + pid->Ki*pid->err_sum + pid->Kd*(pid->err - pid->err_last);
//保存上次误差: 这次误差赋值给上次误差
pid->err_last = pid->err;
return pid->actual_val;
}
(2)PID.h
#ifndef __PID_H
#define __PID_H
//声明一个结构体类型
typedef struct
{
float target_val;//目标值
float actual_val;//实际值
float err;//当前偏差
float err_last;//上次偏差
float err_sum;//误差累计值
float Kp,Ki,Kd;//比例,积分,微分系数
} tPid;
//声明函数
float P_realize(tPid * pid,float actual_val);
void PID_init(void);
float PI_realize(tPid * pid,float actual_val);
float PID_realize(tPid * pid,float actual_val);
#endif
四、PID控制算法的应用和优化
1.PID控制算法的应用
PID控制算法因其结构简单、易于实现、鲁棒性好等特点,被广泛应用于各种工业控制系统中。具体来说,其应用领域包括但不限于:
(1)温度控制:在温度控制系统中,PID控制器通过调节加热或制冷设备的功率,使系统温度稳定在设定值附近。例如,在塑料加工、食品加工等行业中,精确的温度控制对于保证产品质量至关重要。
(2)压力控制:在液压、气压等系统中,PID控制器用于维持系统压力的稳定。通过调节阀门开度或泵的输出功率,PID控制器能够迅速响应压力变化,保持系统压力在设定范围内。
(3)流量控制:在液体或气体流量控制系统中,PID控制器通过调节阀门的开度或泵的转速,实现对流量的精确控制。这对于化工、水处理等行业中的流体传输和分配具有重要意义。
(4)电机控制:在电机控制系统中,PID控制器用于调节电机的转速和转矩。通过实时监测电机的运行状态和负载情况,PID控制器能够调整电机的输入电压或电流,以实现电机的稳定运行和精确控制。
(5)飞行控制:在无人机、飞机等飞行器的控制系统中,PID控制器用于实现飞行器的姿态、速度和位置等参数的精确控制。通过调整飞行器的舵面偏角或发动机推力等参数,PID控制器能够确保飞行器按照预定的轨迹和姿态飞行。
2.PID控制算法的优化
尽管PID控制算法具有广泛的应用价值,但在实际应用中仍需要根据系统的具体特性和控制要求进行优化,以提高控制性能。以下是一些常用的PID控制算法优化方法:
(1)参数整定:PID控制器的性能很大程度上取决于比例、积分和微分系数的选择。因此,需要根据实际系统的特性进行参数整定,以找到最优的参数组合。常用的参数整定方法有试凑法、临界比例度法、衰减曲线法和人工智能优化算法等。
(2)积分饱和限制:为了避免积分饱和现象对系统性能的影响,可以采取积分分离或积分限幅等措施。积分分离是指在偏差较大时暂时取消积分环节,以避免积分过量;积分限幅则是对积分项的输出进行限制,防止其过大导致系统失稳。
(3)微分先行与滤波:微分环节对噪声敏感,容易导致控制器输出波动。为了解决这个问题,可以采取微分先行或滤波措施。微分先行是将微分环节提前到比例和积分环节之前进行计算,以减少噪声对微分环节的影响;滤波则是通过对偏差进行平滑处理来消除噪声干扰。
(4)智能PID控制器:随着人工智能技术的发展,越来越多的研究者将智能算法与PID控制器相结合,形成了智能PID控制器。例如,模糊PID控制器、神经网络PID控制器等。这些智能PID控制器可以根据系统的实时状态自动调整参数,以适应不同的工作条件和环境变化,从而提高系统的自适应性和鲁棒性。
(5)数字PID控制:随着计算机技术的发展,数字PID控制已成为PID控制算法的主要实现方式。数字PID控制通过将连续函数进行离散化,并利用计算机程序实现PID控制和校正。常用的数字PID控制方法有位置式PID、增量式PID以及步进式PID等。其中,增量式PID因其计算量小、稳定性好等优点,在实际应用中得到了广泛应用。