Ball-E V3.0

The First Joystick Driven Prototype

 

New DC motors


The newly purchased JGA25-370 motors could generate 24kg*cm of torque each, and the total weight of Ball-E was 2kg. This meant that even if one of the motor failed to work, Ball E would still possess enough torque to move off from rest.

 

New Stepper Motor Weight Design

 

After trying out multiple designs to alter the center of gravity of Ball-E, our team has settled on two designs. Design 1 where 250g brass weights were attached to the stepper motor. Design 2 which more elegant, but holds lighter weight. With a more optimal design 1, the prototype was able to orbit with a 30 cm turn radius.

 

Joystick Control

 
To enable directional control, we connected a joystick to the ESP Master. The left/right controls were connected to the stepper motors, while the forward/backward controls were connected to the main DC motors.

Updated Arduino Code

// Define pin connections & motor's steps per revolution
const int dirPin = 2;
const int stepPin = 3;
const int stepsPerRevolution = 200;

// ==================== LIBRARIES =========================
#include 

// ==================== PROGRAM STATE VARIABLES =========================
// bool motorRunning = false;
bool motorTest = false;

// ==================== DC MOTOR CONNECTIONS ===========================
// DIR1 DIR2
// LOW LOW = OFF
// LOW HIGH = FORWARD
// HIGH LOW = BACKWARDS
// HIGH HIGH = OFF

const int motorSpeedPin = 11;
const int motorDirPin1 = 9;
const int motorDirPin2 = 13;
const int motorLEDPin = 10;

// ==================== STEPPER MOTOR CONNECTIONS ======================
const int yStepPin = 6;
const int zStepPin = 7;
const int yDirPin = 3;
const int zDirPin = 4;

//=================== JOYSTICK CONNECTIONS =============================
const int Xpin = A0;
const int Ypin = A1;
const int Spin = A2;

// ============================= VARIABLES ==============================
int runCycles = 10;

// ============================= INTERMEDIATE VALUES ==============================
int Xval;
int Yval;
int Sval;
float Xcenter = 365.;
float Fmax = 40.;
float Bmax = 730.;
float Ycenter = 370.;
float Lmax = 460.;
float Rmax = 40.;
int frontSpeed;
int backSpeed;
int tiltRight;
int tiltLeft;
int tilt;

int autoValue = 0;

// =========================== ACCELSTEPPER INSTANCES =============================
AccelStepper yStepper(AccelStepper::DRIVER, yStepPin, yDirPin);
AccelStepper zStepper(AccelStepper::DRIVER, zStepPin, zDirPin);

void setup() {
	// =========================== DC MOTOR PINS =============================
	pinMode(motorSpeedPin, OUTPUT);
	pinMode(motorDirPin1, OUTPUT);
  pinMode(motorDirPin2, OUTPUT);
  pinMode(motorLEDPin, OUTPUT);

  // =========================== DC MOTOR SETTINGS =============================
	// Turn off motors - Initial state
	digitalWrite(motorDirPin1, LOW);
	digitalWrite(motorDirPin2, LOW);
  digitalWrite(motorLEDPin, LOW);

  // =========================== STEPPER MOTOR PINS =============================
  pinMode(yStepPin, OUTPUT);
	pinMode(yDirPin, OUTPUT);
  pinMode(zStepPin, OUTPUT);
	pinMode(zDirPin, OUTPUT);
  
  // =========================== STEPPER MOTOR SETTINGS =============================
  yStepper.setMaxSpeed(20000); // Set maximum speed value for the stepper
  yStepper.setAcceleration(50000); // Set acceleration value for the stepper
  yStepper.setCurrentPosition(0); // Set the current position to 0 steps

  zStepper.setMaxSpeed(20000);
  zStepper.setAcceleration(50000);
  zStepper.setCurrentPosition(0);
	
  // =========================== JOYSTICK PINS =============================
	Serial.begin(115200);
  pinMode(Xpin, INPUT);
  pinMode(Ypin, INPUT);
  pinMode(Spin, INPUT);
}

// ====================================== MAIN LOOP ====================================
void loop() {
  Xval = analogRead(Xpin);
  Yval = analogRead(Ypin);
  Sval = analogRead(Spin);
// ================================== DC MOTOR LOOP ===================================
  // maps joystick x values to speed values 
  backSpeed = (15.)*abs((Xval-Xcenter)/(Bmax-Xcenter)) + (120.);
  frontSpeed = (15.)*abs((Xval-Xcenter)/(Fmax-Xcenter)) + (120.);

  //======================= AUTO TEST OUTPUT ==============
  autoValue += 10;
  if (autoValue>=250) {
    autoValue = autoValue - 250;
  }
  analogWrite(10, autoValue); 
  analogWrite(9, Yval);
 
  if (motorTest == true) { // testing programme
    // delays the motor start for (x) seconds
    setupDelay(60);
    //runs motors at full speed for 30 seconds + 5 second break, (x) times
    runStraight(1); 
    runLeft(1);
    runRight(1);



  } else if (motorTest == false) { // operating programme

    // forward
    if (Xval <= Xcenter) { // Serial.print("forward"); digitalWrite(motorDirPin1, LOW); digitalWrite(motorDirPin2, HIGH); analogWrite(motorSpeedPin, frontSpeed); // backward if (Xval > Xcenter) {
      // Serial.print("backward");
      digitalWrite(motorDirPin1, HIGH);
      digitalWrite(motorDirPin2, LOW);
      analogWrite(motorSpeedPin, backSpeed);
    }
  }  
  // prints current values
  printValues();
  delay(100);
  // }
// ================================== STEPPER MOTOR LOOP ===================================
  // maps joystick y values to tilt values
  tiltRight = (800.)*abs((Yval-Ycenter)/(Rmax-Ycenter));
  tiltLeft = (800.)*abs((Yval-Ycenter)/(Lmax-Ycenter));

  if(Yval >= Ycenter)
    tilt = -tiltLeft;
  if(Yval < Ycenter)
    tilt = tiltRight;
  
  yStepper.moveTo(tilt);
  zStepper.moveTo(-tilt);
  while (yStepper.currentPosition() != tilt || zStepper.currentPosition() != -tilt) {
    printValues();
    yStepper.run(); // Move or step the motor implementing accelerations and decelerations to achieve the target position. Non-blocking function
    zStepper.run();
  }
  printValues();
  delay(50);
 }
}

//============================= MOVEMENT TEST FUNCTIONS ================================

void runStraight(int runCycles) {
  for (int i=0; i< runCycles; i++) {
    // Turn on LED
    digitalWrite(motorLEDPin, HIGH);
    
    // Turn on motors for 30 secs
    digitalWrite(motorDirPin1, HIGH);
    digitalWrite(motorDirPin2, LOW);
    analogWrite(motorSpeedPin, 255);
    delay(10000);
    
    // Now turn off motors
    digitalWrite(motorDirPin1, LOW);
    digitalWrite(motorDirPin2, LOW);

    // Turn off LED
    digitalWrite(motorLEDPin, LOW);
  } 
}

void runLeft(int runCycles) {
  for (int i=0; i< runCycles; i++) {
    // Turn on LED
    digitalWrite(motorLEDPin, HIGH);
    int testTilt = -400;
    // Turn on motors for 30 secs
    digitalWrite(motorDirPin1, HIGH);
    digitalWrite(motorDirPin2, LOW);
    analogWrite(motorSpeedPin, 255);

    // Tilt max right
    yStepper.moveTo(testTilt);
    zStepper.moveTo(-testTilt);
    while (yStepper.currentPosition() != testTilt || zStepper.currentPosition() != -testTilt) {
      printValues();
      yStepper.run();
      zStepper.run();
    }
    
    delay(10000);
    
    // Now turn off motors
    digitalWrite(motorDirPin1, LOW);
    digitalWrite(motorDirPin2, LOW);

    // Turn off LED
    digitalWrite(motorLEDPin, LOW);
  } 
}

void runRight(int runCycles) {
  for (int i=0; i< runCycles; i++) {
    // Turn on LED
    digitalWrite(motorLEDPin, HIGH);
    int testTilt = 400;
    // Turn on motors for 30 secs
    digitalWrite(motorDirPin1, HIGH);
    digitalWrite(motorDirPin2, LOW);
    analogWrite(motorSpeedPin, 255);

    // Tilt max right
    yStepper.moveTo(testTilt);
    zStepper.moveTo(-testTilt);
    while (yStepper.currentPosition() != testTilt || zStepper.currentPosition() != -testTilt) {
      printValues();
      yStepper.run();
      zStepper.run();
    }
    
    delay(10000);
    
    // Now turn off motors
    digitalWrite(motorDirPin1, LOW);
    digitalWrite(motorDirPin2, LOW);

    // Turn off LED
    digitalWrite(motorLEDPin, LOW);
  } 
}


//============================= OTHER FUNCTIONS ================================

void setupDelay(int seconds) {
  //blinks LED for setup
  //delays for 2 mins to setup
  for (int i=0; i<seconds; i++) {
    digitalWrite(motorLEDPin, HIGH);
    delay(500);
    digitalWrite(motorLEDPin, LOW);
    delay(500);
  }
}



void printValues() {
  Serial.print(frontSpeed);
  Serial.print(" ");
  Serial.print(backSpeed);
  Serial.print(" ");
  // Serial.print(tiltRaw);
  // Serial.print(" ");
  Serial.print(tilt);
  Serial.print(" ");
  Serial.print(Xval);
  Serial.print(" ");
  Serial.print(Yval);
  Serial.print(" ");
  Serial.print(autoValue);
  Serial.print(" ");
  Serial.println(Sval);
}




// const int Xpin = A0;
// const int Ypin = A1;

// void setup() {
//   Serial.begin(115200);
//   pinMode(Xpin, INPUT);
//   pinMode(Ypin, INPUT);
// }

// void loop() {
//   int Xval = analogRead(Xpin);
//   int Yval = analogRead(Ypin);
//   int backSpeed = (15.)*abs((Xval-324.)/312.) + (120.);
//   int frontSpeed = (15.)*abs((Xval-324.)/304.) + (120.);
//   int leftSpeed = (15.)*abs((Yval-331.)/113.) + (120.);
//   int rightSpeed = (15.)*abs((Yval-331.)/316.) + (120.);
//   Serial.print(Xval);
//   Serial.print(" ");
//   Serial.print(Yval);
//   Serial.print("|");
//   Serial.print(backSpeed);
//   Serial.print(" ");
//   Serial.print(frontSpeed);
//   Serial.print("|");
//   Serial.print(leftSpeed);
//   Serial.print(" ");
//   Serial.println(rightSpeed);
  

//   delay(100);
// }

	delay(1000); // Wait a second
}

The code above serves as the control mechanism for the robot’s motion and orientation using a joystick interface. It enables the manipulation of the ball’s speed in both forward and backward directions by altering output values of the DC motors, providing a dynamic means of propulsion. Furthermore, the code governs the degree of tilt applied to the ball by altering output values for the stepper motors, facilitating the creation of intricately curved paths with varying radii. Together, this allows Ball-E to navigate through complex trajectories by adjusting its speed and tilt, ultimately enabling precise and agile movement.

 

Exterior Switch

Previously, Ball-E required manual assembly and activation, requiring a repetitive assembly and disassembly processes for each testing session. This proved to be a cumbersome and time-consuming procedure, hindering our ability to efficiently evaluate and iterate on its performance. To streamline our testing process and enhance efficiency, we constructed an external switch that allows us to conveniently power the robot on and off without the need for manual assembly and disassembly. This simple yet effective modification has significantly expedited our testing procedures, enabling us to focus more time on improving the robot’s performance.

Rewiring the Arduino


The previous design iteration proved to be overly complex, and required the use of two separate Arduino boards for its operation. This intricate setup not only added unnecessary complexity but also made wire management a challenging task. However, through rewiring the Arduino, we were able to refine the design and achieve a more streamlined solution. The enhanced design successfully integrated all necessary functionalities within a single Arduino board, simplifying the overall system architecture and improving wire management.

First successful run


 

Feedbacks/Challenges Faced:

  1. The implemented On/Off switch was difficult to access as it was within the Ball-E and required manual probing. We considered implementing the switch on the exterior of Ball-E, but this might create an issue of wires entanglement when Ball-E rotated. A new design of the On/Off switch was required
  2. Prone to scratches. Ball-E’s exterior case was scratched by rougher terrains, and this might impair further developments of the camera as the case may become non-transparent from the scratches. A new material for the exterior case had to be sourced.

Leave a Reply

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