PID算法基础知识

1. 引言

在之前讲解的直流有刷电机驱动章节中,如果电机的负载没有改变,那么只需要设置固定的占空比,电机速度就会稳定在目标范围。然而,在实际的应用中,负载可能会发生变化,此时如果还是输出固定的电压,电机的速度就偏离目标范围了,为了解决这个问题,引入了PID算法,其作用是当系统受到外界干扰而偏离正常状态时,自动调节回正常范围内。常用于水壶保温系统、大棚温控系统、水位控制系统、电机控制系统等。接下来本章将介绍PID的基础知识,了解什么是PID及PID代码如何编写。

2. 什么是PID

PID就是Proportion(比例)、Integral(积分)、 Differential(微分),其控制流程图如下所示:

先来说比例,其公式为u=kp*e。u为输出、kp为比例系数、e为偏差。根据公式可看出输出u与偏差e成正比。以电机转动为例来说明,假设我期望输出的转速是60,但实际的转速是40,现在偏差是20,那此时kp越大,输出就越大,就能越快达到目标转速。但kp不是越大越好,如果kp越大,此时会出现超调或者振荡,会使得系统的稳定性下降。

然而仅有比例环节还不够,因为会存在静态误差。静态误差指的是假设现在需要调节棚内温度为30℃,而实际温度为25℃,此时偏差e=5,Kp为固定 值,如果此时的输出可以让大棚在半个小时之内升温5℃,而外部的温差可以让大棚在半个小 时之内降温 5℃,也就是说,输出 u 的作用刚好被外部影响抵消了,这就使得偏差会一直存在,这个误差就称为静态误差。

因此,为了消除静态误差,引入了积分环节。积分环节可以对偏差e进行积分,只要存在偏差,积分环节就会不断起作用,主要用于消除静态误差,提高系统的无差度。引入积分环节后,比例+积分环节的公式如下:

加入积分环节后,假设比例环节的作用被抵消,一直存在静态误差;那么此时积分环节就会一直累加误差,从而增大输出u,消除静态误差。从公式可以看出,当偏差e累加值或者是积分系数越大,此时输出u就越大,消除静态误差的时间就越短。

同理,积分系数过大,也会引起超调现象,因此积分系数不是越大越好。在积分环节中,只要存在偏差e,就会不断累加偏差值,直到偏差为0。此时累积偏差不再变化,但积分环节依然再发挥作用,因此易引起超调现象。为了解决上述问题,引入了微分环节提前去抑制输出。加入微分环节后公式如下所示:

从上述公式可以看出,微分环节是根据偏差变化量提前做出相应控制,以减小输出,抑制超调。如第n次的偏差减去n-1次的偏差=-3,代入上述公式可知,微分环节会削弱比例环节和积分环节的作用。

3. PID代码编写

知晓了公式原理之后,接下来进行代码编写。首先采用一个结构体来封装PID的相关参数,如下图所示:

cpp 复制代码
typedef struct {
    float kp;           // 比例系数
    float ki;           // 积分系数
    float kd;           // 微分系数
    float error;        // 当前误差
    float last_error;   // 上次误差
    float sum_error;    // 误差累积
    float output;       // 输出值
    float target;       // 目标值
    float actual;       // 实际值
} pid_cfg;

PID输出代码如下:

cpp 复制代码
float  pid_caculate_value(pid_cfg *pid, float actual_value,flot target_value)
{
    pid->target = target_value;         //设置目标值
    pid->actual = actual_value;         //当前值
    pid->error = target_value - actual_value;   //偏差值
    pid->sum_error += pid->error;       //累积偏差值
    pid->output= (pid->kp) * (pid->error) + (pid->ki) * (pid->sum_error) \
    +(pid->kd) * ((pid->error)-(pid->last_error));  //pid输出值 
    
    // 限制积分项,防止积分饱和
    if(pid->sum_error > 1000) pid->sum_error = 1000;
    if(pid->sum_error < -1000) pid->sum_error = -1000;

     // 限制输出范围
    if(pid->output > 1000) pid->output = 1000;
    if(pid->output < 0) pid->output = 0;

    pid->last_error=pid->error;                    //更新偏差值
    return pid->output;                            //返回输出值
}
相关推荐
HVACoder6 小时前
复习下线性代数,使用向量平移拼接两段线
c++·线性代数·算法
爱coding的橙子6 小时前
每日算法刷题Day77:10.22:leetcode 二叉树bfs18道题,用时3h
算法·leetcode·职场和发展
Swift社区6 小时前
LeetCode 404:左叶子之和(Sum of Left Leaves)
算法·leetcode·职场和发展
南枝异客6 小时前
查找算法-顺序查找
python·算法
QuantumLeap丶6 小时前
《数据结构:从0到1》-06-单链表&双链表
数据结构·算法
李牧九丶7 小时前
从零学算法59
算法
一匹电信狗7 小时前
【C++】手搓AVL树
服务器·c++·算法·leetcode·小程序·stl·visual studio
月疯7 小时前
离散卷积,小demo(小波信号分析)
算法
敲代码的瓦龙8 小时前
西邮移动应用开发实验室2025年二面题解
开发语言·c++·算法