Pages: [1]   Go Down
Author Topic: Servo Speed Control  (Read 6349 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 1
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey there,
im working on a Pan/Tilt Camerahead powered by an Arduino Duemilanove.
Im new to programming in C so please dont kill me for my question smiley-grin  ...i´m kind a stuck in a program-problem...  I already read the The Arduino.cc reference, playground but couldn´t find anything for my problem.

My plan is to Control the Speed of a Servo with a Potentiometer.
The pot. is divided into 5 sections in my current program.

Fast Up
Up
Nothing
Down
Fast Down

these movements should be controlled by setting the servoposition to +-2 for fast moving, +-1 for normal speed or +-0 for middle position of the pot, refering to the position the servo was at before...
Ya know what I mean?
So  heres what I dare to call "code" for my thoughts...:

Code:
#include <Servo.h>
#include <Potentiometer.h>

Servo Servo1;  
Potentiometer AccPot1 = Potentiometer(0);

int posServo1 = 90;    // variable to store the servo position
int AccPotPin1 = 0;
int AccPot1Val;
 
void setup()
{
 Servo1.attach(9);
}
 
 
void loop()
{
  
  AccPot1Val = analogRead(0);
  AccPot1Val = map(AccPot1Val, 0, 1023, 0, 4);
  
  if(AccPot1Val == 0);
  {                                                            
    Servo1.read();
    Servo1.write(posServo1 += 2);                                  
    delay(150);                                                
  }
  
  if(AccPot1Val == 1);
  {                                                            
    Servo1.read();
    Servo1.write(posServo1 += 1);                                  
    delay(150);                                                
  }
  
  if(AccPot1Val == 2);
  {                                                            
    Servo1.read();
    Servo1.write(posServo1 += 0);                                  
    delay(150);                                                
  }
  
  if(AccPot1Val == 3);
  {                                                            
    Servo1.read();
    Servo1.write(posServo1 -= 1);                                  
    delay(150);                                                
  }
  
  if(AccPot1Val == 4);
  {                                                            
    Servo1.read();
    Servo1.write(posServo1 -= 2);                                  
    delay(150);                                                
  }
  
}

i set the delay to 150 to check whats going on, because when i run this with shorter delay the servo just makes tiny oscillating movements with no direction and without influence of the poti-setting... smiley-sad

Is this a simple programmig fail or an epic thinking fail?
Please Help...
thanks in advance
non-existence
Logged

Buenos Aires
Offline Offline
Full Member
***
Karma: 0
Posts: 214
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1st off...does it work as it is now WITH the 150ms delay?

Apart from that you could write your code somewhat shorter:

Code:
void loop()
{
  AccPot1Val = analogRead(0);
  AccPot1Val = map(AccPot1Val, 0, 1023, 0, 4);

  if(AccPot1Val == 0) Servo1.write(posServo1 += 2);
  else if(AccPot1Val == 1) Servo1.write(posServo1 += 1);
  //else if(AccPot1Val == 2) Servo1.write(posServo1 += 0);
  else if(AccPot1Val == 3) Servo1.write(posServo1 -= 1);
  else if(AccPot1Val == 4) Servo1.write(posServo1 -= 2);

  delay(150);
}

Your Servo1.read() call does nothing since the function is meant to return a value.

For instance:

whereIsMyServo = Servo1.read();

...would store the returned value in a variable called whereIsMyServo. In your case it returns a value to nowhere. Besides it's not necesary since the value it would return would be the same as that of your variable posServo1.

The delay(150) is called in all cases so why not put it in the end and avoid having it 5 times.

And actually Servo1.write(posServo1 += 0) doesn't do anything either cause you're adding 0 the the variable posServo1, and setting the servo position to where it allread is.

Not sure about the strange servo behaviour BUT:

- You should make sure the posServo1 NEVER exceeds it's min. value 0 or it's max. value 180!!!!

- Have you made sure the analogRead(0) returns the values you expect?

- It could be because the servo doesn't like that you call Servo1.write() while it's still moving and trying to reach the last position you gave it, but of this I'm not sure...

I'm afraid that's all the help I can offer...
« Last Edit: September 29, 2009, 03:29:33 am by Aniss » Logged

0
Offline Offline
Newbie
*
Karma: 1
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Aniss1001!
I played around with the code a little and used the if...else if structure you posted and now it seems like its getting alive...smiley

The Code is now:
Code:
#include <SoftwareServo.h>

SoftwareServo Servo1;
SoftwareServo Servo2;

int potpin1 = 0;
int potpin2 = 1;
int val2;
int val1;
int poservo2;
int poservo1;
int servopin1 = 8;
int servopin2 = 9;

void setup()
{
  Servo1.attach(9);

  Servo2.attach(8);
}

void loop()
{
  val1 = analogRead(potpin1);
  val1 = map(val1, 0, 1023, 0, 6);

  val2 = analogRead(potpin2);
  val2 = map(val2, 0, 1023, 0, 6);
    
  
  
  SoftwareServo::refresh();  


//Speed Steps for Servo1


  if(val1 == 0)
 {
   poservo1 = Servo1.read();
   Servo1.write(poservo1 += 3);
   SoftwareServo::refresh();
 }
  
  else if(val1 == 1)
 {
   poservo1 = Servo1.read();
   Servo1.write(poservo1 += 2);
   SoftwareServo::refresh();
  
 }
  
  else if(val1 == 2)
 {
   poservo1 = Servo1.read();
   Servo1.write(poservo1 += 1);
   SoftwareServo::refresh();
 }
 
  else if(val1 == 3)
  {
    poservo1 = Servo1.read();
    Servo1.write(poservo1 -= 0);
    SoftwareServo::refresh();
   }
  
  else if(val1 == 4)
 {
   poservo1 = Servo1.read();  
   Servo1.write(poservo1 -= 1);
   SoftwareServo::refresh();
 }

   else if(val1 == 5)
 {
   poservo1 = Servo1.read();  
   Servo1.write(poservo1 -= 2);
   SoftwareServo::refresh();
 }
 
   else if(val1 == 6)
 {
   poservo1 = Servo1.read();  
   Servo1.write(poservo1 -= 3);
   SoftwareServo::refresh();
 }
 
 
 //Speed Steps for Servo2
 
   if(val2 == 0)
 {
   poservo2 = Servo2.read();
   Servo2.write(poservo2 += 3);
   SoftwareServo::refresh();
 }
  
  else if(val2 == 1)
 {
   poservo2 = Servo2.read();
   Servo2.write(poservo2 += 2);
   SoftwareServo::refresh();
  
 }
  
  else if(val2 == 2)
 {
   poservo2 = Servo2.read();
   Servo2.write(poservo2 += 1);
   SoftwareServo::refresh();
 }
 
  else if(val2 == 3)
  {
    poservo2 = Servo2.read();
    Servo2.write(poservo2 -= 0);
    SoftwareServo::refresh();
   }
  
  else if(val2 == 4)
 {
   poservo2 = Servo2.read();  
   Servo2.write(poservo2 -= 1);
   SoftwareServo::refresh();
 }

   else if(val2 == 5)
 {
   poservo2 = Servo2.read();  
   Servo2.write(poservo2 -= 2);
   SoftwareServo::refresh();
 }
 
   else if(val2 == 6)
 {
   poservo2 = Servo2.read();  
   Servo2.write(poservo2 -= 3);
   SoftwareServo::refresh();
 }
 
 
  delay(5);
}  


I increased the steps to 7 and modified it for two servos. (Each Servo is controlled by its own pot.)

If I run this program now it works for a normal servo.
But in my Project I will transmit the power of the servo with a tooth belt and use different tooth amounts to slow down the motion. As a consequence of this i need a continuous rotation servo because the servo will have to make more turns to get the belt far enough.
Now if I connect a Servo with removed Potentiometer (continuous Motion) the following happens:
The servo spins into the direction that is given by the setting of the SpeedControlPot. but with constant speed. ?
Im aware that because of the missing pot the servo cant get any position details, this is probably why my idea of "pos = pos+1" thing doesnt work any more and control the speed... (By the way, the hacked servo always spins at full speed)
So how can I do a speed control on a continuous rotation servo?

A workaround that could be used would be attaching the position servo of the potentiometer to the slower secondary axis that is driven by the servo and so make the servo believe that this position is his one, not the direct servo output. Aside from the mechanical difficulties this only allows a 180° angle... :-[
So what now?

Thanks!
« Last Edit: September 29, 2009, 12:00:35 pm by non-existence » Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


 Once you mechanically disconnect the pot to convert the servo for continuous rotation, you no longer control the position  because the servo has no information about where it is. You only speed have control over the speed where approximately 90 degrees is stopped and as the angle increases the servo speeds up in one direction, as it decreases it speed up in the other direction.

Your idea about connecting the pot to the remote wheel is a good one. But if the remote wheel needs to turn more t360 degrees then you may be able to rig up some gear reduction so that the pot turns 180 degrees when your wheel rotates 360 degrees
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 309
Posts: 26520
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There was a thread recently about using continuous rotation modded servo.
I think if you want to stop the servo, you can use the "detach" method, otherwise, you need to experiment with "angles" around 90 degrees to find the point where the servo stops.
Also, consider using the "time" (pulse width) approach to specify servo position, rather than "degrees".
Logged

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

0
Offline Offline
Newbie
*
Karma: 1
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I just messed arround with the servo insides a bit and found out that if you set the position to 90° and take the potentiometer that originally detectect the position inside of it you can actually also control the speed of the movement.
If you turn the poti to what is expected to be the 90° angle (where the resistance is half as big as at the max. position) the motor stops.
Ive you turn the pot. slightly forwards/backwards the motor begins to turn into both directions ( depending on the pot. direction).
And it also accelerates the more you turn it away from the stop postion. (So this is probably a thing caused by the servo-intern-circuit)
I measuered the values and found out that it is a 4,6kOhm Pot. The stop point for 90 degrees is (logicaly half of the max. resistance) at 2,3kOhm. In a range that is about 1,3kOhm large, around the stop value you are in speed controlled mode.

So i will try to  make a little circiout that allows me to control that 1,3KOhm range. And use it thate way to have continuous rotation and speed control as a workaround...

Or what other methods could work?

If i´d set the resistance value at the ports for the poti on the servo-board to a fix value that stands for 90° and add (according to a value inputed by a potentiometer [ to set the speed]) let´s say one degree to the servoposition, write that to the servo and reset the postion value back to 90... what happens?

Does the arduino say goto position 91 and the servo is like yeah okay, and turns the motor on forever, cause the resistors tell him he is still at pos90? how could this be solved?

Or does the arduino tell the servo goto 91, the motor runs and when the value on the arduino is set back to 90 the motor stops?

Please Help smiley-sad

Thanks! smiley
« Last Edit: September 29, 2009, 07:11:04 pm by non-existence » Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Does the arduino say goto position 91 and the servo is like yeah okay, and turns the motor on forever, cause the resistors tell him he is still at pos90?
That's pretty much it.

In a normal (non continuous rotation) servo, the electronics in the servo compares the position command pulses to the actual position as measured on the pot. It will turn the motor in the direction needed to move the pot to minimize the error (difference between commanded and actual position).  The power applied to drive the motor is increased if the error is large and decreased when the error is small.

In a continuous rotation servo, the pot is physically disconnected from the shaft. The servo electronics responds to an error value by powering the motor as if it could move the pot into the commanded position. The pot never moves but the result is that the motor direction and speed is controlled by the command pulses.
Logged

Buenos Aires
Offline Offline
Full Member
***
Karma: 0
Posts: 214
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Just to clear this up: The code I posted before did EXACTLY the same as your code. I just cleaned it up. Having a lot of unnecesary code is one way to invite errors.

Your second example also has a lot of unnecesary code...

The poservo1 = Servo1.read() is unnecesary cause the value of poservo1 is ALLREADY the same as Servo1.read() in all cases, as I explained before. Besides the SoftwareServo::refresh() could be placed in the end like delay(5) thus you'd only have to write it once. Your code would then be:

Code:
if(val1 == 0) Servo1.write(poservo1 += 3);
else if(val1 == 1) Servo1.write(poservo1 += 2);
.
.
.
.

SoftwareServo::refresh();
delay(5);

...Just a tip!


And the idea of a servo modified for continuous rotation is exactly that you no longer control the position but instead it's speed. It allways "thinks" it's position is 90 degrees, so if you tell it to go to 90 it wont move. Tell it to go to 91 and it will move SLOWLY in one direction. 89 and it will move slowly in the other direction. 180 or 0 and it will move FAST in their respective directions...

Isn't this what you want?

Another thing: It seems that your code keeps adding/subtracting to the poservo1/poservo2 variables INDEFINATELY when val1/val2 is less/more than 3, so eventually you'll surpass it's valid min/max values 0/180.

I would try a simpler code like this:

Code:
void loop() {
      val1 = analogRead(potpin1);
      poservo1 = map(val1, 0, 1023, 0, 180);
      Servo1.write(poservo1);
      SoftwareServo::refresh();
      delay(5);
}

But then again I'm not sure exactly what you wanna do?
« Last Edit: September 30, 2009, 01:54:37 am by Aniss » Logged

0
Offline Offline
Newbie
*
Karma: 1
Posts: 7
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Heureka! (As once an old greek said)
So it works now. Thanks for helping mem, Aniss1001 and AWOL.

I took the pot. that was originaly mounted inside the servo for positioning outside and ran a program that set the servo to a 90° angle. This way i could easily find the value where the servo stopped with the ex-servo-potentiometer ( half the resistance of the full pot. value ). So i set it to that value and sent the arduino servo.write() commands of 91°, 92°,... and so on. That way i found out that there are (at least concerning the servo i used) 13° around 90° where the servo´s speed is throttled.
I added a Potentiometer on an Analog input and divided its value to 30 steps. Two of these (in the middle position of the pot., two to make it a broader stop position) are used to stop the servo, by sending a 90° request. The other 14 steps on each sides send position signals to the servo which increase the servos speed when the pot.´s offset is increased, refering to the middle position. This way you can control speed and direction directly with one potentiometer.

This is my code:
Code:
// Controlling a Continuous Rotation Modded Servo´s Speed and Direction with a Potentiometer
// by non-existence

#include <Servo.h>
 
Servo myservo1;  

int potpin1 = 0;  // defines pin used for the potentiometer
int pot1val;    

void setup()
{
  myservo1.attach(6);  // defines pin used for the servo
}

void loop()
{
  pot1val = analogRead(potpin1);
  pot1val = map(pot1val, 0, 1023, 0, 29);

//Speed Steps for myservo1


  if(pot1val == 0)
 {
    myservo1.write(76);
 }
  else if(pot1val == 1)
 {
    myservo1.write(77);
 }
  else if(pot1val == 2)
 {
    myservo1.write(78);
 }
  else if(pot1val == 3)
 {
    myservo1.write(79);
 }
  else if(pot1val == 4)
 {
    myservo1.write(80);
 }
  else if(pot1val == 5)
 {
    myservo1.write(81);
 }
 else if(pot1val == 6)
 {
    myservo1.write(82);
 }
  else if(pot1val == 7)
 {
    myservo1.write(83);
 }  
  else if(pot1val == 8)
 {
    myservo1.write(84);
 }  
  else if(pot1val == 9)
 {
    myservo1.write(85);
 }  
  else if(pot1val == 10)
 {
    myservo1.write(86);
 }  
  else if(pot1val == 11)
 {
    myservo1.write(87);
 }  
  else if(pot1val == 12)
 {
    myservo1.write(88);
 }  
  else if(pot1val == 13)
 {
    myservo1.write(89);
 }  
  else if(pot1val == 14)
 {
    myservo1.write(90);
 }
  else if(pot1val == 15)
 {
    myservo1.write(90);
 }
  else if(pot1val == 16)
 {
    myservo1.write(91);
 }
  else if(pot1val == 17)
 {
    myservo1.write(92);
 }
  else if(pot1val == 18)
 {
    myservo1.write(93);
 }
  else if(pot1val == 19)
 {
    myservo1.write(94);
 }
 else if(pot1val == 20)
 {
    myservo1.write(95);
 }
  else if(pot1val == 21)
 {
    myservo1.write(96);
 }  
  else if(pot1val == 22)
 {
    myservo1.write(97);
 }  
  else if(pot1val == 23)
 {
    myservo1.write(98);
 }  
  else if(pot1val == 24)
 {
    myservo1.write(99);
 }  
  else if(pot1val == 25)
 {
    myservo1.write(100);
 }  
  else if(pot1val == 26)
 {
    myservo1.write(101);
 }  
  else if(pot1val == 27)
 {
    myservo1.write(102);
 }  
  else if(pot1val == 28)
 {
    myservo1.write(103);
 }
  else if(pot1val == 29)
 {
    myservo1.write(104);
 }
 
}

Its a bit stretched but so its better readable for human eyes smiley-wink

I'm planning to use this for a remote camera head on a camera crane.
Logged

London
Offline Offline
Tesla Member
***
Karma: 10
Posts: 6255
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You can simplify the code  if you map the pot input directly to the servo output:
Code:
#include <Servo.h>

Servo myservo1;

int potpin1 = 0;  // defines pin used for the potentiometer
int pot1val;
int servoVal

void setup()
{
  myservo1.attach(6);  // defines pin used for the servo
}


void loop()
{
  pot1val = analogRead(potpin1);
  servoVal = map(pot1val, 0, 1023, 76, 104);
  myservo1.write(servoVal);
  delay(20);  
}

The delay in loop prevents the code from constantly rewriting values to the servo. The servo library disables interrupts when a new value is written and there is no point in doing this more often then every 20ms or so, the value is only used every 20 ms.
Logged

Pages: [1]   Go Up
Jump to: