运动控制(七)开关vs线性:比例控制

在上一篇文章中,我们看到了如何通过牺牲一些速度和/或准确性来提高开/关控制的稳定性。如果我们不想放弃这么多目标,另一种改进控制的方法是尝试不同的算法。我们的开/关控制算法可能不是我们正在做的事情的最佳选择,比如定位一个物体相对于另一个物体的位置。例如,想象一下,你会使用开/关控制算法来定位你的汽车相对于红绿灯的任务。你会怎么做:让你的汽车以恒定速度驶向红绿灯,然后当你到达红绿灯时突然猛踩刹车。你实际上不能立即停下来,所以当你最终在十字路口停下来时,你会把车挂上倒挡,猛踩油门,你倒车全速直到你的车再次到达红绿灯,这时你再次猛踩刹车。你再次超过红绿灯,所以你再次换到前进挡,再次猛踩油门。你继续这样做,直到灯光最终变绿,然后你前往下一个红绿灯重复这个过程。如果你真的这样做了,你可能会伤害到某人,你肯定会毁了你的车。好的司机实际上做的与此非常不同,他们看到前方的红绿灯,估计他们的车和交通灯之间的距离,并在到达之前开始减速。离交通灯越近,开得越慢。通过这种方式,他们可以在不倒车的情况下平稳地停在交通灯处。这种控制算法通常被称为线性控制。与使用单一速度(要么开启要么关闭)不同,速度会根据两个物体之间的相对位置进行调整。

最简单的线性控制类型称为比例控制。在这个算法中,速度被计算为一个常数(称为增益Gain)乘以误差(error),即物体所在位置与我们想要的位置之间的差异。让我们在我们的代码中尝试这个。要为我们的电机使用比例控制,我们需要根据电机位置的误差来改变电机的速度。所以我们在代码中要做的第一件事是创建一个变量来计算误差,这个常数在方程中表示为Kp。我们将这个设置为浮点值,现在将其设置为0.5。稍后在本视频中,我们将看看改变这个常数值的效果。

// Define the proportional constant (Kp) to tune the responsiveness
float Kp = 0.5;  // Adjust this value to tune the proportional control

在我们得到计数之后,我们可以计算误差。误差是我们想要的位置和我们实际所在的位置之间的差异。

  // Read the current encoder position
  currentPosition = myEncoder.read();

  // Calculate the error between the target and current position
  long error = targetPosition - currentPosition;

现在,如果误差大于0,我们希望电机向前移动,否则如果误差小于或等于零,我们希望向反方向转动。当误差等于零时,我们无论如何都会关闭电机,因为我们正在计算与误差成比例的速度。

  // Control motor direction and speed
  if (error > 0) {
    // Rotate forward
    digitalWrite(IN1, HIGH);  // Forward direction
    digitalWrite(IN2, LOW);
    analogWrite(ENABLE1, motorSpeed);  // Proportional speed
  } else {
    // Rotate backward
    digitalWrite(IN1, LOW);   // Backward direction
    digitalWrite(IN2, HIGH);
    analogWrite(ENABLE1, motorSpeed);  // Proportional speed
  }

现在我们希望根据误差计算速度,而不是像这里一样是一个恒定值。让我们为速度创建一个变量。让我们将其设置为整数。

  // Calculate the motor speed using proportional control
  // Speed is proportional to the error (bigger error = higher speed)
  int motorSpeed = abs(error) * Kp;

好的,现在如果误差大于零,我们可以计算速度为我们的常数Kp乘以误差,因为误差是正的,我们会得到一个正的速度。然而,我们不希望速度超过255,因为那是我们可以写入我们的PWM比较器的最大值。所以如果速度大于255,我们将将其设置为255。然后我们将速度值写入比较器。否则,即如果误差小于或等于零,我们需要计算速度为负Kp乘以误差。那是因为我们的速度值是我们写入比较器的,比较器值永远不能是负的。所以如果速度大于255,我们将速度设置为255,

  // Cap the motor speed between 0 and 255 (PWM range)
  motorSpeed = constrain(motorSpeed, 0, 2550);

全部代码如下:

#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 = 250;   // 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, 2550);

  // 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 > 0) {
    // Rotate forward
    digitalWrite(IN1, HIGH);  // Forward direction
    digitalWrite(IN2, LOW);
    analogWrite(ENABLE1, motorSpeed);  // Proportional speed
  } else {
    // Rotate backward
    digitalWrite(IN1, LOW);   // Backward direction
    digitalWrite(IN2, HIGH);
    analogWrite(ENABLE1, motorSpeed);  // Proportional speed
  } 

  delay(50);  // Small delay for stability (can be adjusted or removed)
}

运行结果:

图片

Kp=0.5

让我们回到代码并改变Kp的值,将Kp的值设置为0.8我们应该看到不稳定性增加了一点,我们现在的超调比KP为0.5时更多,但运动仍然稳定。

图片

Kp=0.8

让我们尝试将KP更改为1,看看会发生什么。当KP等于1时,系统变得不稳定。

图片

Kp=1

通常,当你增加Kp时,你会有更少的稳定性,更少的误差和更大的速度。但如果Kp太大,系统会变得不稳定。