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
First successful run
Feedbacks/Challenges Faced:
- 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
- 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.