servo.writeMicroseconds() does not seem to yield correct results

I was using servo.write and I got a fine result, but I needed more resolution/slower speed so I wanted to use writeMicroseconds() instead.

This part of code succesfully moves the motors. Depending on that flag 'occupied' the motors must take positions of either lowPos or highPos. I shift 1 us at the time every 20ms.

That repeat macro just uses millis(), nothing fancies.

void moveArms()
{
    REPEAT_MS( 20 )
    {
        uint16_t setpoint ;

        if( occupied ) setpoint = highPos ;
        else           setpoint =  lowPos ;
        
        if( pulseUs < setpoint ) pulseUs ++ ; // close
        if( pulseUs > setpoint ) pulseUs -- ; // open
        
        arm1.writeMicroseconds(pulseUs) ;     // replace by write microseconds
        arm2.writeMicroseconds(pulseUs) ;
        arm3.writeMicroseconds(pulseUs) ;
        arm4.writeMicroseconds(pulseUs) ;
    }
    END_REPEAT
}

Now the part where I suspect I am doing something wrong.

In the initialisation I use this code to calculate pulse durations. I insert degrees in the map function and I expect correct pulse times comming out.

I map 0-180 degrees to 1000-2000us and I want lowPos to be 20 degrees and highPos 100 degrees.

    lowPos  = map(  20, 0, 180, 1000, 2000 ) ;
    highPos = map( 100, 0, 180, 1000, 2000 ) ;

As far as internet tells me, I think I have entered the correct numbers in map()

The motor should rotate 80 degrees, but from the looks of it, they rotate about 35 degrees.
Serial printout of the outcome gives me 1111 and 1555 respectively. This appears to be correct.

I also printed the pulseUs values during movement and every step between 1111 and 1555 is used as parameter with writeMicroseconds();

lowPos

highPos

Am I making some horrible mistake here or is writeMicroseconds() doing something else I think it should?

I use an atmega328, Arduino's Servo library and Mg90 analog servo motors

Kind regards,

Bas

Where did You read that posting a snippet is recommended?
Try this topic: How to get the best out of this forum - Using Arduino / IDE 1.x - Arduino Forum

There's lots of servo running available. Most projects send the angle they need. If speed must be controlled, send single steps at an appropriate rate.
You likely complicate things. Look around for servo using projects.

I doubt 1us increments is doing you any good. Most hobby servos have deadbands that are spec'd at 5, 8, and even 10 us. As for repeatability, well, let's just say that with that deadband, approaching the same "point",(really, angle), from either direction will likely result in different final positions.
But, the real answer is in testing, so test, test, test, and remember, each and every servo will likely be different when tested to that degree.

What happens with a straight:

void loop(void){
    arm1.write(20);
    delay(5000);
    arm1.write(1111); 
    delay(5000);
    arm1.write(80);
    delay(5000);
    arm1.write(1555);
    delay(5000);
}

Generally Servo pulses repeat every 50ms or so; trying to update the time at intervals smaller than that may cause weird behavior (as a pulse is "restarted" before the old one is over.)

2 Likes

This test sketch will show microseconds vs degrees.

/*
 Try this test sketch with the Servo library to see how your
 servo responds to different settings, type a position
 (0 to 180) or if you type a number greater than 180 it will be
 interpreted as microseconds(544 to 2400), in the top of serial
 monitor and hit [ENTER], start at 90 (or 1472) and work your
 way toward zero (544) 5 degrees (or 50 micros) at a time, then
 toward 180 (2400). 
*/
#include <Servo.h>
Servo servo;

void setup() {
  // initialize serial:
  Serial.begin(9600); // set serial monitor baud rate to match
                      // set serial monitor line ending to "NewLine"
  servo.write(90);
  servo.attach(9);
  prntIt();
}

void loop() {
  // if there's any serial available, read it:
  while (Serial.available() > 0) {

    // look for the next valid integer in the incoming serial stream:
    int pos = Serial.parseInt();
    if(Serial.read() == '\n'){}
    pos = constrain(pos, 0, 2400);
    servo.write(pos);
    prntIt();
  }
}
void prntIt()
{
  Serial.print("  degrees = "); 
  Serial.print(servo.read());
  Serial.print("\t");
  Serial.print("microseconds =  ");
  Serial.println(servo.readMicroseconds());
}  

Printing this out and putting under your servo horn might help.

image

I think Servo.h uses a different timing mechanism than micros() and can reach 1us resolution.

1 Like

Hopefully, you are correct, I've never got a hobby type servo to give that resolution (about 0.1 degree).
EDIT: I deleted that line in post #7.

1 Like

The main mistake is, that you assume a servo moves 180 degrees when the pulse length varies from 1000 to 2000µs. This is not the case. Most servos are even not able to span a moving range of 180° at all. And if they are able to do so, the need a wider span of pulse length than 1000...2000 µs. This is why the servo lib maps 0..180° to 544 to 2400 µs, als @Delta_G already pointed out in post #3.

No, the repetiion rate is nominally 20ms ( or 50Hz ). The servo lib creates the pulses in ~20ms intervalls - no matter how often you create a .write() call. If you create the calls too often, then part of them are ignored.

Yes, the creating of the pulses is done in timer1 interrupts ( on an AVR ). But 1µs accuracy can not be garanteed by this approch - even if the timer has that resolution. Interrupts may be influenced by other interrupts.

oops. I got my numerator a denominator mixed up. 50Hz is right, not 50ms !
Sorry about that.

1 Like

Ok thanks, I will try to to change the map values to 544us and 2400us respectively this evening. I never knew the pulse lengths were actually beyond 1000us and 2000us.

I ofcourse already figured out the resolution does only do some good and not that much. The movement is slightly smoother but not that much really.

Therefor I'll also buy a digital servo and see how that one performs. I can't even set these servo's to <15 degrees. They reach their mechanical restrains around 15 degrees or so. Luckily for me it aren't those plastic gear types.

I'll let you know the results.

Kind regards,

Bas

P.S.

Where did You read that posting a snippet is recommended?

Posting a snippet of code is generally useless. The problem is usually in another part of the program.

Usually does not mean always. I actually do know what I am doing. I therefor posted all relevant information and as you can see by the other comments (which were helpfull) the people here clearly identified what I was doing wrong.

You likely complicate things. Look around for servo using projects.

I already said I had a perfectly fine working setup with the write() function. So why on earth would I need to look around for other's servo projects? And what do you think is complicated? I was shifting one degree at the time and now I shift 1us at the time.

I am sorry but I find your post not even remotely helpfull and negatively biased, which I find very unfortunate. If I want to farm negativity and/or insults I would ask this question on stack overflow

Some Experimental Results:
SG-90 Servo Motor
Arduino UNO
Pulse width measurement technique: pulseIn() function
Input data: InputBox of Serial Monitor

Shaft Position      write(argDeg)       Pulse Width           
0    deg            argDeg = 0          533  us                     
180  deg            argDeg = 180        2377 us
5    deg            argDeg = 5          585  us
10   deg            argDeg = 10         637  us
15   deg            argDeg = 15         688  us
.........................................................................
175  deg            argDeg = 175        2325 us
180  deg            argDeg = 230        2377 us

The Motor does not respond to a command less than 5 deg though the pulse width changes.

==> myServo.writeMicroseconds(map(y, 0, 180, 533, 2377)); //y in degree

1 Like

I was talking to railroader, not to you ofcourse. Your help is in fact greatly appreciated. Mostly because you simply help.

He on the otherhand was not being helpful nor tried to be helpful. I ask for help and what do I get back? An opinion that my question sucks

Where did You read that posting a snippet is recommended?
Try this topic: How to get the best out of this forum - Using Arduino / IDE 1.x - Arduino

I show code snippets in which it is not to hard to see that I move servos one step at the time and I get:

If speed must be controlled, send single steps at an appropriate rate.

I am litterly doing exactly this in the code snippet, I provided.

I get untrue assumptions thrown at me. And I get told just to look on the internet...

You likely complicate things. Look around for servo using projects.

So yeah again, I am sorry, but I am honestly not feeling any good intentions here.

It would seem to me that "I can promise that won't happen because you'll never get help from me again" kind of blunts the message of "Let it go. Who cares. Ignore it."

End result (with mg90)

Delta_G, my apologies. You are wright. I shouldn't have reacted to the negativity. I should have known, it could only become worse.

And FWIW out of gratefulness I will donate 10$ to Arduino. I also donate some dough to KiCad from time to time.

Kind regards and thanks for all your help,

Bas

EDIT

afbeelding

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.