03.5 Implementation
1. Fabrication and assemby
1.1. Claw implementation
1.1.1. Parts table
Parts name | CAD | Quantities |
|---|---|---|
Link 1 | 1 | |
Link 2 | 4 | |
Link 3 |
| 4 |
Link 4 with rack |
| 1 |
Motor (FS90R) with pinion |
| 1 |
Pin hinge |
| 12 |
1.1.2. Implementation process
All parts of the claw, including the pin hinges, were 3D-printed. Because the claw is very small and cannot easily accommodate bearings—and to further reduce self-weight—we used customized printed hinges to connect the various links.
The assembly process is as follows: First, mount the motor and pinion onto the ground link (L1). Next, slide link L4 into L1. Then, attach all L2 links to L1 using the printed hinges. Finally, install all L3 links to complete the structure.
1.1.3. Implemented claw
As shown in the video, the implemented claw could successfully grab a golf-size foam ball:
1.2. Arm implementation
1.2.1. Parts table
Parts name | CAD | Quantities |
|---|---|---|
Part 1 |
| 1 |
L1 |
| 1 |
L2 |
| 1 |
L3 |
| 1 |
L4 |
| 1 |
L5 |
| 1 |
L6 |
| 1 |
L7 |
| 1 |
L8 |
| 2
|
Rod |
| 1 |
Translation bearing holder |
| 1 |
MG 995 Servo |
| 1 |
1.2.2. Implementation process
The assembly process is as follows: First, mount the motor and the linear guide rail onto Part 1. Next, attach link L4 to the motor’s servo horn. Then, install the slider block onto the guide rail. After that, connect L4 and the slider using link L5, forming the slider–crank mechanism that converts motor rotation into linear slider motion. Finally, assemble the arm links in order: L2, L8 → L1, L8 → L3, L6 → L7, completing the lifting arm structure.
1.2.3. Implemented arm
1.3. Translation system implementation
1.3.1. Parts table
Parts name | CAD or parts | Quantities |
|---|---|---|
End Support 1 |
| 1 |
End Support 2 |
| 1 |
Trolley |
| 1 |
Motor Mount (Adapter not shown) |
| 1 |
Screw Bolt and Lead Screws |
| 1 |
Rod |
| 2 |
Extrusion Rail |
| 4 |
Linear Bearings |
| 4 |
Rotational Bearing (8mm) |
| 2 |
1.3.2. Implementation process
The end supports were 3D-printed to allow custom mounting of the motor, screw bolt, radial bearings, and guide rods, as well as easy attachment of the gantry to the aluminum extrusion rail frame. Similarly, the trolley was also 3D-printed to allow a custom fit for the linear bearings and mounting of the extension arm, with special consideration given to rod spacing to provide clearance for the extension arm stepper motor. A custom motor mount adapter was 3D-printed to connect the stepper motor shaft to the lead screw, and a matching lead-screw nut was used to connect the trolley to the screw bolt.
The assembly process follows this order: end support 2 (with bearings), guide rods, screw bolt, trolley (with bearings), end support 1, motor and motor mount, and finally the extrusion rails.
1.3.3. Implemented translation system
1.4. Final implementation
For the final implementation, all three subsystems were combined: the claw, arm, and horizontal translation system. The arm was mounted to the trolley of the horizontal translation system, followed by the attachment of the claw. A wooden base was constructed to create multiple designated levels (buckets) for the system to drop the colored balls.
Additionally, a 3D-printed housing was made to hold an Arduino color sensor, allowing for autonomous detection of the ball color. All wiring for the project was routed through the aluminum extrusion rails and secured along the frame, with the Arduino mounted to the end support.
2. Electronics and circuitry
The electronics in this system consists of an Arduino uno and breadboard attached to the side of the frame, connected to 3 servo motors and a color sensor. The servo for the arm slider crank and the claw mechanism are constantly moving throughout the process, so the wiring was designed to allow for slack while not getting in the way of movement. The color sensor and the lead screw servo are fixed to the frame so the wiring is fixed as well. The larger servos are powered through a 6V DC power adapter plugged into the wall and the Arduino is powered through a laptop connection.
3. Software development
The software of this robotic system is broken into two parts. The first part is the kinematic analysis code that gives the time values for each angle step to minimize acceleration at the claw. The second part is the Arduino code that actually runs the entire robot. The kinematic analysis code takes the lengths of all the arms, thresholds for maximum acceleration and desired velocity, and the angle range to be simulated. It then iteratively calculates the acceleration at each point on the system and iteratively changes the timestep to get the calculated acceleration and velocity to acceptable levels. After it does this for the entire range, it does the same thing starting from the maximum angle to the minimum. These 2 arrays of timesteps are spliced together with the second array in reverse in order to get the entire timestep array for moving the arm down. The Arduino code checks the values given by the color sensor, and determines if there is an orange ball, a white ball, or nothing. If there is nothing the sensor checks again but if there is a ball the code uses the color to determine which variables to use for distance and height. It uses an array of timesteps given by the kinematic analysis code to carefully lower the arm and pick up the ball, then return back up. The lead screw servo then is spun until the mechanism is in the correct place to put the ball down and uses another timestep array to carefully lower the ball and drop it. Lastly the code brings the arm back up and resets it to hovering above the color sensor, waiting for another ball to be recognized.
#include <Servo.h> // Include the Servo library
int zeroto90array[] = {700, 887, 1056, 1208, 1344, 1464, 1569, 1660, 1738, 1815, 1892, 1969, 2046, 2123, 2200, 2277, 2354, 2431, 2508, 2585, 2661, 2737, 2813, 2888, 2963, 3037, 3111, 3184, 3257, 3329, 3401, 3472, 3542, 3612, 3681, 3749, 3817, 3884, 3950, 4015, 4080, 4144, 4207, 4269, 4330, 4380, 4429, 4477, 4524, 4570, 4616, 4661, 4705, 4748, 4790, 4832, 4873, 4913, 4952, 4990, 5027, 5064, 5100, 5135, 5169, 5202, 5234, 5265, 5296, 5326, 5355, 5383, 5411, 5439, 5467, 5495, 5523, 5551, 5580, 5611, 5645, 5681, 5721, 5767, 5822, 5887, 5963, 6051, 6670, 7929};
int closearray[] = {700, 887, 1056, 1208, 1344, 1464, 1569, 1660, 1738, 1815, 1892, 1969, 2046, 2123, 2200, 2277, 2354, 2431, 2508, 2585, 2661, 2731, 2801, 2871, 2941, 3011, 3081, 3151, 3221, 3291, 3361, 3431, 3501, 3571, 3649, 3740, 3845, 3965, 4101, 4254, 4888, 7044};
int fararray[] = {700, 887, 1056, 1208, 1344, 1464, 1569, 1660, 1738, 1815, 1892, 1969, 2058, 2160, 2276, 2407, 2554, 2718, 3355, 4555};
double fartime = 47750;
double closetime = 24000;
double maxanglefar = 18;
double maxangleclose= 40;
int newanglearray[] = {};
int newtime = fartime;
int newangle = 89;
int counter= 0;
int farcounter = 0;
Servo myservo; // Create a servo object
Servo screw;
Servo clawservo;
const int STOP = 92; // Your stop value, calibrated
const int SPEED_FWD = 14; // Forward rotation offset
const int SPEED_REV = 12; // Reverse rotation offset (integer is fine)
const int TIME_FWD = 600; // Forward rotation time (ms)
const int TIME_REV = 500; // Reverse rotation time (ms)
#define S0 2
#define S1 3
#define S2 4
#define S3 5
#define sensorOut 6
#define WINDOW_SIZE 5
int readings[WINDOW_SIZE];
int index = 0;
long sum = 0;
int averaged_value = 0;
int val = 0;
// Stores frequency read by the photodiodes
int redFrequency = 0;
int greenFrequency = 0;
int blueFrequency = 0;
int ballcheck = 0;
#define servoPin 9
#define screwPin 7
void setup() {
pinMode(S0, OUTPUT);
pinMode(S1, OUTPUT);
pinMode(S2, OUTPUT);
pinMode(S3, OUTPUT);
// Setting the sensorOut as an input
pinMode(sensorOut, INPUT);
// Setting frequency scaling to 20%
digitalWrite(S0,HIGH);
digitalWrite(S1,LOW);
// Initialize readings array to 0
for (int i = 0; i < WINDOW_SIZE; i++) {
readings[i] = 0;
}
myservo.attach(servoPin); // Attaches the servo on pin 9 to the servo object // Move the servo to the 90-degree position
screw.attach(screwPin);
screw.write(90); // stop the motor
clawservo.attach(10);
delay(5000);
clawservo.write(STOP + SPEED_FWD);
delay(TIME_FWD);
clawservo.write(STOP);
delay(1000);
clawservo.write(STOP - SPEED_REV);
delay(TIME_REV);
clawservo.write(STOP);
delay(1000);
Serial.begin(9600);
}
void loop() {
digitalWrite(S2,LOW);
digitalWrite(S3,LOW);
// Reading the output frequency
redFrequency = pulseIn(sensorOut, LOW);
// Printing the RED (R) value
//Serial.print("R = ");
//Serial.print(redFrequency);
delay(100);
// Setting GREEN (G) filtered photodiodes to be read
digitalWrite(S2,HIGH);
digitalWrite(S3,HIGH);
// Reading the output frequency
greenFrequency = pulseIn(sensorOut, LOW);
// Printing the GREEN (G) value
//Serial.print(" G = ");
//Serial.print(greenFrequency);
delay(100);
// Setting BLUE (B) filtered photodiodes to be read
digitalWrite(S2,LOW);
digitalWrite(S3,HIGH);
// Reading the output frequency
blueFrequency = pulseIn(sensorOut, LOW);
// Printing the BLUE (B) value
//Serial.print(" B = ");
//Serial.print(blueFrequency);
delay(100);
//Serial.print(", ");
if (redFrequency < 35 && greenFrequency > 40 && blueFrequency > 40) {
//Serial.println("Orange");
val = 1;
newtime = closetime;
newangle = maxangleclose;
}
else if (redFrequency < 35 && greenFrequency < 35 && blueFrequency < 30) {
//Serial.println("White");
val = -1;
newtime = fartime;
newangle = maxanglefar;
}
else{
//Serial.println("Cannot confirm");
val = 0;
}
sum = sum - readings[index]; // Subtract the oldest reading
int value = val; // Read the new sensor value
readings[index] = value; // Store the new reading
sum = sum + value; // Add the new reading to the sum
index = (index + 1) % WINDOW_SIZE; // Advance the index, wrapping around
averaged_value = sum / WINDOW_SIZE; // Calculate the average
if (averaged_value == 1){
Serial.println("Orange");
ballcheck = 1;
}
else if (averaged_value == -1){
Serial.println("White");
ballcheck = 2;
}
else{
Serial.println("Cannot confirm");
ballcheck = 0;
}
if (ballcheck != 0){
if (ballcheck == 1){
for (int angle = 0; angle <= 89; angle += 1) {
myservo.write(angle);
if (counter == 0){
delay(zeroto90array[counter]);
}
if (counter!= 0){
delay(zeroto90array[counter]-zeroto90array[counter-1]);
}
counter = counter + 1;
}
delay(1000);
clawservo.write(STOP + SPEED_FWD);
delay(TIME_FWD);
clawservo.write(STOP);
delay(1000);
// And back from 180 to 0 degrees:
for (int angle = 89; angle >= 0; angle -= 1) {
counter = counter - 1;
myservo.write(angle);
if (counter == 0){
delay(zeroto90array[counter]);
}
if (counter!= 0){
delay(zeroto90array[counter]-zeroto90array[counter-1]);
}
}
delay(1000);
screw.write(180); // rotate the motor counter-clockwise
delay(closetime);
screw.write(90); // stop the motor
delay(1000); // stay stopped
for (int angle = 0; angle <= newangle; angle += 1) {
myservo.write(angle);
if (farcounter == 0){
delay(closearray[farcounter]);
}
if (farcounter!= 0){
delay(closearray[farcounter]-closearray[farcounter-1]);
}
farcounter = farcounter + 1;
}
Serial.println('hi');
delay(1000);
clawservo.write(STOP - SPEED_REV);
delay(TIME_REV);
clawservo.write(STOP);
delay(1000);
for (int angle = newangle; angle >= 0; angle -= 1) {
farcounter = farcounter - 1;
myservo.write(angle);
if (farcounter == 0){
delay(closearray[farcounter]);
}
if (farcounter!= 0){
delay(closearray[farcounter]-closearray[farcounter-1]);
}
}
delay(1000);
clawservo.write(STOP + SPEED_FWD);
delay(TIME_FWD);
clawservo.write(STOP);
delay(1000);
screw.write(0); // rotate the motor clockwise
delay(closetime); // keep rotating
screw.write(90); // stop the motor
delay(1000); // stay stopped
clawservo.write(STOP - SPEED_REV);
delay(TIME_REV);
clawservo.write(STOP);
delay(1000);
// Nothing needed in the loop for a fixed position
ballcheck = 0;
for (int i = 0; i < WINDOW_SIZE; i++) {
readings[i] = 0;
}
sum = 0;
}
if (ballcheck == 2){
Serial.print(newtime);
for (int angle = 0; angle <= 89; angle += 1) {
myservo.write(angle);
if (counter == 0){
delay(zeroto90array[counter]);
}
if (counter!= 0){
delay(zeroto90array[counter]-zeroto90array[counter-1]);
}
counter = counter + 1;
}
delay(1000);
clawservo.write(STOP + SPEED_FWD);
delay(TIME_FWD);
clawservo.write(STOP);
delay(1000);
// And back from 180 to 0 degrees:
for (int angle = 89; angle >= 0; angle -= 1) {
counter = counter - 1;
myservo.write(angle);
if (counter == 0){
delay(zeroto90array[counter]);
}
if (counter!= 0){
delay(zeroto90array[counter]-zeroto90array[counter-1]);
}
}
delay(1000);
screw.write(180); // rotate the motor counter-clockwise
delay(fartime);
screw.write(90); // stop the motor
delay(1000); // stay stopped
for (int angle = 0; angle <= newangle; angle += 1) {
myservo.write(angle);
if (farcounter == 0){
delay(fararray[farcounter]);
}
if (farcounter!= 0){
delay(fararray[farcounter]-fararray[farcounter-1]);
}
farcounter = farcounter + 1;
}
Serial.println('hi');
delay(1000);
clawservo.write(STOP - SPEED_REV);
delay(TIME_REV);
clawservo.write(STOP);
delay(1000);
for (int angle = newangle; angle >= 0; angle -= 1) {
farcounter = farcounter - 1;
myservo.write(angle);
if (farcounter == 0){
delay(fararray[farcounter]);
}
if (farcounter!= 0){
delay(fararray[farcounter]-fararray[farcounter-1]);
}
}
delay(1000);
clawservo.write(STOP + SPEED_FWD);
delay(TIME_FWD);
clawservo.write(STOP);
delay(1000);
screw.write(0); // rotate the motor clockwise
delay(fartime); // keep rotating
screw.write(90); // stop the motor
delay(1000); // stay stopped
clawservo.write(STOP - SPEED_REV);
delay(TIME_REV);
clawservo.write(STOP);
delay(1000);
// Nothing needed in the loop for a fixed position
ballcheck = 0;
for (int i = 0; i < WINDOW_SIZE; i++) {
readings[i] = 0;
}
sum = 0;
}
}
}