Hello,
I know this is probably the most discussed topic here, but I am sorry I have tried everything and I can't seem to get the result I need.
I have a dc motor with a quadrature encoder which gives 200 cpr. I am trying to control it in a way that if I provide like 50 pulses as set point, the motor turns about 90 degrees.
I tried implementing the PID the usual way. I tried implementing it in the ISR with using single channel output of the Encoder so that every time interrupt occurs, a new pwm value is also calculated.
I think the arduino UNO i am using is too slow to count each pulse or whatever... This is the code I wrote. (Though I have made many changes)
This code works for me. Motor encoder i connected to pin2&3 setpoint encoder is connected to pin 4&5. Max pulse freq is about 30kHz
#include <TimerOne.h>
const int outPwm = 9;//pin for speed control
const int outDir = 8;//pin for direction control
//constants for PID calculation
const unsigned long samplePeriod = 400;//time between pid calculations in us
const long kp = 12;//proportional gain
const long ki = 3;//integral gain
const long kd = 2700;//derivative gain
const byte shift = 2;//for shifting/scaling output value
const long outMax = (1024<<shift)-1;//keep max output <=1023
const byte iShift = 11;//right shift for i term
const long iMax = (1024<<(iShift+shift))-1;//keep i term contribution <=1023 and avoid windup
const int stepFrame = 1500;//Number of values for step response
//variables for PID calculation
volatile long iTerm;//summing up integral error
volatile long lastPos;//for calculation of derivative
volatile int output;//output to speedcontrol
enum {FWD, REW} dir;
//data for position calculation
const char encTable[16] ={0, 1, -1, -0, -1, 0, -0, 1, 1, -0, 0, -1, -0, -1, 1, 0};//gives -1, 0 or 1 depending on encoder movement
volatile byte encState;//remembering the encoder output and acting as index for encTable[]
volatile byte stepState;//for edge detection on step input = D10
volatile byte inp;//for reading encoder pins
volatile byte setInp;//input signals from second encoder
volatile byte setState;//remembering setpoint encoder state
volatile long setPos;//position setpoint, updated by second encoder
volatile long actPos;//actual position from encoder
volatile long error;
volatile long dInput;
volatile byte sched;//for scheduling calculations
unsigned long t;//timekeeper
void setup(){
pinMode(outPwm, OUTPUT);
pinMode(outDir, OUTPUT);
pinMode(signalPin, OUTPUT);
Timer1.initialize(samplePeriod);
Timer1.attachInterrupt(doPID);
Timer1.pwm(outPwm, 0);
}//setup()
void loop(){
while(1){
PINB |= (1<<4);//toggle signal pin
//aquire input values
inp = PIND;//read inputs 0..7
encState = ((encState<<2)|((inp>>2)&3))&15;//use encoder bits on pins 2&3 and last state to form index
actPos += encTable[encState];//update actual position on encoder movement
setState = ((setState<<2)|((inp>>4)&3))&15;//use encoder bits and on pins 4&5 last state to form index
setPos += encTable[setState];
}//while(1)
}//loop()
void doPID(){
/*Compute all the working error variables*/
error = setPos-actPos;
iTerm += (ki*error);
if(iTerm>iMax) iTerm = iMax;
else if(iTerm<-iMax) iTerm = -iMax;
dInput = (actPos-lastPos);
/*Compute PID Output*/
output = kp*error+(iTerm>>iShift)-kd*dInput;
if(output < 0){
dir = REW;
output = -output;
}else dir = FWD;
//if(output<0)
if(output > outMax) output = outMax;
Timer1.setPwmDuty(outPwm, output);
digitalWrite(outDir, dir);
lastPos = actPos;
}//doPID()