Connect with us

Need help implementing PI control on an Arduino UNO

Discussion in 'Microcontrollers, Programming and IoT' started by Sci-fi guy, Apr 9, 2015.

Scroll to continue with content
  1. Sci-fi guy

    Sci-fi guy

    6
    0
    Oct 12, 2014
    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;
    }
     
  2. GreenGiant

    GreenGiant

    841
    6
    Feb 9, 2012
    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.
     
Ask a Question
Want to reply to this thread or ask your own question?
You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Electronics Point Logo
Continue to site
Quote of the day

-