10.4 Implementation

10.4 Implementation

Bill of Materials:

image-20250501-213429.png
Bill of Materials (BOM)

 

Fabrication and Assembly: 

Laser Cut Parts:

We used a laser cutter to manufacture several mechanical components from ¼” acrylic:

  • Long Links (8)

  • Four Bar Connector Links (2)

  • Base Plate (1)

  • Side Plates (2)

These components were designed in CAD and exported as DXF files for cutting.
Note: DXF files are provided below. Please note they are not to scale.

Figure 1. DXF of Long Link, Base Plate, Slide Plate, and Short Link

3D Printed Parts:

Several components were designed in SolidWorks and 3D printed using PLA. These include:

  • Gear Assembly (2)

    • Gears with a keyed cutout connection to the link

  • Spur Gears (2)

    • 10-tooth Spur Gear with 5mm diameter Bore to connect to the Actuator

    • 120-tooth Spur Gear with 6mm diameter Bore to connect to input link

  • Grabber Arm (1)

    • Designed to hold the Pallet and Load

  • Pegs (7)

    • For non-critical press fit connections, it holds the system together

    • 5x length 22mm

    • 2x length 12mm

  • Short Spacers (6)

    • To push links onto different planes to allow fluid motion with little interplay

Designs were exported as STL files and printed using a layer height of 0.20mm and 15% infill.

 

Figure 2. Gear Assembly
Figure 3. 120 Teeth Spur Gear
image-20250501-202010.png
Figure 4. 10 Teeth Spur Gear
Figure 5. Grabber Arm 
Figure 6. Pegs
Figure 7. Short Spacers 

Rods & Bearings:

We used 6mm steel rods to connect the linkages and mount the pivot points of the four-bar mechanism, allowing the structure to rotate freely as part of the forklift motion. To secure the holder assemblies, we used M3 screws along with lock nuts to ensure that the joints remained tight during operation. Additionally, ball bearings with 6mm inner diameter were integrated at each rotating joint to enable rotation at the joints with smooth, low-friction movement.

Electronics and Circuitry: 

Components: 

  • Arduino Uno 

  • A4988 Stepper Motor Driver 

  • 100 μF Ceramic Capacitor 

  • 1000-ohm Potentiometer 

  • Joystick

  • NEMA 17 Stepper Motor

  • 12V DC Power Supply 

Electrical Configuration:

Figure 7. Final Electronics
circuitdiagram_complete.png
Figure 8. Circuit Diagram

Software Development: 

Joystick Code Description (Program 1):

Program 1 enables real-time control of the end effector using a joystick. The user can move the joystick forward or backward to command the lifter to move upward or downward, respectively. To avoid potential instability associated with mid-lift acceleration, the lifter moves at a fixed, constant speed, functioning as a three-pole switch. When the joystick is released, the lifter immediately stops. To prevent unintended motion due to minor joystick imperfections, a deadzone is implemented where small input signals are ignored.

Potentiometer/Button Code Description (Program 2):

Program 2 takes a different approach compared to Program 1. Instead of operating with real-time control, this program allows the user to set a desired angular displacement by adjusting a potentiometer (POT). When the button is pressed, the program calculates the difference between the current resistance value of the POT and the last recorded value (which defaults to zero at the start). It then multiplies this resistance difference by an experimentally determined calibration coefficient to calculate the time needed to run the motors at a preset constant speed. After moving to the new position, the program saves the updated resistance value to track the current location.

The calibration coefficient is calculated using a simple formula:
Calibration Coefficient = Full Cycle Time / Resistance Span

Here, Full Cycle Time is the time it takes the mechanism arm to move from its minimum to maximum position at the set constant speed. Resistance Span is the maximum resistance of the potentiometer (1024 ohms in our case). Using a potentiometer with a larger resistance range could allow for slightly more precise positioning.

In the future, Program 2 could be expanded by integrating PID control to enable the lifter arm to automatically reach different shelf levels with greater accurately.

 

Program 1:

// Joystick-Controlled Stepper Motor System // Define analog input pins for joystick axes const int VRxPin = A0; // X-axis input const int VRyPin = A1; // Y-axis input // Define digital output pins for stepper motor control #define DIR_PIN 2 // Direction control pin #define STEP_PIN 3 // Step control pin // Setup function: Initialize serial communication and configure output pins void setup() { Serial.begin(9600); pinMode(DIR_PIN, OUTPUT); pinMode(STEP_PIN, OUTPUT); } // Helper variables int current_direction = HIGH; // Tracks the current direction of the motor bool want_move = false; // Flag to determine if motor movement is required void loop() { // To change which joystick axis controls the motor (X or Y): // Swap the assignments below. For example: // int yValue = analogRead(VRxPin); // int xValue = analogRead(VRyPin); // Then remove or comment out the two lines directly below this note. int xValue = analogRead(VRxPin); int yValue = analogRead(VRyPin); // Define the joystick's neutral (rest) position const int ySSval = 508; // Define a deadband range around the neutral position // Increasing this value reduces sensitivity (i.e., larger joystick movement required to activate motor) const int range = 300; // Determine whether the joystick is sufficiently displaced to warrant motor movement if ( (yValue > ySSval && yValue > (ySSval + range)) || (yValue < ySSval && yValue < (ySSval - range)) ) { want_move = true; if (yValue > ySSval) { // Move motor in one direction if (current_direction != HIGH) { digitalWrite(DIR_PIN, HIGH); current_direction = HIGH; } } else if (yValue < ySSval) { // Move motor in the opposite direction if (current_direction != LOW) { digitalWrite(DIR_PIN, LOW); current_direction = LOW; } } } else { want_move = false; // Joystick is within the neutral range; no movement } // Execute motor stepping if movement is requested Serial.println(current_direction); if (want_move) { digitalWrite(STEP_PIN, HIGH); delayMicroseconds(4000); // Adjust delay to control stepping speed digitalWrite(STEP_PIN, LOW); delayMicroseconds(4000); // Adjust delay to control stepping speed } else { Serial.println("pass"); } }

 

Program 2:

#include <AccelStepper.h> // Pin assignments const int potentiometerPin = A3; // Analog pin connected to potentiometer const int buttonPin = 6; // Digital pin connected to pushbutton // Variables for potentiometer calibration int initialPotValue = 0; int currentPotValue = 0; int resistanceDifference = 0; float calibrationCoefficient = 10.0; // Coefficient to convert resistance change to motor run time (to be calibrated) int motorRunTime = 0; // Time to run the motor in milliseconds // Stepper motor setup (adjust pin numbers and motor specs as needed) AccelStepper stepper(AccelStepper::DRIVER, 3, 2); // (Step pin, Direction pin) void setup() { // Initialize button input and serial communication pinMode(buttonPin, INPUT); Serial.begin(9600); // Initialize stepper motor settings stepper.setMaxSpeed(300); // Set maximum motor speed stepper.setAcceleration(200); // Set motor acceleration // Record the initial potentiometer reading initialPotValue = analogRead(potentiometerPin); } void loop() { // Read the current potentiometer value currentPotValue = analogRead(potentiometerPin); // Check if the pushbutton is pressed if (digitalRead(buttonPin) == HIGH) { delay(300); // Debounce delay Serial.println("BUTTON PRESSED"); // Calculate the change in potentiometer resistance resistanceDifference = currentPotValue - initialPotValue; // Calculate how long the motor should run (based on calibration coefficient) motorRunTime = resistanceDifference * calibrationCoefficient; Serial.println(motorRunTime * 0.001); // Output run time in seconds for reference // Command the stepper to move forward by a set amount (e.g., 1000 steps) stepper.moveTo(stepper.currentPosition() + 1000); stepper.setSpeed(100); // Set motor speed (adjust as needed) // Run the motor at constant speed for the calculated duration unsigned long startTime = millis(); while (millis() - startTime < motorRunTime) { stepper.runSpeed(); } // After movement, update the initial potentiometer reading initialPotValue = currentPotValue; } // Optional: Maintain background motor activity (uncomment if needed) // stepper.run(); }

Assembly:

  1. Bearing and Gear Installation
    Bearing press-fits were installed at joint interfaces, with outer diameters glued to acrylic links for smooth rotation.

  2. Linkage and Spacer Assembly
    Acrylic links and spacers were connected to form the two double four-bar mechanisms, with 3D printed pegs inserted at joints to reduce reliance on glue and enhance stability. The gear assemblies were press-fit onto the rods, and the gear assembly was aligned to connect the two double four-bar mechanisms at the desired length, ensuring synchronized motion.

  3. Base Attachment
    The linked four-bar mechanisms were attached to the acrylic base. This grounded the system, providing stability and alignment.

  4. Grabber and Pallet Integration
    The 3D-printed grabber, designed as a forklift mechanism, was installed between the end effectors of the parallel four bars.

  5. Spur Gears
    Once the mechanical assembly was connected, the large spur gear (12:1 reduction) was glued to the input rod to serve as the interface between the electrical and mechanical systems.

  6. Electronics Setup
    Separately, the electronic circuit, including the Arduino and sensors, was developed, and software was tested with an isolated Nema 17 motor to ensure precise control.

  7. System Integration
    Hardware and software were integrated at the spur gear connection to drive the system via the motor. The software and motor performed well, but slippage of the large spur gear due to insufficient adhesive strength under torque demands limited performance.

Challenge
The slippage of the large gear highlighted an oversight in the gear-to-rod connection, as the adhesive couldn’t withstand the system’s torque. While the mechanical and control systems functioned as intended, this issue prevented the full potential, emphasizing the need for robust connections like keyed designs.