Maker Pro
Maker Pro

Need help implementing PI control on an Arduino UNO

Hi everyone, I'm trying to use PI (Proportional-Integral) control in order to control the velocity of a DC motor. I'm using an Arduino UNO with the Arduino motor shield connected to the DC motor. The motor has a quadrature encoder which emits 64 pulses per revolution. The user can change the speed (RPM) of the motor via the serial port.

The problem I'm facing is that the system ignores the speed commands I send to it and continues to alter the speed of the DC motor itself randomly. The code I've written is below. Could you take a look at it and help me in finding the problem?

#include "TimerOne.h"

int pwm = 0;
const int motorBrake = 8;
const int motorDirection = 13;
const int pwmPin = 11;

const int encA = 2;
const int encB = 3;
volatile double count = 0;
volatile double Setpoint = 0;

unsigned long lastTime;
double Input, Output = 0;
double errSum, lastErr = 0;
double kp, ki;

void setup()
{
//Setup Serial port
Serial.begin(9600);
//Setup Motor
pinMode(motorBrake, OUTPUT);
pinMode(motorDirection, OUTPUT);
//Setup Encoder
pinMode(encA, INPUT);
pinMode(encB, INPUT);
attachInterrupt(0, encoderInt, CHANGE);
attachInterrupt(1, encoderInt, CHANGE);
//Timer1 setup
Timer1.initialize(10000);
Timer1.attachInterrupt(rpmCalculate);

digitalWrite(motorDirection, HIGH);
digitalWrite(motorBrake, LOW);
SetTunings(0.6, 0.8);//Kp and ki values.

}

void loop()
{
if(Serial.available())
{
Input = Serial.parseInt();
}
Compute();
if(Output < 5000)
{
pwm = map(Output, 100, 5000, 1, 255);//The Max RPM is 5000, measured before.
}
else
{
pwm = 255;
}
analogWrite(pwmPin, pwm);
Serial.println(Setpoint);
//delay(3000);
}

void Compute()
{
/*How long since we last calculated*/
unsigned long now = millis();
double timeChange = (double)(now - lastTime);
/*Compute all the working error variables*/
double error = Input - Setpoint;
errSum += (error * timeChange);
/*Compute PID Output*/
Output = (kp * error) + (ki * errSum);
/*Remember some variables for next time*/
lastErr = error;
lastTime = now;
}
void SetTunings(double Kp, double Ki)
{
kp = Kp;
ki = Ki;
}

void encoderInt()
{
count++;
}

void rpmCalculate()
{
Setpoint = ((count/64)*60)/0.01;
count = 0;
}
 
At first glance I feel like you are resetting your Setpoint too often, Timer1.Initialize(10000); means its period is 10 milliseconds, so you are triggering rpmCalculate() every 10 milliseconds which resets the count.

Regardless of the resetting count, you may be a little too aggressive with your PI corrections, I see you have a delay in there but it is commented out, I feel like it would not be able to adjust the speed fast enough before it reads the error again.

PI reading the error too quickly can lead to excessive correction which leads to larger error etc.

either adding this delay beck in or maybe add in a loop that reads the speed stores it, then reads it again and compare those, when they are within say 5 counts of each other that is when you would move back and check the error again.
 
Top