首页 > 编程语言 >位置式与增量式PID控制器理论与C++实现

位置式与增量式PID控制器理论与C++实现

时间:2024-10-19 12:21:19浏览次数:3  
标签:控制器 PID float pid C++ error max Dbuf

1理论推导

1.1PID

u(t)=K_{p}\left[e(t)+\frac{1}{T_{i}} \int_{0}^{t} e(t) d t+T_{d} \frac{d e(t)}{d t}\right]
式中:

  • u(t) ——控制器的输出;
  • e(t)——控制器的输入(常常是设定值与被控量之差);
  • Kp——控制器的比例放大系数;
  • Ti——控制器的积分时间;
  • Td——控制器的微分时间;

1.2位置式PID

u(k)为第k次采样时刻控制器的输出值,可得离散的PID算式,又称位置式PID算式:

u(k)=K_{p}e(k)+K_{i}\sum_{i=0}^{k}e(i)+K_{d}[e(k)-e(k-1)]

  • e(k) : 用户设定的值(目标值) - 控制对象的当前的状态值
  • Kp : e(k)
  • Ki : ∑e(i) 误差的累加
  • Kd : e(k) - e(k-1) 这次误差-上次误差

1.3增量式PID

由离散的PID算式可得:

\Delta u(k)=u(k)-u(k-1)=K_p\Delta e(k)+K_ie(k)+K_d[\Delta e(k)-\Delta e(k-1)]

其中:  \Delta e(k)=e(k)-e(k-1)

  • e(k) : 用户设定的值(目标值) - 控制对象的当前的状态值
  • Kp : e(k)-e(k-1) 这次误差-上次误差
  • Ki : e(k) 误差
  • Kd : e(k) - 2e(k-1)+e(k-2) 这次误差-2*上次误差+上上次误差

1.4位置式与增量式PID控制器优缺点

1.4.1位置式PID控制器

优点:

  • 高精度:适用于需要高精度控制的系统。

  • 稳定性好:位置式控制通常能提供良好的稳定性。

缺点:

  • 计算量大:因为需要对误差进行累加,计算复杂且资源需求较高。

  • 故障影响大:如果计算机出现故障,控制器输出的大幅度变化会引起执行机构位置的大幅度变化。

1.4.2增量式PID控制器

优点:

  • 响应速度快:计算过程简单,仅与最近3次的采样值有关。

  • 故障影响小:每次只输出控制增量,发生故障时影响较小,对生产过程影响不大。

缺点:

  • 精度较低:相较位置式PID,可能无法达到同样的高精度。

  • 稳定性差:在某些情况下稳定性可能不如位置式PID。

2编程实现

2.1PID.h

#ifndef PID_H
#define PID_H
#define LimitMax(input, max)   \
    {                          \
        if (input > max)       \
        {                      \
            input = max;       \
        }                      \
        else if (input < -max) \
        {                      \
            input = -max;      \
        }                      \
    }
struct PID_FUNCTION
{
    int mode;
    //PID 三参数
    float Kp;
    float Ki;
    float Kd;

    float max_out;  //最大输出
    float max_iout; //最大积分输出
	float deadband; //死区

    float set;
    float fdb;
    float out;
    float Pout;
    float Iout;
    float Dout;
    float Dbuf[3];  //微分项 0最新 1上一次 2上上次
    float error[3]; //误差项 0最新 1上一次 2上上次
};
enum PID_MODE
{
    PID_POSITION,
    PID_DELTA
};
float PID_calc(PID_FUNCTION *pid, float ref, float set);
void PID_init(PID_FUNCTION *pid, int mode, const float PID[3], float max_out, float max_iout, float deadband);
void PID_clear(PID_FUNCTION *pid);
#endif //PID_H

2.2PID.cpp

#include "PID.h"
#include "math.h"

void PID_init(PID_FUNCTION *pid, int mode, const float PID[3], float max_out, float max_iout, float deadband)
{
    if (pid == nullptr || PID == nullptr)
    {
        return;
    }
    pid->mode = mode;
    pid->Kp = PID[0];
    pid->Ki = PID[1];
    pid->Kd = PID[2];
    pid->max_out = max_out;
    pid->max_iout = max_iout;
    pid->deadband = deadband;
    pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
    pid->error[0] = pid->error[1] = pid->error[2] = pid->Pout = pid->Iout = pid->Dout = pid->out = 0.0f;
}

float PID_calc(PID_FUNCTION *pid, float ref, float set)
{
    pid->error[2] = pid->error[1];
    pid->error[1] = pid->error[0];
    pid->set = set;
    pid->fdb = ref;
    pid->error[0] = set - ref;
    if (fabsf(pid->error[0]) <= pid->deadband)
        pid->error[0] = 0.0f;
    if (pid->mode == PID_POSITION)
    {
        pid->Pout = pid->Kp * pid->error[0];
        pid->Iout += pid->Ki * pid->error[0];
        pid->Dbuf[2] = pid->Dbuf[1];
        pid->Dbuf[1] = pid->Dbuf[0];
        pid->Dbuf[0] = (pid->error[0] - pid->error[1]);
        pid->Dout = pid->Kd * pid->Dbuf[0];
        LimitMax(pid->Iout, pid->max_iout);
        pid->out = pid->Pout + pid->Iout + pid->Dout;
        LimitMax(pid->out, pid->max_out);
    }
    else if (pid->mode == PID_DELTA)
    {
        pid->Pout = pid->Kp * (pid->error[0] - pid->error[1]);
        pid->Iout = pid->Ki * pid->error[0];
        pid->Dbuf[2] = pid->Dbuf[1];
        pid->Dbuf[1] = pid->Dbuf[0];
        pid->Dbuf[0] = (pid->error[0] - 2.0f * pid->error[1] + pid->error[2]);
        pid->Dout = pid->Kd * pid->Dbuf[0];
        pid->out += pid->Pout + pid->Iout + pid->Dout;
        LimitMax(pid->out, pid->max_out);
    }
    return pid->out;
}

void PID_clear(PID_FUNCTION *pid)
{
    if (pid == nullptr)
    {
        return;
    }
    pid->error[0] = pid->error[1] = pid->error[2] = 0.0f;
    pid->Dbuf[0] = pid->Dbuf[1] = pid->Dbuf[2] = 0.0f;
    pid->out = pid->Pout = pid->Iout = pid->Dout = 0.0f;
    pid->fdb = pid->set = 0.0f;
}

2.3使用范例

PID_FUNCTION PID_Y;

static float pid_y[3] = {1.2, 0.0, 1}; //kp、ki、kd参数

PID_init(&PID_Y, PID_POSITION, pid_y, 2.5, 0, 0.02);//放到函数里初始化

vy = PID_calc(&PID_Y,now_y, target_y);//放到函数里计算

参考文献

位置式 和 增量式 PID

标签:控制器,PID,float,pid,C++,error,max,Dbuf
From: https://blog.csdn.net/weixin_67363538/article/details/143068672

相关文章

  • C++内存模型实践探索
    前言C++对象模型是个常见、且复杂的话题,本文基于ItaniumC++ABI通过程序实践介绍了几种简单C++继承场景下对象模型,尤其是存在虚函数的场景,并通过图的方式直观表达内存布局。本文展示的程序构建环境为Ubuntu,glibc2.24,gcc6.3.0。由于clang和gcc编译器都是基于ItaniumC++ABI......
  • C++ WM\_COPYDATA 实现进程通信
    基于MFC用于存储数据的自定义结构体:structMSG_STRUCT{ wchar_tmsg[256];};发送端,发送按钮响应事件的代码voidCSendWM_COPYDATADlg::OnBnClickedSendmessage(){ MSG_STRUCTsmsg; GetDlgItemText(IDC_MESSAGE,smsg.msg,256); HWNDhTargetWnd=NULL; hTargetWnd=::Fi......
  • C++使用共享内存实现进程间通信
    C++使用共享内存实现进程间通信文件映射是一种实现进程间单向或双向通信的机制。它允许两个或多个本地进程间相互通信。为了共享文件或内存,所有的进程必须使用相同的文件映射的名字或是句柄。为了实现共享文件,第一个进程先调用CreateFile方法。接下来调用CreateFileMappin......
  • 算法笔记 C/C++快速入门 | 全章节整理
    目录零.【C语言中的输入和输出函数】sscanf应用场景1:解析用户输入应用场景2:解析文件内容应用场景3:处理网络协议数据应用场景4:字符串解析和数据转换应用场景5:解析复杂的日志数据其他应用场景:scanf 一【编程语言相关】c和cpp二.【数据结构相关】结构体循环定......
  • C++之类和对象2
    文章目录1.类的默认成员函数有哪些2.构造函数3.析构函数4.拷贝构造函数5.赋值运算符重载函数5.1运算符重载5.2赋值运算符重载函数6.取地址运算符重载函数6.1const成员函数6.2普通取地址重载函数与const取地址运算符重载函数1.类的默认成员函数有哪些默认构造函数是......
  • 【C++】string类(1)
    ......
  • 【GESP】C++一级练习BCQM3048,顺序执行、逆推运算
    一道根据结果,一步一步逆推之前结果的题,完全顺序语句执行题目题解详见:https://www.coderli.com/gesp-1-bcqm3048/【GESP】C++一级练习BCQM3048,顺序执行、逆推运算|OneCoder一道根据结果,一步一步逆推之前结果的题,完全顺序语句执行。https://www.coderli.com/gesp-1-bcqm3048/......
  • 【GESP】C++一级练习BCQM3049,细胞分裂
    GESP一级知识点整形int和for循环练习。题目题解详见:【GESP】C++一级练习BCQM3049,细胞分裂|OneCoder【GESP】C++一级练习BCQM3049,细胞分裂|OneCoderGESP一级知识点整形int和for循环练习。https://www.coderli.com/gesp-1-bcqm3049/C++GESP专项交流频道:GESP学习交流频道......
  • 【信奥赛·C++基础语法】CSP-J C++ 指针与引用
    序言指针和引用是非常重要的概念,它们提供了对内存的直接访问和操作方式,使得程序员能够更加灵活地处理数据哈,理解指针和引用的工作原理以及正确使用它们,对于编写高效、安全的C++程序至关重要。一、指针的基本概念指针的定义和作用指针是一个变量,它存储了另一个变量的内......
  • QT/c++相关记录
     QT的大部分容器类(如QString、QVector等)都是使用隐式共享(implicitsharing)技术,这是通过写时复制(copy-on-write,COW)实现的优化模式。理解这一点的关键在于,Qt的容器类需要在对象拷贝时高效处理数据,而隐式共享则允许在栈上操作容器的同时,在需要时共享内部数据的堆上存储。......