Help with continuous rotation servos

Hi everyone,

I just completed my first obstacle avoidance robot build and I'm having a bit of an issue with one of my drive servos turning slower than the other causing my robot to track in a circle. I am wondering if there is something that I can do (aside from buying another servo and hoping that it matches the speed of one of the other two) to change the speed that the slower one rotates at so that my robot tracks straight.

I'm currently using two Parallax continuous rotation servos; any advice you can provide is both welcome and appreciated. Thanks in advance!

Here is the full code for my build, just in case.

#include <Servo.h>

#define trigPin 2   // Trigger pin on ultrasonic sensor
#define echoPin 4   // Echo pin on ultrasonic sensor
#define leftPin 8  // Right drive-servo
#define pingPin 10  // Ultrasonic sensor servo
#define rightPin 12  // Left drive-servo

long duration, distance, leftDistance, rightDistance;

Servo rightServo;
Servo pingServo;
Servo leftServo;

void setup()
{
  Serial.begin(9600);
   
  rightServo.attach(rightPin);
  pingServo.attach(pingPin);
  leftServo.attach(leftPin);
  
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(rightPin, OUTPUT);
  pinMode(pingPin, OUTPUT);
  pinMode(leftPin, OUTPUT);
  
  pingServo.write(90);
}

void moveForward()
{
  leftServo.write(180);
  rightServo.write(0);
}

void moveBackward()
{
  leftServo.write(0);
  rightServo.write(180);
}

void turnLeft()
{
  leftServo.write(0);
  rightServo.write(0);
}

void turnRight()
{
  leftServo.write(180);
  leftServo.write(180);
}

void halt()
{
  leftServo.write(90);
  rightServo.write(90);
}

void ping()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2.0) / 29.1;
  
  Serial.print(distance);
  Serial.println(" cm.");
  
  delay(500);
}

void scanArea()
{
  pingServo.write(45);
  delay(500);
  ping(); 
  leftDistance = distance;
  delay(50);
  pingServo.write(135);
  delay(500);
  ping();
  rightDistance = distance;
  delay(50);
  pingServo.write(90);
  delay(50);
}

void loop()
{
  ping();
  if (distance > 20.0)
  {
    moveForward();
  }
  else if(distance <= 20.0)
  {
    halt();
    delay(50);
    scanArea();
    delay(50);
    if (leftDistance < rightDistance)
    {
      moveBackward();
      delay(1000);
      turnRight();
      delay(1500);
    }
    else if (leftDistance >= rightDistance)
    {
      moveBackward();
      delay(1000);
      turnLeft();
      delay(1500);
    }
  }
}

Suppose you send servo.write(180) to both motors and one goes a little too fast. All you need to do is send servo.write(180) to the slow servo and servo.write(aLittleBitLessThan180) to the other motor.

If you read about servo.writeMicroseconds() and use that you would have finer control of the motor speed.

...R

I think you will find that servos modified for continuous rotation (and thus becoming non-servos, but rather just bidirectional geared variable speed motors) are very non-linear in their response from +/- stop value. Most of the speed change will happen closer to the stop value with less change towards the control end point values. They can be made to work but you will have to spend a lot of time compensating for the specific response value needed for each servo to spin at the same speed over their speed range.

Good luck
Lefty

Thanks for the advice, I will experiment with that information tonight and see what I can come up with for a solution!

Some servo test code for use with the pc keyboard to increment the values increase/decrease sent to a servo for testing.

// 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 
// 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
}

Thank you all for the help. I was able to get it tracking straight with a value of 98 for the left servo or 1555 using servo.writeMicroseconds(). Now it's time to add on / modify / create more stuff!