I was trying to apply PI controller to a motor using Arduino. I have somewhat achieved the task but the problem is that it oscillates too much. Also i couldn't figure out what to do when pwm signal are calculated as negative
int counter = 0;
double end_time=0;
double ini_time=0;
double rpm=0;
double my_time=0;
double temp = 0;
int pid_speed=0;
float kp=2.25;
float ki=.01;
float kd=0.005;
int prev_error=0;
double pwm_sig=0;
int integrator = 0;
int motor_sig_pin= 5;
int error=0;
int absolute = 0;
float desired_rpm=1000; //my desired RPM value
double zero_time=0;
double i_time=0;
double f_time=0;
int rpm_tacho=0;
int previous_error=0;
int derivative=0;
void setup() {
pinMode(2,INPUT);
digitalWrite(2,HIGH);
pinMode(motor_sig_pin,OUTPUT);
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
digitalWrite(9,HIGH);
digitalWrite(10,LOW );
attachInterrupt (0,Count,RISING);
ini_time = micros();
i_time=micros();
Serial.begin(115200);
}
void Count(){ //calculating time for one round using encoder
counter++;
if (counter>=100){
end_time=micros()-ini_time;
ini_time=micros();
counter=0;
}
}
void loop() {
rpm = ((1000000*60)/(end_time));
rpm = abs(rpm);
rpm_tacho = ((1000000*60)/(zero_time));
if (rpm <10 || rpm>5000){
rpm=0;
}
error=desired_rpm-rpm;
integrator += error;
pid_speed = kp*error; + ki*integrator;
pid_speed=(int)pid_speed;
Serial.print(pid_speed);
Serial.print(" ");
if (pid_speed> 0){
if (pid_speed>255)
analogWrite(motor_sig_pin,255);
else
analogWrite(motor_sig_pin,pid_speed);
}
if(pid_speed<0){
if(pid_speed<-255)
{
analogWrite(motor_sig_pin,0);
}
else
{
pid_speed=abs(pid_speed);
analogWrite(motor_sig_pin,255-pid_speed);
}
}
Serial.print (" ");
Serial.println(rpm);
}
end_time
is used both in normal context and in interrupt context, it should therefore be qualified asvolatile
. 2) In loop, asend_time
is more than one byte, you should copy it to a local variable while blocking interrupts. 3) You are not usingkd
. 4) There is a;
before+ ki*integrator
, which means that the computation of the integral term is a no-op.noInterrupts(); double end_time_copy = end_time; interrupts();
, then use the copy instead of the original. OtherwiseCount()
may updateend_time
while you are in the process of reading it.ini_time
andend_time
should beunsigned long
oruint32_t
, otherwise the tachometer will return garbage whenmicros()
rolls-over to zero. 6) Then there is no point inabs(rpm)
, asrpm
cannot be negative. 7)rpm_tacho
is unused, and computed by dividing by zero. 8) You should properly indent your code before posting it.constrain()
. Or better yet, use a ready-made PID library.