Designing a Quadruped Robot – Part 3

The key technique for having the quadruped move smoothly is interpolation. Dynamixel servos (or any other servo, really) accept some value to use as the destination. The Dynamixel SDK uses the term “goal position” for this. If you send the servo this value, it will move as quicky as possible to the destination, which will not result in smooth movement when coordinating multiple servos.

To interpolate the servo for smooth movement, the goal is to move it in small increments between the current position and the destination. My solution is generally to always keep the time between increments the same, but vary the number of increments to change the speed of the servo.

A basic example for interpolating a single servo might look something like this (excuse the pseudo-ish code throughout this post):

void interpolate_servo(float current_position, float desired_position, int increments) {
  float increment_amount = (desired_position - current_position) / increments;
  for(int i = 0; i < increments; i++) {
    current_position += increment_amount;
    move_servo_to(current_position);
    delay(10);
  }
}

Of course “move_servo_to” will vary based on the servos used, and should accept an angle and convert it to a value the servo understands. Also, this method would have to accept the ID of the servo you’re controlling somehow. If writing object oriented code, it could be a method of the “servo” class.

This works well if you’re only moving one servo at a time. But if you’re trying to move a whole leg, this routine will have to finish moving one servo before you can call it for another servo. Therefore, the interpolation routine should be done on the entire leg and not the servos directly. If you look back at Designing a Quadruped Robot – Part 2, some simple code could be written which would move the entire leg to a destination when given a point in 3D space. The key then is to apply the interpolation technique above on the “move_leg” function.

An example for doing this with an entire leg would look like this:

void interpolate_leg(float current_x, float current_y, float current_z, float desired_x, float desired_y, float desired_z, int increments) {
  float x_increment = (desired_x - current_x) / increments;
  float y_increment = (desired_y - current_y) / increments;
  float z_increment = (desired_z - current_z) / increments;

  for(int i = 0; i < increments; i++) {
    current_x += x_increment;
    current_y += y_increment;
    current_z += z_increment;
    move_leg_to(current_x, current_y, current_z);

    delay(10);
  }
}

Again, this works well for a single leg. But what about the four legs on the robot? You don’t want to have to move only one at a time or things look pretty bad. I’m sure you’re starting to see the pattern.

At this point I suggest using c++ and creating a leg class that holds a “move_to” method, “update” method, the current coordinates of the leg, the desired coordinates, the number of increments, and the IDs of the servos that make it up (IDs in the case of Dynamixels. If you’re using normal servos you’ll need some other system of identifying them). This way you can simply set the desired location and number of increments, and call “leg.update()”. The code would look something like this:

class Leg {
  float current_x, current_y, current_z;
  float desired_x, desired_y, desired_z;
  int increments;
  unsigned char coxa_servo_id, femur_servo_id, tibia_servo_id;

  public:
    Leg(unsigned char, unsigned char, unsigned char);
    set_desired_position(float, float, float);
    set_increments(int);
    update();
    move_to();
};

// Constructor here to set up servo IDs
Leg::Leg(unsigned char coxa_servo_id, unsigned char femur_servo_id, unsigned char tibia_servo_id) {
  ...
}

// move the leg one increment
void Leg::update() {
  float x_increment = (desired_x - current_x) / increments;
  float y_increment = (desired_y - current_y) / increments;
  float z_increment = (desired_z - current_z) / increments;

  if(increments > 0) {
    current_x += x_increment;
    current_y += y_increment;
    current_z += z_increment;
    move_to(current_x, current_y, current_z);
    increments--;
}

// other class member functions like "move_to" etc. below...
...

This way in the main loop you can simply go through each leg calling leg.update(), and if a leg has an “increments” value greater than 0 it will move towards its destination; otherwise nothing will happen.

Now each leg can be moved independently and at the same time! Please keep in mind that this is simplified code describing concepts. In reality there are structs to hold points and rotations, a “Joint” class containing servo ID info and abstracted code to control the servo, a bunch of member variables in the leg class to hold angular information, etc., but this should be enough to get things working.

This is the part where you could probably come up with some neat walking gait on your own, but I’ll talk quickly about it at a high-level since it’s frequently asked about. I will probably do a more descriptive post about it as well.

For the entire robot to move, two diagonally opposite legs lift at the same time, move forward, and set back down. The other set of legs now lifts and does the same thing while the first two legs move backward which pushes the robot forward. The path that the legs follow while on the ground moving backward determines the overall path of the robot. The code just needs to be written so that the path can be a mathematical function (in the case of turning it’s an arc, forward it’s a line, etc.). There are a ton of edge cases to consider though. What if the robot wants to stop? Do the legs just return to their “home” position regardless of their current location, or would this cause them to drag the feet across the carpet potentially stripping the servo gears? What if you want to quickly switch from walking forward to turning in a tight circle? Should you return to home and then start turning or is there a faster way? Hopefully I can address these issues as well in the next post.

Advertisements

Posted on May 1, 2014, in Programming, Robotics. Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: