Arduino使用PID库,轻松搞定PID

Arduino PID库使用讲解。
原文的地址在这儿:http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
还参考了 syl312 的帖子: http://www.arduino.cn/thread-4535-1-1.html

每个接触PID的人,都知道PID的公式吧:
Arduino使用PID库,轻松搞定PID

然后根据这个公式,就可以编出计算output的arduino程序如下:
Arduino使用PID库,轻松搞定PID

注:程序源码可以在原文上复制。

可是在上面的程序中,当PID不规则的调用时就会出现两个问题:
1.有时候定时调用,有时候又停止调用,将得不到PID的持续稳定特性。
2.需要额外对积分和微分进行数学计算,因为他们都是和时间息息相关的。

解决方法:保证PID在一个固定的的时间间隔内被调用,我通过每个周期内事先设置好的采样时间调用compute函数,PID再决定是计算还是立即返回数值。一旦我们知道PID在固定时间间隔内被调用,积分和微分的计算就能被简化。
于是我们的程序就变成了:

Arduino使用PID库,轻松搞定PID

灰色部分是新加的语句。
在这个新的程序中,作者在29行把时间转化为秒。这样可以简化运算。同时新加入后面两个函数把PID参数转化为以秒为基准的参数。

当上面的PID程序在运行中,可能会出现微分过冲的问题。
Arduino使用PID库,轻松搞定PID
看上面的图,因为error=Setpoint-Input,只要Setpoint发生改变,就会导致error的值发生一个突变。在微分运算里这种微分值的突变是无穷大的。
解决方法:
Arduino使用PID库,轻松搞定PID

于是我们的程序就变成了:
Arduino使用PID库,轻松搞定PID

可以看到我们只是用-dInput代替掉了+dError。


当我们的PID在运行的时候,这个时候改变PID的参数,会导致输出发生变化,如:
Arduino使用PID库,轻松搞定PID

我们只是把ki的值减少一半,输出就减少了一半。为什么会这样呢?可以从下面的积分公式里找到原因:
Arduino使用PID库,轻松搞定PID

这就解释了为什么KI没改变之前系统工作的一直很稳定。突然,你乘了一个新的KI值与之前的偏差累计总和。这样带来的变化不是我们所希望的,我们只想改变后能朝着我们希望的方向发展。
解决方法:解决的方法有很多,我在最新的Arduino PID库里使用的方法是重新调整errSum(偏差总和).KI变为原来的两倍时,把errSum变为原来的一半,虽然这个方法有点笨拙,下面有更加明智的方法。这个方法要求有基础的代数基础或者计算技巧。
Arduino使用PID库,轻松搞定PID

把KI乘到里面,虽然看起来没什么变化,但是我们将会看到这个小变化带来了很大的不同。现在我们将error和Ki相乘。并且把乘积和保存起来,当Ki变化时,这时不会有很大的变化了,因为之前的KI的乘积和值已经存储起来了。
于是我们的程序就变成了:
Arduino使用PID库,轻松搞定PID

这样,我们再改变参数,输出也不会发生很大的变化。
Arduino使用PID库,轻松搞定PID
当我们的PID在运行的时候,有时候会发生摆尾的情况。如图:
Arduino使用PID库,轻松搞定PID 
这种情况发生在PID认为它能做一些它实际上做不到的事情时。比如最大只能输出255,可是PID输出了300。原因在于PID并不知道最大的输出只能为255。所以我们在程序里给PID的输出值加一些限制。告诉PID可以输出的最大值为多少。我们的程序就变成了:

Arduino使用PID库,轻松搞定PID 
Arduino使用PID库,轻松搞定PID 

当有的时候,你不需要使用PID计算的输出值,想用自己设定的值。(比如想让输出为0),则你的程序可能会这样写:
void loop()
{
Compute();
Output=0;
}

这样的话,不管PID想输出什么,程序始终输出0。这样就会使PID变得很迷惑:“我尝试着改变output,却什么都没有发生,怎么办?我把改变的力度增大吧。”这样,当你停止使用自己设定的值作为输出,重新使用PID作为输出时。PID输出很大的值,这样系统就乱套了。
因此我们要在程序里,加一个开关,在我们使用自己设定的值时,把PID关掉,不让它进行计算。
我们的程序就变成了:
Arduino使用PID库,轻松搞定PID 
Arduino使用PID库,轻松搞定PID 


在上面的程序中,我们给PID加了一个开关,但是当我们把PID的开关打开,重新回到PID的时候,会发生下面的情况:
Arduino使用PID库,轻松搞定PID 

没错,PID的输出值会跳回成它发出的上一个输出值。并且从那个值开始来调整自己。
因此当开关重新打开时,我们需要一个初始化函数,来重新初始化一下数据,先看程序:
Arduino使用PID库,轻松搞定PID 
Arduino使用PID库,轻松搞定PID 

可以知道,当换模式的时候,会执行 Initialize() 函数,把lastInput的值更新成现在的采样值,把ITerm的值更新成output的值,这样就避免了第一次执行PID时的跳变了。
- 本文内容来自网络,如有侵权,请联系本站处理。

2022-07   阅读(2806)   评论(0)
 标签: 创客 Arduino PID 平衡车

涨知识
LED点阵屏

LED点阵屏通过LED(发光二极管)组成,以灯珠亮灭来显示文字、图片、动画、视频等,是各部分组件都模块化的显示器件,通常由显示模块、控制系统及电源系统组成。

评论:
相关文章
Arduino 数据结构: 队列Queue

Queue 库提供了一个通用的 C++ 动态队列实现,专为在 Arduino 项目中使用而定制。


小鹏浇花套件单机版程序V1.0.0

本程序是小鹏物联网智能浇花套件的单机版程序(不连接物联网),供同学们参考。


Arduino 数据结构: ArrayList

ArrayList 类是一个 C++ 模板类,它提供了 ArrayList 的实现,以便轻松存储任何指定类型的值。它允许使用索引进行高效存储和检索,支持排序操作。


ESP32 FreeRTOS 双核使用

ESP32系列(包括ESP32-S3)搭载Xtensa双核处理器,默认情况下Arduino框架仅使用单核运行用户代码,通过多核编程,可以充分利用硬件资源来提升系统响应和性能。


理解 MSBFIRST(最高有效位)和 LSBFIRST(最低有效位)

在本文中,先解释 MSB(最高有效位)和 LSB(最低有效位)的概念,以及 MSBFIRST 和 LSBFIRST。然后展示了 MSBFIRST 和 LSBFIRST 的使用如何影响移位寄存器的输出。


Arduino 和 TB6612FNG 驱动直流电机

TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速与方向,输入电压在3V~12V,因此在集成化、小型化的电机控制系统中,它可以作为理想的电机驱动器件。


Arduino-ESP32 Preferences库使用详解

Arduino-ESP32项目提供的Preferences库是一个专为ESP32设计的非易失性存储解决方案,它替代了传统的Arduino EEPROM库,提供了更强大、更可靠的数据存储功能。


Arduino Serial 接收数字

在Arduino中,通过串行端口接收数字通常涉及使用Serial.read()、Serial.readString()、Serial.parseInt()等方法。



Arduino 高级编程:控制乐高EV3马达

本文介绍一种使用Arduino开发板控制乐高EV3马达的方法。