Implementation (11)
Manufacturing Method
All main parts were fabricated with FDM, using PLA.
As an additive manufacturing process, FDM is very versatile and requires no additional effort when producing many different parts compared to multiple of the same part. It is also capable of producing complex 3D geometries not possible with conventional manufacturing methods such as milling.
FDM was also chosen over other additive manufacturing methods due to the ease of use and minimal post processing required. FDM machines are relatively affordable compared to other AM processes. The material used is widely available in an easy to handle filament form and is nontoxic with very little odor, unlike resin used in stereolithography or DLP. Also unlike stereolithography or DLP, the part does not need additional post processing, apart from the removal of support material. This greatly speeds up iteration and production.
One issue to pay attention to with FDM is the loss of dimensional accuracy on overhangs beyond 45 degrees. The down-face of overhang layers past this angle tends to be stringy and slightly sag due to gravity pulling the extruded filament downwards before it can harden. Sometimes, support material can adhere to the part and ruin the surface finish.
Another issue is the dimensional offset due to the width of the bead of extruded material. The bead is centered on the dimension specified in the CAD model and does not account for this bead width. As such, dimensions of features such as holes where a shaft must pass through, must have their dimensions offset.
Material
PLA has satisfactory material properties and is easy the store and use, minimizing the effort required to produce parts.
PLA has good tensile strength at room temperatures, sufficient for our use case and operating conditions. The arm is a proof-of-concept and as such takes no load; thus the strength of PLA is sufficient, even if it is lower compared to other available materials such as nylon or ABS.
It is also not an exaggeration to state that every consumer FDM machine produced today is capable of using PLA to achieve satisfactory results. As such, the material is desirable when attempting to produce parts with good tolerances, such as meshing gears or sliders. No additional consideration is required when using the material, unlike other materials which may require constant drying and heating during the production process such as nylon.
Designing for Manufacture
Designs were adapted to account for the strengths and weaknesses of the manufacturing method chosen. The changes are listed as follows.
Holes or other dimensionally sensitive mating surfaces are offset 0.1 to 0.25 mm to account for the bead width of the extruded material.
The circular cavities on the Bevel Center have been enlarged to account for the sagging of the upper surface of the cavity due to gravity.
The crank rocker connector has octagonal ends. This prevents overhangs from occurring due to the mounting tabs and provides additional strength at the mounting point.
To prevent loss of dimensional accuracy caused by sagging overhangs or the need to use supports, which would be difficult to remove from such a small space, the sides of the slider are angled at 45 degrees.
Design for Assembly
The design was further modified to make assembly possible.
Bushings were created to account for the dimensional discrepancy between the 1/8” square shafts and 7mm ID of the bearings used. (yes, the design uses both imperial and metric. yes, that is incredibly cursed. no, that should not be done. Considerable design work had been done in metric before the decision was made to use VEX components, which were imperial sized)
The crank arm was raised at the end in order to account for the thickness of the motor which was to be screwed on. A circular cut was created to provide space for the planet gear.
Screw holes were countersunk to ensure clearance for screw heads.
Holes into which screws were threaded were not offset so that the screw threads would interfere with the hole walls.
The shaft inserts of the gears do not have dimensional offsets in order to create a tight press fit to minimize backlash.
The rocker bracket was modified to increase range of motion by creating a semicircular cutout, as the old design would collide with the crank rocker connector and restrict motion.
Original crank rocker connector design | New crank rocker design |
Spacers were used to join parts at fixed distances from each other.
Holes where joints would be located were designed to take screws, which can be cantilevered while also providing rigid connections - as opposed to shafts, which must be supported from both ends.
Electronics
The same motors as those used for the robot car project were used here, due to their low cost, ease of setup and control, and relatively small size and low weight.
It was requested by the teaching team that the students keep costs low wherever possible. As a result, the small yellow motors were reused from the robot car project. Two additional motors were obtained as 4 were needed, at a rate of 8 for 10 dollars.
Since the motors are low voltage (6V), the LM298 motor controller can be used, with each controller driving two motors. The LM298 is small and cost effective, and we already had one from the robot car project.
The motors have an all plastic construction, keeping the weight low. As such, structural pieces and gears could be made smaller as the load requirements are much lower. The motors are also relatively small with convenient mounting points so that designing around them was easy.
The one drawback of the motors is the lack of precise motion control. However, due to the project’s focus on mechanisms rather than control, this was not an issue.
An arduino was used to control the entire system, as it is easy to setup and program. We also had one from the robot car project. The signal outputs were also of sufficient voltage to be used by the motor controllers and it could be powered by relatively small batteries.
Mateo, Aakarsh, Yaman add ur stuff here
Software
The following is the arduino code used to take inputs from 2 joysticks to control the 4 motors.
// ===================== TUNING SETTINGS =====================
// Caps the maximum motor speed (0–255)
const int MAX_SPEED_MC1 = 90;
const int MAX_SPEED_MC2 = 90;
// Minimum PWM to overcome static friction and prevent motor stalling
const int MIN_START_PWM_MC1 = 150;
const int MIN_START_PWM_MC2 = 150;
// Deadzone to ignore small joystick noise around center
const int DEADZONE = 15;
// Limits how fast PWM can change (larger value = faster response)
const int RAMP_STEP = 10;
// Joystick direction inversions
const bool INVERT_Y_JOY1 = true;
const bool INVERT_Y_JOY2 = true;
// Swap motor outputs if wiring is reversed
const bool SWAP_LEFT_RIGHT_MC1 = false;
const bool SWAP_LEFT_RIGHT_MC2 = false;
// Flip motor directions if a motor spins the wrong way
const int DIR_FLIP_LEFT_MC1 = -1;
const int DIR_FLIP_RIGHT_MC1 = +1;
const int DIR_FLIP_LEFT_MC2 = -1;
const int DIR_FLIP_RIGHT_MC2 = +1;
// ===================== JOYSTICK PINS (X/Y SWAPPED) =====================
const int J1_X = A3;
const int J1_Y = A2;
const int J2_X = A1;
const int J2_Y = A0;
// ===================== MOTOR CONTROLLER 1 (L298N #1) =====================
const int MC1_ENA = 5; // PWM speed control for motor A
const int MC1_ENB = 6; // PWM speed control for motor B
const int MC1_IN1 = 2; // Direction pins
const int MC1_IN2 = 3;
const int MC1_IN3 = 4;
const int MC1_IN4 = 9;
// ===================== MOTOR CONTROLLER 2 (L298N #2) =====================
const int MC2_ENA = 10;
const int MC2_ENB = 11;
const int MC2_IN1 = 7;
const int MC2_IN2 = 8;
const int MC2_IN3 = 12;
const int MC2_IN4 = 13;
// ===================== INTERNAL STATE FOR RAMPING =====================
int mc1_left_out = 0, mc1_right_out = 0;
int mc2_left_out = 0, mc2_right_out = 0;
// ---------------- Reads a joystick axis and maps it to -255..255 ----------------
int readAxis(int pin, bool invert) {
int v = analogRead(pin); // Raw ADC value (0–1023)
int centered = v - 512; // Center around zero
if (invert) centered = -centered;
// Ignore small drift near center
if (abs(centered) < DEADZONE) return 0;
centered = constrain(centered, -512, 511);
return map(centered, -512, 511, -255, 255);
}
// ---------------- Smoothly ramps current value toward target ----------------
int rampToward(int current, int target, int step) {
if (current < target) return min(current + step, target);
if (current > target) return max(current - step, target);
return current;
}
// ---------------- Enforces a minimum PWM to start motor motion ----------------
int applyMinStartPWM(int cmd, int minStartPwm) {
if (cmd == 0) return 0;
int sign = (cmd > 0) ? 1 : -1;
int mag = abs(cmd);
if (mag < minStartPwm) mag = minStartPwm;
return sign * mag;
}
// ---------------- Sets motor direction and PWM on an L298N channel ----------------
void setMotor(int enPin, int inA, int inB, int pwmSigned) {
int pwm = abs(pwmSigned);
pwm = constrain(pwm, 0, 255);
if (pwmSigned > 0) {
digitalWrite(inA, HIGH);
digitalWrite(inB, LOW);
} else if (pwmSigned < 0) {
digitalWrite(inA, LOW);
digitalWrite(inB, HIGH);
} else {
// Motor coasts when command is zero
digitalWrite(inA, LOW);
digitalWrite(inB, LOW);
}
analogWrite(enPin, pwm);
}
// ---------------- Converts joystick X/Y into left/right motor commands ----------------
void driveControllerFromJoystick(
int x, int y,
int maxSpeed,
int minStartPwm,
bool swapLR,
int dirFlipL, int dirFlipR,
int enA, int in1, int in2,
int enB, int in3, int in4,
int &outL, int &outR
) {
// Differential drive mixing
int leftCmd = y + x;
int rightCmd = y - x;
// Normalize to avoid clipping
int maxMag = max(abs(leftCmd), abs(rightCmd));
if (maxMag > 255) {
leftCmd = (leftCmd * 255) / maxMag;
rightCmd = (rightCmd * 255) / maxMag;
}
// Scale to maximum speed limit
leftCmd = (leftCmd * maxSpeed) / 255;
rightCmd = (rightCmd * maxSpeed) / 255;
leftCmd = constrain(leftCmd, -maxSpeed, maxSpeed);
rightCmd = constrain(rightCmd, -maxSpeed, maxSpeed);
// Apply direction flips
leftCmd *= dirFlipL;
rightCmd *= dirFlipR;
// Swap motors if required
if (swapLR) { int t = leftCmd; leftCmd = rightCmd; rightCmd = t; }
// Apply minimum start PWM
leftCmd = applyMinStartPWM(leftCmd, minStartPwm);
rightCmd = applyMinStartPWM(rightCmd, minStartPwm);
// Ramp outputs for smooth motion
outL = rampToward(outL, leftCmd, RAMP_STEP);
outR = rampToward(outR, rightCmd, RAMP_STEP);
// Send commands to motors
setMotor(enA, in1, in2, outL);
setMotor(enB, in3, in4, outR);
}
void setup() {
// Configure all motor control pins as outputs
pinMode(MC1_ENA, OUTPUT); pinMode(MC1_ENB, OUTPUT);
pinMode(MC1_IN1, OUTPUT); pinMode(MC1_IN2, OUTPUT);
pinMode(MC1_IN3, OUTPUT); pinMode(MC1_IN4, OUTPUT);
pinMode(MC2_ENA, OUTPUT); pinMode(MC2_ENB, OUTPUT);
pinMode(MC2_IN1, OUTPUT); pinMode(MC2_IN2, OUTPUT);
pinMode(MC2_IN3, OUTPUT); pinMode(MC2_IN4, OUTPUT);
}
void loop() {
// Read joystick inputs
int x1 = readAxis(J1_X, false);
int y1 = readAxis(J1_Y, INVERT_Y_JOY1);
int x2 = readAxis(J2_X, false);
int y2 = readAxis(J2_Y, INVERT_Y_JOY2);
// Drive motor controller 1 with joystick 1
driveControllerFromJoystick(
x1, y1,
MAX_SPEED_MC1,
MIN_START_PWM_MC1,
SWAP_LEFT_RIGHT_MC1,
DIR_FLIP_LEFT_MC1, DIR_FLIP_RIGHT_MC1,
MC1_ENA, MC1_IN1, MC1_IN2,
MC1_ENB, MC1_IN3, MC1_IN4,
mc1_left_out, mc1_right_out
);
// Drive motor controller 2 with joystick 2
driveControllerFromJoystick(
x2, y2,
MAX_SPEED_MC2,
MIN_START_PWM_MC2,
SWAP_LEFT_RIGHT_MC2,
DIR_FLIP_LEFT_MC2, DIR_FLIP_RIGHT_MC2,
MC2_ENA, MC2_IN1, MC2_IN2,
MC2_ENB, MC2_IN3, MC2_IN4,
mc2_left_out, mc2_right_out
);
delay(10); // Loop timing
}Mateo, Aakarsh, Yaman add ur stuff here
if my teammates don’t do their part pls don’t take points off of me thx thx T_T (Justin)
also if one of u 3 (mateo aakarsh yaman) have made your changes pls delete these 2 lines