How to implement a PID controller on Arduino
We continue with the series of posts dedicated to the theory of controllers, seeing how to implement a PID control in a microprocessor like Arduino.
In previous posts we have seen an introduction to control theory, presented the on/off controller with hysteresis, the powerful PID controller, and seen how to adjust the parameters of a PID controller.
Now it’s time to leave the theory, dust off the keyboard, see how a PID controller is implemented in a microprocessor like Arduino. Fortunately, making a basic PID is quite simple and, as we will see in the second part, we have a very complete library available to make it even easier.
A PID is an essential element when addressing a large number of interesting projects, such as automatically regulating the level of light, maintaining temperature, ensuring that a motor rotates at a constant speed, stabilizing platforms, making a robot move straight, or even robots that remain balanced on two wheels.
我们将继续撰写关于控制器理论的系列文章,看看如何在 Arduino 等微处理器中实现 PID 控制。 在之前的文章中,我们已经介绍了控制理论,介绍了带滞后的开/关控制器和功能强大的 PID 控制器,并了解了如何调整 PID 控制器的参数。 现在是时候抛开理论,看看如何在 Arduino 等微处理器中实现 PID 控制器了。 幸运的是,制作一个基本的 PID 非常简单,而且正如我们将在第二部分看到的那样,我们有一个非常完整的库,可以让制作变得更加容易。 在处理大量有趣的项目时,PID 是一个必不可少的元素,例如自动调节光照度、保持温度、确保电机以恒定的速度旋转、稳定平台、使机器人笔直移动,甚至使机器人在两个轮子上保持平衡。
Manual PID controller
First let’s see how to implement a simple controller ourselves. In fact, the code is not overly complex. Let’s assume our controller takes the input of the controlled variable at A0, and outputs it using a PWM signal on pin 3.
The code for a basic PID could be as follows
首先,让我们看看如何自己实现一个简单的控制器。 事实上,代码并不复杂。 假设我们的控制器在 A0 端接收受控变量的输入,并在 3 引脚上使用 PWM 信号输出。 基本 PID 的代码如下
// Pin assignments
const int PIN_INPUT = A0;
const int PIN_OUTPUT = 3;
// Controller constants
double Kp=2, Ki=5, Kd=1;
// External controller variables
double Input, Output, Setpoint;
// Internal controller variables
unsigned long currentTime, previousTime;
double elapsedTime; //经过时间
double error, lastError, cumError, rateError;
void setup()
{
Input = analogRead(PIN_INPUT);
Setpoint = 100;
}
void loop(){
Input = analogRead(PIN_INPUT); // read a controller input
Output = computePID(Input); // calculate the controller
delay(100);
analogWrite(PIN_OUTPUT, Output); // write the controller output
}
double computePID(double inp){
currentTime = millis(); // get current time
elapsedTime = (double)(currentTime - previousTime); // calculate elapsed time
error = Setpoint - Input; // determine the error between setpoint and measurement
cumError += error * elapsedTime; // calculate the integral of the error
rateError = (error - lastError) / elapsedTime; // calculate the derivative of the error
double output = kp*error + ki*cumError + kd*rateError; // calculate the PID output
lastError = error; // store previous error
previousTime = currentTime; // store previous time
return output;
}
As we can see it is not too difficult. We have a ‘computePID()’ function that does all the work. In this function we calculate the time elapsed between calls, which we need to calculate both the derivative and the integral of the error.
Next, we compare the controller input with the setpoint to determine the error and perform the ‘pseudo’ integrals and derivatives (their discrete equivalent). Finally, we calculate the system response using the PID formula, and save the values for the next cycle.
It wasn’t that hard, right? However, although it is completely functional, our PID controller is quite simple. Therefore, it has certain deficiencies in situations that frequently occur in reality.
We could improve our code, but it was not going to be so simple or fast to do it. And the best part is, it’s not necessary! To our delight, someone has done all the work for us, as we will see in the next section.
我们可以看到,这并不难。 我们有一个 "computePID() "函数来完成所有工作。 在这个函数中,我们计算两次调用之间的时间间隔,这是计算误差的导数和积分所必需的。 接下来,我们比较控制器输入和设定点,以确定误差,并执行 "伪 "积分和导数(离散等效)。 最后,我们使用 PID 公式计算系统响应,并为下一个周期保存数值。 这并不难,对吗? 不过,虽然功能齐全,但我们的 PID 控制器却非常简单。 我们可以改进我们的代码,但这样做并不简单,也不快速。 最重要的是,这没有必要! 让我们高兴的是,有人已经为我们完成了所有的工作,我们将在下一节看到。
PID controller with library
Fortunately we have available the PIDController library, based on the ArduinoPID library developed by Brett Beauregard.
ArduinoPID is a little wonder that contains many improvements over a basic PID, which are detailed in this link from the author. I recommend that you read the documentation of the library because it is a whole class on PID controllers.
With this, creating a PID controller on Arduino is as simple as the following code.
幸运的是,PIDController 库基于 Brett Beauregard 开发的 ArduinoPID 库。 ArduinoPID 是一个小奇迹,它包含了许多对基本 PID 的改进,作者在此链接中详细介绍了这些改进。 我建议您阅读该库的文档,因为它是一个关于 PID 控制器的完整类。 有了它,在 Arduino 上创建一个 PID 控制器就像下面的代码一样简单。
#include <PIDController.h>
const int PIN_INPUT = A0;
const int PIN_OUTPUT = 3;
PID::PIDParameters<double> parameters(4.0, 0.2, 1);
PID::PIDController<double> pidController(parameters);
void setup()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Setpoint = 100;
pidController.TurnOn();
}
void loop()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Update();
analogWrite(PIN_OUTPUT, pidController.Output);
}
We have seen that it is quite easy to implement a PID controller on a microprocessor like Arduino. And it is even easier with the little gem of an Arduino PID library. So there is no reason to have excuses or fears when implementing a PID controller in our projects.
In the next posts of the series, we will start to see practical examples of projects where we can implement a PID controller on Arduino. See you soon!
我们已经看到,在 Arduino 这样的微处理器上实现 PID 控制器非常容易。 有了 Arduino PID 库这个小玩意,实现起来就更容易了。 因此,在我们的项目中实施 PID 控制器时,我们没有理由找借口或害怕。 在本系列的下一篇文章中,我们将开始介绍在 Arduino 上实施 PID 控制器的项目实例。 再见!
文章评论