Servo Library and PID library combined together.

Hi everyone. I am currently working on a robotic arm which uses RC servo motors and connected to Arduino Uno R3. In order to provide position accuracy, i am working on implementing the PID algorithm to my design. Currently i have complete control over my robotic arm for movement purposes (each joints). The basic working principle of my servo motor is shown in the attachment (sorry for the drawing, i did it in paint).

What happens is that the user sets the position of lets say servo ‘a’ to 180 degrees by using the serial monitor (motor gets input through serial communication) and the motor a rotates 180 degrees.

Now here is where i am trying to implement the PID algorithm using the <PID_v1.h> library provided by Brett Beauregard.

What happens now in the PID section is that (the basic idea):

Setpoint = 180 //(ignore ‘a’ for motor identification)
Input = 178 //(Read by the potentiometer and mapped accordingly to the range of movement)
analogWrite(6,Output); //(servo motor attached to pin 6)

i am using <Servo.h> library and <PID_v1.h> library to obtain the desired output.

Now the question is

  1. PID output is a range of 1 to 255 based on my understanding which simply means the percentage
    for PWM therefore does it mean that i have to use analogWrite(Output) or just use for example
    myservoa.write(Output). If i use myservoa.write(Output) it will translate/understand the (Output)
    to/as degrees of movement right which causes my servo motor to go haywire?

  2. When i use analogWrite(6,Output), the process that takes place is motor moves 180 degrees
    (suppose user input value), analog pin reads and maps accordingly and shows 178 degrees. That
    means 2 degrees error. PID will take both setpoint and input and compute to rectify the error and
    end result is 180 degrees being read by the potentiometer which means i manage to rectify the
    error. But now the problem is any values tuned in the PID setting (Kp, Ki, Kd) results in accurate
    position control. Which means i am trying to make it overshoot or make it reach steady state a
    little bit later. To do so i change the values of Kp Ki Kd but there is no effect at all. Yes the whole
    idea of PID is obtaining the setpoint which i am getting but i want to show different tuning resulting
    in different behaviour.

  3. Do i need to map the Output (1-255) to 0-180 as shown in the lines below.

x = map(Output, 0, 255, 0, 180);
myservoa.write(x);

The “modified” code for controlling servo motor a is shown below (code was designed by zoomkat)

if(readString.indexOf('a') >0) {myservoa.write(n);
          
          while(outputValuea!=n)
          {
          
          int sensorValuea = analogRead(A0);     // readings from pot
            outputValuea = map(sensorValuea, 0, 1023, 0, 270);       //full range of pot is 270 degrees
              Serial.println(outputValuea);
              Input = outputValuea;                   //input is the pot reading mapped respectively
              Setpoint = n;                               //set point is the user input 
              myPID.SetMode(AUTOMATIC);
              myPID.Compute();
              analogWrite(6,Output);  
            delay(1000);
              int newsensorValuea = analogRead(A0);                                  //For confirmation
              newoutputValuea = map(newsensorValuea, 0, 1023, 0, 270);      //For confirmation
              Serial.println(newoutputValuea);}}                                         //For confirmation

The changes in PID tuning does not affect the outcome of the position control at all although it does rectifies the error.

If my questions are not clear let me know as i will try to simplify it and ask again.

Thank you in advance.

which causes my servo motor to go haywire?

Your picture does not show your servo power ground and the arduino ground connected together, which is required.

I'm not sure why you need to use PID. Are these continuously rotating servos (AKA ex-servos)?

Hi guys. Thank you for replying.

Sorry zoomkat, the picture i have attached had a mistake in it. In real time hardware, i have connected together the ground of the servo motor and the ground pin on the arduino.

wildbill, the servo motors that i am using are not continuously rotating servos. They are having a full range of movement of only from 0 degrees to 180 degrees. The PID used here is to rectify even the tiny bit of error. I am actually trying to achieve very high accuracy using cheap RC servo motor.

  1. Do i need to map the Output (1-255) to 0-180 as shown in the lines below.

No, just use the PID library function SetOutputLimits(). Then use servo.write with the PID output. You might want to use writeMicroseconds instead, although you should probably test your servos to see if they can even get down to 1 degree precision before you do so.

In my testing a typical hobby servo is capable of maybe .5 deg. resolution. Typical hobby servos have a built in dead band to limit hunting behavior which seems to be ~5us. Below is some servo test code you can use to move the servo in 1us increments to observe its behavior.

// zoomkat 3-28-14 serial servo incremental test code
// using serial monitor type a character (s to increase or a 
// to decrease) and enter to change servo position 
// (two hands required, one for letter entry and one for enter key)
// use strings like 90x or 1500x for new servo position 
// for IDE 1.0.5 and later
// Powering a servo from the arduino usually *DOES NOT WORK*.

#include<Servo.h>
String readString;
Servo myservo;
int pos=1500; //~neutral value for continous rotation servo
//int pos=90;

void setup()
{
  myservo.attach(7, 400, 2600); //servo control pin, and range if desired
  Serial.begin(9600);
  Serial.println("serial servo incremental test code");
  Serial.println("type a character (s to increase or a to decrease)");
  Serial.println("and enter to change servo position");
  Serial.println("use strings like 90x or 1500x for new servo position");
  Serial.println();
}

void loop()
{
  while (Serial.available()) {
    char c = Serial.read();  //gets one byte from serial buffer
    readString += c; //makes the string readString
    delay(2);  //slow looping to allow buffer to fill with next character
  }
  if (readString.length() >0) {
    if(readString.indexOf('x') >0) { 
      pos = readString.toInt();
    }

    if(readString =="a"){
      (pos=pos-1); //use larger numbers for larger increments
      if(pos<0) (pos=0); //prevent negative number
    }
    if (readString =="s"){
      (pos=pos+1);
    }

    if(pos >= 400) //determine servo write method
    {
      Serial.println(pos);
      myservo.writeMicroseconds(pos);
    }
    else
    {   
      Serial.println(pos);
      myservo.write(pos); 
    }
  }
  readString=""; //empty for next input
}