Notifications
Clear all

Need help with servo calibration

4 Posts
2 Users
0 Likes
5,760 Views
Robo Pi
(@robo-pi)
Robotics Engineer
Joined: 5 years ago
Posts: 1669
Topic starter  

I'm working with a Me Arm and using the PCA9685 servo controller board.  I'm having difficulty understanding how to calibrate the servo motors for the Adafruit Library code.

Important preliminary Information:

The servo motors have been correctly physically calibrated during construction of the arm. The arm works perfectly when using the standard servo library and writing angles to the servo motors.   So that much has been done.  But now that I'm using the PCA9685 board and the Adafruit Library code the arm is no longer calibrated correctly.   So it appears that I need to do some software tweaking.

Here's the code I'm currently running:

//#include  

#include  // Include Wire Library for I2C Communications
#include  // Include Adafruit PWM Library for using the PAC9685 board
#define MIN_PULSE_WIDTH       650 // Variables for the PCA9685 board
#define MAX_PULSE_WIDTH       2350 // Variables for the PCA9685 board
#define FREQUENCY             50 // Variables for the PCA9685 board

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40); // The PCA9685 board I2C address begins at 0x40

String str_angles = ""; // Used for Serial input from monitor
int base_angle = 90; // Intialize base angle to 90 degrees
int u_arm_angle = 180; // Intialize upper arm angle to 90 
int l_arm_angle = 90; // Intialize lower arm angle to 90 

int base = 0; // used to select which servo to move
int u_arm = 1; // used to select which servo to move
int l_arm = 2; // used to select which servo to move

void setup() 
{ 
  Serial.begin(9600); // Use Serial monitor 
  pwm.begin(); // start Adafruit object
  pwm.setPWMFreq(FREQUENCY); // set frequency
} 
 
void loop() 
{ 
        Serial.println("Enter Angles as 3-digit inputs eg.  000,000,000  Base, U_arm, L_arm"); 
        while (Serial.available() == 0){} // wait for input
         
        // Take in all three angles a single string.
          str_angles = Serial.readString(); 
          //  Add input rrror checking routine here ~~~ To be done later ~~~

        // convert angels to integers
            base_angle = str_angles.substring(0,4).toInt(); 
            u_arm_angle = str_angles.substring(4,8).toInt(); 
            l_arm_angle = str_angles.substring(8).toInt(); 

        // Write the angles to the monitor screen and the to the servos
            Serial.print("Setting Base Angle = ");
            Serial.println(base_angle,DEC);   
            move_Arm(base_angle,base);      
            Serial.print("Setting Upper Arm Angle = ");
            Serial.println(u_arm_angle,DEC);
            move_Arm(u_arm_angle,u_arm);        
            Serial.print("Setting Lower Arm Angle = ");
            Serial.println(l_arm_angle,DEC);
            move_Arm(l_arm_angle,l_arm);        
}

void move_Arm(int angle, int motorOut)
{
  int pulse_width, pulse_wide;
  // Trying to map the angle correctly here
  pulse_wide = map(angle,0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
  pulse_width = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096);
  // Display values of pulse_wide and pulse width for debugging
    Serial.print("Pulse Wide = ");
    Serial.println(pulse_wide,DEC);
    Serial.print("Pulse Width =");
    Serial.println(pulse_width,DEC);
  // send the control value out to the correct servo motor. 
  pwm.setPWM(motorOut, 0, pulse_width);
}
 

 I modified this from Bill's code example in his video on the PCA9685 board.   I imagine my problem is in the move_Arm() method.   In the mapping of the angle to pulse_wide

pulse_wide = map(angle,0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);

I'm just not understanding what numbers I need to get for the final pulse_width?

Here's what I get when I run the program for angles 0 through 180.

Entered Angles as 3-digit inputs eg. 000,000,000 Base, U_arm, L_arm
Setting Base Angle = 0
Pulse Wide = 650
Pulse Width = 133
Setting Upper Arm Angle = 0
Pulse Wide = 650
Pulse Width = 133
Setting Lower Arm Angle = 0
Pulse Wide = 650
Pulse Width = 133

Entered Angles as 3-digit inputs eg. 090,090,090 Base, U_arm, L_arm

Setting Base Angle = 90
Pulse Wide = 1500
Pulse Width = 307
Setting Upper Arm Angle = 90
Pulse Wide = 1500
Pulse Width = 307
Setting Lower Arm Angle = 90
Pulse Wide = 1500
Pulse Width = 307

Entered Angles as 3-digit inputs eg. 180,180,180 Base, U_arm, L_arm
Setting Base Angle = 180
Pulse Wide = 2350
Pulse Width = 481
Setting Upper Arm Angle = 180
Pulse Wide = 2350
Pulse Width = 481
Setting Lower Arm Angle = 180
Pulse Wide = 2350
Pulse Width = 481

I'm getting pulse widths from 133 to 481.   But I'm not understanding what numbers  for a final pulse width I should be shooting for?    Or how to properly map my 0 to 180 angles to obtain a proper value for pulse_wide.

Anyone know how to properly map to a servo when using the Adafruit library and a PCA9685 board.

Thanks.

DroneBot Workshop Robotics Engineer
James


   
Quote
Robo Pi
(@robo-pi)
Robotics Engineer
Joined: 5 years ago
Posts: 1669
Topic starter  

Update:

I'm making some progress on my own.  I found the Adafruit PDF file for the PCA9685 and it has some explanations for how to calibrate servos by trial and error method of just trying min and max values for the pulse width.    So I'll write up a small calibration program where I can just enter in pulse width values directly on the serial monitor until I find values that put the servos in the correct positions.   Then I'll have the numbers I'm looking for. ? 

In the meantime I'm going to bed at 3:00 AM.   This is too late for a 70 year old robotics nerd to be staying up trying to calibrate servo motors.  .

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote
(@twobits)
Member
Joined: 5 years ago
Posts: 113
 

Yes, I think calibration that is the correct approach. Servos in the price range we use tend to need tweaking. Then you can add fudge factors to your code to compensate for irregularities in your hardware.

Not unlike a Harbor Freight tool... If you know what you are getting and willing the work around the quirks you can get some bargains.


   
ReplyQuote
Robo Pi
(@robo-pi)
Robotics Engineer
Joined: 5 years ago
Posts: 1669
Topic starter  

I just posted this in my main Me Arm thread.  But since I also started this thread asking about how to calibrate these servos I figured I better post what I've done here too.   I think this will solve my problem.

Progress Report:

I wrote up a small program to find all my limiting pulse width numbers. The program is extremely crude, but I'll post it anyway just in case someone might find it useful in some way. It allows the control of 4 servos individually based on changing the pulse width in steps of 10 increments or decrements at a time. That was enough for me to find the limits of my Me Arm.

Unfortunately because of the mechanical design of the Me Arm things are not as simple as they could be. This has to do with the way the upper and lower arms of the Me Arm interact with each other. How they behave can depend on what position the other half of the arm is already in. So when programming the Me Arm we need to take into consideration what the optimal positions are for the upper and lower parts of the arm. The claw and the base rotation are independent and so they will be easier to deal with.

First the Results: (note that these results may be unique to my Me Arm)

For the Base servo I got the following:

The numbers are pulse widths to be used in the Adafruit command.

Base:
Left maximum travel = 140
Right maximum travel = 450
Centering the arm = 290

Upper Arm:
Fully Retracted = 230
Fully extended = 470
Midpoint = 350 * can depend on the position of the lower arm

Lower Arm:
Maxium up = 460
Maxium down = 190
Midpoint = 290 * can depend on the position of the upper arm.

Claw:
Fully open = 140
Fully closed = 220

So as you can see each servo will need to have its own mapping in the Arduino program.

Hopefully this will all work out and we can move forward with trying to program the arm to do some interesting tasks.

Here's the code I used to discover the pulse width limits of the Me Arm. It's a crude program that could use so prettying up. I just wanted to get these numbers and move forward.

/* Me_Arm_Servo_Calibration_V1
 * by Robo Pi
 */

#include  // Include Wire Library for I2C Communications
#include  // Include Adafruit PWM Library for using the PAC9685 board
#define FREQUENCY             50 // Variables for the PCA9685 board

// The PCA9685 board I2C address is 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40); 

String str_input = ""; // Used for Serial input from monitor
int int_Char; // Used for Serial input
int base_angle; //  Base angle
int u_arm_angle; //  Upper arm angle 
int l_arm_angle; // Lower arm angle
int claw_angle; // Claw angle

// Servo PCA9685 board pins
int servo_pin;
int base = 0; 
int u_arm = 1; 
int l_arm = 2; 
int claw = 3;

void setup() 
{ 
  Serial.begin(9600); // Use Serial monitor 
  pwm.begin(); // start Adafruit object
  pwm.setPWMFreq(FREQUENCY); // set frequency
} 
 
void loop() 
{ 
  Serial.println("Enter 0, 1, 2, or 3 for Base, U_arm, L_arm or Claw"); 
  while (Serial.available() == 0){} // wait for input
  str_input = Serial.readString(); 
    //  Add input rrror checking routine here ~~~ To be done later ~~~
  servo_pin = str_input.toInt();
  move_Arm(servo_pin);       
}

void move_Arm(int motorOut)
{
  int pulse_width = 300;
  while(true)
  {
      Serial.println("Enter 1 or 2 to inc or dec angle, or 3 to end");
      while (Serial.available() == 0){} // wait for input
      str_input = Serial.readString();
      int_Char = str_input.substring(0,1).toInt();
      //Serial.println(int_Char,DEC); 

    switch (int_Char)
    {
      case 1: 
        pulse_width = pulse_width - 10;
        Serial.print("Pulse Width = ");
        Serial.println(pulse_width,DEC);
        pwm.setPWM(motorOut, 0, pulse_width);
        break;
      case 2: 
        pulse_width = pulse_width + 10;
        Serial.print("Pulse Width = ");
        Serial.println(pulse_width,DEC);
        pwm.setPWM(motorOut, 0, pulse_width);
        break;
      case 3: 
        return;    
      }
    }
  }
  

  

DroneBot Workshop Robotics Engineer
James


   
ReplyQuote