Class Servo Method does not work

19 May 2011

Hello,

I Want to create a generic method that receives an object of the Servo type, and then actuates on the pin defined to be the servo:

void rotateLeft(Servo motor)  // spins the servo 90º to the left
{
    for (int pos = 2450; pos > 1425; pos -= 25) {
            motor.SetPosition(pos); 
            wait_ms(20);
            wait(0.02);
        }
}

Then on int main(), I just need to create the servo object, and pass it to the method (the servo library uses is: http://mbed.org/users/jdenkers/libraries/Servo/lgfiff/docs/Servo_8h_source.html):

int main ()
{
  MotorH.Enable(2450,20000);
  rotatesLeft(MotorH);
}

Is it possible that when the Servo object is passed to the method, it looses it's association to pin 21?

Thanks!

19 May 2011

You could use this library:-

Import librarySimpleRCservos

A simple API/Software interface to the LPC17xx PWM peripheral to provide control to up to six RC type servo controllers.

Here's an example:-

#include "mbed.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

// See http://mbed.org/users/AjK/libraries/SimpleRCservos/latest
#include "SimpleRCservos.h"

// MotorH is Servo6, which connects to p21.
#define MOTOR_H SimpleRCservos::Servo6

SimpleRCservos servos;

void rotateLeft(SimpleRCservos::Servo motor)  // spins the servo 90º to the left
{
    // Rotate at 10degrees per second.
    for (double pos = 0.0; pos > -90.0; pos -= 1.0) {
        servos.position(motor, pos);
        wait(0.1);
    }
}


int main() 
{
    servos.setLimits(MOTOR_H, -90.0, +90.0); // define logical limits, +/-90 degrees.
    servos.enable(MOTOR_H); // Enable the PWM outout.

    // Led1 on to show program start.
    led1 = 1;
    
    rotateLeft(MOTOR_H);

    // Led2 on when rotate complete and program ended.
    led1 = 2;
    
    // End, loop forever.
    while(1);
}

Since what you did appeard to be a single function that controls different motors with the same style interface you could do it something like this instead:-

#include "mbed.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

// See http://mbed.org/users/AjK/libraries/SimpleRCservos/latest
#include "SimpleRCservos.h"

class Motor {
protected:
    double _current_position;
    double _desired_position;
    double _step;
    int    _poll_interval;
    SimpleRCservos::Servo _motor;
    SimpleRCservos *_servos;
    Ticker _servo_poll;
public:
    Motor(SimpleRCservos *servos, SimpleRCservos::Servo motor, double min = -90.0, double max = +90.0) { 
        _servos = servos;
        _motor = motor; 
        _servos->setLimits(_motor, min, max); // define logical limits.
        _servos->enable(_motor); // Enable the PWM outout.
        _current_position = 0.0;
        _desired_position = 0.0;
        _step = 1.0;
        _poll_interval = 10000; // 100ms.
        _servo_poll.attach_us(this, &Motor::poll, _poll_interval); 
    }
    
    void poll(void) {
        if (_desired_position > _current_position) {
            _current_position += _step;
            // Don't allow the servo to oscillate around _desired_position.
            if (_desired_position < _current_position) { 
                _current_position = _desired_position; 
            }
            _servos->position(_motor, _current_position);                        
        }
        else if (_desired_position < _current_position) {
            _current_position -= _step;
            // Don't allow the servo to oscillate around _desired_position.
            if (_desired_position > _current_position) {
                _current_position = _desired_position; 
            }
            _servos->position(_motor, _current_position);            
        }
    }
    
    void position(double position = 90.0)  // spins the servo 90º to the left
    {
        _desired_position = position;
    }
    
    void setStep(double d) { _step = d; }
    
    double getStep(void) { return _step; } 
    
    void setPollInterval(int i) { 
        _poll_interval = i;
        _servo_poll.detach();
        _servo_poll.attach_us(this, &Motor::poll, _poll_interval); 
    }
    
    int getPollInterval(void) { return _poll_interval; }
    
};

SimpleRCservos servos;

Motor motor_front_left(&servos, SimpleRCservos::Servo6); // p21
Motor motor_front_right(&servos, SimpleRCservos::Servo5); // p22
Motor motor_rear_left(&servos, SimpleRCservos::Servo4); // p23
Motor motor_rear_right(&servos, SimpleRCservos::Servo3); // p24


int main() 
{
    // Led1 on to show program start.
    led1 = 1;
    
    motor_front_left.position(90.0);
    motor_front_right.position(90.0);
    motor_rear_left.position(90.0);
    motor_rear_right.position(90.0);
    
    // Led2 on when rotate complete and program ended.
    led2 = 1;
    
    // End, loop forever.
    while(1);
}
19 May 2011

I should add some comments about that actually.

If you look at your code and my first example you'll see the rotateLeft() function is a blocking operation. What this means is when you call this function it will not return until the servo movement is complete. This is because you use wait() inside the for() loop to time the position changes. While thismaybe ok for a single servo, imagine you had several (think wheels on an RC car, control surfaces on a model aeroplane). In this case the blocking operation would allow you move only one servo at a time. In the case of a model aeroplane, it'll crash in more ways than one. For an RC car it's going to travel every where in circles!

What you want is a way to move multiple servos at the same time. The second example, although more complex, does this as the movement changes are done using a periodic ticker callback that looks at the "desired position" against the current position. If they differ the servos are moved _step amount closer to the desired position on each poll interval.

Using this method you can call methods on different servos one after the other and they will all work independently from one another. That's why I wrapped the servo control in a class. To abstract the servo control into a nice neat easy to use package.

[Edit] Note, I added methods to set the step and poll interval. This basically allows you to set the rates of change of the servos by changing the step amount and the poll frequency. This is just an example of how to get going. You can flesh out the class to add more or different control techniques. But hopefully will give you a good start and idea of where to go next with you project. Good luck!

Hope that helps and makes sense.

19 May 2011

In fact, I liked this example I added it to the library. So the above "complex example" now just becomes:-

#include "mbed.h"

DigitalOut led1(LED1);
DigitalOut led2(LED2);

#include "SimpleRCservos.h"
#include "SimpleServoControl.h"

SimpleRCservos servos;

SimpleServoControl motor_front_left(&servos, SimpleRCservos::Servo6); // p21
SimpleServoControl motor_front_right(&servos, SimpleRCservos::Servo5); // p22
SimpleServoControl motor_rear_left(&servos, SimpleRCservos::Servo4); // p23
SimpleServoControl motor_rear_right(&servos, SimpleRCservos::Servo3); // p24


int main() 
{
    // Led1 on to show program start.
    led1 = 1;
    
    motor_front_left.position(90.0);
    motor_front_right.position(90.0);
    motor_rear_left.position(90.0);
    motor_rear_right.position(90.0);
    
    // Led2 on when program ended.
    led2 = 1;
    
    // End, loop forever.
    while(1);
}
20 May 2011

Hello,

Thanks a lot for your answers. I will test it, and give you feedback as soon as possible.

Was a doing something wrong on my method? Is there a specific reason that explains the fact thar the servo type object, when received by the method, not to work?

By the way, is there a way to read my servo1 current position, in order to set servo2 to the position of servo1?

Cheers!

20 May 2011

Carlos Martins wrote:

By the way, is there a way to read my servo1 current position, in order to set servo2 to the position of servo1?#

No. Think about it for a second. Simple RC servos that take a PWM signal that "demands a position". However, there's no feedback from the servo coming back to the Mbed (or other controller) that actually says where the servo really is in the physical world. The servo itself determines the accel/decel profiles internally. So in the simplest case you cannot accurately track one servo against another with just "positional demands" alone (each servo could be driving different loads and therefore have different movement profiles).

The only way for you to make one servo track another is to provide some sort of actual real world positional feedback of the servos and then write your own control algorithm to actually set the demand signals.

[edit: it is worth saying that the idea of the servo is to move to the desired position and it'll do that so long as you have designed your system so that the servo can drive it's load within it's given parameters. With that said, tracking one against another should simply be a case of demanding teh same position of both servos at the same time. But you can't ever be 100% sure without your position feedback system such as shaft encoders/pots/etc).

20 May 2011

Ok, Thanks Andy!

I will give you feedback as soon as possible.

23 May 2011

Hello Andy,

It worked perfectly, thanks a lot!

Carlos.

05 Jun 2011

Andy K wrote:

Carlos Martins wrote:

By the way, is there a way to read my servo1 current position, in order to set servo2 to the position of servo1?#

No. Think about it for a second. Simple RC servos that take a PWM signal that "demands a position". However, there's no feedback from the servo coming back to the Mbed (or other controller) that actually says where the servo really is in the physical world. The servo itself determines the accel/decel profiles internally. So in the simplest case you cannot accurately track one servo against another with just "positional demands" alone (each servo could be driving different loads and therefore have different movement profiles).

The only way for you to make one servo track another is to provide some sort of actual real world positional feedback of the servos and then write your own control algorithm to actually set the demand signals.

[edit: it is worth saying that the idea of the servo is to move to the desired position and it'll do that so long as you have designed your system so that the servo can drive it's load within it's given parameters. With that said, tracking one against another should simply be a case of demanding teh same position of both servos at the same time. But you can't ever be 100% sure without your position feedback system such as shaft encoders/pots/etc).

I modified one of my HiTec HS-422 Servos... I simply ran a fouth wire from the Servo's potentiometer back to one of my Analogue inputs (this was when I was still using Arduinos)... Once calibrated it did work... and might be an option for someone here?

It was a cheap feedback failsafe for a remote throttle I had built. If I was doing it again, I'd probably wire in a second Pot instead, just to be sure ;)