2016/02/05

Slicers

As an exercise at the Recurse Center, I wrote a slicer  in Python to convert an STL file into layers of PNG files.

At first, I thought I could calculate the lines of the layers from the triangles in the STL file, and then flood fill the closed area. This didn't quite work, because some layers would have 1 pixel features, and the flood fill would cause the whole image to be white.

After some research into how others have done this, I was inspired to compute the amount of times a ray passes through the mesh surface. All my rays were in the x-direction, and each layer would filter the polygon/triangle/facets at its height. The rest was geometry to find the edge points on the xy-plane, and then find the x-coordinate for each row in the layer image.

While my new inspiration took a different high-level approach, I ended up using much of the math I had been using to draw the outlines, particularly my calculation of the triangle edges in the layer. The difference was how I found the interior of the object.

I also added a feature I didn't see in Creation Workshop. I've been frustrated that I cannot see the holes in my lift bed when I place the object in the scene. My slicer takes a mask file that locates those holes, allowing the slicer to find the best placement for the object on the bed automatically.

2015/10/14

Failed bottom-side bleed through solutions

Lately, I've been focused on trying to solve my bottom-side bleed through. I still have not solved the issue, but I'm making a list of what hasn't worked for me.

1. Changing to a darker color (red to black)
2. Adding pigment to the resin, (granted, I tripled the pigment in my ignorance)
3. Continuous printing to the first 3 bottom layers
4. Not dipping the first 3 bottom side layers

Curiously, the bottom layer of my pigment test shape deformed like a ceiling patio shade suspended on posts, with the posts being the support nails. That layer is very slowly moved down for the next layer, so hydrodynamics are not deforming the layer. My current hypothesis is resin shrinkage deforms the bottom layer.

The following pictures are of the bottom layer of the object.

 Standard dip method, only the color changed

Continuously printed dice to the first 3 bottom layers, then standard dip method

First 3 layers were not dipped, but simply moved down for the next layer

Standard dipped wedge

First 3 bottom layers were not dipped, just moved down for the next layer


On a side note, in trying to fix the issue I have implemented continuous printing for the nail supports, which helps to shorten the printing time, and then dip the remaining layers for the actual object.

2015/09/15

Changing to black SubG+

I've been printing only in red Makerjuice to this point. I decided it was time to try changing the color, as I have the Makerjuice variety pack. Since I'm still trying to solve the bottom side bleed-through, I decided black would be the next resin to try.

Black did not alleviate the bleed-through

The black is translucent when using my smartphone flashlight

Even the black is translucent to bright visible light on parts 1 or 2 mm thick, which means a layer 0.1 mm is not opaque enough to prevent the layer above from also curing resin below the layer. Keep in mind that this is occurring for visible light, not UV, which may be different. Still, a change in color did not help eliminate the bleed-through, so I'm hoping more pigment will.

On another note, you can see cracking of the surface layer of the dice, particularly in the illuminated dice picture. In my and others experience, this is related to the isopropyl alcohol soak time. A quick rinse is all that is needed.

2015/09/08

Continuous Printing Tests

After the outer ring took 4.5 hours to print, I decided to try some continuous prints that promise to be faster. My definition for a continuous print is where the layers aren't dipped, but the projector continuously displays the layer while the lift very slowly lowers. Since continuous printing does not have a dip phase, my layer time is cut in half! I've seen some videos of this happening, particularly from Carbon 3D. Continuous printing can work for top-down 3D printers because the oxygen in the atmosphere inhibits the top layer of resin, on the order of hundreds of micrometers, from curing. This inhibited layer allows resin to flow over the object during printing. However, the viscosity and thinness of the layer slow the flow of resin so that layers with large areas do not form correctly.

I wanted to know:
  1. Does continuous printing work with my printer?
  2. How fast is continuous printing exactly?
  3. What is the maximum wall thickness before the resin cannot flow correctly?
I started with a tear-drop shape again, this time with 5 of various diameters: 10 mm, 5 mm, 3 mm, 2 mm, and 1 mm. I decided to start with a rate of 10 microns per second, which is similar to 10 seconds a 100 micron layer I normally use. I did slow the first 300 microns to get the resin to bond with the lift, using about 5 microns per second. After seeing the relative success of these tear-drops, I decided to print a dice with the same settings.

 Tear drops with a height of 5 mm and the 12 mm dice

The 10 mm drop had holes that came from the side and traveled into the center, like a bubble formed on the edge and slowly moved toward the center with the resin flowing from the top layer. The 12 mm dice exhibited the same problem. However, 5 mm down printed fine.

I decided to try slowing down the rate to about 6 microns per second, and print a second dice. Since the z-layer resolution only affects how long a layer is displayed, not the print time, I also improved the z-layer resolution to 10 microns instead of 100!

Below are photos comparing (left to right):
  1. The best dice printed using the non-continuous method
  2. The 6 micron per second dice
  3. The 10 micron per second dice

 Continuous printing does not help bottom side bleed-through

 The 2 side is the roughest for continuous printing, yet smooth for the layer dice. Perhaps this is a mask difference, since I changed the projector mask between these prints.

 The 6-side tells the most information

The 6-side is the top side, with the dots mostly clearly showing the 100 micron layer size. The 6 micron per second dice does not show this due to a 10 micron layer size. However, both continuously printed dice are hollow, with holes that formed on the 1-side (the opposite side) going completely through the dice. Some of those holes filled at the very end for the 6 micron per second dice, as seen by a translucent top layer. The dots on the dice must have improved the resin flow at the very end.

I believe the holes are formed by fast moving resin. As the resin hardens, there are less places for the resin to flow to the top. With less places, the resin must flow faster through the remaining holes. However, fast moving resin does not get a chance to polymerize, leaving a channel that starts from the edge and ends toward the center, where the resin is most displaced. You might visualize the channel as a hole in each layer that slowly moves toward the center. The angle between the normal vector of the top plane and the channel is on the order of 15 degrees when starting from the edge, at least, for a 12 mm dice. Slowing the print speed reduces speed of the resin in the channels, and fills more holes (as seen between the 6 and 10 micron per second dice). Perhaps another day I will experiment with less speed, but I was already approaching my non-continuous print speeds and didn't care to go slower.

I have updated the git repository with pidishc.py, for continuous printing. I might use this for thin, shelled out components in the future, but ONLY for these. The most exciting feature of continuous printing is the removal of the time spent dipping, and print speed can be improved by orders of magnitude if I can improve the UV output of the projector. Prints would go from roughly 1 cm per hour to 7.2 cm per hour, but with an extreme limitation of the shape.

To answer the questions from above:
  1. My printer can do continuous printing
  2. The print speed is 6 microns per second
  3. Print walls can be about 3-5 mm at their thickest before holes start to form.

Planetary Gear Systems

Ever since I was introduced to Lego Technic, I've been fascinated by gears. My favorite part of any Lego model were the parts that moved, even if by a simple joint, because you could manifest your imagination, making the play more real. Gears enabled the connection of movements: a turning tire moved the engine piston, the steering wheel actually turned the wheels.

I want to understand planetary gear systems. Planetary gear systems make up automatic transmissions in cars, and a differential planetary gear system can make very high ratios in little space. These systems are much more difficult than the Lego models I built, where gear ratios were simply ratios of teeth, and had only 1 gear ratio.

I started by designing the sun and planet gears. This design wasn't trivial, because most gear teeth are shaped by an involute curve, creating the involute gear profile. AutoCAD doesn't have a command to take formulas for curves, so I seached online, finding an AutoLISP script that would generate a gear. Unfortunately, this script takes many inputs that seem to require a spreadsheet of calculations to properly make a gear. I was quite frustrated until I found this gear template generator that reduced the inputs to something that made intuitive sense - Tooth pitch, number of teeth, and pressure angle. This generator used the Machinists Handbook formulas referenced in this youtube video (more succinctly here). I modified the AutoLISP script to take my inputs and to fully create a gear, learning LISP in the process. You can copy it here. My appreciation goes to the gear template generator author, whose webpage helped me understand the subtle differences between internal and external gears. Also much appreciation to the original author of the AutoLISP script, without which I could not learn LISP or AutoLISP.

Once I made the gears, I started designing the system, which consists of:
  • Sun gear
  • Planet gears
  • Ring gear
  • Planet carrier
  • Ring gear lid
  • Sun gear crank (I cheated and used another planet carrier)

Planet gears around the sun gear (with hex middle)

Minus the bottom side bleed-through, these gears came out pretty good. I'll sand down the over-exposed bottom. I ended up drilling the center holes because some gears had a tight fit on the planet carrier.

The ring gear printed at a 20 degree slant to prevent shrink warping

The slant did not prevent the bottom side bleed-through


The planet carriers were printed at a 20 degree slant, causing a uniform ripple texture

I printed 2 planet carriers: 1 to hold the planet gears, 1 to act as a lever to the sun gear. On both, the lowest axle is slightly deformed and not at a right angle to the disc plane. The unpictured side has a hex key to fit into the sun gear or another gear set, though I didn't make the shaft long enough to insert into another planetary gear system.

I printed the lid at 20 degrees with more than enough support



The system mostly assembled

Everything fits in and does rotate, even without lubricant

The lid, in addition to being stubbly due to all the supports, is slightly bent. I'm not sure if this is warp (the planet carriers and outer ring both slightly curved) or from my pulling of the object off the lift after the print.

The lid mounting holes are 3 mm for a screw and nut, though they seem just a little too tight.

In spite of all the warping, the gears do turn! Having the actual object in hand has solidified my understanding of the gear ratios in a way reading and watching illustrations just couldn't do.

2015/08/31

AutoLISP Gear Generator

This script adds 2 commands to AutoCAD: extgear (external gear) and intgear (internal gear). The gear will be drawn around the origin point, so keep the drawing area clear!
Used with AutoCAD 2016.


;GEAR.LSP - This program generates involute curve profile.

(defun c:extgear ()
  (setq numt (getint "Number of Teeth:<24> "))
  (if (= numt nil)
    (setq numt 24))
  (setq cirp (getreal "Circular Pitch:<3.00> "))
  (if (= cirp nil)
    (setq cirp 3.000))
  (setq prsa (getreal "Pressure Angle:<20.0> "))
  (if (= prsa nil)
    (setq prsa 20.0))
  (setq clrc (getreal "Clearance:<0.1> "))
  (if (= clrc nil)
    (setq clrc 0.1))

  (setq diap (/ pi cirp))
  (setq adum (/ 1.0 diap))
  (setq ddum (+ adum clrc))
  (setq tt (- (/ cirp 2.0) clrc))

  (setvar "orthomode" 0)
  (setvar "coords" 1)
  (setvar "osmode" 0)
;START CALCULATIONS
;
  (setq pnts 19) ;determines involute curve accuracy
  (setq prsa (/ (* prsa pi) 180.0)) ;pressure angle to radians
  (setq pitr (/ numt (* diap 2.0))) ;calc pitch radius
  (setq irad (- pitr ddum))         ;calc inside radius
  (setq orad (+ pitr adum))         ;calc outer radius
  (setq basr (* pitr (cos prsa)))   ;calc base radius of gear
  (setq z (- (expt orad 2.0) (expt basr 2.0)))  ;three lines of
  (setq x (sqrt z))                             ;code to make
  (setq paodd (atan (/ x basr)))                ;an arccosine

  (if (> basr irad)
    (PROGN
      (setq p 0.0)
      (setq ps (polar p2 (/ pi 2.0) basr))
    )
    ;else
    (PROGN
      (setq z (- (expt irad 2.0) (expt basr 2.0)))  ;three lines of
      (setq x (sqrt z))                             ;code to make
      (setq p (atan (/ x basr)))                    ;an arccosine
      (setq e1 (sin p))
      (setq e2 (cos p))
      (setq e (/ e1 e2))
      (setq j (- e p))
      (setq x1 (* (/ (sin j) (cos p)) basr))
      (setq y1 (* (/ (cos j) (cos p)) basr))
      (setq ps (list x1 y1))
    )
  )

  (setq incr (/ (- paodd p) pnts))
  (setq tta (* diap (/ tt numt)))
  (GRAPHSCR)
  (setq p2 '(0 0))     ;Center of Gear
  (setq x2 (CADR p2))
  (setq y2 (CAR p2))
  (setq y5 (+ y2 basr))
  (setq y6 (+ y2 irad))
  (setq p6 (list x2 y6))
  (setq p5 (list x2 y5))
  ;(setq pz1 (polar p2 1.95 (* basr 0.9)))
  ;(setq pz2 (polar p2 1.35 (* orad 1.5)))
  (setq pz1 (polar p2 (* 1.25 pi) (* orad 1.5)))
  (setq pz2 (polar p2 (* 0.25 pi) (* orad 1.5)))
  (COMMAND "CIRCLE" P2 pitr) ;place pitch circle
  ;(COMMAND "CIRCLE" P2 basr) ;place base circle
  ;(COMMAND "CIRCLE" P2 irad) ;place inside circle
  (COMMAND "ZOOM" "w" pz1 pz2) ;Window for drawing curve
  (setq s (ssadd))
  (setq le (entlast))
;
;begin a loop for placement of coordinate pairs
;the command pline will be used to generate a polyline whose
;vertices will be computed by a standard involute curve formula
;drawn from base diameter to the od of the part
;
;In polar coordinates, the involute curve is defined by
;                       r = basr / cos(p)
;                       theta = tan(p) - p
;
;converting this to cartesian (flipping x and y) gives
;x = r*sin(theta) = (basr / cos(p)) * sin(tan(p)-p)
;y = r*cos(theta) = (basr / cos(p)) * cos(tan(p)-p)
;
  (setq test 0)
  (if (> basr irad)
    (PROGN
      (COMMAND "LINE" p6)
      (COMMAND p5)
      (COMMAND "")
    )
  )
  ;(initget "Y y N n")
  (COMMAND "PLINE" ps)
  (setq p (+ incr p))
  (while (> pnts 0)
    (setq e1 (sin p))
    (setq e2 (cos p))
    (setq e (/ e1 e2))
    (setq j (- e p))
    (setq x1 (* (/ (sin j) (cos p)) basr))
    (setq y1 (* (/ (cos j) (cos p)) basr))
    (setq x3 (+ x2 x1))
    (setq y3 (+ y2 y1))
    (setq p3 (list x3 y3))
    (COMMAND p3)
    (setq p (+ incr p))
    (setq pnts (- pnts 1))
    (if (/= test 1)
      (PROGN
        (setq hyp (sqrt (+ (expt x1 2) (expt y1 2))))
        (if (> hyp irad)
          (progn
            (setq pint p3)
            (setq test 1))
        )
      )
    )
  )
  (COMMAND "")
  (initget "Y y N n")
  (setq ans Y)
  (if (/= ans "N")
    (PROGN
      (setq pz3 (polar p2 (* pi 1.25) (* orad 1.6)))
      (setq pz4 (polar p2 (* pi 0.25) (* orad 1.6)))
      (setq ang13 (/ (sin prsa) (cos prsa)))
      (setq ang11 (- ang13 prsa))
      (setq x11 (* (sin ang11) pitr))
      (setq y11 (* (cos ang11) pitr))
      (setq p11 (list x11 y11))
      (setq ang (angle p2 p11))
      (setq angi (- ang tta))
      (setq p12 (polar p2 angi (/ ORAD 0.8)))
      (setq beta (angle p2 p3))
      (setq ang2 (- (* angi 2.0) beta))
      (setq p15 (polar p2 ang2 orad))
      (setq adj1 (- ang2 j))
      (setq adj2 (+ adj1 (/ (* pi 2.0) numt)))
      (setq adj3 (+ adj2 0.3))
      (setq p22 (polar p2 adj3 irad))
      (setq p17 (polar p2 adj2 irad))
      (setq p18 (polar p2 adj2 basr))
      (setq angr (* (+ ang11 tta) (/ 180.0 pi)))
      (setq p21 (polar p2 ang2 (/ orad 0.8)))
      (setq angm (+ angi (/ pi numt)))
      (setq p19 (polar p2 angm irad))
      (setq angj (* (+ ang11 tta) 2.0))
      (setq angk (/ (- (/ (* 2.0 pi) numt) angj) 2.0))
      (setq angl (+ (/ pi 2.0) angk))

      (if (< basr irad)
        (PROGN
          (setq psang (- (/ pi 2.0) (angle p2 ps)))
          (setq p23 (polar p2 (+ adj2 psang) irad))
          (setq p24 ps)
        )
        ;else
        (PROGN
          (setq p23 (polar p2 adj2 irad))
          (setq p24 p6)
        )
      )
      (COMMAND "MIRROR" "W" p22 p21 "" p2 p12 "")
      (COMMAND "ARC" p15 "c" p2 p3)
      (COMMAND "ARC" p24 "c" p2 p23)
      (COMMAND "ROTATE" "w" pz3 pz4 "" p2 angr)
      (while (setq le (entnext le))
        (ssadd le s)
      )
      (COMMAND "ARRAY" s "" "p" p2 numt "" "")
    )
  )
  (Prompt "\nDone!  ")
  (princ)
)








(defun c:intgear ()
  (setq numt (getint "Number of Teeth:<24> "))
  (if (= numt nil)
    (setq numt 24))
  (setq cirp (getreal "Circular Pitch:<3.00> "))
  (if (= cirp nil)
    (setq cirp 3.000))
  (setq prsa (getreal "Pressure Angle:<20.0> "))
  (if (= prsa nil)
    (setq prsa 20.0))
  (setq clrc (getreal "Clearance:<0.1> "))
  (if (= clrc nil)
    (setq clrc 0.1))

  (setq diap (/ pi cirp))
  (setq ddum (/ 1.0 diap))
  (setq adum (+ ddum clrc))
  (setq tt (+ (/ cirp 2.0) clrc))

  (setvar "orthomode" 0)
  (setvar "coords" 1)
  (setvar "osmode" 0)
;START CALCULATIONS
;
  (setq pnts 19) ;determines involute curve accuracy
  (setq prsa (/ (* prsa pi) 180.0)) ;pressure angle to radians
  (setq pitr (/ numt (* diap 2.0))) ;calc pitch radius
  (setq irad (- pitr ddum))         ;calc inside radius
  (setq orad (+ pitr adum))         ;calc outer radius
  (setq basr (* pitr (cos prsa)))   ;calc base radius of gear
  (setq z (- (expt orad 2.0) (expt basr 2.0)))  ;three lines of
  (setq x (sqrt z))                             ;code to make
  (setq paodd (atan (/ x basr)))                ;an arccosine
  (if (> basr irad)
    (PROGN
      (setq p 0.0)
      (setq ps (polar p2 (/ pi 2.0) basr))
    )
    ;else
    (PROGN
      (setq z (- (expt irad 2.0) (expt basr 2.0)))  ;three lines of
      (setq x (sqrt z))                             ;code to make
      (setq p (atan (/ x basr)))                    ;an arccosine
      (setq e1 (sin p))
      (setq e2 (cos p))
      (setq e (/ e1 e2))
      (setq j (- e p))
      (setq x1 (* (/ (sin j) (cos p)) basr))
      (setq y1 (* (/ (cos j) (cos p)) basr))
      (setq ps (list x1 y1))
    )
  )
  (setq incr (/ (- paodd p) pnts))
  (setq tta (* diap (/ tt numt)))
  (GRAPHSCR)
  (setq p2 '(0 0))     ;Center of Gear
  (setq x2 (CADR p2))
  (setq y2 (CAR p2))
  (setq y5 (+ y2 basr))
  (setq y6 (+ y2 irad))
  (setq p6 (list x2 y6))
  (setq p5 (list x2 y5))
  ;(setq pz1 (polar p2 1.95 (* basr 0.9)))
  ;(setq pz2 (polar p2 1.35 (* orad 1.5)))
  (setq pz1 (polar p2 (* 1.25 pi) (* orad 1.5)))
  (setq pz2 (polar p2 (* 0.25 pi) (* orad 1.5)))
  (COMMAND "CIRCLE" P2 pitr) ;place pitch circle
  ;(COMMAND "CIRCLE" P2 basr) ;place base circle
  ;(COMMAND "CIRCLE" P2 irad) ;place inside circle
  (COMMAND "ZOOM" "w" pz1 pz2) ;Window for drawing curve
  (setq s (ssadd))
  (setq le (entlast))
;
;begin a loop for placement of coordinate pairs
;the command pline will be used to generate a polyline whose
;vertices will be computed by a standard involute curve formula
;drawn from base diameter to the od of the part
;
;In polar coordinates, the involute curve is defined by
;                       r = basr / cos(p)
;                       theta = tan(p) - p
;
;converting this to cartesian (flipping x and y) gives
;x = r*sin(theta) = (basr / cos(p)) * sin(tan(p)-p)
;y = r*cos(theta) = (basr / cos(p)) * cos(tan(p)-p)
;
  (setq test 0)
  ;(COMMAND "LINE" p6)
  ;(COMMAND p5)
  ;(COMMAND "")
  ;(initget "Y y N n")
  (COMMAND "PLINE" ps)
  (setq p (+ incr p))
  (while (> pnts 0)
    (setq e1 (sin p))
    (setq e2 (cos p))
    (setq e (/ e1 e2))
    (setq j (- e p))
    (setq x1 (* (/ (sin j) (cos p)) basr))
    (setq y1 (* (/ (cos j) (cos p)) basr))
    (setq x3 (+ x2 x1))
    (setq y3 (+ y2 y1))
    (setq p3 (list x3 y3))
    (COMMAND p3)
    (setq p (+ incr p))
    (setq pnts (- pnts 1))
    (if (/= test 1)
      (PROGN
        (setq hyp (sqrt (+ (expt x1 2) (expt y1 2))))
        (if (> hyp irad)
          (progn
            (setq pint p3)
            (setq test 1))
        )
      )
    )
  )
  (COMMAND "")
  (initget "Y y N n")
  (setq ans Y)
  (if (/= ans "N")
    (PROGN
      (setq pz3 (polar p2 (* pi 1.25) (* orad 1.6)))
      (setq pz4 (polar p2 (* pi 0.25) (* orad 1.6)))
      (setq ang13 (/ (sin prsa) (cos prsa)))
      (setq ang11 (- ang13 prsa))
      (setq x11 (* (sin ang11) pitr))
      (setq y11 (* (cos ang11) pitr))
      (setq p11 (list x11 y11))
      (setq ang (angle p2 p11))
      (setq angi (- ang tta))
      (setq p12 (polar p2 angi (/ ORAD 0.8)))
      (setq beta (angle p2 p3))
      (setq ang2 (- (* angi 2.0) beta))
      (setq p15 (polar p2 ang2 orad))
      (setq adj1 (- ang2 j))
      (setq adj2 (+ adj1 (/ (* pi 2.0) numt)))
      (setq adj3 (+ adj2 0.3))
      (setq p22 (polar p2 adj3 basr))
      (setq p17 (polar p2 adj2 irad))
      (setq p18 (polar p2 adj2 basr))
      (setq angr (* (+ ang11 tta) (/ 180.0 pi)))
      (setq p21 (polar p2 ang2 (/ orad 0.8)))
      (setq angm (+ angi (/ pi numt)))
      (setq p19 (polar p2 angm irad))

      (if (< basr irad)
        (PROGN
          (setq psang (- (/ pi 2.0) (angle p2 ps)))
          (setq p23 (polar p2 (+ adj2 psang) irad))
        )
        ;else
        (setq p23 (polar p2 adj2 basr))
      )
      (COMMAND "MIRROR" "W" p22 p21 "" p2 p12 "")
      (COMMAND "ARC" p15 "c" p2 p3)
      (COMMAND "ARC" ps "c" p2 p23)
      (COMMAND "ROTATE" "w" pz3 pz4 "" p2 angr)
      (while (setq le (entnext le))
        (ssadd le s)
      )
      (COMMAND "ARRAY" s "" "p" p2 numt "" "")
    )
  )
  (Prompt "\nDone!  ")
  (princ)
)

Perfecting the Oldham Coupler

I redesigned the printer end cap with more support for the tabs. I also moved the top layer's corner supports to the very corners, as the nub left after removing the support is much easier to sand down at the corner.

Wavy tabs are a result of z-wobble

These tabs did bend outward still, but not as much. Putting this end cap on was harder though, since the tabs acted like a spring with a stronger spring constant.

I noticed the z-wobble had returned. Since the only thing that had changed was time, I figured the WD-40 I had used to lubricate the oldham coupler had failed. The coupler had quite a bit of friction preventing the plastic disc from sliding, so I decided to reduce this by sanding down the inner edges of the disc. Luckily, I had some 1500 grit sand paper on hand, which, while slow, allowed for much more precise sanding. Many metal friction fits are only 0.004 of an inch; removing too much material causes backlash. After sanding, the plastic disc slid without much force at all, and engaged it's metal counterparts without backlash.

I sanded the inside notches of the blue plastic