Team 1: Machine Building

2-axis Multi-fab Machine

Highlights


Details

This is a 2 week assignment in which we have to build and automate a machine in a group.

Team:

1. Ideation

At the start of the assignment, team members came up with the following ideas:

We decided to build a CNC Lathe, since we did not have one in the lab.

Lathe design inspiration

The design was based on the following implementations:

https://github.com/Intrinsically-Sublime/Printable-Lathe-V2

He included a 3D-printed chuck too, and powered by a drill just like how we are planing to use.

Another design that we could use is this one, the OpenSCAD file for which is available here.

2. Material Required

We did not have the MTM stages or the Gestalt boards in the lab, so we had to make or salvage all the parts.

We needed the following material:

We decided to 3D print the lathe parts, some in PLA using the Ultimaker and others, like the coupler, in ABS using the Stratasys printer.

The X, Y stages would be designed and laser-cut out of acrylic.

For guide rails, we would salvage parts from a scrapyard.

Sibu had a spare RAMPS board, which we could use.

We had stepper motors in the lab.

3. Pooling Parts

Our instructor set a rule of not buying any mechanical components for the machine, instead salvage or make it yourself. We are strictly adhering to the rule that "we will be making or salvaging whatever we could, we will do the best we can".

Scrap Hunt

Anyway a few of us Sibu, Yadu, Vishnu, Nadeem and Safwan went in search of old printers from E-waste collectors. We were hopping to get Motors including stepper motors, threaded rods, smooth rods, timing belts, other useful mechanical components like gears, springs, screws. We could also get many useful electronics components too, like power-bricks (power bricks of the printers), positions sensors, switches, connectors etc.
Salvaging

In summary, this is what we got.

We also got smooth rods for the guide rails. But we couldn't get any threaded rods, and we need it for the machines.
There are again multiple options, could use standard threaded rods that are used for fastening, we could use long bolts, or we could use the special high quality threaded rods made for actuation, like the ones used in Ultimaker's Z-axis. There was one vendor how refused to give anything short of 3m. We didn't require long threaded rods as this is a experimental build, need only small axes. We could use the long bolts instead, and we decided to do so.

3D-Printing Lathe Parts

This is a nice 3D-printable chuck designed by Bob and made by Yuri9999 from Thingiverse.

chuck.

Source file available here.

This chuck will hold the workpiece, which will be attached to a Drill or some other motor or Dremel tool. But Dremel may not have the sufficient Torque for the purpose, it definitely has the speed.

The front housing too needed to be printed, but we decided to do it later as we needed to catch up on building the stages.
For the construction will be using 6mm acrylic and may be the thick, 12mm or 18mm plywood as a base. We will be making a press-fit assembly in acrylic but will also use cyano-acrylic glue (super-glue) for extra strength.
For the guide rods we planned to use 2 of the 8mm rods and 2 of the 6mm rods.

With chuck done (Except for the front houseing),so we have decided to take the reference of this lathe design and work upon it to make the X-Y carriage.
Also, we need to work on to design a coupler for the stepper motor to the treaded rod coupler, 5 mm to X mm coupler with parametric design.
This was the thingiverse design we have taken into consideration for the coupler. We took a test print in Ultimaker, and found out that the piece cannot withstand much strain. So, we took print in ABS plastic using dimensions.
reconfigurable stage

The base should be made using the laser cutter or the shopbot. We are not opting for 3D printing, as it takes considerable amount of time, won't be able to withstand stress and won't be accurate enough.

X-Y Stages

The tool need to move in the horizontal plane (X and Y), but unlike 3D-printer X-Y motion this need not be fast. In fact it has to be slow and precise movement. So a lead-screw is the best solution. We plan to use normal threaded rod and bolts for the actuation, as this sis a prototype and proof of concept work. A screw based motion has other advantages like it can withstand lot of axial load.

We decided to use a simple stage, 2 guide rodes and a threaded rod at the center attached to a stepper. Something like the first picture in this page, something simple as the modular stages reconfigurable stage Source.

But this need to be strong too, made using plywood or acrylic. Need not be long as the examples above, we need only about 10-15cm motion along X and Y.

4. Construction

Chuck

Jaseel and Nisha 3D printed the chunk parts using the the two 3D printers here.

The three drive gears were printed using our Stratasys Dimensions printer for the strength and quality it provides. This part is turned using an Allen-key and this will spin a connected gear which will turn a spiral piece and will tighten or loosen the lathe jaws.
We did this so that the drive gears would be stronger (using ABS) compared to printing using PLA plastic in the Ultimaker (we did not have abs plastic spools for Ultimaker 2 in our inventory).
Gear drives

The rest of the were printed using ultimaker, PLA plastic.
3dpartss

All the parts of the chuck, except for the front housing were printed in green is PLA plastic in Ultimaker. These parts are very weak, cannot handle the load, but will do fine for demonstration purpose and prototyping a proof of concept version.

X and Y axes(stages)

We need to design the X and Y stages using the materials available at the lab and the salvaged parts.
So, we decided to make something simple using the smooth rods we have and the two long bolts. The basic design comes from Sibu's Computer-Controlled Cutting module, where he did a press-fit box in OpenSCAD.
The file was modified to get the moving carriage and the overall axes. The holes are meant for m8 threaded rod. The holes for the guide rods are such that either m6 or m8 smooth rods, the two set of walls has two different holes.

The nut design was sketched using OpenSCAD. Nut Design File Also, the test and final cut for the nut was sketched using OpenSCAD which is parametric. Nut Test Design File

This is the modified OpenSCAD file for the X or Y carriage.


module copy_mirror_adj(vec=[1,1,0])
      {
        children();
        mirror([1,0,0])mirror(vec) children();
      }
module copy_mirror_opp(vec=[0,1,0])
      {
        children();
        mirror(vec) children();
      }
module copy_tran(vec=[0,0,0])
      {
        children();
        translate(vec) children();
      }

      $fn = 64; //smoother render, this number gives how many frangments are used for a circle

      l = 60;
      b = 60;
      h = 30;

      base_raise = 0;

      beam_width= .4;
      thick = 6;

      g_rod_1_dia = 5.95;
      g_rod_2_dia = 7.95;
      t_rod_dia = 8 ;

      number_of_slots_base_len = 3;
      number_of_slots_base_wid = 3;
      number_of_slots_wall = 2;

      wall_length = l;
      wall_width = b;
      wall_height = h+2*thick;

      num_slots_base_l = number_of_slots_base_len * 2;
      num_slots_base_b = number_of_slots_base_wid * 2;
      num_slots_wall_side = number_of_slots_wall * 2;
      base_slot_width_l = l / ( num_slots_base_l + 1);
      base_slot_width_b = b / ( num_slots_base_b + 1);
      fit_base_slot_width_l = base_slot_width_l + beam_width;
      fit_base_slot_width_b = base_slot_width_b + beam_width;
      wall_slot_width = wall_height / ( num_slots_wall_side + 1);
      fit_wall_slot_width = wall_slot_width + beam_width;
      slot_depth = thick;





//base and top
      copy_tran([l + 2*thick +1,b + 2*thick +1, 0])
      union(){
          translate([-l/2,-b/2,0])
          union(){
            for ( i = [ 1: 2: num_slots_base_l ] ) //top and bottom slots
            {
                translate ( [ 0, b, 0 ] )
                translate ( [ (i+.5) * base_slot_width_l , slot_depth/2, 0 ] )
                square ( size = [ fit_base_slot_width_l, slot_depth ], center = true );

                translate ( [ 0, -slot_depth, 0 ] )
                translate ( [ (i+.5) * base_slot_width_l , slot_depth/2, 0 ] )
                square ( size = [ fit_base_slot_width_l, slot_depth ], center = true );
            }

            for ( i = [ 1: 2: num_slots_base_b ] ) //left and right slots
            {
                translate ( [ l, 0, 0 ] )
                translate ( [ slot_depth/2, (i+.5) * base_slot_width_b, 0 ] )
                square( size = [ slot_depth, fit_base_slot_width_b ], center = true );

                translate ( [ -slot_depth, 0, 0 ] )
                translate ( [ slot_depth/2, (i+.5) * base_slot_width_b, 0 ] )
                square (size = [ slot_depth, fit_base_slot_width_b ], center = true );
            }
            square ( size = [l, b], center = false );
            }
    }

//wallls
//top and bottom walls
    copy_mirror_opp(vec=[0,1,0])
    translate([-l/2,-b/2,0])
    union()
    {
        difference()
        {
            translate ( [ 0, b+1+2*thick, 0 ] )
            square ( size = [wall_length, wall_height], center = false );

            for ( i = [ 1: 2: num_slots_base_l ] )
                {
                    translate ( [ 0, b + 1 + 2*thick + base_raise, 0 ] )
                    translate ( [ (i+.5) * base_slot_width_l, slot_depth/2, 0 ] )
                    square ( size = [ base_slot_width_l, slot_depth ], center = true );
                }
            for ( i = [ 1: 2: num_slots_base_l ] )
                {
                    translate ( [ 0, b + 1 + thick + base_raise + wall_height, 0 ] )
                    translate ( [ (i+.5) * base_slot_width_l, slot_depth/2, 0 ] )
                    square ( size = [ base_slot_width_l, slot_depth ], center = true );
                }

            translate([2*thick, b + 1 + 3*thick + base_raise + g_rod_1_dia, 0 ])
            circle(d=g_rod_1_dia);

            translate([l-2*thick, b + 1 + 3*thick + base_raise + g_rod_1_dia, 0 ])
            circle(d=g_rod_1_dia);

            translate([l/2, b + 2 + 3*thick + base_raise + t_rod_dia, 0 ])
            circle(d=t_rod_dia);
        }
        for ( i = [ 0: 2: num_slots_wall_side ] )
            translate ( [ l, b+1+2*thick, 0 ] )
            translate ( [ slot_depth/2, (i+.5) * wall_slot_width, 0 ] )
            square( size = [ slot_depth, wall_slot_width], center = true );

        for ( i = [ 0: 2: num_slots_wall_side ] )
            translate ( [ -slot_depth, b+1+2*thick, 0 ] )
            translate ( [ slot_depth/2, (i+.5) * wall_slot_width, 0 ] )
            square (size = [ slot_depth, wall_slot_width ], center = true );
    }

//left and right walls
    copy_mirror_opp(vec=[1,0,0])
    mirror([1, 1, 0])
    translate([-b/2,-l/2,0])
    union()
    {
        difference()
        {
            translate ( [ 0, l+1+2*thick, 0 ] )
            square ( size = [wall_width, wall_height], center = false );

            for ( i = [ 1: 2: num_slots_base_b ] )
            {
                translate ( [ 0, l + 1 + 2*thick + base_raise, 0 ] )
                translate ( [ (i+.5) * base_slot_width_b, slot_depth/2, 0 ] )
                square ( size = [ base_slot_width_b, slot_depth ], center = true );
            }
            for ( i = [ 1: 2: num_slots_base_b ] )
            {
                translate ( [ 0, l + 1 + thick + base_raise + wall_height, 0 ] )
                translate ( [ (i+.5) * base_slot_width_b, slot_depth/2, 0 ] )
                square ( size = [ base_slot_width_b, slot_depth ], center = true );
            }

            translate([2*thick, l + 1 + 3*thick + base_raise + g_rod_2_dia, 0 ])
            circle(d=g_rod_2_dia);

            translate([b-2*thick, l + 1 + 3*thick + base_raise + g_rod_2_dia, 0 ])
            circle(d=g_rod_2_dia);

            translate([b/2, l + 2 + 3*thick + base_raise + t_rod_dia, 0 ])
            circle(d=t_rod_dia);
        }

        for ( i = [ 1: 2: num_slots_wall_side ] )
            translate ( [ wall_width, l+1+2*thick, 0 ] )
            translate ( [ slot_depth/2, (i+.5) * wall_slot_width, 0 ] )
            square( size = [ slot_depth, fit_wall_slot_width], center = true );

        for ( i = [ 1: 2: num_slots_wall_side ] )
            translate ( [ -slot_depth, l+1+2*thick, 0 ] )
            translate ( [ slot_depth/2, (i+.5) * wall_slot_width, 0 ] )
            square (size = [ slot_depth, fit_wall_slot_width ], center = true );

The result of this code is the following.
Cutcodes

The OpenSCAD render of the above code.
Render
The original toolpost design supposed to move along X and Y like the ultimaker toolholder. Lathe Design - original file.
Post processing image, was then imported this file to Rhino and added a mounting holes and also added a few bits and pieces which will be using later as a retention washer or something. The links to all the formats are given, in case of improper scaling issues.

The design files in SVG . DWG . DXF formats.
Nut Test Cuts

The test cuts made for the 6mm smooth rod, was testing the perfect diameter of the holes which wont be too tight or too loose on the rod, so that the box can slide freely on the smooth rods. This also tests a tight fitting piece for fixing the nuts to the carriage/box. This tests are made in wood, but they were also made in acrylic and also for the 8mm smooth rods. Finally decided to acrylic as the plywood quickly wear off and becomes loose.
The X-Y carriage design is ready, now we need to make an assembly for this box to slide. The base design for this also came from the above OpenSCAD design, this file is also edited in Rhino to have the mounting holes. The sizes of the stages are determined by the length of the smooth rods we have.
During the construction we actually made a few mistakes and wasted a few pieces of acrylic, we should have done a test cut on cardboard before!.
So to compensate the wasted acrylic sheet, Sibu decided to do the test cuts in a cardboard sheet cut from Amazon Cardboard packaging.

Cardboard

Cardboard

Design and Final Box assembly

The final box assembly, Notice the use of two nuts on the bolts, and a spring in between the nuts. This spring will keep the nuts under load and backlash should be less. Also, the two nuts will provide extra stability. The nuts are fixed using the hexagonal acrylic pieces, they will be glued to the box two lock the positions of the nuts. I have only done this for X-axis, so that I can compare the results.
Box

The below is the cut file for the Y stage. The origin of all these files are the OpenSCAD file, they are then modified to have suitable mounting holes and slots for fixing the smooth rods.
Y-Stage

The Y-stage ready to be mounted on the X-box/carriage. Three m4 bolts have been used to fix the mounting plate to the bottom of the Y-stage. This will be fitted (press fit) on the X-carriage. The motors too are press-fitted and also supported by a piece of acrylic base-plate glued to the main assembly.
Mount

The complete assembly with a 'makeshift' pen plotter and RAMPS 1.4 controller board.
Assembly

Automation

We did not have Gestalt boards or the MTM stages in the lab. Sibu tried to fabricate Gestalt and Fabnet boards. However this was taking up too much time and we decided to use RAMPS (RepRap Arduino Mega Pololu Shield).

We might replace this with a stateless Fabnet-based approach later.

The first step was to check if RAMPS was working fine and could move a single stepper motor.

We hooked up the boards and motor and used RAMPS test code to test it.

Here is the modified code we used:

#define X_STEP_PIN         54
#define X_DIR_PIN          55
#define X_ENABLE_PIN       38

#define LED_PIN            13

void setup() {
  pinMode(LED_PIN  , OUTPUT);

  pinMode(X_STEP_PIN  , OUTPUT);
  pinMode(X_DIR_PIN    , OUTPUT);
  pinMode(X_ENABLE_PIN    , OUTPUT);

  digitalWrite(X_ENABLE_PIN    , LOW);
}

void loop () {
  if (millis() %1000 <500)
    digitalWrite(LED_PIN, HIGH);
  else
    digitalWrite(LED_PIN, LOW);

  int i;

  digitalWrite(X_DIR_PIN    , HIGH);
  for (i = 0; i < 5000; i++) {
    digitalWrite(X_STEP_PIN    , HIGH);
    delay(1);
    digitalWrite(X_STEP_PIN    , LOW);
  }

  digitalWrite(X_DIR_PIN    , LOW);
  for (i = 0; i < 5000; i++) {
    digitalWrite(X_STEP_PIN    , HIGH);
    delay(1);
    digitalWrite(X_STEP_PIN    , LOW);
  }
}

Next step was to figure out a way to control the machine with an instruction set rather than hardcoded stepper movements.

For this we decided to implement a simple G-code interpreter, which would run on RAMPS and communicate with a host using a serial interface.

We found a very useful implementation of a 2-axis G-code interpreter here.

The full source is available here:

In the video below you can see the interaction being demoed:


The next step was to write a simple way to input g-code commands. We used pyserial for this.

import serial
import fileinput

def consumeLines():
    while True:
        l = ser.readline().strip()
        print ">>> " + l
        if l == ">": # wait for the device to be ready for the next command
            break
    
ser = serial.Serial('/dev/cu.usbmodem1411', baudrate=9600, timeout=None) # block forever on reads
ser.isOpen()

consumeLines()

for cmd in fileinput.input():
    print "sending cmd: " + cmd.strip()
    ser.write(cmd)
    consumeLines()

ser.close()

Now we can invoke it like this:

$ python serialsend.py square.gcode

And square.gcode can contain raw gcode commands like this:

G00 X10000
G00 Y10000
G00 X0
G00 Y0

Possible Improvements

Twist in the tale!

Perhaps you noticed it already, we started with the plan to make a lathe and ended up making a plotter.

We may still continue with the lathe, the difficult part was the constructions and controlling of the stages, which has been achieved.

We have already build a plate to mount the tool head (which is a broken modela bit) and attached it to the machine.

toolhead