Go Down

Topic: Servo Sweep between 2 bounds (Read 175 times) previous topic - next topic

3DWarrior

I have a simple servo HS-322HD
I am having this rotate an object. What I want to do is be able to rotate at random from one place to another with a random delay, between the 2 max bounds. Basically, picture a skull rotating right a left at a neck joint. I never want it to go past the angles below as it doesn't look right to turn the head 180 deg, plus I have physical clearance issues. (still playing with the angles to see what clears correctly)

I want that to turn as smooth as possible (slow is good) so if there is a better way to create micro stepping, please let me know. I don't care very much for torque as it's just a tabletop "toy". Want it to just sit on my desk rotate left and right randomly all day.

Here is what I am mocking up in code, and it's turning passed the limits and not very well either.
90deg is my center positioning.

I am using an old Duemilanove I hade laying around for a few years.

Code: [Select]
#include <Servo.h>

Servo myNeck; 
//Neck limits
int maxRight = 70; //45
int maxLeft = 110; //135

int angleIncrement = 1;
int incrementDelay = 15;

// delay limits
int minDelay =10000; //1 min = 60000
int maxDelay =100000; //30 min = 1800000

void setup() {
  myNeck.attach(9);  // attaches the servo on pin 9 to the servo object
}

void loop() {
  int posNew = random(maxRight,maxLeft);
    for (int pos = maxLeft; pos <= maxLeft; posNew += angleIncrement) {
    myNeck.write(posNew);
    delay(incrementDelay);
  }
 
  delay(random(minDelay, maxDelay));
 
  for (int pos = maxLeft; pos >= maxRight; posNew -= angleIncrement) {
    myNeck.write(posNew);
    delay(incrementDelay);
  }
}


Thanks a bunch!

Robin2

This isn't going to work
Code: [Select]
for (int pos = maxLeft; pos <= maxLeft; posNew += angleIncrement) {
because you are not upating pos so it will never be equal to maxleft.
Just do an ordinary FOR that goes through a number of steps and increment the position within the loop
Code: [Select]
for (byte step = 0; step < numSteps; step ++) {
    newPos += angleIncrement;
    myNeck.write(posNew);
    delay(incrementDelay);
}


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

vinceherman

Slow and smooth?

Use Servo.writeMicroseconds() instead of Servo.write()
This gives you about 1000 steps in the range of movement instead of 180.
Servo.write() simply maps the degrees into a number of microseconds and calls Servo.writeMicroseconds()


The following 3 pairs are (roughly) equivalent:
Servo.writeMicroseconds(1000) and Servo.write(0)
Servo.writeMicroseconds(1500) and Servo.write(90)
Servo.writeMicroseconds(2000) and Servo.write(180)

3DWarrior

This isn't going to work
Code: [Select]
for (int pos = maxLeft; pos <= maxLeft; posNew += angleIncrement) {
because you are not upating pos so it will never be equal to maxleft.
Just do an ordinary FOR that goes through a number of steps and increment the position within the loop
Code: [Select]
for (byte step = 0; step < numSteps; step ++) {
    newPos += angleIncrement;
    myNeck.write(posNew);
    delay(incrementDelay);
}


...R

Thanks for a quick reply.

When I do a typical for loop like you have above I am always passed the limits I need to be. It's 0-180 situation unless I am not understanding this correctly.


Robin2

Thanks for a quick reply.

When I do a typical for loop like you have above I am always passed the limits I need to be.
Then you need an explicit check for the limits. Something like
Code: [Select]
if (newPos > maxPos) {
  newPos = maxPos;
}


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Johan_Ha

Your linked page said:
Max Travel (out of box)   201°

On same page check the range of the PWM. And use Servo.writeMicroseconds() instead of Servo.write(), as vinceherman pointed out. Not because better accuracy, but because your servo doesn't understand the degrees at all! The servo lib assumes max travel 180° instead of 201°.
____________________
- One day my stepper motor driver works like a charm. No task is too big for it and I can do anything with it. Next day it refuses to work and even the tiniest motor blows its fuse. What's wrong with it?
- It's bipolar.

slipstick

The servo.write 0 to 180 "angle" is really confusing. Given the actual microsecond values it writes (540 - 2400) it just means turn the maximum amount the servo can turn. Since for different servos that maximum could be anywhere from 90 degrees to about 270 (or even 6 full turns for a winch servo) it ALWAYS needs to be calibrated for the actual servo you're using.

BTW have a look at the VarSpeedServo library https://github.com/netlabtoolkit/VarSpeedServo You don't need to mess about with sweeping the servo and controlling the sweep time yourself. Just tell it in the write() how far you want it to move and how fast (or slow). The library does it for you. I always use it for animating skeletons (o.k. I've only done two so far but that's what I used).

Steve

3DWarrior

Awesome!

Thank you so much for the guidance and pointing towards some new material to learn. (All thou I must say everything is new at this point)

Ended up using the slipstick's method with a VarSpeedServo library.

Here is what I have in case someone else needs this working, and it works well for what I needed.

Code: [Select]
#include <VarSpeedServo.h>
 
VarSpeedServo myNeck; 
const int servoPin = 9; // attaches the servo on pin 9 to the servo object

//Neck limits
int maxRight = 70; //45
int maxLeft = 110; //135
int turnSpeed = 10;
int posNew = 90;

// delay limits
int minDelay =1000; //1 min = 60000
int maxDelay =10000; //30 min = 1800000
 
void setup() {
  myNeck.attach(servoPin);  // attaches the servo on pin 9 to the servo object
}

void loop() {
  posNew = random(maxRight,maxLeft);
  myNeck.write(posNew,turnSpeed,true);
  delay(random(minDelay, maxDelay));
}

Go Up