Arduino PID 直流电机位置控制
Arduino PID DC Motor Position Control
我正在做一个控制工程项目,为自动天线跟踪系统实施 PID 电机位置控制。该系统包含直流电机、绝对编码器和电机驱动器。
一切都按预期工作,但有一点。电机无法在接近 0 度(350 - 359、0 - 10 度)的设定值处停止。使用代码:
#include <PID_v1.h>
int RPWM = 5;
int LPWM = 6;
int L_EN = 7;
int R_EN = 8;
boolean pin_state[10];
byte input_pin[] = {1, 2, 3, 4, 9, 10, 11, 12, 13};
int dec_position = 0;
int dc = 0;
double kp = 50, ki = 45, kd = 2;
double input = 0, output = 0, setpoint = 0;
volatile long encoderPos = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
void setup() {
pinMode(L_EN, OUTPUT);
pinMode(R_EN, OUTPUT);
pinMode(RPWM, OUTPUT);
pinMode(LPWM, OUTPUT);
for (byte i = 0; i < 9; i++) {
pinMode(input_pin[i], INPUT);
}
TCCR1B = TCCR1B & 0b11111000 | 1;
myPID.SetMode(AUTOMATIC);
myPID.SetSampleTime(1);
myPID.SetOutputLimits(-255, 255);
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
}
void loop() {
if (Serial.available() > 0) {
String baca = Serial.readString();
setpoint = baca.toInt();
}
ReadEncoder();
input = dc;
myPID.Compute();
pwmOut(output);
}
void pwmOut(int out) {
if (out > 0) {
analogWrite(RPWM, out);//Sets speed variable via PWM
}
else {
analogWrite(LPWM, abs(out));//Sets speed variable via PWM
}
}
void ReadEncoder() {
// FOR READING ENCODER POSITION, GIVING 0-359 OUTPUT CORRESPOND TO THE ENCODER POSITION
for (byte i = 0; i < 9; i++) {
pin_state[i] = !(digitalRead(input_pin[i]));
}
dec_position = (pin_state[8] * 256) + (pin_state[7] * 128) + (pin_state[6] * 64) + (pin_state[5] * 32) + (pin_state[4] * 16) + (pin_state[3] * 8) + (pin_state[2] * 4) + (pin_state[1] * 2) + pin_state[0];
dc = map(dec_position, 0, 500, 0, 360);
}
当设定值介于 10 - 350 之间时,系统运行良好。但如果不是,电机永远不会停止旋转。
我知道问题是由于一点位置超调导致编码器读取一个非常大的错误。
例如,如果设定点是0度,电机旋转到达它。电机旋转速度随着其 "now" 位置接近 0 度而减慢,但系统并非没有超调。因此,即使 1 度超调也会导致误差值为 -359(设定点 - 现在位置)并且电机再次旋转以到达所需位置。
需要帮助解决这个问题。抱歉英语不好。
我还没有读你的代码。但是,要达到 "set point" (SV),您应该为 "present value" (PV) 提供误差容限 (EA)
例如:EA = SV - PV。
如果 EA = (-2,+2)degree 那么它达到 "now position"
而且,你不应该用度数来表示角度,你应该把它转换成位置(通过脉冲计算)
希望这个概念能帮到你。
这是解决方案
double error;
if (SP>PV) {
if (abs(SP-PV) < abs(-360 + SP - PV)) error = SP - PV;
else error = -360 + SP - PV;
}
else{
if(abs(SP-PV)< abs(360 - PV + SP)) error = SP - PV;
else error = 360 - SP + PV;
}
取而代之的是简单的现值减去错误的设定点。上面的代码 return 从当前值到设定点的最短路径。
我正在做一个控制工程项目,为自动天线跟踪系统实施 PID 电机位置控制。该系统包含直流电机、绝对编码器和电机驱动器。
一切都按预期工作,但有一点。电机无法在接近 0 度(350 - 359、0 - 10 度)的设定值处停止。使用代码:
#include <PID_v1.h>
int RPWM = 5;
int LPWM = 6;
int L_EN = 7;
int R_EN = 8;
boolean pin_state[10];
byte input_pin[] = {1, 2, 3, 4, 9, 10, 11, 12, 13};
int dec_position = 0;
int dc = 0;
double kp = 50, ki = 45, kd = 2;
double input = 0, output = 0, setpoint = 0;
volatile long encoderPos = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
void setup() {
pinMode(L_EN, OUTPUT);
pinMode(R_EN, OUTPUT);
pinMode(RPWM, OUTPUT);
pinMode(LPWM, OUTPUT);
for (byte i = 0; i < 9; i++) {
pinMode(input_pin[i], INPUT);
}
TCCR1B = TCCR1B & 0b11111000 | 1;
myPID.SetMode(AUTOMATIC);
myPID.SetSampleTime(1);
myPID.SetOutputLimits(-255, 255);
digitalWrite(L_EN, HIGH);
digitalWrite(R_EN, HIGH);
}
void loop() {
if (Serial.available() > 0) {
String baca = Serial.readString();
setpoint = baca.toInt();
}
ReadEncoder();
input = dc;
myPID.Compute();
pwmOut(output);
}
void pwmOut(int out) {
if (out > 0) {
analogWrite(RPWM, out);//Sets speed variable via PWM
}
else {
analogWrite(LPWM, abs(out));//Sets speed variable via PWM
}
}
void ReadEncoder() {
// FOR READING ENCODER POSITION, GIVING 0-359 OUTPUT CORRESPOND TO THE ENCODER POSITION
for (byte i = 0; i < 9; i++) {
pin_state[i] = !(digitalRead(input_pin[i]));
}
dec_position = (pin_state[8] * 256) + (pin_state[7] * 128) + (pin_state[6] * 64) + (pin_state[5] * 32) + (pin_state[4] * 16) + (pin_state[3] * 8) + (pin_state[2] * 4) + (pin_state[1] * 2) + pin_state[0];
dc = map(dec_position, 0, 500, 0, 360);
}
当设定值介于 10 - 350 之间时,系统运行良好。但如果不是,电机永远不会停止旋转。
我知道问题是由于一点位置超调导致编码器读取一个非常大的错误。
例如,如果设定点是0度,电机旋转到达它。电机旋转速度随着其 "now" 位置接近 0 度而减慢,但系统并非没有超调。因此,即使 1 度超调也会导致误差值为 -359(设定点 - 现在位置)并且电机再次旋转以到达所需位置。
需要帮助解决这个问题。抱歉英语不好。
我还没有读你的代码。但是,要达到 "set point" (SV),您应该为 "present value" (PV) 提供误差容限 (EA)
例如:EA = SV - PV。 如果 EA = (-2,+2)degree 那么它达到 "now position"
而且,你不应该用度数来表示角度,你应该把它转换成位置(通过脉冲计算)
希望这个概念能帮到你。
这是解决方案
double error;
if (SP>PV) {
if (abs(SP-PV) < abs(-360 + SP - PV)) error = SP - PV;
else error = -360 + SP - PV;
}
else{
if(abs(SP-PV)< abs(360 - PV + SP)) error = SP - PV;
else error = 360 - SP + PV;
}
取而代之的是简单的现值减去错误的设定点。上面的代码 return 从当前值到设定点的最短路径。