Simple 4 Servo Arduino Robot

Simple 4 Servo Arduino Robot

Robots are really cool, but they often take time to build. This project was designed and deployed over two afternoons, to use at a high school engineering summer camp. The goals were to make it quick, cheap, and modular.

// Goals

  • Simple, cheap robot
  • Modular for future projects
  • Fast build time
  • Use available materials

// Inventory

This is a list of what you need to build the robot. If you already have these parts, or ones that are similar, use those!

 

// Construction:

Let’s put together a robot! We chose tongue depressors for our structural material. They are light weight and strong, cheap, flexible, and easy to work with. We used some random nuts and machine screws we found in our shop to assemble them.

To begin, we wanted to make the legs strong enough to hold the robot’s weight, so we glued three tongue depressors together per leg, and mounted the servo “horns” into them with screws and wood glue. It might be necessary to drill (or poke) holes for the screws; for us, some of the pieces cracked without pilot holes.

To build the chassis, we started by making the left and right struts, held together with small nuts and bolts. We mounted the servos in these struts using nothing but friction, sliding the front and back struts between the servos and the side struts. We decided to assemble it this way because it makes it easy to disassemble and simple to adjust the width of the robot.

Finally we mounted the breadboard to a couple of tongue depressors using tape, and attached it to the chassis… using more tape 😉 You can never have enough tape around.

// Design… How can this robot walk?

How does a 4-legged robot walk? Bipeds walk by throwing themselves off balance, then moving a leg to catch themselves, while using arms to counterbalance the movement. This requires a lot of flexibility and moving parts. Quadrupeds move in somewhat similar way, but use the extra two legs to push and pull, meaning they have a lot more stability and require less flexibility. Quadruped animals move in many ways; There is skittering, trotting, loping, jumping, galloping, etc… We tried out several different motions, including dancing, and found that some work better than others.

// Code

Before writing any code, we did some thinking about what we actually wanted. It is easy to get lost in the code once you’ve started, unless you have a decent idea of how you want it to turn out.

We wanted modularity, because it is easier to reuse modular code, as well as adding new features. To get this modularity, we decided on the classic programming paradigm of Classes.

We also wanted direct control over the robot; to be able to start and stop movements mid stride. For this we chose Interrupts.

Classes

What are classes? They are a way of putting together a lot of information about the same thing, such as properties and rules. For example, a Ball class would have the shape of the ball, it’s radius, weight, color, and rules for how it rolls. We are focused on servos, so we made a servo class.

The Arduino IDE comes with a Servo class that is very handy and quick to use, but we needed to expand its functionality. The easiest way to do this is with a wrapper class; a class that uses the regular Servo class, but adds all of the information and rules that we want. This lets us easily change the rules for all of our servos, such as setting thresholds for how far they go while taking a step, remembering prior locations, and rules for how to take a step.

#include <Servo.h>


class Servo2
{
  public:

    Servo servo; 
    int currentPos; // Current position 
    int restingPos; // Basic resting position 
    int foreLim; // Forward limit 
    int backLim; // Backward limit

    Servo2( int restingPos, int foreLim, int backLim ) {
      //
      // 
      this->currentPos = restingPos;
      this->restingPos = restingPos;
      this->foreLim = foreLim;
      this->backLim = backLim;
    }
    void attach(int i) {
      servo.attach(i);
      write(restingPos);
    }
    void write(int i) {
      //
      // This wraps the Servo.write function. It serves four purposes:
      // 1. Retains the updated servo position  
      // 2. Gives us external access to the servo. I find this helpful for testing out new ideas and for debugging
      // 3. Allows other class functions (methods) have a more controlled and straightforward way to write to the servo
      // 4. We can easily add new behaviors, ones that effect all servo writes
      //
      servo.write(i);
      currentPos = i;
      Serial.println(currentPos);
    }

    void step(){
      //
      // This performs a single step from either backwards or forwards
      //
      if (currentPos == restingPos) currentPos = foreLim;
      else if (currentPos == foreLim) currentPos = backLim;
      else if (currentPos == backLim) currentPos = foreLim;
      write(currentPos);
    }

    void rest(){
      //
      // Return to resting position
      //
      currentPos = restingPos;
      write(currentPos);
    }

    void forward(){
      //
      // This is our forwards step
      //
      currentPos = foreLim;
      write(currentPos);
    }

    void backward(){
      //
      // This is our backwards step
      //
      currentPos = backLim;
      write(currentPos);
    }
    

};

This is not strictly a programming tutorial, so I’ll cover the code in brief. We first make a new class called Servo2. In this class, we define forward limit, backward limit, current position, and resting position. This holds just about everything we need to know about our servo. Then we define some basic actions, stepping, returning to resting position, forward and backward leg movements. We also made a write function, which let’s us have fine grain control over all of our other movements.

Setup

Here we create our four servos, and attach them to their corresponding pin numbers.


// Create our new servos. These will be different for you, I tuned them using trial and error
// Servo2 testServo( int restingPos, int forLimit, int backLimit )
Servo2 servoFR(75,85,65); 
Servo2 servoBR(80,90,70);  
Servo2 servoFL(75,65,85);  
Servo2 servoBL(65,58,78);  

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // Attach each servo to corresponding pin, front right to pin 9, etc...
  servoFR.attach(9);              
  servoBR.attach(8);              

  servoFL.attach(7);
  servoBL.attach(6);
}

Main Loop

Because we didn’t want a massive main loop that was difficult to test with, we went with a simple Boolean check to see if we wanted the bot to be moving or not. We can replace the testBed() function with any other function, and easily start or stop it by changing “go” to true or false.

bool go = false;

void loop() {
  if (go) {
    testBed();
  }
}

 

Interrupts

First of what is an interrupt, and how does it work on an Arduino? An interrupt is very true to its name; it is a function that is called when a specific action occurs. On an Arduino, that means everything else stops while this function runs.

We use a special Arduino function called serialEvent(). This function is an interrupt that is called every time serial commands are sent to the Arduino.

First, we capture the command from the serial input. Then we use a switch statement to decide which command was sent.

void serialEvent() {
  char ch;

  if (Serial.available()) { // Is there anything to read?
    ch = Serial.read(); // ok, let's save it
!
  }
  switch (ch) {
    case 'q': // step front left
      servoFL.step(); 
      break;
      
    case 'e': // step front right
      servoFR.step(); 
      break;

    case 'z': // step back left
      servoBL.step(); 
      break;

    case 'c': // step back right
      servoBR.step(); 
      break;   

    case 'f': // push all legs forward
      servoFL.forward(); 
      servoFR.forward(); 
      servoBL.forward();
      servoBR.forward();
      break;

    case 'b': // pull all legs backwards
      servoFL.backward(); 
      servoFR.backward(); 
      servoBL.backward();
      servoBR.backward();
      break;

    case 's': // start program / stop program
      go = !go; 
      break;   

    case 'r': // reset
      servoFR.rest(); // return to resting position
      servoBR.rest(); // return to resting position

      servoFL.rest(); // return to resting position
      servoBL.rest(); // return to resting position
     
    break;   
  
  }

}

Movements

Finally, we can program in our functions for movement. This is where testing comes in strong. Because we are only using one joint per leg, we cannot use traditional forms of walking. Instead, we need to get creative with momentum and timing. I’ve left out my more polished functions, because they spoil all of the fun; use these as a starting point, and post comments if you come up with something cool!

void testBed() {
    int timeD = 90;
    servoFR.step(); 
    delay(timeD);  
    servoBL.step();
    delay(timeD);
    servoFL.step(); 
    delay(timeD);
    servoFR.step(); 
    delay(timeD);        
    servoBR.step();
    delay(timeD);    
    servoBL.step();
    delay(timeD);   
    servoFL.step(); 
    delay(timeD);
    servoBR.step();
    delay(timeD);
    
}


void danceDanceYeah() {

    // Right side oscillate 
    int timeD = 200;
    servoFR.step(); 
    delay(timeD);
    servoFR.step(); 
    delay(timeD);    
    servoBR.step();
    delay(timeD);
    servoBR.step();
    delay(timeD);

    // Left side oscillate 
    servoFL.step(); 
    delay(timeD);
    servoFL.step(); 
    delay(timeD);    
    servoBL.step();
    delay(timeD);
    servoBL.step();
    delay(timeD);    
}


void goBackward() {
    int timeD = 150;
    servoFL.step(); 
    delay(timeD);
    servoFR.step(); 
    delay(timeD);    
    servoBL.step();
    delay(timeD);   
    servoBR.step();
    delay(timeD);
    
    servoFL.step(); 
    delay(timeD);    
    servoFR.step(); 
    delay(timeD);
    servoBL.step();
    delay(timeD);
    servoBR.step();
    delay(timeD);  
}

 

                                                                                                               Instructions Produced by Steve Wells

 

Leave a Reply

Your email address will not be published. Required fields are marked *