Electronic – arduino – Problems with Arduino PID tuning on DC motor

arduinopid controller

I'm using a shaft encoder attached to a DC motor to measure RPM, a L293D IC to drive the motor and a Arduino board. My objective is to control the DC motor speed to roughly 200 RPM, and I'm trying to use the PID library of the arduino to achieve this. My problem, right now, is with the tuning. The system oscillates a lot, and I've tried several methods, but nothing seems to work. I've tried setting Kd and Ki to zero and adjusting Kp until I get a steady oscillation, without success. Also, adjusting Kd isn't doing damping. I've tried using a simple Python program to plot the RPM in function of time and this is what I'm getting: enter image description here

Below is the Arduino code I'm using:

#include <PID_v1.h>

#define SpeedSetPoint 200

double Input, Output, Setpoint; //PID Variables

int potKp = 2;
int potKd = 3;
int potKi = 4;
float Kp = 0, Kd = 0, Ki =0;


PID myPid(&Input, &Output, &Setpoint, 0, 0, 0, DIRECT); 

//RPM Counter
unsigned long start;
const byte encoderPinA = 2;//A pin -> interrupt pin 0
const byte encoderPinB = 4;//B pin -> digital pin 4
volatile long pulse;
volatile bool pinB, pinA, dir;
const byte ppr = 25, upDatesPerSec = 2;
const int fin = 1000 / upDatesPerSec;
const float konstant = 60.0 * upDatesPerSec / (ppr * 2);
int rpm=0;

int outputValue=0;

//DC Motor Pins
int pwm = 5;
int motorA1 = 10;
int motorA2 = 9;

void setup(){
  Serial.begin(19200);
  Input = 25;
  Setpoint = SpeedSetPoint;
  myPid.SetMode(AUTOMATIC);

  attachInterrupt(0, readEncoder, CHANGE);
  pinMode(encoderPinA,INPUT_PULLUP);
  pinMode(encoderPinB,INPUT_PULLUP);

  pinMode(pwm, OUTPUT);
  pinMode(motorA1, OUTPUT);
  pinMode(motorA2, OUTPUT);

  //Start DC Motor
  digitalWrite(pwm, HIGH);
  digitalWrite(motorA1, HIGH);
  digitalWrite(motorA2, LOW);
}


void loop() {

  Kp = analogRead(potKp)/100.0;
  Ki = analogRead(potKi)/1000.0;
  Kd = analogRead(potKd)/1000.0;

  myPid.SetTunings(Kp, Ki, Kd);



  if(millis() - start > fin)
  {
    start = millis();
    rpm = pulse * konstant;
    rpm = abs(rpm);
    Serial.println(rpm);
    pulse = 0;
  }//End RPM reading

  Input = rpm;
  myPid.Compute();
  outputValue = Output;
  //outputValue = map(outputValue, 0, 400, 180, 255);
  analogWrite(pwm, outputValue);

  Serial.print("RPM = ");
  Serial.print(rpm);
  Serial.print("\t");
  Serial.print("Kp = ");
  Serial.print(Kp);
  Serial.print("\t");
  Serial.print("Ki = ");
  Serial.print(Ki);
  Serial.print("\t");
  Serial.print("Kd = ");
  Serial.println(Kd);
}

void readEncoder()
{
  pinA = bitRead(PIND,encoderPinA);
  pinB = bitRead(PIND,encoderPinB);
  dir = pinA ^ pinB;          // if pinA & pinB are the same
  dir ? --pulse : ++pulse;    // dir is CW, else CCW
}

Any tips?

Best Answer

The first thing that jumps out from your plot is the large and somewhat random variations in successive iterations. That means you're not running the PID loop fast enough. For something like a motor, every 5 to 10 ms (100-200 Hz) is probably good, but of course that depends on the motor. Typically you want 20 to 50 iterations within the first major time constant of the system.

Only after the loop is running fast enough does tuning make any sense.

Start with just the P term, keeping I and D at 0. Crank up P to just below where it causes ringing. Now slowly bring up the I term. This will cause oscillations when too high. Find a value that sortof works, and tweak the P term again, etc. Find a set of P,I values that work.

Often you stop there, since using the D term makes the system very susceptible to noise. If your feed back signal is clean then start with the good P and I values, and slowly increase D. On some systems D is actually negative to be useful. Look at the math of your implementation carefully to see which polarity to use.

Think of the D term logic as "We're already heading in the right direction, so back off the drive a bit". This can be useful to dampen things so that the other terms can be set more aggressively and you therefore get faster overall step response to within some error.

Related Topic