user1597: My goal is to deliver very small quantities (100 microliters) of fluid to the (approximate) center of a 25mm x 75mm surface. My first idea was to repurpose an old inkjet printer for the project. I thought I'd load the fluid dispensers (probably 3ml syringes) into the unit where the ink cartridges would go and use an arduino to manipulate the linear encoder used by the printer to tell the dispenser unit where to go.
Main problem at the moment is that I'm having trouble getting the dispenser unit to go exactly where I want it to. I've tried incorporating the PID library into my code to get the head to oscillate around the desired position until it stops at the right location. However the oscillation never stops and the head always moves beyond the desired position. 
I'm trying to figure out the easiest way to get this done. Maybe that'll mean moving away from using the inkjet printer but I'd like to get some feedback here before I make any decisions. 
Code: 
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"
#include <PID_v1.h>

//
//// Interrupt information
//// Int 0 on pin 2 
//// Int 1 on pin 3 Not used
//
#define encoderI 2  // Interrupt 0 is use
#define encoderQ 4 // Only use one interrupt in this example
#define maxPos   3733 // left hand side
#define minPos   0    // right hand side 
#define maxSpeed 150
#define minSpeed 0
#define Freq     1600
#define maxWait  500 //12 millisec before stopping the motor
#define maxPositionError 10 //max error from actual desired position. It is approximately 0.032 of an inch
#define PIDsampleTime 25

//
//// Tuning parameters 
double kP=0.37; //Initial Proportional Gain 
double kI=2; //Initial Integral Gain 
double kD=0.020; //Initial Differential Gain 
double Drive = 0;

int outputSign;
//
double Setpoint, Input, Output;

PID myPID(&Input, &Output, &Setpoint,kP,kI,kD, DIRECT);

volatile int ActualHeadPosition; //current position will be modified with each interrupt
volatile int PreviousHeadPosition; //Previous position will be modified with each interrupt
volatile uint8_t Speed = maxSpeed;                      //Speed for the dispenser head
volatile unsigned long lastPositionTime;
volatile int setPoint;
volatile int Err;
volatile int lastError;
volatile int Integral;

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
//
//// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *dispenserDCMotor = AFMS.getMotor(1);
//
void setup() {
     Serial.begin(9600);           // set up Serial library at 9600 bps also this is the baud rate per second for the input?
     pinMode(encoderI, INPUT); //pin #2 is assigned as an input
     pinMode(encoderQ, INPUT); 
     attachInterrupt(0, handleEncoder, CHANGE); 
//  
     AFMS.begin(Freq);  
//   
//    // Set the speed to start, from 0 (off) to 255 (max speed)
    dispenserDCMotor->setSpeed(Speed);
    dispenserDCMotor->run(FORWARD);
//    // turn on motor
    dispenserDCMotor->run(RELEASE);
//     
  Setpoint = 1800;
  myPID.SetSampleTime(PIDsampleTime);
  myPID.SetOutputLimits(minSpeed, maxSpeed);

    //turn the PID on
    myPID.SetMode(AUTOMATIC);

     //Initialize procedure should be the first thing
    Initialize();
}

void loop() {
  Serial.print(", ActualHeadPosition: ");Serial.print(ActualHeadPosition);Serial.print(", SetPoint: ");Serial.println(setPoint);

  setPoint = 1800;
  Input = ActualHeadPosition;
  outputSign = myPID.Compute();
  Err = setPoint - ActualHeadPosition;
  Serial.print("PID Output");Serial.print(Output);Serial.print(" Error: ");Serial.println(Err);
  Speed = Output;
  if (Err > 0){
    moveLeft();
    Serial.println("moveLeft");
  }
   else if (Err < 0){
     moveRight();
     Serial.println("moveRight");
  }

};
//
void handleEncoder(){  
  lastPositionTime = millis();
   PreviousHeadPosition = ActualHeadPosition;
   if(digitalRead(encoderI) == digitalRead(encoderQ)){
     //dispenser is moving to the left
     ActualHeadPosition++;
   }
   else {
     //dispenser is moving to the right
     ActualHeadPosition--;
}

 }

void moveLeft(){
  uint8_t i;
  dispenserDCMotor->run(FORWARD);
  dispenserDCMotor->setSpeed(Speed);  
}

void moveRight(){
  uint8_t i;

  dispenserDCMotor->run(BACKWARD);
  dispenserDCMotor->setSpeed(Speed);  
}

boolean isMotorMoving(){
  if (((millis() - lastPositionTime) > maxWait))
    return false;
  else
    return true;
}

void Initialize(){
    //Move to origin
    moveRight();
    while (isMotorMoving()){
  //    Serial.println("MOVING!");
  delay(10);
    }
    ActualHeadPosition = 0;
    PreviousHeadPosition = 0;
    dispenserDCMotor->setSpeed(0);
    dispenserDCMotor->run(RELEASE);

}


user1585: The easiest option, using the code you have, is to spend a little time tuning the PID algorithm. The lines you are interested in are these
double kP=0.37; //Initial Proportional Gain 
double kI=2; //Initial Integral Gain 
double kD=0.020; //Initial Differential Gain

PID takes a setpoint and a current location, calculates error, and uses it to make a new output that should sent the location to the setpoint. It does this by adding 
P*(error)+I*(all historic error added together)+D*(recent change in error)
By changing the multipliers P, I, and D, you change how it reacts to different situations. In your system, the D value will have very little effect (at least in my experience). You should probably keep that where it is, or even just set it to 0.
The P value controls direct response to error, and should be the main component in your setup. Try increasing this to make it move faster, and mildly decrease the oscillations.
The I value controls how much all historic error affects output. It is the factor leading to all the oscillations. Lowering it will make your output settle sooner. It also assists when fighting other constant forces, or if static friction keeps the head locked in one position too close for the P term to overpower. My guess is in your isolated and low friction system, it should be very low.
Increment these values slowly (.05 at a time maybe) and experiment, you should be able to fix your oscillations and make the system behave how you want. 

1597: <p>My goal is to deliver very small quantities (100 microliters) of fluid to the (approximate) center of a 25mm x 75mm surface. My first idea was to repurpose an old inkjet printer for the project. I thought I'd load the fluid dispensers (probably 3ml syringes) into the unit where the ink cartridges would go and use an arduino to manipulate the linear encoder used by the printer to tell the dispenser unit where to go.</p>

<p>Main problem at the moment is that I'm having trouble getting the dispenser unit to go exactly where I want it to. I've tried incorporating the PID library into my code to get the head to oscillate around the desired position until it stops at the right location. However the oscillation never stops and the head always moves beyond the desired position. </p>

<p>I'm trying to figure out the easiest way to get this done. Maybe that'll mean moving away from using the inkjet printer but I'd like to get some feedback here before I make any decisions. </p>

<p>Code: </p>

<pre><code>#include &lt;Wire.h&gt;
#include &lt;Adafruit_MotorShield.h&gt;
#include "utility/Adafruit_PWMServoDriver.h"
#include &lt;PID_v1.h&gt;

//
//// Interrupt information
//// Int 0 on pin 2 
//// Int 1 on pin 3 Not used
//
#define encoderI 2  // Interrupt 0 is use
#define encoderQ 4 // Only use one interrupt in this example
#define maxPos   3733 // left hand side
#define minPos   0    // right hand side 
#define maxSpeed 150
#define minSpeed 0
#define Freq     1600
#define maxWait  500 //12 millisec before stopping the motor
#define maxPositionError 10 //max error from actual desired position. It is approximately 0.032 of an inch
#define PIDsampleTime 25

//
//// Tuning parameters 
double kP=0.37; //Initial Proportional Gain 
double kI=2; //Initial Integral Gain 
double kD=0.020; //Initial Differential Gain 
double Drive = 0;

int outputSign;
//
double Setpoint, Input, Output;

PID myPID(&amp;Input, &amp;Output, &amp;Setpoint,kP,kI,kD, DIRECT);

volatile int ActualHeadPosition; //current position will be modified with each interrupt
volatile int PreviousHeadPosition; //Previous position will be modified with each interrupt
volatile uint8_t Speed = maxSpeed;                      //Speed for the dispenser head
volatile unsigned long lastPositionTime;
volatile int setPoint;
volatile int Err;
volatile int lastError;
volatile int Integral;

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
//
//// Select which 'port' M1, M2, M3 or M4. In this case, M1
Adafruit_DCMotor *dispenserDCMotor = AFMS.getMotor(1);
//
void setup() {
     Serial.begin(9600);           // set up Serial library at 9600 bps also this is the baud rate per second for the input?
     pinMode(encoderI, INPUT); //pin #2 is assigned as an input
     pinMode(encoderQ, INPUT); 
     attachInterrupt(0, handleEncoder, CHANGE); 
//  
     AFMS.begin(Freq);  
//   
//    // Set the speed to start, from 0 (off) to 255 (max speed)
    dispenserDCMotor-&gt;setSpeed(Speed);
    dispenserDCMotor-&gt;run(FORWARD);
//    // turn on motor
    dispenserDCMotor-&gt;run(RELEASE);
//     
  Setpoint = 1800;
  myPID.SetSampleTime(PIDsampleTime);
  myPID.SetOutputLimits(minSpeed, maxSpeed);

    //turn the PID on
    myPID.SetMode(AUTOMATIC);

     //Initialize procedure should be the first thing
    Initialize();
}

void loop() {
  Serial.print(", ActualHeadPosition: ");Serial.print(ActualHeadPosition);Serial.print(", SetPoint: ");Serial.println(setPoint);

  setPoint = 1800;
  Input = ActualHeadPosition;
  outputSign = myPID.Compute();
  Err = setPoint - ActualHeadPosition;
  Serial.print("PID Output");Serial.print(Output);Serial.print(" Error: ");Serial.println(Err);
  Speed = Output;
  if (Err &gt; 0){
    moveLeft();
    Serial.println("moveLeft");
  }
   else if (Err &lt; 0){
     moveRight();
     Serial.println("moveRight");
  }

};
//
void handleEncoder(){  
  lastPositionTime = millis();
   PreviousHeadPosition = ActualHeadPosition;
   if(digitalRead(encoderI) == digitalRead(encoderQ)){
     //dispenser is moving to the left
     ActualHeadPosition++;
   }
   else {
     //dispenser is moving to the right
     ActualHeadPosition--;
}

 }

void moveLeft(){
  uint8_t i;
  dispenserDCMotor-&gt;run(FORWARD);
  dispenserDCMotor-&gt;setSpeed(Speed);  
}

void moveRight(){
  uint8_t i;

  dispenserDCMotor-&gt;run(BACKWARD);
  dispenserDCMotor-&gt;setSpeed(Speed);  
}

boolean isMotorMoving(){
  if (((millis() - lastPositionTime) &gt; maxWait))
    return false;
  else
    return true;
}

void Initialize(){
    //Move to origin
    moveRight();
    while (isMotorMoving()){
  //    Serial.println("MOVING!");
  delay(10);
    }
    ActualHeadPosition = 0;
    PreviousHeadPosition = 0;
    dispenserDCMotor-&gt;setSpeed(0);
    dispenserDCMotor-&gt;run(RELEASE);

}
</code></pre>
 1585: <p>The easiest option, using the code you have, is to spend a little time tuning the PID algorithm. The lines you are interested in are these</p>

<pre><code>double kP=0.37; //Initial Proportional Gain 
double kI=2; //Initial Integral Gain 
double kD=0.020; //Initial Differential Gain
</code></pre>

<p>PID takes a setpoint and a current location, calculates error, and uses it to make a new output that should sent the location to the setpoint. It does this by adding </p>

<p>P*(error)+I*(all historic error added together)+D*(recent change in error)</p>

<p>By changing the multipliers P, I, and D, you change how it reacts to different situations. In your system, the D value will have very little effect (at least in my experience). You should probably keep that where it is, or even just set it to 0.</p>

<p>The P value controls direct response to error, and should be the main component in your setup. Try increasing this to make it move faster, and mildly decrease the oscillations.</p>

<p>The I value controls how much all historic error affects output. It is the factor leading to all the oscillations. Lowering it will make your output settle sooner. It also assists when fighting other constant forces, or if static friction keeps the head locked in one position too close for the P term to overpower. My guess is in your isolated and low friction system, it should be very low.</p>

<p>Increment these values slowly (.05 at a time maybe) and experiment, you should be able to fix your oscillations and make the system behave how you want. </p>
