MAD BoB

So I figured it was time for me to have a go at building a BoB. I have been looking at all the BoB builds in envy and been wanting to build one for a long time.. I printed the parts several months ago but didn't have time or all the parts I needed until now.

I wanted to add a little extra to my build and decided to add one more servo with a missile launcher. I will try to document my build here and write about all my pitfalls and challenges.

  

The missile launcher is originally a spare part for a WLtoys drone. It can be found here http://www.amazon.com/WLtoys-Quadcopter-Missile-Launcher-V959-19/dp/B00CR9SFKE. It is really easy to wire up. Yellow wire goes to a 5V pin and the white wire to ground. Pull the pin high and the missiles will launch. The red and black wires are shorted every time the release mechanism is triggered. This can be used for a more fine tuned controll over the launcher. In my project I will just pull the yellow wire high. 

First I needed to figure out where I could put the launcher and the servo inside the head and still have enough room for electronics and a battery. I decided to go with as high as the servo could go inside the head and in the center. I drilled a hole and fitted the servo with two screws from the outside. The battery I wanted to use was a 850mAh 7.4V LiPo. For microcontroller I chose an Arduino Nano I had lying around.

I cut a protoboard to an appropriate size and placed the Ultrasonic and Arduino Nano on the board to test that it would fit inside the head together with the servo and battery. This worked well and I was ready to get to work. The first thing I soldered on was the female headers for the Nano. I wanted to use headers to be able to remove the Nano even though they added to the total height of the board. I then soldered the Ultrasonic sensor to the board. This was a mistake. It was constantly in the way and I should have waited with it to make the soldering easier. But it worked out fine in the end. 

I used a Mini 3A Step-Down Regulator to regulate the power from the lipo to the servos. I then connected the lipo directly to the VIN input on the Arduino Nano. I had a lot of problems with this approach and used a lot of time troubleshooting it with CKR members. After a while we found out that it was the Nano that was not able to remain steady with the lipo as it requires at least 7V. Bdk6 gave me the solution to put a big and small capacitor on the output of the regulator and feed 5V directly from it to the 5V pin on the Arduino. This worked perfectly.

On the other side of the board I placed all the leds for the mouth and used their legs to position them correctly for the holes. This also worked out well. Because of the servo and the size of the battery I figured out that everything would not fit inside the head and decided to try to use this to my advantage and make something cool. I designed an extension for it in Inventor and gave it headlight and taillight. This was 1 cm high and just enough to fit everything inside. I also soldered on a header for my HC-05 bluetooth module. What remains now is to shorten the servo cables and write the code.

As seen on the two images below the servo cables need shortening before the final assembly can be made. Hopefully I will do this tommorrow and get the post updated with the first walk.

12.10

Today I got the cables shortened and everything assembled and ran some test code to check if everything was ok. I added some videos of the progress. I still have some issues with the sonar and a have not tested the bluetooth module yet. I'm thinking about maybe adding support for Cevs new program if the module is supported.

‚Äč

MAD BoB - first walk
MAD BoB - fire!

Main code:

#include <Servo.h>
#include <NewPing.h>

Servo leftAnkle;
Servo leftHip;
Servo rightAnkle;
Servo rightHip;
Servo missileArm;

const byte leftHipPin = 9;
  byte leftHipCenter = 90;//Center limit
  byte leftHipIn = 75;//In limit
  byte leftHipOut = 105;//Out limit
  byte leftHipCurrent;//Current position
  float leftHipInc;//Calculated increment

const byte rightHipPin = 10;
  byte rightHipCenter = 90;
  byte rightHipOut = 105;
  byte rightHipIn = 75;
  byte rightHipCurrent;
  float rightHipInc;

const byte leftAnklePin = 11;
  byte leftAnkleCenter = 85;
  byte leftAnkleDown = 70;
  byte leftAnkleUp = 100;
  byte leftAnkleCurrent;
  float leftAnkleInc;

const byte rightAnklePin = 12;
  byte rightAnkleCenter = 85;
  byte rightAnkleDown = 100;
  byte rightAnkleUp = 70;
  byte rightAnkleCurrent;
  float rightAnkleInc;

const byte missileArmPin = 8;
  byte missileArmCenter = 90;
  byte missileArmDown = 70;
  byte missileArmUp = 110;

//Modes
byte WalkMode = 0;
byte WalkRequest = 0;
byte OldWalkMode = 0; 
  const byte WALK_STOP = 0;
  const byte WALK_FORWARD = 1;
  const byte WALK_BACKWARDS = 2;
  const byte WALK_LEFT = 3;
  const byte WALK_RIGHT = 4;
  
byte RobotMode;	
  byte StopMode = 0;		
  byte RoamMode = 1;		
  byte WaitMode = 2;
  byte FireMode = 3;		

//Timers
long WalkTimer;				
int WalkDelay = 200;			                                        
int LoopDelay = 50;

long SubStepTimer;                      
int SubSteps;                          
long AvoidTimer = 0;
long AvoidTimerWalk = 0;
int AvoidTime = 6000; //The time to keep avoiding


//Misc pins
const byte redLedPin = 2;
const byte yellowLedPin = 3;
const byte greenLedPin = 4;
const byte blueLedPin = 5;
const byte triggerPin = 6;
const byte echoPin = 7; 
const byte frontLedPin = A4;
const byte rearLedPin = A5;
const byte firePin = A3;

byte maxDistance = 200;

NewPing sonar(triggerPin, echoPin, maxDistance);

int avoidDistance = 20;
int distance;
long TurnDirection;

// Set up stuff
void setup(){
  Serial.begin(115200);

  pinMode(redLedPin, OUTPUT);
  pinMode(yellowLedPin, OUTPUT);
  pinMode(greenLedPin, OUTPUT);
  pinMode(blueLedPin, OUTPUT);
  pinMode(frontLedPin, OUTPUT);
  pinMode(rearLedPin, OUTPUT);
  pinMode(firePin, OUTPUT);

  randomSeed(analogRead(0));

  SubSteps = (WalkDelay / LoopDelay);
  
  digitalWrite(redLedPin, HIGH);
  digitalWrite(yellowLedPin, HIGH);
  digitalWrite(greenLedPin, HIGH);
  digitalWrite(blueLedPin, HIGH);

  leftAnkle.attach(leftAnklePin);
  leftHip.attach(leftHipPin);
  rightAnkle.attach(rightAnklePin);
  rightHip.attach(rightHipPin);
  missileArm.attach(missileArmPin);
   
  InitLegs();
  delay(2000);//Give it some time to center the legs
  WalkStop();

  distance = sonar.ping_cm();

  AvoidTimer = 0;
  AvoidTimerWalk = 0;  
  WalkTimer = millis();

  RobotMode = StopMode;
}

void loop(){

  distance = sonar.ping_cm();
  if (distance < 5){
    distance = 9999;
  }
  
  switch (RobotMode){
    case StopMode:
      WalkRequest = WALK_STOP;
      if (distance < 10){
        RobotMode = RoamMode;
      }
      Serial.println("MODE STOP!");
      break;
    case WaitMode:
      WalkRequest = WALK_STOP;
      if (distance < 10){
       RobotMode = RoamMode;
      }
      Serial.println("MODE WAIT!");
      break;
    case RoamMode:
      WalkRequest = WALK_FORWARD;
      if(distance < 3){
        RobotMode = WaitMode;  
      }
      Serial.println("MODE ROAM!");
      break;
  }

  if (distance > 10 && distance < avoidDistance && RobotMode != StopMode){
    if (AvoidTimer == 0){
      AvoidTimer = millis();
      AvoidTimerWalk = millis();
      TurnDirection = random(1,10);// never could get random(1) to give me a 1 or 0 so doing 10 and comparing for < 5 or greater
    }
  }

  if ((getElapsedTime(AvoidTimer) > AvoidTime)){
    AvoidTimer = 0;
    AvoidTimerWalk = 0;
    //RobotMode = RoamMode;
  }
  
  if (AvoidTimer > 0){
    AvoidItem();      
  }

  SetWalkMode();

  delay(LoopDelay);
}

void InitLegs(){
  leftHipCurrent = leftHipCenter;
  rightHipCurrent = rightHipCenter;
  leftAnkleCurrent = leftAnkleCenter;
  rightAnkleCurrent = rightAnkleCenter;

  CenterPosition();
}

// Walk methods
void WalkStop(){
  if (WalkMode != WALK_FORWARD) {
    SetFrontLed(LOW);
    SetRearLed(LOW);
    AllLedsOff();
    WalkMode = WALK_STOP;
    WalkTimer = millis();
  }

  CenterPosition();
}

void WalkForward(){
  if (WalkMode != WALK_FORWARD){
    SetFrontLed(HIGH);
    SetRearLed(LOW);
    AllLedsOn();
    WalkMode = WALK_FORWARD;
    WalkTimer = millis();
  }

  int CurrentMainStep = (int) WalkStep(WalkTimer, WalkDelay, 5);

  switch (CurrentMainStep){
  case 0:
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleUp);
    MoveRightAnkle(rightAnkleDown);
    break;
  case 1:
    SubStepTimer = millis();
    MoveLeftHip(leftHipIn);
    MoveRightHip(rightHipIn);
    break;
  case 2: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  case 3: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleDown);
    MoveRightAnkle(rightAnkleUp);
    break;
  case 4: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipOut);
    MoveRightHip(rightHipOut);
    break;
  case 5: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  }
}

void WalkBackwards(){
  if (WalkMode != WALK_BACKWARDS){
    SetFrontLed(LOW);
    SetRearLed(HIGH);
    AllLedsOff();
    WalkMode = WALK_BACKWARDS;
    WalkTimer = millis();
  }

  int CurrentMainStep = (int) WalkStep(WalkTimer, WalkDelay, 5);
  switch (CurrentMainStep){
  case 0: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleUp);
    MoveRightAnkle(rightAnkleDown);
    break;
  case 1: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipOut);
    MoveRightHip(rightHipOut);
    break;
  case 2: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  case 3: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleDown);
    MoveRightAnkle(rightAnkleUp);  
    break;
  case 4: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipIn);
    MoveRightHip(rightHipIn);
    break;
  case 5: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  }
}

void WalkLeft(){
  if (WalkMode != WALK_LEFT) {
    SetFrontLed(LOW);
    SetRearLed(LOW);
    AllLedsOff();
    SetLed(redLedPin);
    WalkMode = WALK_LEFT;
    WalkTimer = millis();
  }

  int CurrentMainStep = (int) WalkStep(WalkTimer, WalkDelay, 5);
  switch (CurrentMainStep){
  case 0: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleUp);
    MoveRightAnkle(rightAnkleDown);
    break;
  case 1: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipIn);
    MoveRightHip(rightHipOut);
    break;
  case 2: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  case 3: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleDown);
    MoveRightAnkle(rightAnkleUp);
    break;
  case 4: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipOut);
    MoveRightHip(rightHipIn);
    break;
  case 5: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  } 
}

void WalkRight(){
  if (WalkMode != WALK_RIGHT) {
    SetFrontLed(LOW);
    SetRearLed(LOW);
    AllLedsOff();
    SetLed(blueLedPin);
    WalkMode = WALK_RIGHT;
    WalkTimer = millis();
  }
  int CurrentMainStep = (int) WalkStep(WalkTimer, WalkDelay, 5);
  switch (CurrentMainStep){
  case 0: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleUp);
    MoveRightAnkle(rightAnkleDown);
    break;
  case 1: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipOut);
    MoveRightHip(rightHipIn);
    break;
  case 2: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  case 3: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleDown);
    MoveRightAnkle(rightAnkleUp);
    break;
  case 4: 
    SubStepTimer = millis();
    MoveLeftHip(leftHipIn);
    MoveRightHip(rightHipOut);
    break;
  case 5: 
    SubStepTimer = millis();
    MoveLeftAnkle(leftAnkleCenter);
    MoveRightAnkle(rightAnkleCenter);
    break;
  } 
}

//Move single leg servo
void MoveLeftHip(byte APosition){
  if (getElapsedTime(SubStepTimer) < LoopDelay){
    leftHipInc = (APosition - leftHipCurrent) / (SubSteps);
  }

  leftHipCurrent = leftHipCurrent + leftHipInc;
  leftHip.write(leftHipCurrent);
}

void MoveRightHip(byte APosition){
  if (getElapsedTime(SubStepTimer) < LoopDelay)
  {
    rightHipInc = (APosition - rightHipCurrent) / (SubSteps);
  }

  rightHipCurrent = rightHipCurrent + rightHipInc;
  rightHip.write(rightHipCurrent);
}

void MoveLeftAnkle(byte APosition){
  if (getElapsedTime(SubStepTimer) < LoopDelay)
  {
     leftAnkleInc = (APosition - leftAnkleCurrent) / (SubSteps);
  }

  leftAnkleCurrent = leftAnkleCurrent + leftAnkleInc;
  leftAnkle.write(leftAnkleCurrent);
}

void MoveRightAnkle(byte APosition){
  if (getElapsedTime(SubStepTimer) < LoopDelay){
     rightAnkleInc = (APosition - rightAnkleCurrent) / SubSteps;
  }

  rightAnkleCurrent = rightAnkleCurrent + rightAnkleInc;
  rightAnkle.write(rightAnkleCurrent);
}


//Step timing methods
int WalkStep(long AWalkTimer, long AWalkDelay, long AMaxStep){
  int result;
  result = ((millis() - AWalkTimer) / AWalkDelay);
  if (result > AMaxStep){
    result = 0;
    WalkTimer = millis();
  }
  return result;
}

int AvoidStep(long AAvoidTimer, long AAvoidDelay, long AMaxStep){
  int result;
  result = ((millis() - AAvoidTimer) / AAvoidDelay);
  if (result > AMaxStep){
    result = 0;
    AvoidTimer = millis();
  }
  return result;  
}


// Direct positioning
void LeftAnklePosition(byte APosition){
    leftAnkleCurrent = APosition;
    leftAnkle.write(APosition);
}

void RightAnklePosition(byte APosition){
    rightAnkleCurrent = APosition;
    rightAnkle.write(APosition);
}

void LeftHipPosition(byte APosition){
    leftHipCurrent = APosition;
    leftHip.write(APosition);
}

void RightHipPosition(byte APosition){
    rightHipCurrent = APosition;
    rightHip.write(APosition);
}

void CenterPosition(){
  LeftHipPosition(leftHipCenter);
  RightHipPosition(rightHipCenter);
  LeftAnklePosition(leftAnkleCenter);
  RightAnklePosition(rightAnkleCenter);
}

//Avoidance
void AvoidItem(){
  if (getElapsedTime(AvoidTimerWalk) < (AvoidTime/2)){
    WalkRequest = WALK_BACKWARDS;
  }

  if ((getElapsedTime(AvoidTimerWalk) > (AvoidTime/2) && getElapsedTime(AvoidTimerWalk) < AvoidTime)){
    if (TurnDirection < 6){
      WalkRequest = WALK_RIGHT;
    }else{
      WalkRequest = WALK_LEFT;
    }      
  }

  if ((getElapsedTime(AvoidTimerWalk) > AvoidTime)){
    WalkRequest = WALK_FORWARD;
    AvoidTimer = 0;    
  }
}

void SetWalkMode(){
  switch (WalkRequest){
    case WALK_STOP: 
      WalkStop();       
      break;
    case WALK_FORWARD:
      WalkForward();    
      break;
    case WALK_BACKWARDS: 
      WalkBackwards();  
      break;
    case WALK_LEFT: 
      WalkLeft();     
      break;
    case WALK_RIGHT: 
      WalkRight();      
      break;
  }  
}

// Returns the elapsed time in millis for the timer variable passed
long getElapsedTime(long ATimer){
  long result;
  result = (millis() - ATimer);
  return result;
}

//Led control
void SetFrontLed(int AState){
  digitalWrite(frontLedPin, AState);
}

void SetRearLed(int AState){
  digitalWrite(rearLedPin, AState); 
}

void SetLed(byte ALedPin){
  digitalWrite(ALedPin, HIGH);  
}

void AllLedsOn(){
  digitalWrite(redLedPin, HIGH);
  digitalWrite(yellowLedPin, HIGH);
  digitalWrite(greenLedPin, HIGH);
  digitalWrite(blueLedPin, HIGH);  
}

void AllLedsOff(){
  digitalWrite(redLedPin, LOW);
  digitalWrite(yellowLedPin, LOW);
  digitalWrite(greenLedPin, LOW);
  digitalWrite(blueLedPin, LOW);  
}

 

Comments

Submitted by Roxanna77 on Sun, 2015-10-11 18:06

Roxanna77's picture
Very cool, I like the missile launcher, nice addition!
Submitted by cevinius on Sun, 2015-10-11 19:56

cevinius's picture
This is a really great build, FH! :D
Submitted by cevinius on Mon, 2015-10-12 19:44

cevinius's picture
Just saw the shooting and walking videos! Fantastic work, FH!
Submitted by fhareide on Wed, 2015-10-14 06:44

fhareide's picture
Glad you like it! Is it possible to use your app with a HC-05 bluetooth module now?
Submitted by cevinius on Thu, 2015-10-15 00:31

cevinius's picture
The app can now connect to HC-05! I can add some shoot/aim commands for you at some point! :D
Submitted by Roxanna77 on Tue, 2015-10-13 11:28

Roxanna77's picture
Very cool FH!
Submitted by fhareide on Wed, 2015-10-14 06:48

fhareide's picture
Thanks! Yes CKR needed a 3d print area. Now we just need to find out what it should contain.
Submitted by craighissett on Tue, 2015-10-13 16:33

This is great man! I love the added rocket launcher ha ha! I still harbour dreams of getting my own BoB. If/when I do get him I am going to put a WiPy in and see what fun can be had with a wifi enabled Bob :-)
Submitted by fhareide on Wed, 2015-10-14 06:47

fhareide's picture
Thank you! It was a very fun build. Would love to see your BoB here on CKR!
Submitted by craighissett on Wed, 2015-10-14 07:19

I'll certainly be documenting my Bob to go on here, there and everywhere! Almost done tidying up my old projects so will be putting them on here and robotrebels too :-)
fhareide's picture
Author: 
fhareide
Last updated: 
29 Dec 2015 - 18:03
Robot type: