PID motor behaviour

Hi,

Im trying to control the position of a DC motor (EMG30) with the rear shaft mounted encoder. The board used is an arduino Uno with a microbot dc motor shield driving the motor. Initially the code I was using to read the quadrature encoder used if statements however I have since adjusted this to use interrupts. When using if statements the motor would not intially turn unless by hand annd then get stuck at full speed where the encoder would fluctuate between 1 or 2 bits (Assuming this was missed digital reads). However once the motor had reached the setpoint, by hand turning or of its own accord it was responding as one would expect to disturbances.

When using interrupts however this same fluctating behaviour appears when being hand turned regardless of where the setpoint is. Now it cannot reach the setpoint.

To conclude the main issues are:

Encoder wont increment up to setpoint

Motor will not self start

#include <motorshield.h>
#include <PID_v1.h>
#define pinA    2
#define pinB    3

motorshield MotorShield;
double kp = 2 , ki =0 , kd =0 ;             // modify for optimal performance
double input = 0, output = 0, setpoint;
long temp;
int encoderPos =0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, REVERSE);  // if motor will only run at full speed try 'REVERSE' instead of 'DIRECT'


void setup() {
  MotorShield.initialize(); // initialize the Motor Shield hardware  
  TCCR1B = TCCR1B & 0b11111000 | 1;                   // set 31KHz PWM to prevent motor noise
  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(1);
  myPID.SetOutputLimits(-255, 255);
  Serial.begin (115200);                              // for debugging
  pinMode(pinA, INPUT_PULLUP); 
  pinMode(pinB, INPUT_PULLUP); 
  attachInterrupt(digitalPinToInterrupt(2),pa1,CHANGE);
  attachInterrupt(digitalPinToInterrupt(3),pa2,CHANGE);
  encoderPos =0;
}

void loop() {
  setpoint=200;                       // modify to fit motor and encoder characteristics, potmeter connected to A0
  input = encoderPos ;                                // data from encoder 
  Serial.println(input);
  myPID.Compute();                                    // calculate new output
  pwmOut(output);                                     // drive L298N H-Bridge module
}

void pwmOut(int out) {                                // to H-Bridge board
  if (out > 0) {
 MotorShield.setMotorDir(1, 1);
 MotorShield.setMotorSpeed(1, out);
  }
  else {
   MotorShield.setMotorDir(1,0);
   MotorShield.setMotorSpeed(1, abs(out));
  }
}

void pa1()
{
if(digitalRead(pinA) == digitalRead(pinB))
{
  encoderPos++;
 }
else
{
  encoderPos--;
}
}

void pa2()
{
if(digitalRead(pinA)==digitalRead(pinB))
{
  encoderPos--;
 }
else
{
  encoderPos++;
}  
}

To read a value from a multi-byte variable that is updated by an ISR you need to pause interrupts briefly while taking a copy of the value - otherwise the value may change while it is being read. Like this

noInterrupts();
  input = encoderPos ;
interrupts();

I believe you are not using an absolute position encoder and in that case the system has no means to know where it is in any absolute sense. When a complete revolution has taken place the motor will be at the same place but the encoderPos value will be completely different,

How is the system supposed to know that the motor needs to start moving and in what direction?

I suspect that you need some means to move the motor to a ZERO position when the Arduino starts and then use that as a reference point. But the solution may be more complex than that.

...R

That is why the 0 was specified, however once the PID is working an endstop switch will be added

jaq252:
That is why the 0 was specified,

What "0" ?

...R

for the initial encoder position

jaq252:
for the initial encoder position

If you really want help you need to be a bit more informative. I have no idea what you are talking about or how it relates to my Reply #1

...R

Do not double post.

You haven't declared ISR variables as volatile...