syntax-highlighter

Showing posts with label 3D printing. Show all posts
Showing posts with label 3D printing. Show all posts

Friday, May 31, 2013

pyPolyCSG library updated

I have updated the pyPolyCSG python constructive solid geometry library to use the most recent version of the Carve CSG library. The updates to Carve appear to make it significantly more robust as well as much faster.

The update to pyPolyCSG:
  • Improves the robustness, at least on my existing scripts
  • Improves the speed of performing Boolean operations
  • Fixes a bug where vertex normals and texture coordinates were treated as vertices (thanks to Ryan Rix for finding this)
  • Adds the ability to transform polyhedra by a 3x3 or 4x4 matrix (again thanks to Ryan Rix!)
You can get the updated version from github: https://github.com/jamesgregson/pyPolyCSG

Wednesday, March 13, 2013

Reducing Warping/Shrinkage in Large 3D Prints

I've been doing a bit more 3D printing lately and having problems with large 3D prints warping due to uneven cooling. Most of the prints I work on are fairly large, roughly 10x8x4 cm or larger, so this can be a big problem, particularly if the dimensions change enough to mess up the fit of parts. Basically the problem is that as layers cool, they contract and pull off the build-surface. Generally I'm printing PLA on painter's tape, so this will just pull up the tape. An example is shown in the photo below:


You can see how the tape is pulled up at the near edge; this is about 2mm of shrinkage which is not too bad. But this is also a relatively small part, perhaps 5x5x4 cm, and the problem gets worse as the parts get bigger.  A partial fix is to use a heated bed, but the Gen6 electronics for my printer do not support controlling a bed.

However a more practical solution was suggested by another member of the Vancouver Hack Space.  He's working on a very large printer for very large prints; I highly suggest visiting his site ottersoft.ca if you're interested.  Anyway, his suggestion is to space the part up from the bed using some dummy geometry and then print with support material.  The support material forms less connected layers than the object, which gives it more...'give' when printing. Here's an example:


The little block is some dummy geometry added to lower the bottom of the geometry about 1.5 cm. When printed with support, the extra flex of the support lets the object stay attached to the bed without the terrible shrinking.


Part of this seems to be the extra flex, but I suspect that another part is less direct contact with the large metal build-plate, which acts like a heat sink, so the whole part cools more uniformly.


After pulling it off the build plate, you can see that the support material on both sides is in contact with the table, indicating little to no warping.  And this is a big part, roughly 10x8x3 cm.


The downside is that you now have to deal with the support material. If this is the same as the print material, it can be difficult to clean off without leaving a nasty surface finish or changing the part accuracy. For example, filing the support out of the horizontal holes without accidentally changing the hole diameter.  But once done carefully, the part is remarkably accurate: the height as measured at all four corners only differs by about 0.15 mm (with a layer height of 0.3 mm), and the holes are actually round and not elliptical.

I wish that I could take credit for this trick, but that belongs to Loial (ottersoft.ca) of VHS (hackspace.ca)

Sunday, February 10, 2013

CNC Firmware running on Arduino Uno

In an earlier post I described some prototype CNC firmware that I had gotten running on the Teensy3.0.  The Teensy3.0 is a 32-bit ARM board and is pretty nice, but I wanted to see if the code would also run on an Arduino Uno.  This is a much more restrictive platform; only 2k of ram and 32k of program space.  However, after a bit of porting of the hardware layer, it compiles and seem to run no problem.  Here's a screen-capture from a (brief) session, the pulse-ticks stuff is debugging the motion control module.


Note that this is running the full GCode interpreter, with spec-compliant command sorting and an 8-move lookahead buffer.  The step timer is running off a 2KHz Timer1 interrupt.  I'm hoping to increase this to 8+ KHz, at least on other platforms, which should make smoother motion control possible, but at the moment I'm just happy that my ANSI C + POSIX code that I've developed on on a 64bit quad core machine with 16Gb ram ports easily to an 8bit system with 2k or ram and only interrupts after changing less than 50 hardware-specific lines of code.

I've written the code this way for just this reason; by keeping all the hardware specific stuff isolated in a hardware abstraction layer I'm hoping to make a code base that stands the test of time, from small embedded platforms to boards like the Raspberry Pi, to full-blown PCs.  So far so good, it's running on an 8bit AVR, a 32 bit embedded ARM and a full X86_64 PC.

Wednesday, January 30, 2013

Start of CNC Firmware Running on the Teensy 3.0

I've been intermittently working on some CNC firmware with the goal of getting it to run on everything from an Arduino to a standard PC.  It's a fun project because it's very resource constrained but involves a number of different parts, like GCode parsing, asynchcronous programming, portability and motion control for non-Cartesian machines.

In the interests of portability I've been writing the various components in plain ANSI C (except the comments, I like my double slashes!).  By doing this I expect the code to be portable across a wide variety of platforms, from AVRs to the Propoeller child to ARM to PCs, with minimal effort.

My hopes are that the firmware will conform to the NIST GCode specification as closely as is manageable.  That said, some sacrifices will have to made as the spec calls for around 20k of addressable space for expressions, which exceeds the total RAM of Arduinos and even the Teensy 3.0.  However I hope to get mostly spec-compliant in terms of order of operations, expressions (if not the addressable space) and features.  Where possible I am also allowing the specific capabilities to be determined by preprocessor macros.

I've done a bit of work on the GCode interpreter, getting some test code together that parses GCode expressions (this led to my Mathematical Expression Parser in C) as well as an arbitrary dimension DDA implementation.  To date these have been just simple tests, nothing actually running on real hardware.  I've also done some early tests on doing feedrate optimization and

However today I made some good progress on actually getting some real firmware started.  I got my early GCode interpreter stripped down and abstracted out all the hardware-specific stuff into a hardware layer.  I then wrote the hardware abstraction layer for the Teensy 3.0 version of the Arduino environment and fired up the code, using the following string to test with:


N1229.0(This is a comment)G01x110.0   y330.0 M7 F20.0(MSG: another comment)

And it worked!


The screenshot above shows the GCode interpreter code loaded up in the Arduino environment, the Teensy loader application that programs the Teensy and the serial output of the parser. Note that this is not just reading the commands linearly, it is fully parsing the input, splitting it into distinct commands, sorting the commands by operation precedence (as described in the NIST spec) and finally calling back to the Arduino sketch with each command.

Parsing the string and calling back takes about 250 us (the remaining time taken is up by serial communications) so more than fast enough to fill up a lookahead buffer.  Total RAM used is about 5k, so this won't run on an Uno but does run handily on the Teensy 3.0. Ditto goes for the flash at 33Kb currently. This is pretty hefty compared to other firmware, but I think portability and cleanness makes up for that.

The next steps will be to get the kinematic/motion control stuff fleshed out.  I think I should be able to use by DDA implementation running with the Teensy timers pretty easily.  Then it's just tweaking and adding features...

Wednesday, December 19, 2012

Parametrically Designed Gearboxes

Using my Python CSG and Gear libraries, I've been able to start parametrically designing parts.  This can sort of be done with OpenSCAD, but the lack of proper variables and functions makes it difficult.  Recently I tried designing a full gearbox.  The script is shown below: it's a bit messy, but you get the idea:


import gears
import pyPolyCSG as csg

def make_clamp_hub( B ):
    thickness = 9
    hub = csg.cylinder( B/2.0+6, thickness, True )
    hub = hub + csg.cylinder( 4.0, B+10, True ).rotate( 90.0, 0.0, 0.0 ).translate( B/2+3, 0, 0 )
    hub = hub - csg.cylinder( 2.0, 100, True ).rotate( 90.0, 0.0, 0.0 ).translate( B/2+3, 0, 0 )
    hub = hub - csg.box( B/2+6, thickness, 2, True ).translate( B/2+3.5, 0, 0 )
    return hub.rotate(90,0,0).translate( 0, 0, thickness/2-0.01 )

def make_gear( pressure_angle, pitch, teeth, thickness, bore ):
    px, py = gears.gears_make_gear( pressure_angle, teeth, pitch )
    coords = []
    for i in range( 0, len(px) ):
        coords.append( ( px[i], py[i] ) )
    gear = csg.extrusion( coords, thickness ) + make_clamp_hub( bore ).translate( 0, 0, thickness )
    gear = gear - csg.cylinder( bore/2.0, thickness*100, True ).rotate( 90.0, 0.0, 0.0 )
    return gear

pressure_angle = 20.0
pitch          = 0.8
N1             = 12
B1             = 8.0+0.7
T1             = 10.0

N2             = 36
B2             = 5.0+0.7
T2             = 5.0

backlash       = 1.0

dp1 = gears.gears_pitch_diameter( pressure_angle, N1, pitch )
dp2 = gears.gears_pitch_diameter( pressure_angle, N2, pitch )
do1 = gears.gears_outer_diameter( pressure_angle, N1, pitch )
do2 = gears.gears_outer_diameter( pressure_angle, N2, pitch )
dc  = ( dp1 + dp2 )/2.0 + backlash

gear1 = make_gear( pressure_angle, pitch, N1, T1, B1 )
gear1.save_mesh("gear_%gdeg_P%g_%d_tooth.obj" % ( pressure_angle, pitch, N1 ))

gear2 = make_gear( pressure_angle, pitch, N2, T2, B2 )
gear2.save_mesh("gear_%gdeg_P%g_%d_tooth.obj" % ( pressure_angle, pitch, N2 ))


def hole_xy( x, y, radius, height ):
    return csg.cylinder( radius, height, True ).rotate( 90.0, 0.0, 0.0 ).translate( x, y, height/2.0 )

B1s        = 10.0
B1p        = 22
B2p        = 10

box_pad    = 5.0
screw_diam = 4.0
box_thick  = 4.0
nema_offset  = 31.0/2.0

box_height = max( (do1, do2 ) ) + box_pad*2.0
box_width  = max( ( dc + (max( ( B1p, do1 ) )+do2)/2.0 + box_pad*2.0, nema_offset+dc+dp1/2 + screw_diam*2.0 + box_pad*2.0 ) )
screw_x_off = box_width/2.0-screw_diam
screw_y_off = box_height/2.0-screw_diam

g1_coords = ( box_pad+max((do1,B1p))/2.0, box_height/2.0 )
g2_coords = ( g1_coords[0]+dc, g1_coords[1] )

plate = csg.box( box_width, box_height, box_thick )
plate = plate - hole_xy( box_pad, box_pad, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( box_width-box_pad, box_pad, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( box_width-box_pad, box_height-box_pad, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( box_pad, box_height-box_pad, screw_diam/2.0, box_thick*2.0 )


plate = plate + hole_xy( g1_coords[0], g1_coords[1], B1p/2.0+4.0, box_thick ).translate( 0, 0, box_thick/2+3 )
plate = plate - hole_xy( g1_coords[0], g1_coords[1], B1p/2.0, box_thick ).translate( 0, 0, box_thick/2+3 )
plate = plate - hole_xy( g1_coords[0], g1_coords[1], B1s/2.0, box_thick*2.0 )

plate = plate - hole_xy( g2_coords[0], g2_coords[1], B2p/2.0, box_thick*2.0 )
plate = plate - hole_xy( g2_coords[0]-nema_offset, g2_coords[1]-nema_offset, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( g2_coords[0]+nema_offset, g2_coords[1]-nema_offset, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( g2_coords[0]+nema_offset, g2_coords[1]+nema_offset, screw_diam/2.0, box_thick*2.0 )
plate = plate - hole_xy( g2_coords[0]-nema_offset, g2_coords[1]+nema_offset, screw_diam/2.0, box_thick*2.0 )
#plate = plate + gear1.translate( g1_coords[0], g1_coords[1], -10.0 )
#plate = plate + gear2.translate( g2_coords[0], g2_coords[1], -10.0 )

plate.save_mesh( "gearbox_plate.obj" )

This generates two gears and two gearbox plates that are mounted together using M3 screws.  The printed gearbox is shown below, although I'm missing some bearings and hardware.  Each plate has a recess into which a 608ZZ bearing can be press-fit.  Depending on the orientation it can either serve as a fixed or floating end.


I hope to use gearboxes like this to increase the speed that I can drive leadscrews at for my CNC project, which currently has pretty limited feedrates.  This would allow the use of cheap hardware store threaded rods, rather than expensive leadscrews, while still being a self-locking drive.

Sunday, December 2, 2012

Improving part surface finish and accuracy for casting

3D printing is great, but RepRap style printers generally don't produce great surface quality, while also having problems reproducing exact dimensions.  Most people address the second issue by filing their parts to fit, but I wanted to see if I could simply print test parts and adjust the dimensions a bit.


This seems to work pretty well, the part shown above had the post mounting holes pattern negatives (the pegs in the above photo) off by about 1.2 mm.  So I just subtracted that from the initial diameter and regenerated the part with my Python CSG library and, to my surprise, the diameters came out correct to within 2 thou.  The same goes for the horizontally oriented cylinder, one simple iteration of correction produced the correct diameters within a very small tolerance.  I've also found that printing external perimeters at 20 mm/s while printing everything else at the maximum reliable 80 mm/s feedrate for my RepRap gives the best surface finish and fastest printing time of all the options I've tried.

To improve the surface finish of the parts I tried filling the surface with Bondo body-filler to fill in the filament marks.  I wasn't sure if the Bondo would dissolve the PLA, but it seems to be fine.  After a light surface sanding the final part had a very clean surface finish, with little to no filament marks and consistent, accurate diameters.


The pink in the above photo is the Bondo fairing compound.  You can get an idea of how thin the coating is since you can still see the dimension adjustments I'd marked on the part through the fairing.  This coating just fills in the small gaps between the filament and allows the surface to be quickly sanded smooth.  This should dramatically improve the surface finish of cast parts.

Spending a bit more time on the patterns, embedding positives of the cores to be used in casting (the vertical pegs and horizontal cylinder above) and moving to an open-topped casting method should improve the quality of my cast parts considerably.  By using an open mold, I'm hoping that the air-bubbles that ruined the previous part can be popped at the exposed resin surface easily.  I've also remove sharp concave corners in the pattern, by filleting the relevant edges, in the hopes of improving these features.  Finally the time spent finishing the pattern should pay off in spades, since it will reduce cleanup of each cast part by the same amount.

Pourable OOGOO

Recently I have developed an interest in using resin casting to produce small numbers (several dozen at most) of high-quality plastic parts.  The motivation for this is to be able to produce stiffer epoxy parts for my CNC quickly and at relatively low cost.  I've mostly been investigating a material called OOGOO, a Sugru 'substitute' made from silicone and cornstarch which is cheap, easy to get the raw ingredients for, and seems very well suited to casting resin parts, based on my experiments.  Below you can see the first ever resin casting I've made; I freely admit that it's a piece of junk, but turned out surprisingly well given the amount of knowledge I had about casting and is impressively strong compared the the 3D printed original.


However a drawback of OOGOO is that it basically forms a putty, so quite a bit of care is needed to make sure that you get good contact with the pattern in order to not leave voids in the mold.  People have tried making a pourable OOGOO using Xylene as a thinner (see comments in the OOGOO Instructable link above), but I can't seem to get Xylene in quantities less than about 4L, which is way too big a container to store in my apartment.

I've read in random forums that White Gas can be substituted for Xylene to thin OOGOO. 'White Gas' appear to be a catchall term and may or may not be lighter fluid depending on the brand and region where you live. So I decided to give it a go with lighter fluid to see i) if it thinned OOGOO to the point where it could be poured and ii) if the thinned OOGOO would ever set up properly.  The results, at least at the early stages, are 'yes' and 'maybe' respectively.  I'm hoping that a bit of time will change this to 'yes' and 'yes'.

After mixing a lot of lighter-fluid into the OOGOO, I definitely obtained something pourable.  I have no idea how much, I was simply winging it, but I would guess two parts lighter-fluid to one part each of silicone and corn-starch.  I poured it over a scrap PLA printed part, not knowing if the lighter-fluid would dissolve it:


At this (estimated) ratio, it was still quite thick, probably comparable to a cold molasses and so some coaxing was required to get it onto the part.  However it slowly settled on the part and settled around it.  I used PAM cooking spray as a release agent, again hearing on a random forum that it worked well.


Next time I would skip the release agent since I don't think the OOGOO sticks particularly well to the PLA printed part, and wherever it contacted the part but other OOGOO flowed over it the two bits of fluid would not adhere well.


Detail transfer from the part was excellent, but the OOGOO remained very, very soft after about an hour.  I'm hoping that this is simply because the majority of the solvent (lighter fluid) hasn't evaporated yet, and that once it does the mold will be more rigid.  At the moment the material is not stiff and tear-resistant enough to be usefully used as a mold, it will simply get destroyed when demolding the first few parts.

The bits left inside the jar that I mixed the thinned OOGOO appear to be much stiffer, so I'm hoping that a few days left to blow off the volatiles from the lighter-fluid will result in a stiffer material, but even several hours in the mold is still quite spongy.  I'll check it again after a few days and see if the mold is somewhat usable.  If so then I will probably start thinning my OOGOO.  I've also disccovered a tin of Xylene on the shelf in the parking garage, so I may steal a hundred mL or so, and if it works better, replace the tin with one I can dip into guilt-free from time to time.

Thursday, November 29, 2012

Improving 3D print quality, by doing the obvious

Some time ago I bought and assembled a RepRap Prusa with the intention of using it to prototype things.  It has saved me countless hours in prototyping my CNC but getting good quality prints out of it has been challenging at times.  Granted my standards for good have gone up even as I've developed a hunger to print as fast as possible.

Printing fast has worked for the CNC, which has large, fairly simple geometries.  It's nice to be able to print an axis end in two hours, rather than eight.  But when I try to print small, accurate objects I tend to run into issues, especially with PLA.  The biggest one has been that smooth surfaces end up ridgy, see for example the print in the image below:


That print is supposed to be a linear bushing with circular sections, but the print is garbage due to the ridges.  I tried a lot of things to reduce them, most focused on the mechanical rigidity of the printer.  I tightened everything up and added diagonal braces in the X-direction. I even 'floated' the entire printer by sitting it on soft foam blocks so that the entire printer could move as a rigid body when the bed and extruder moved rather than cushioning jerk with elastic deformation.  Amazingly enough, this simple idea CAN actually improve print quality for wobbly printers quite a bit, but did not fix my problem.

The focus on mechanics and dynamics is probably not surprising given my background, but it turned out to be misplaced.  What should have clued me in that the mechanics were not to blame is that everything got a lot worse with PLA, even when using the same travel/speed/acceleration settings that I had been using with ABS.

Eventually I did realize this and started looking for extruder/heat problems, measuring and adjusting temperatures and filament diameters, changing nominal nozzle diameters and so on.  None of these seemed to have any effect.

Finally I ran across a post (I don't remember where I found it), that said to just print perimeters slower.  This seemed crazy since i) the mechanics were sound ii) the extruder is perfectly happy running quite quickly with the beast of a stepper I have on it.  But I tried it and my prints instantly got better.


Here is a print with perimeter speed of 40 mm/s.  Although it's difficult to tell in the photo, the surface is actually quite a bit better than the bushing printed above using 80 mm/s perimeters. Finally here is the same print, but with 20 mm/s perimeters:


The difference in the photos is much more evident here and even more so in the real world.  Note that all prints were made with aggressive, active cooling.  So what gives? Why does the print quality improve when printing slower in PLA, even though the extruder and printer stiffness are the same that was used for ABS at much higher speeds?

I have a theory, taken from the post that recommended printing slower and combined with (my own) conjecture about the print material.  The theory is that communication lag between the host software and firmware causes the motion to stutter a bit, particularly on smooth curves that are discretized with many small segments.  With limited lookahead, the printer does a few segments very quickly and empties its buffer before the host can refill it.  This causes the printer to periodically, but regularly, stall.  Since the temperature of the extruder has to be kept high to print at high speeds, the PLA in the nozzle is runny and gobs out during these stalls, creating the surface ridges.  I suspect the surface imperfections are less noticeable with ABS because it generally is not as liquid as the PLA, allowing you to get away with higher feeds without noticing the buffer starvation.

I should be able to test this by changing the baudrate on the printer.  Doubling the send-rate should allow me to increase (ideally double) the feedrate, but will require recompiling the firmware to test.  When I get a chance to test the idea out, I'll post the results.

In hindsight, slowing things down was an obvious thing to try.  My Prusa bobs like a cork on the soft-feet at 80 mm/s and all of my experience on other 3D printers suggests that slower printing gives improved results.  But I wanted to have it all and kept increasing the feedrate.  However, if I am right about this, perhaps I can go back to printing at high feeds while keeping the high print quality.