PID控制器及其C++实现

2023-05-16

PID控制器原理

PID控制器实际上是对偏差的控制,其原理图如下:
这里写图片描述
其数学的表达如下:
u(x)=Kp(err(t)+1T.err(t)dt+TDderr(t)dt) u ( x ) = K p ( e r r ( t ) + 1 T . ∫ e r r ( t ) d t + T D d e r r ( t ) d t )
其中: u(x) u ( x ) 是系统的输出
我们将其离散化,可得到:
u(k)=Kperr(k)+Kierr(j)+Kd(err(k)err(k1)) u ( k ) = K p e r r ( k ) + K i ∑ e r r ( j ) + K d ( e r r ( k ) − e r r ( k − 1 ) )
其中: Kp K p 是比例系数
Ki K i 是积分系数
Kd K d 是微分系数.


先简单介绍一下这三个项的作用:
1.比例环节 Kp,作用是加快系统的响应速度,提高系统的调节精度,副作用
是会导致超调;
2.积分环节 Ki,作用是消除稳态误差,副作用是导致积分饱和现象;
3.微分环节 Kd,作用是改善系统的动态性能,副作用是延长系统的调节时间,但其抗干扰性差。
上面是位置式的PID controller,还有增量式的PID controller,其数学表达如下:
Δu(k)=Kp(err(k)error(k1))+Kierr(k)+Kd(err(k)2err(k1)+err(k2)) Δ u ( k ) = K p ( e r r ( k ) − e r r o r ( k − 1 ) ) + K i e r r ( k ) + K d ( e r r ( k ) − 2 e r r ( k − 1 ) + e r r ( k − 2 ) )
增量式的PID controller考虑的是三个时刻的误差,其控制更稳定,但是计算输出时要注意加上增量( u(k+1)=u(k)+Δu(k) u ( k + 1 ) = u ( k ) + Δ u ( k ) )


位置式PID controller实现

代码如下:

#include <iostream>

using namespace std;

void PID_init();
float PID_realize(float speed);

struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;








int main()
{
    cout<<"System begin \n";
    PID_init();
    int count=0;
    while(count<1000)
    {
    float speed=PID_realize(200.0);
    cout<<"第 "<<count<<" 次的速度为: "<<speed<<endl;
    count++;
    }
    return 0;

}


void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
cout<<"PID_init end \n";
}


float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
pid.integral+=pid.err;
pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}

增量式PID controller实现

代码如下:

#include <iostream>

using namespace std;

void PID_init();
float PID_realize(float speed);

struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_next;//定义下一个偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;








int main()
{
    cout<<"System begin \n";
    PID_init();
    int count=0;
    while(count<1000)
    {
    float speed=PID_realize(200.0);
    cout<<"第 "<<count<<" 次的速度为: "<<speed<<endl;
    count++;
    }
    return 0;

}


void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_next=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
cout<<"PID_init end \n";
}


float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    float
    incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
    pid.ActualSpeed+=incrementSpeed;
    pid.err_last=pid.err_next;
    pid.err_next=pid.err;
    return pid.ActualSpeed;
}

积分分离

在普通 PID 控制中,引入积分环节的目的,主要是为了消除静差,提高控制精度。但是在启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成 PID 运算的积分积累,导致控制量超过执行机构可能允许的最大动作范围对应极限控制量,从而引起较大的超调,甚至是震荡,这是绝对不允许的。为了克服这一问题,引入了积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。
代码如下:

#include <iostream>
#include<cmath>

using namespace std;

void PID_init();
float PID_realize(float speed);

struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;

bool index;
int main()
{
    cout<<"System begin \n";
    PID_init();
    int count=0;
    while(count<1000)
    {
    float speed=PID_realize(200.0);
    cout<<"第 "<<count<<" 次的速度为: "<<speed<<endl;
    count++;
    }
    return 0;

}


void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.04;
pid.Kd=0.2;
cout<<"PID_init end \n";
}


float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
if(abs(pid.err)>180)
{
index=1;
}else{
index=1;
pid.integral+=pid.err;
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.
err-pid.err_last);
//算法具体实现过
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}

抗积分饱和

所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID 控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出 U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算 u(k)时,首先判断上一时刻的控制量 u(k-1)是否已经超出了极限范围: 如果 u(k1)>umaxu(k1)>umax u ( k − 1 ) > u m a x u ( k − 1 ) > u m a x ,则只累加负偏差; 如果 u(k1)<umin u ( k − 1 ) < u m i n ,则只累加正偏差。从而避免控制量长时间停留在饱和区。
代码如下:

#include <iostream>

using namespace std;

void PID_init();
float PID_realize(float speed);

struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
float umax;//执行机构可执行的最大值
float umin;//执行机构可执行的最小值
}pid;








int main()
{
    cout<<"System begin \n";
    PID_init();
    int count=0;
    while(count<1000)
    {
    float speed=PID_realize(200.0);
    cout<<"第 "<<count<<" 次的速度为: "<<speed<<endl;
    count++;
    }
    return 0;

}


void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.1;
pid.Kd=0.2;
pid.umax=400;
pid.umin=-200;
cout<<"PID_init end \n";
}


float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    if(pid.ActualSpeed>pid.umax)
    {
    //灰色底色表示抗积分饱和的实现
    if(abs(pid.err)>200)
    //蓝色标注为积分分离过程
    {
    index=0;
    }else{
    index=1;
    if(pid.err<0)
    {
    pid.integral+=pid.err;
    }
    }
    }else if(pid.ActualSpeed<pid.umin){
    if(abs(pid.err)>200)
    //积分分离过程
    {
    index=0;
    }else{
    index=1;
    if(pid.err>0)
    {
    pid.integral+=pid.err;
    }
    }
    }else{
    if(abs(pid.err)>200)
    //积分分离过程
    {
    index=0;
    }else{
    index=1;
    pid.integral+=pid.err;
    }
    }
    pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.
    err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}

参考

PID控制器c语言设计
PID控制器-维基

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PID控制器及其C++实现 的相关文章

  • PI闭环的FPGA实现

    PID闭环的FPGA实现 1 原理分析 最近小张同学在做项目的时候发现PI闭环的FPGA学习资料很少 秉持着 既然没有轮子 那么自己就造一个的原则 于是乎自己写了个PI的Verilog程序 FPGA中实现PI闭环与DSP STM32 arm
  • LADRC的学习——用simulink搭建仿真模型

    作者 墨心 时间 2019 7 25 用simulink搭建仿真模型 前面两篇博客主要讲了ADRC的相关概念和知识 并且尝试着搭建模型和仿真 之后学习了PID的相关知识 了解了Kp Ki Kd三个参数的意义 接下来 主要根据高志强教授的论文
  • 控制算法之PID算法

    控制算法之PID算法 从入门到理解到应用 一发入魂 云 社区 腾讯云 tencent com
  • 工程实践---ZN法整定PID

    工程实践 ZN法整定PID 知乎 zhihu com
  • 串级PID与单极PID的区别

    目录 前言 一 什么是串级PID 二 串级PID与单极PID比较 1 控制小车开到某一位置 2 平衡小车速度控制 三 什么时候用串级PID 结语 前言 本文将讲述串级PID与单极PID的区别 并由此引出什么时候用串级PID 对于想深入学习P
  • PID控制算法(PID控制原理与程序流程)

    PID控制算法 PID控制原理与程序流程 暗影玄极 博客园 cnblogs com
  • 将手柄传递到管道中

    说我有 node foo js node bar js 有没有办法将 foo 的标准输入句柄传递给 bar js 我有一个罕见的情况 我想在管道中进行向后通信 至少我知道我可以发送node bar js的pidnode foo js 鉴于
  • C++ 在 Windows 中发送简单信号

    Windows 上是否有相当于kill 的函数 int kill pid t pid int sig 如果没有 是否可以根据进程的 PID 来测试进程是否正在运行 Thanks Windows 没有 Unix 意义上的信号 您可以使用Ope
  • 有人可以解释一下 Erlang 中 Pid(进程标识符)的结构吗?

    有人能解释一下 Erlang 中 Pid 的结构吗 Pids 看起来像这样
  • C - 获取用popen打开的进程的PID

    我有一个用 C 编写的程序 它使用 popen 打开另一个程序 我想获取该程序的 pid 或某种处理程序 以便在一定时间限制后 或者在它超出某些 ram 和 stdout 限制时杀死它 我认为这必须用ptrace来完成 它需要PID 但我不
  • 如何迭代 PCB 以显示 Linux 内核模块中的信息?

    我想编写一个 Linux 内核模块 它可以显示所有正在运行的进程的 PID 我有以下代码 procInfo c My Kernel Module for process info include
  • 如何在 OSX 10.9 中从 ProcessSerialNum 获取 PID?

    GetProcessPID在 OSX 10 9 中被标记为已弃用 并附有注释 使用适当的 processIdentifier 属性 NSRunningApplication 对象 问题是构造类方法NSRunningApplication没有
  • IIS应用程序池PID

    有谁熟悉获取与进程 ID 关联的应用程序池的方法吗 我正在使用 Win32 Process 查询 W3WP 服务并返回 PID 现在我正在尝试获取与其关联的应用程序池 在 Windows Server 2008 上 情况发生了变化 in s
  • 打印 pid_t 的正确 printf 说明符是什么

    我目前正在使用显式转换为 long 并使用 ld用于印刷pid t 是否有一个说明符 例如 z for size t for pid t 如果不是最好的打印方式是什么pid t 没有这样的说明符 我认为你在做什么 铸造pid t to lo
  • 使用批处理查找java PID

    我需要从 Windows 批处理控制台知道 java 进程 PID echo off set p CD FOR F tokens 1 A IN JAVA HOME bin jps exe v find p DO SET str A echo
  • 如何查看linux中特定进程每5秒的内存消耗情况

    我只是想知道如何找到特定进程在特定时间 比如5秒 的内存消耗 我是linux新手 因此 详细的步骤将不胜感激 Use top p PID其中 PID 是进程 ID 应显示有关进程的信息 包括使用的系统内存百分比 类型d以及一个以秒为单位的整
  • 查看用户最近执行的Android任务

    我想查看我的 Android 手机最近的任务 我尝试了一些来自互联网的代码 但没有一个能正常工作 我只想获取用户最后执行的应用程序的PID和名称 例如 如果我执行计算器应用程序 然后执行我创建的最近任务应用程序 则该应用程序应该能够告诉我类
  • 如何在Windows中通过端口查找PID并使用java杀死找到的任务

    我需要通过进程端口在java代码中杀死进程 我可以在 cmd 中手动执行此操作 例如 C gt netstat a n o findstr 6543 TCP 0 0 0 0 6543 0 0 0 0 0 LISTENING 1145 TCP
  • 如何通过MATLAB命令获取外部程序(由MATLAB调用)的PID?

    我很好奇如何获取 MATLAB 在 Windows 中 调用的外部程序的 PID 例如 我通过命令调用 MATLAB 中的记事本 记事本 exe or 系统 记事本 exe 我想在调用此记事本后立即获取它的PID 由于一台计算机上可能会同时
  • 如何在bash中列出所有后台pid

    要么我无法正确表达我的搜索 要么答案不容易找到 但我正在尝试找出如何列出我的所有后台任务 PID 例如 到目前为止 我发现要列出我们使用的最后一个 PID 但现在我想列出之前任务的 PID 如果存在 但我找不到如何做到这一点 最终我想列出我

随机推荐

  • F407标准库之时钟系统

    主要参考正点原子数据手册和源码资料等 第19讲 STM32时钟系统精讲 哔哩哔哩 bilibili 此处记录较为重要或者较易出错的一些遗漏之处 xff0c 作为补充 一般而言 xff0c 时钟越高 xff0c 速度越快 xff0c 但同时抗
  • F407标准库之定时器

    主要参考正点原子数据手册和源码资料等 第31 通用定时器基本原理讲解 哔哩哔哩 bilibili 此处记录较为重要或者较易出错的一些遗漏之处 xff0c 作为补充 定时器中断 定时器相关的库函数主要集中在固件库文件 stm32f4xx ti
  • F407标准库之基础知识

    关于STM32的结构体封装 在STM32中 xff0c 有两种容易弄混的结构体封装 第一种是系统对底层寄存器的封装 结构体类型定义好之后 xff0c 是不会分配地址空间的 xff0c 此时只是个类型定义 xff0c 之后使用的时候 xff0
  • c++架构师需要掌握哪些知识

    目录 本文技术梳理主要针对于三类人群的技术需求 c c 43 43 Linux服务器端开发岗位分析 经常被问到的问题 xff1a 技术体系建立的好处 c c 43 43 Linux服务器开发技术学习路径 一 精进基石 二 高性能网络设计 三
  • cmake:使用execute_process调用shell命令或脚本

    CMake可以通过execute process调用shell命令或者脚本 xff0c 其原型如下 xff1a execute process COMMAND lt cmd1 gt args1 COMMAND lt cmd2 gt args
  • 树莓派3B+上安装ubutun mate 18.04.2

    1 准备16G以上储存卡 xff0c 读卡器 2 准备两个软件 xff1a SDCardFormatter Win32DiskImager分别用于储存卡格式化和写入系统文件 链接如下 xff1a 链接 xff1a https pan bai
  • linux应用编程--思维导图

    思维导图软件是xmind 下载源文件点击打开链接
  • 深度学习中Batch、Iteration、Epoch的概念与区别

    在神经网络训练中 xff0c 一般采用小批量梯度下降的方式 Batch Epoch Iteration 就是其中的重要的概念 我们要理解懂得它们都是什么以及它们之间的区别 1 Batch 每次迭代时使用的一批样本就叫做一个Batch xff
  • STM32使用CubeMAX配置的串口中断接收方法

    STM32使用CubeMAX配置的串口中断接收方法 目录 1 定位串口中断发生的地方 2 处理串口中断接收的流程是 xff1a xff08 1 xff09 初始化串口 xff08 2 xff09 在main中第一次调用接收中断函数 xff0
  • SAP 寻找增强点的方法

    SAP中寻找增强的实现方法 SAP 增强已经发展过几代了 xff0c 可参考 SAP 标准教材 BC425 和 BC427 简单的说SAP的用户出口总共有四 代 1 第一代 基于源代码的增强 SAP提供一个空代码的子过程 xff0c 在这个
  • SNMPV3的实现原理

    在snmp发展到V3版本后 xff0c 把snmp的安全性提升到一个新高度 xff0c 这同时也带来了实现上的复杂性 在02年 xff0c 03年我都曾经想进一步的了解它的实现 xff0c 但都没什么进展 这次在实现Csnmp的过程中 xf
  • ubuntu更新错误:dists/artful/main/binary-arm64/Packages 404 Not Found

    Failed to fetch http archive ubuntu com ubuntu dists artful main binary arm64 Packages 404 Not Found IP 91 189 88 162 80
  • 个人公众号开通啦!!!!

    已经开通了个人微信公众号 xff1a 编程时光机 以后会在公众号里和大家分享知识和生吞活 xff0c 欢迎大家关注 xff01 xff01
  • 小白学AI系列(一)-- AI简史

    经过一段时间的酝酿 xff0c 小白学AI系列也正是开始了 xff01 小编将从三个阶段和大家一起入门人工智能 xff0c 掌握常用机器学习算法和数据分析技巧 小编专业为数据融合方向 xff0c 也曾接触过机器学习 xff0c 但由于人工智
  • 小白学AI系列(二) -- Python模块和函数

    原文地址 xff1a 小白学AI系列 xff08 二 xff09 Python模块和函数 今天的内容是带大家学习解释性语言 Python 小编有学过一段时间的C 43 43 和Matlab 相对于二者而言 xff0c Python是作为学习
  • PX4固定翼调试校准流程及实验相关问题记录分析

    pixhawk固定翼调试流程 对于px4固件 xff0c 其对应选择的一般是qgroundcontrol地面站 xff08 APM一般使用Mission Planner xff09 本次调试的固件版本是1 6 5dev xff08 最新的固
  • Ubuntu16.04下PX4环境快速搭建及uORB通信机制

    Ubuntu16 04下的环境搭建 之前搭建PX4环境常常编译不通 xff0c cmake gcc 以及交叉编译器gcc arm none eabi的版本问题导致make固件报错 xff0c 好不容易编译通过了 xff0c 在进行安装jMA
  • PX4固件通过UART连接串口读取超声波,和树莓派3通信

    添加串口读取程序 首先在Firmware msg文件夹下添加rw uart msg span class hljs keyword char span span class hljs number 5 span datastr span c
  • PX4自主飞行相关问题

    调试入坑 赶在回去之前把10月1日新校区试飞相关问题记录一下 首先是调试相关问题 调试具体流程 在校准遥控器时经常出现校准一半就停止的问题 xff0c 期初认为是固件问题 xff0c 换了1 6 5 1 6 3 xff0c 1 5 5三个固
  • PID控制器及其C++实现

    PID控制器原理 PID控制器实际上是对偏差的控制 其原理图如下 其数学的表达如下 u x 61 K p e r r t 43 1 T e r r t d t 43 T D d e r r t d t u x