Introduction: Robotic Arm With Bipolar Stepper

Overview:

This project is about the creation of a 3-point axis robotic arm using bipolar motors, with the arm being controlled by an old PlayStation2 controller. Furthermore we have implemented a sensor connected to as small fan. The idea behind this was the creation of a controlable air-cooling solution for those hot summer days.

Details:

This project was constructed with the use of an Arduino UNO, a robotic arm and several smaller bits (a full component list is available further down). We have used a lot of ready-made code, that we got from the web, due to the fact that the use of Arduino code was paramount to this project. We were lucky enough to get our hands on an old PlayStation2 controller, which we could use because of the many available axis functions, and the possibility of the controlling the fan with knobs on it.

We used three EasyDriver stepper controllers together with the Arduino, in order to convert the signals from the PS2 controller to the bipolar motors.

Step 1: Schematic and Component List

This is an overview of the complete schematics and available components we have used in this project.

The components included are as follows:

  • Berger Lahr bipolar stepper motor (already mounted on the robotic arm)
  • Arduino UNO
  • PlayStation2 joystick
  • Cyber 310 Robot arm
  • Ultrasonic proximity sensor HC-SR04
  • Breadboard
  • PlayStation2 vibrator motor and rotor from a RC helicopter
  • EasyDriver 4.4 Stepper Motor Driver
  • 5V AC/DC Power supply

We primarily chose these components, because they were readily available to us. We also thought it would be fun to get a larger robotic arm working, compared to our fellow classmates. Although as we came to realise, our ambition exceeded our ability.

Step 2: Contructing the Operational Device

We soldered and connected several bits of print and a lot of wires together in order to get a proper running circuit to control our robotic arm.

The design itself is based primarily on the overview featured above in this section, although several modifications were made during the actual construction. A couple of these were made to implement the ultrasonic sensor, at a later stage of development.

Step 3: Test Code for Robotic Arm

Test code for robotic arm included below

We used this bit of code to test whether or not the robotic arm was actually working,since we were having trouble getting a reaction with the full code (included in Step 6). Several parts of the code have been outcommented, because it is not used in the actual device.

#define step_pin 6  // Pin 6 connected to Steps pin on EasyDriver<br>#define dir_pin 7  // Pin 7 connected to Direction pin
//#define MS1 5       // Pin 5 connected to MS1 pin
//#define MS2 4       // Pin 4 connected to MS2 pin
#define SLEEP 10     // Pin 10 connected to SLEEP pin
#define X_pin A0    // Pin A0 connected to joystick x axis
 
int direction;    // Variable to set Rotation (CW-CCW) of the motor
int steps = 1025; // Assumes the belt clip is in the Middle
 
void setup() {
//   pinMode(MS1, OUTPUT);
//   pinMode(MS2, OUTPUT);
   pinMode(dir_pin, OUTPUT);
   pinMode(step_pin, OUTPUT);
   pinMode(SLEEP, OUTPUT);
   
   digitalWrite(SLEEP, HIGH);  // Wake up EasyDriver
   delay(5);  // Wait for EasyDriver wake up
   
 
/* Configure type of Steps on EasyDriver:
// MS1 MS2
//
// LOW LOW = Full Step //
// HIGH LOW = Half Step //
// LOW HIGH = A quarter of Step //
// HIGH HIGH = An eighth of Step //
*/
 
//   digitalWrite(MS1, LOW);      // Configures to Full Steps
//   digitalWrite(MS2, LOW);    // Configures to Full Steps
    
}
 
void loop() {
  while (analogRead(X_pin) >= 0 && analogRead(X_pin) <= 100) {
    if (steps > 0) {
      digitalWrite(dir_pin, HIGH);  // (HIGH = anti-clockwise / LOW = clockwise)
      digitalWrite(step_pin, HIGH);
      delay(1);
      digitalWrite(step_pin, LOW);
      delay(1);
      steps--;
    }      
  }
  
    while (analogRead(X_pin) > 100 && analogRead(X_pin) <= 400) {
      if (steps < 512) {
        digitalWrite(dir_pin, HIGH);  // (HIGH = anti-clockwise / LOW = clockwise)
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps++;
      }    
      if (steps > 512) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps--;
      }
    }    
      
    while (analogRead(X_pin) > 401 && analogRead(X_pin) <= 600) {
      if (steps < 1025) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH );
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps++;
      }    
      if (steps > 1025) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps--;
      } 
    } 
 
    while (analogRead(X_pin) > 601 && analogRead(X_pin) <= 900) {
      if (steps < 1535) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps++;
      }    
      if (steps > 1535) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps--;
      }    
    }   
   
    while (analogRead(X_pin) > 900 && analogRead(X_pin) <= 1024) {
      if (steps < 2050) {
        digitalWrite(dir_pin, HIGH);
        digitalWrite(step_pin, HIGH);
        delay(1);
         digitalWrite(step_pin, LOW);
        delay(1);
        steps++;
      }
    }
    }

Step 4: Sensor Code

Code for ultrasonic senor included

The code chosen for the sensor is constructed so that the fan will run, when it registers an object within a range of between 10 to 20 centimeters from its echo-point.

The physical construcion was based on the schematic above, although we change both the pins and the range.

/*<br> HC-SR04 Ping distance sensor:
 VCC to arduino 5v
 GND to arduino GND
 Echo to Arduino pin 8
 Trig to Arduino pin 9
*/
#define echoPin 11 // Echo Pin
#define trigPin 12 // Trigger Pin
#define LEDPin 8 // Onboard LED
int maximumRange = 20; // Maximum range needed
int minimumRange = 10; // Minimum range needed
long duration, distance; // Duration used to calculate distance
void setup() {
 Serial.begin (9600);
 pinMode(trigPin, OUTPUT);
 pinMode(echoPin, INPUT);
 pinMode(LEDPin, OUTPUT); // Use LED indicator (if required)
}
void loop() {
/* The following trigPin/echoPin cycle is used to determine the
 distance of the nearest object by bouncing soundwaves off of it. */
 digitalWrite(trigPin, LOW);
 delayMicroseconds(2);
 digitalWrite(trigPin, HIGH);
 delayMicroseconds(10);
 digitalWrite(trigPin, LOW);
 duration = pulseIn(echoPin, HIGH);
 //Calculate the distance (in cm) based on the speed of sound.
 distance = duration/58.2;
 if (distance >= maximumRange || distance <= minimumRange){
 /* Send a negative number to computer and Turn LED ON
 to indicate "out of range" */
 Serial.println("fuckboy");
 digitalWrite(LEDPin, LOW);
 }
 else {
 /* Send the distance to the computer using Serial protocol, and
 turn LED OFF to indicate successful reading. */
 Serial.println(distance);
 digitalWrite(LEDPin, HIGH);
 }
 //Delay 50ms before next reading.
delay(50); }

Step 5: A Little Bit of Video (and the Troubles)

The featured video above shows one of our biggest problems.

We simply do not have enough voltage power to run the robotic arm itself. The machine is definitely receiving the signals, but it is so little that it can barely even turn the knob, which drives the axis point of the arm.

The main problem was concentrated around getting the right voltage to the motors, because of the difference between the maximum amount of power-input, you can safely put into the Arduino and amount of power required to get the actual motors themselves running properly.

Step 6: The Full Master Controlling Code

Full code for robotic arm included below.

We had a few problems with code that we used, but since we also had some problems with the voltage power, as explained in Step 5, we had a hard time sorting through the bulk of it all. It should be noted that this section of code does not contain the code for the sensor.

<pre><pre>#ifndef _stepLib_h_<br>#define _stepLib_h_
#include "Arduino.h"
// define our stepper class
class stepMotor {
   public:
      stepMotor(byte stepPin, byte dirPin); // our stepper object with variables stepPin and dirPin
      void step(unsigned int stepFreq); // our stepping function which takes as an input our stepping frequency
   private:
     unsigned long _time; // current time
     unsigned long _lastStepTime; // time at which we last stepped
     unsigned long _stepPeriod; // time between a half period - this is the same as our delay(X) of part 1
     byte _stepPin;
     byte _dirPin;
     boolean _stepCycle; // defines if we are on the HIGH or LOW side of our step cycle
};
#endif
#include "Arduino.h"
#include "stepLib.h"
// used for declaring our motor and initializing it
stepMotor::stepMotor(byte stepPin, byte dirPin) {
   _stepPin = stepPin;
   _dirPin = dirPin;
   // define our digital pins as output
   pinMode(_stepPin, OUTPUT);
   pinMode(_dirPin, OUTPUT);
   // initialize our digital pins to LOW
   digitalWrite(_stepPin, LOW);
   digitalWrite(_dirPin, LOW);
   _stepCycle = false; // this keeps track of which end of the step cycle we are on: high or low
}
// function responsible for driving our digital pins high/low at the proper frequency
// input is the stepping frequency
void stepMotor::step(unsigned int stepFreq) {
   _time = micros(); // get the current time
   _stepPeriod = 1000000 / stepFreq; // get our step period (in micro-seconds) from the user given step frequency; we lose a bit of accuracy here since we've defined _stepPeriod as an unsigned long instead of a float, but that's ok...
   // if the proper amount of time has passed, let's go ahead and proceed to the next half of our step cycle
   if (_time >= _lastStepTime + _stepPeriod) {
      digitalWrite(_stepPin, _stepCycle == true); // a compact way of writing either HIGH/LOW to our step pin based on where we are on our step cycle
      _stepCycle = !_stepCycle; // this simply flips our Boolean
      _lastStepTime = _time; // update the time we last stepped
   } 
}
#include "stepLib.h"
// define a constant value named stepPin and assign the value 8 to it - this value will not change during our code
// this assumes digital pin 8 of your Arduino is attached to the step input of your driver
#define stepPin 9
// define a constant value named dirPin and assign the value 8 to it - this value will not change during our code
// this assumes digital pin 9 of your Arduino is attached to the step input of your driver
#define dirPin 8
// instantiate a new object in our stepMotor library named slider
// we are essentially declaring that we want to add a stepper motor named slider that has our defined stepPin and dirPin
stepMotor slider(stepPin, dirPin);
// setup() loop, the Arduino only runs through this once
void setup() {
}
// loop() loop, the Arduino continuously cycles through this as fast as it can
void loop() {
  slider.step(50); // step our motor at a given frequency (Hz)
}
#include "stepLib.h"
// define our step pins
# define sliderStep 9
# define panStep 11
# define tiltStep 7
// define our direction pins
# define sliderDir 8
# define panDir 10
# define tiltDir 6
// instantiate a new object in our stepMotor library named slider
// we are essentially declaring that we want to add a stepper motor named slider that has our defined stepPin and dirPin
stepMotor slider(sliderStep, sliderDir);
stepMotor pan(panStep, panDir);
stepMotor tilt(tiltStep, tiltDir);
// setup() loop, the Arduino only runs through this once
void setup() {
}
// loop() loop, the Arduino continuously cycles through this as fast as it can
void loop() {
  slider.step(50); // step our motor at a given frequency (Hz)
  pan.step(10); // step our motor at a given frequency (Hz)
  tilt.step(100); // step our motor at a given frequency (Hz)
}
#ifndef _stepLib_h_
#define _stepLib_h_
#include "Arduino.h"
// define our stepper class
class stepMotor {
   public:
      stepMotor(byte stepPin, byte dirPin); // our stepper object with variables stepPin and dirPin
      void step(unsigned int stepFreq); // our stepping function which takes as an input our stepping frequency
      void setDir(boolean dir); // function that allows us to set our direction of rotation
   private:
     unsigned long _time; // current time
     unsigned long _lastStepTime; // time at which we last stepped
     unsigned long _stepPeriod; // time between a half period - this is the same as our delay(X) of part 1
     byte _stepPin;
     byte _dirPin;
     boolean _stepCycle; // defines if we are on the HIGH or LOW side of our step cycle
};
#endif
#include "Arduino.h"
#include "stepLib.h"
// used for declaring our motor and initializing it
stepMotor::stepMotor(byte stepPin, byte dirPin) {
   _stepPin = stepPin;
   _dirPin = dirPin;
   // define our digital pins as output
   pinMode(_stepPin, OUTPUT);
   pinMode(_dirPin, OUTPUT);
   // initialize our digital pins to LOW
   digitalWrite(_stepPin, LOW);
   digitalWrite(_dirPin, LOW);
   _stepCycle = false; // this keeps track of which end of the step cycle we are on: high or low
}
// function responsible for driving our digital pins high/low at the proper frequency
// input is the stepping frequency
void stepMotor::step(unsigned int stepFreq) {
   _time = micros(); // get the current time
   _stepPeriod = 1000000 / stepFreq; // get our step period (in micro-seconds) from the user given step frequency; we lose a bit of accuracy here since we've defined _stepPeriod as an unsigned long instead of a float, but that's ok...
   // if the proper amount of time has passed, let's go ahead and proceed to the next half of our step cycle
   if (_time >= _lastStepTime + _stepPeriod) {
      digitalWrite(_stepPin, _stepCycle == true); // a compact way of writing either HIGH/LOW to our step pin based on where we are on our step cycle
      _stepCycle = !_stepCycle; // this simply flips our Boolean
      _lastStepTime = _time; // update the time we last stepped
   }
}
// given a boolean user input, set our direction of travel to that input
void stepMotor::setDir(boolean dir) {
  digitalWrite(_dirPin, dir);
}
#include "stepLib.h"
// define our step pins
# define sliderStep 9
# define panStep 11
# define tiltStep 7
// define our direction pins
# define sliderDir 8
# define panDir 10
# define tiltDir 6
// define the pins on which we've put our N.O. buttons
#define button1 2
#define button2 3
// our motor step frequencies
int sliderFreq = 300;
int panFreq = 10;
int tiltFreq = 100;
// instantiate a new object in our stepMotor library named slider
// we are essentially declaring that we want to add a stepper motor named slider that has our defined stepPin and dirPin
stepMotor slider(sliderStep, sliderDir);
stepMotor pan(panStep, panDir);
stepMotor tilt(tiltStep, tiltDir);
// setup() loop, the Arduino only runs through this once
void setup() {
  // define our button pins as input pullup type - see http://arduino.cc/en/Tutorial/DigitalPins#.Uyphr4WN7q4
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
}
// loop() loop, the Arduino continuously cycles through this as fast as it can
void loop() {
  if (digitalRead(button1) == LOW && digitalRead(button2) == HIGH) { // if button1 is pressed and button2 is not pressed
    slider.setDir(true);
    pan.setDir(true);
    tilt.setDir(true);
  } else if (digitalRead(button1) == HIGH && digitalRead(button2) == LOW) { // if btton1 is not pressed and button2 is pressed
    slider.setDir(false);
    pan.setDir(false);
    tilt.setDir(false);
  }
  if (digitalRead(button1) == LOW || digitalRead(button2) == LOW) { // if either button is pressed
    slider.step(sliderFreq); // step our motor at a given frequency (Hz)
    pan.step(panFreq); // step our motor at a given frequency (Hz)
    tilt.step(tiltFreq); // step our motor at a given frequency (Hz)
  }
  if (digitalRead(button1) == LOW && digitalRead(button2) == LOW) { // if both buttons are pressed together
    sliderFreq += 10;
    panFreq += 10;
    tiltFreq += 10;
    delay(10); // delay just a short while otherwise the double button presses causes our frequency to increase too quickly (we need to allow for the user to release the buttons)
  }
}
#include "stepLib.h"
// define our step pins
# define sliderStep 9
# define panStep 11
# define tiltStep 7
// define our direction pins
# define sliderDir 8
# define panDir 10
# define tiltDir 6
// define the pins on which we've put our N.O. buttons
#define button1 2
#define button2 3
// define our joystick pins; NOTE we are using analog pins, not digital
#define LRjoystickPin 27 // left-right joystick
#define UDjoystickPin 28 // up-down joystick
// our motor step frequencies
int sliderFreq = 50;
int panFreq = 300;
int tiltFreq = 100;
// other variables
byte deadband = 50; // size of deadband, from joystick neutral position, in which we assume we are reading 0
unsigned int LRjoyValue = 0;
unsigned int UDjoyValue = 0;
// instantiate a new object in our stepMotor library named slider
// we are essentially declaring that we want to add a stepper motor named slider that has our defined stepPin and dirPin
stepMotor slider(sliderStep, sliderDir);
stepMotor pan(panStep, panDir);
stepMotor tilt(tiltStep, tiltDir);
// setup() loop, the Arduino only runs through this once
void setup() {
  // define our button pins as input pullup type - see http://arduino.cc/en/Tutorial/DigitalPins#.Uyphr4WN7q4
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(LRjoystickPin, INPUT);
  pinMode(UDjoystickPin, INPUT);
}
// loop() loop, the Arduino continuously cycles through this as fast as it can
void loop() {
  // read our joystick values and store them
  LRjoyValue = analogRead(LRjoystickPin); // acts just like digitalRead, but for analog pins
  UDjoyValue = analogRead(UDjoystickPin); // acts just like digitalRead, but for analog pins
  // control our pan with the LR joystick
  if (LRjoyValue > 512+ deadband) { // joystick is outside of deadband, move right
    pan.setDir(true);
    pan.step(panFreq);
  } else if (LRjoyValue < 512- deadband) { // joystick is outside of deadband, move left
    pan.setDir(false);
    pan.step(panFreq);
  }
  // control our tilt with the UD joystick
  if (UDjoyValue > 512 + deadband) { // joystick is outside of deadband, move up
    tilt.setDir(true);
    tilt.step(panFreq);
  } else if (UDjoyValue < 512 - deadband) { // joystick is outside of deadband, move down
    tilt.setDir(false);
    tilt.step(panFreq);
  }
  // control our slider stepper with the two buttons, just like we did previously
  if (digitalRead(button1) == LOW && digitalRead(button2) == HIGH) { // if button1 is pressed and button2 is not pressed
    slider.setDir(true);
  } else if (digitalRead(button1) == HIGH && digitalRead(button2) == LOW) { // if btton1 is not pressed and button2 is pressed
    slider.setDir(false);
  }
  if (digitalRead(button1) == LOW || digitalRead(button2) == LOW) { // if either button is pressed
    slider.step(sliderFreq); // step our motor at a given frequency (Hz)
  }
}