Strange servo behaviour (?)

Posted on
  • Hi,

    I've got the MeArm recently and I'm trying to control the servos.

    It comes with those cheap blue servo motors (sg90), which are depicted in the servo tutorial:
    http://www.espruino.com/Servo+Motors

    I've tried this simple example just to move a part between two positions in 3 seconds each time it moves.

    var s = require("servo").connect(C7);
    
    //s.move(0); // move to position 0 over 1 second
    //s.move(1); // move to position 1 over 1 second
    //s.move(0.5, 3000); // move to position 0.5 over 3 seconds
    
    // move to position 0 over 1 second, then move to position 1
    s.move(0, 3000, function() {
      s.move(1, 3000);
    });
    

    My question now:
    Is it normal, that as soon as I execute the first move for a specific servo object (even multiple other ones) it will move instantly/quickly instead over the specified time?
    After that first move all moves will be executed accordingly.

    BR
    Daniel

  • I took a look at the module... and noticed, that the internally defined currentPos(ition) variable is not initialized (and how could it... it cannot, because there is no way to read the servo position to initialize it - no closed loop servo...).

    On a first call after connect, the demanded and passed on pos is gulped up for the initialization. When it then comes to calculate the pulse length for gradually moving there, the formula fails - has to fail - and no 'gradual' takes place. The failure is though very smart and catches 2 birds with one stone: servo is in demanded position and currentPos is properly initialized to match... ;-)

    Therefore, a separate, initial call has to be made to either position 0 or 1 (actually any position in between works as well), to get the currentPos initialized.

    Btw, I do not have the hardware to verify... it is just a thought experiment based on code analysis - which means, I could be totally wrong... :((((

    Here is the module code to work through the first call:

    exports.connect = function (pin) {
      var interval, currentPos;
    
      return {move:function(pos, time, callback) {
        if (time===undefined) time = 1000;
        var amt = 0;
        if (currentPos===undefined) currentPos = pos;
        if (interval) clearInterval(interval);
        var initial = currentPos;
        interval = setInterval(function() {
          if (amt>1) {
            clearInterval(interval);
            interval = undefined;
            amt = 1;
            if (callback) callback();
          }
          currentPos = pos*amt + initial*(1-amt);
          digitalPulse(pin, 1, 1+E.clip(currentPos,0,1));
          amt += 1000.0 / (20*time);
        }, 20);
      }};
    };
    

    On the very first call after connect, position 0 is demanded and to be reached in 3000[ms].

    In the code section before the interval - precisely in line 7 - the condition evaluates to true and currentPos is set to demanded pos, which is 0, and the same value is then taken on by initial.

    In the interval callback function - on first call, amt is still 0 as initialized in the begin of the call, and therefore, the block of lines 11 through 16 is skipped.

    In line 17, the 'new' currentPos is calculated for executing the first amount of movement 0 and should be a fraction according to the time given or the 1000[ms] default. But with currentPos equal 0 and taken on by initial in line 9, it IS ALREADY the demanded position 0. So the very first pulse sent in line 18 is 1[ms] and is the pulse length to tell the servo to 'go to position 0' - and the obedient servo goes right as fast it can into the the final position: 0.

    The 'after math' of the first callback invocation as any other after it - until the time is up, which is 3000[ms] / 50[ms] = 60 times - calculates an amt, but the currentPos will always be 0 because of the formula and pos AND initial do not change and are both still 0.

    In other words, with the very first pulse ever, the servo jumps right into the demanded position, and is then (again) 'going there' for the demanded time, before another the call back is checked and executed or another call is made.

    To make your code work, position to any value 0..1 before doing your sequence...

    Conclusion: works as designed!

  • As @allObjects says, the servo motors don't ever communicate their position back to Espruino - the Espruino board just has to tell them where to go.

    Unfortunately it means that the first time you tell them where to move, they just go there as quickly as they can. After that, Espruino knows where it last told them to go, so it can make sure they move very slowly.

  • Hm well in that case I'll have to move the parts in "0" position manually before connecting the MeArm because one problem which is being caused by this is that for example the servo at the base, which holds the whole unit, starts oscillating back and forth because of the quick movement and the inertia of the arm.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Strange servo behaviour (?)

Posted by Avatar for stozk @stozk

Actions