Skip to content

11. Machine Design and Mechanical Design

This week, our group decided to design, produce, and automate a claw machine! You can access our group site here.

Assignment

group assignment:

  • design a machine that includes mechanism+actuation+automation+application

  • build the mechanical parts and operate it manually

individual assignment:

  • document the group project and your individual contribution

Planning

Starting out, after brainstorming some machine ideas, we eventually agreed to follow through with the claw machine. Although the design of the claw machine was intuitive, and there were many existing examples, this project still involved a lot of components. For one, we needed to build a CoreXY system gantry with some form of z-axis built-in for the claw. Additional elements involved a joystick (movement), a 16x2 I2C LCD screen (displays time left), an light up push-button (automates claw), and neopixel strips (decoration).

Research

Individual Contribution

For my individual contribution, I mainly worked on the frame, parts of the gantry, assembling the claw machine, and testing additional electronics.

Claw Machine Frame

For the frame, I started out by sketching the sides of the claw machine, the top and bottom, and the playing area, before adding slots and tabs accordingly. I had measured the wood beforehand and added a .005” offset to the thickness (.47” > .475”), as suggested by Mr. Budzichowski and Mr. Dubick.

Here is the scaled down version of the sketch.

Here are the parameters I established initially:

After I finished designing the individual components, I extruded the sketches by .475” and used the align tool to assemble them.

Another aspect of this design was to leave enough room for the gantry at the top. After communicating with Connor, I decided that 3.5” was enough to fitthe gantry (~2” tall).

We saved the file as a .dxf and imported it into Aspire to begin generating the toolpaths.

I had to generate a bunch of toolpaths for this design, as there were several profiles and pockets of various depths. I first configured the job setup:

After checking if all vectors were closed, I used the built-in dogbone feature and added them to the design accordingly (I changed the measurement value to the appropriate tool diameter/2):

INSERT IMAGE OF VECTOR IN ASPIRE

In terms of milling, I followed the our lab’s ShopBot procedures. I first ran an aircut at a 2” z-offset, before homing and running the job again.

The only issue during this process was that the tabs were too shallow and didn’t hold up during the pocket toolpaths. Thankfully, I noticed this issue early on, and with the help of Garrett Nelson, I used the brad nail gun to fasten the wood down again. Later on, Isimply used a chisel and a mallot to remove the sides from the sacrificial board.

After sanding down the edges with 80-100 grit sandpaper, we were able to easily assemble the sides of the claw machine.

For easy access, we decided to just assemble the sides and playing area. Here’s what it looks like:

Acrylic Panels

Next, I needed to install acrylic panels for the sides and back of the claw machine. Because I had created the rectangle pocket during the CNC job, designing the panels was super quick and easy. I cut 2 ~11” x 13.5” pieces of 1/4” acrylic, and a ~13.3” x 12” piece on the laser cutter.

I also added four circles to fit m3 screws.

The first time I ran the job, it didn’t cut through entirely, so I had to run the job again.

Aligning the panels to the window, Alana and I drilled holes through the sides of the claw machine and installed m3 screws/nuts. Here is what the front looks like after assembling:

Neopixels Installation

For the top of the machine, I also wanted to include a set of neopixels to serve as a decorative piece for the claw machine. I first soldered and tested a strand of neopixels using the following code, which was taken from Dariyah Strachan’s documentation:

// A basic everyday NeoPixel strip test program.

// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
//   connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
//   a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1:
#define LED_PIN    D3

// How many NeoPixels are attached to the Arduino?
#define LED_COUNT 30

// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)


// setup() function -- runs once at startup --------------------------------

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  strip.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  strip.show();            // Turn OFF all pixels ASAP
  strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}


// loop() function -- runs repeatedly as long as board is on ---------------

void loop() {
  // Fill along the length of the strip in various colors...
  colorWipe(strip.Color(255,   0,   0), 50); // Red
  colorWipe(strip.Color(  0, 255,   0), 50); // Green
  colorWipe(strip.Color(  0,   0, 255), 50); // Blue

  // Do a theater marquee effect in various colors...
  theaterChase(strip.Color(127, 127, 127), 50); // White, half brightness
  theaterChase(strip.Color(127,   0,   0), 50); // Red, half brightness
  theaterChase(strip.Color(  0,   0, 127), 50); // Blue, half brightness

  rainbow(10);             // Flowing rainbow cycle along the whole strip
  theaterChaseRainbow(50); // Rainbow-enhanced theaterChase variant
}


// Some functions of our own for creating animated effects -----------------

// Fill strip pixels one after another with a color. Strip is NOT cleared
// first; anything there will be covered pixel by pixel. Pass in color
// (as a single 'packed' 32-bit value, which you can get by calling
// strip.Color(red, green, blue) as shown in the loop() function above),
// and a delay time (in milliseconds) between pixels.
void colorWipe(uint32_t color, int wait) {
  for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    delay(wait);                           //  Pause for a moment
  }
}

// Theater-marquee-style chasing lights. Pass in a color (32-bit value,
// a la strip.Color(r,g,b) as mentioned above), and a delay time (in ms)
// between frames.
void theaterChase(uint32_t color, int wait) {
  for(int a=0; a<10; a++) {  // Repeat 10 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in steps of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show(); // Update strip with new contents
      delay(wait);  // Pause for a moment
    }
  }
}

// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel.
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this loop:
  for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
    // strip.rainbow() can take a single argument (first pixel hue) or
    // optionally a few extras: number of rainbow repetitions (default 1),
    // saturation and value (brightness) (both 0-255, similar to the
    // ColorHSV() function, default 255), and a true/false flag for whether
    // to apply gamma correction to provide 'truer' colors (default true).
    strip.rainbow(firstPixelHue);
    // Above line is equivalent to:
    // strip.rainbow(firstPixelHue, 1, 255, 255, true);
    strip.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}

// Rainbow-enhanced theater marquee. Pass delay time (in ms) between frames.
void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      strip.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<strip.numPixels(); c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / strip.numPixels();
        uint32_t color = strip.gamma32(strip.ColorHSV(hue)); // hue -> RGB
        strip.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      strip.show();                // Update strip with new contents
      delay(wait);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
    }
  }
}

Seeing that it worked, we readjusted the soldering and wiring to fit within the channel on the top piece.

Whew! Cheers to 50+ neopixels installed and working!

CoreXY

For the gantry, Connor Cruz designed a CoreXY and simulated it in Fusion360. CoreXY uses two main stepper motors and belts to move along the x and y axis. Here is a diagram of the gantry:

Initially, the gantry was designed to have two parallel components for the pulleys and a middle section, but after re-evaluating the structure and the length of the sides (what could be 3D printed vs. what couldn’t), I removed the middle section and modified the sides to join together.

Here was his initial gantry design:

Here is his final gantry design assembled:

However, we still needed to figure out how to move the claw up and down to grab onto the items. Our inital idea was to use a pulley system with the claw attached, but we weren’t sure if it would work. After meeting up, we settled on using a stepper motor with a lead screw. I began designing a simple connector piece to connect the gantry slider with the stepper motor. I used the [OnShape library] to obtain models for the motor and slider, before projecting the screw holes onto my new sketch and adding a .001” offset. Here is what it looks like:

I imported the 20 mm T-slot to simulate what it would look like attached to the linear axis:

I saved the file as a .STL and brought it into PrusaSlicer to generate the g-code. With Kabir Nawaz’s help, I saved it on my USB and printed it on my PrusaMini.

The only issue was that the models were slightly inaccurate, causing the alignment to be off. Thankfully, the difference wasn’t drastic, so I just used a drill to modify the size. Here is what it looks like fully assembled:

For installation, I used M3 screws of various lengths.

Although this piece fits the slider, we ultimately had to redesign it to account for the hooks, which would keep the belts stationary while they wrapped around the pulleys (refer back to the CoreXY diagram for more information).

Reflection


Last update: April 12, 2024