是时候用一个叫做PID的算法来完成我们对运动控制的研究了。实际上,我们已经开始了对这个控制算法的研究,因为PID中的P代表比例,我们已经实现了比例控制。那是我们设置电机速度为某个常数乘以定位误差的算法,我们称这个常数为增益(gain),我们也看到了改变增益的效果。
在我们今天看PID控制的I和D部分之前,让我们花点时间研究量化改变Kp增益的效果。代码如下:
#include <Encoder.h> // Include Encoder library
// Define motor control pins
int IN1 = 8;
int IN2 = 9;
int ENABLE1 = 10; // PWM for speed
// Define encoder pins and create Encoder object
Encoder myEncoder(2, 3); // Channel A = pin 2, Channel B = pin 3
long targetPosition = 1000; // 1/4 cycle, assuming 1000 pulses for a full cycle
long currentPosition = 0; // Current position from the encoder
// Define the proportional constant (Kp) to tune the responsiveness
float Kp = 0.5; // Adjust this value to tune the proportional control
void setup() {
// Set motor control pins as outputs
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENABLE1, OUTPUT);
// Initialize serial communication for debugging
Serial.begin(9600);
// Reset encoder position to 0 at start
myEncoder.write(0);
}
void loop() {
// Read the current encoder position
currentPosition = myEncoder.read();
// Calculate the error between the target and current position
long error = targetPosition - currentPosition;
// Calculate the motor speed using proportional control
// Speed is proportional to the error (bigger error = higher speed)
int motorSpeed = abs(error) * Kp;
// Cap the motor speed between 0 and 255 (PWM range)
motorSpeed = constrain(motorSpeed, 0, 255);
// Print positions and speed for debugging
Serial.print("Current Position: ");
Serial.print(currentPosition);
Serial.print(" | Target Position: ");
Serial.print(targetPosition);
Serial.print(" | Error: ");
Serial.print(error);
Serial.print(" | Motor Speed: ");
Serial.println(motorSpeed);
// Control motor direction and speed
if (error > 5) {
// Rotate forward
digitalWrite(IN1, HIGH); // Forward direction
digitalWrite(IN2, LOW);
analogWrite(ENABLE1, motorSpeed); // Proportional speed
} else if (error < -5) {
// Rotate backward
digitalWrite(IN1, LOW); // Backward direction
digitalWrite(IN2, HIGH);
analogWrite(ENABLE1, motorSpeed); // Proportional speed
} else {
// Stop the motor when within tolerance of target position
analogWrite(ENABLE1, 0); // Stop motor by setting speed to 0
}
delay(50); // Small delay for stability (can be adjusted or removed)
}
我们将运行三个不同的Kp值:0.3,0.5,0.8,记录下电机和位置和时间的关系并绘制出图表。

绘制好的图表如下:

我们感兴趣的第一个时间值被称为上升时间(Rise Time)。上升时间是从运动开始到首次达到目标位置所需的时间。如果我们仔细查看我们的图表,我们可以看到对于所有这三个,上升时间发生在某个地方,我们可以看到橙色线,即Kp=0.8,有最短的上升时间,红色是下一个最短的上升时间,然后蓝色的上升时间最长。
Kp | Rise Time(ms) |
0.3 | 0.4 |
0.5 | 0.25 |
0.8 | 0.2 |
另一个我们感兴趣的速度值被称为峰值时间(Peek Time)。峰值时间就像它听起来的那样,是从运动开始到达到第一个最大值或第一个峰值所需的时间。对于峰值时间和上升时间,较小的数字更好,一般来说,随着Kp的增大,这些值应该下降,随着Kp的减小,上升时间和峰值时间应该增加。
Kp | Peak Time(ms) |
0.3 | 0.45 |
0.5 | 0.35 |
0.8 | 0.3 |
现在还有一个时间值,它有点跨越了速度和稳定性之间的界限,这被称为稳定时间(Setting Time)。稳定时间是从运动开始到运动不再振荡所需的时间。稳定时间的趋势与峰值时间和上升时间的趋势相反,随着Kp的增大,峰值时间和上升时间变小,但稳定时间变长。那是因为随着Kp的增加,稳定性降低,换句话说,控制变得不太稳定。
Kp | Setting Time(ms) |
0.3 | 0.5 |
0.5 | 0.7 |
0.8 | 2.35 |
让我们在这里做另一个稳定性的测量,另一个稳定性的测量是超调(Overshoot)。超调是一个百分比,它是响应在其峰值时超过目标位置的量。随着Kp的增大,超调量通常会增加。
Kp | Overshoot |
0.3 | 0% |
0.5 | 9.2% |
0.8 | 23.2% |
到目前为止,上升时间和峰值时间与速度有关,稳定时间和超调与稳定性有关,还有一个我们需要的测量值,那就是准确性(Accuracy)。对于准确性,我们最感兴趣的值是所谓的稳态误差(Steady State Error)。稳态误差是响应停止振荡后定位中的误差量。稳态误差总是正的,所以我们取目标位置和实际位置之间差异的绝对值。
Kp | Steady state error |
0.3 | 2 |
0.5 | 2 |
0.8 | 4 |
这里你看到的这种趋势是典型的趋势,随着Kp的增大,稳态误差会变小。所以在这里我们有两个值是关于速度的,两个值是关于稳定性的,还有一个值是关于准确性的。
Speed | Stability | Accuracy | |||
Kp | rise time | peak time | setting time | overshoot | steady state error |
0.3 | 0.4 | 0.45 | 0.5 | 0 | 2 |
0.5 | 0.25 | 0.35 | 0.7 | 9.20% | 2 |
0.8 | 0.2 | 0.3 | 2.35 | 23.20% | 4 |
在下一个讲座中,我们将看看我们如何使用这些数字来为我们的控制器找到一个良好的控制算法和良好的控制增益。