syntax-highlighter

Showing posts with label CNC. Show all posts
Showing posts with label CNC. Show all posts

Sunday, May 26, 2013

Smooth Feedrate Envelopes for Motion Control, Part II

In the previous post, I derived equations for smooth feedrate control of stepper motors, claiming that by using a smoother feedrate envelope that the motors could be driven faster with less chance of skipping steps and losing position.

In this post, I demonstrate this with a real stepper motor and show that it actually does work: using the envelopes does actually prevent the motors from losing steps.  My test setup is a single NEMA 17 stepper, driven by one of my A4988 driver breakouts, which is controlled by an Arduino sketch running on the Arduino Due.  I'm using half-stepping on the motors, driven by a 200KHz timer interrupt step callback which decides whether or not to step based on the interpolated supplied delays for the start and end of each move.  The move itself approximates a square wave, first accelerating from a slow feedrate, then performing a constant speed portion, then decelerating back to the initial feedrate.

The video below shows the stepper being driven using a constant acceleration profile, which causes the kinks in the feedrate graph in my previous post. You can clearly see it moving around on the table and stalling frequently before it reaches the top speed.


In contrast, here is the result using the third-order cubic feedrate envelope for the same set of moves. The stepper is easily able to handle the top speed and jerks around considerably less on the table.  Of course this comes at a price, a higher pulse-frequency must be used to resolve the acceleration profile.


You can get the code I used for this from following link: https://sites.google.com/site/jamesgregson/tmp/linear_move.zip, it includes a multi-axis DDA implementation suitable for use with timer-interrupts as well as the code for evaluating the feedrate envelopes.

Sunday, February 10, 2013

Prototype 5-Axis CNC Board

Since I'm working on some CNC firmware, I thought I needed a test platform.  I didn't want to rip-apart the CNC so I decided to build a new 5-axis board using the new Pololu stepper drivers. These are nice little cheap boards and their advantage over the ubiquitous A4988 drivers is that they don't need cooling up to 1.5A per phase.  This makes things way easier, since those tiny little heatsinks for the A4988 chips are scattered all over my apartment, double-sided tape all linted up so they won't stick, just waiting to be stepped on.  They hurt even more than stepping on a Lego block, little buggers.

Anyway, I figured I should just go to five axes, which would allow me to control the 3D printer eventually (1X, 1Y, 2Z, 1E).  So I built a protoboard test board:


I laid out the board and soldered it up.  It was pretty easy except for having to drill a few extra holes in the power-rails to get the spacing I wanted.  After electrically testing everything I decided not to blow all 5 drivers simultaneously on a meticulously copied wiring error, opting instead for only two.

The MCU controlling the whole mess is an Arduino Uno.  I have the step pins on PortD and the direction pins on PortB, which allows fast bitwise operations to be used for stepping.  While picking up the stuff for the prototype board I also bought a proto-screw-shield, wanting a more resilient mounting option for the Uno:





I have the early version of my CNC firmware loaded onto the Arduino.  After firing up the serial terminal, I had the thing running!



Not super crazy, but still rewarding nonetheless.

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.

Wednesday, November 7, 2012

A second attempt at mold making

With the success of the last attempt at Oogoo-based mold making, I decided to try it on one of the current CNC parts.  The last attempt went well, but there were areas that could be improved.  To that end I bought real food-coloring, to get more consistent coloring of the Oogoo.  This is not important just for appearance, but also to see if it is mixed well.  I also bought a small plastic box that would contain the pattern so that I could pack the mold-material into the box around the pattern and be able to apply some reasonable force locally to get surface contact without deforming the mold elsewhere.


Here you can see the part with the steel rods that I'm using as patterns for the mounting holes.  I then put this in the container and packed Ooogoo around it as tightly as possible.  Using the plastic box really improved this step, since I could pack down the Oogoo without separating the mold from the pattern somewhere else.


I then waited about 30 minutes for the material to set and become rubbery and then pulled it out of the box, slit the mold around the edge with a box-cutter.  Here you can see the semi-separated pattern:


I then pulled out the pattern rods, slit one side of the through-hole and pulled out the pattern.


The surface quality is excellent, you can see the individual filaments of the printed part, as well as some 'flashing' where the pattern rods for the mounting holes didn't exactly meet the center hole.


You can also see where the acetic acid rusted the mounting hole peg.  Not sure how to avoid this, it seems to happen even when the pegs are oiled with vegetable oil (to act as a a release agent).


The top of the mold also shows filament marks.  Perhaps next time I'll fill the surface a bit and sand it down to improve the surface quality.  Anyway, the next step will be to cut sprues and risers to get the resin in and out of the mold.  I hope to try out actually casting parts in the next few days once I get some resin.

My first attempt at mold-making

Having finally gotten the CNC up and running, I'm kind of interested in seeing how to manufacture it cheaply and quickly.  The design of the machine centers around 3D printed plastic parts that bolt to aluminum cheeseplates.  These printed parts are the weakest parts of the machine; they are great in the sense that you can build a mill without owning a mill, but bad because the strength simply isn't there.   They are also time-consuming to print: most parts take 30min each or so, meaning that to make a copy would take a couple days or so.  The partially assembled machine is shown below, everything white is 3D printed, and most of the parts are not actually visible.
 

Enter casting.  Cast parts can solve a few of these problems.  Cast parts are likely to be much stronger than the 3D prints because the parts will be solid material and less prone to delamination than the current parts.  They can also be made relatively quickly using epoxy or polyester resins, which set very quickly.  Not much faster than the individual prints take, but multiple parts can be cast in parallel, so an entire axis could be cast at once and be ready in a an hour or so, instead of closer to 8 right now.  Cast parts can be further strengthened by the addition of chopped glass fibers, yielding FRC or FRP (fiber-reinforced composite/plastic) parts, although this will add considerable complexity to the process and likely require vacuum-assisted resin-transfer molding.

A downside is that the casting materials and resins can be quite expensive.  Wanting to get my feet wet, I turned to OOGOO, a Sugru substitute made from Silicone caulking, corn-starch and food coloring (actually I used Mio water flavor to color the first batch).  Below you can see the first test, including all ingredients and the first part still en-molded.


It is important to get the right caulking.  You want clear 100% silicone caulking, with no anti-fungal additives, and preferably a brand that mentions that it will stink of acetic acid (or is 'acid-cure').  'Neutral-cure' is a no-no, as is acrylic caulking.  At first I tried GE Silicone II but this failed to properly set.  GE Silicone 1 is what I eventually ended up using with good results, which is good because it's about $4 per 300 mL, making it one of the cheapest caulks there.

I don't have photos during the process because it was astonishingly messy, but can say that I mixed roughly 2:1 cornstarch to silicone by volume after adding mixing a few drops of water and coloring to the silicone.  You don't have to add coloring, but it helps you tell if the stuff is well mixed once you add the cornstarch.  Mixing the cornstarch is hard work, particularly in quantity, but you want a consistently colored, pastry like result.  Basically as you mix, you want the stuff to kind of tear and crumble at first but eventually reform and blend together.

Be quick though, because with these ratios you have limited working time.  After about 10 minutes the stuff sets up into a putty that becomes harder to work with and by 20 minutes it's effectively rubber.  However you can always mix more and it will bond just fine to Oogoo that has already set.  With my first batch made, I globbed it onto one of the pre-redesign CNC parts, ever cautious that it would dissolve it to nothing or light on fire.  After about 30 minutes I cut open the mold with great trepidation:


I needn't have worried, the ABS part was just fine (as are PLA parts, by all appearances).  Separating the mold showed that the Oogoo is perfect for this sort of thing:



Here you can see the pattern still embedded in half of the mold and then fully separated.  I used vegetable oil as release agent on the pattern prior to making the mold; this worked well, but it is hard to get the Oogoo around the part without also rubbing oil on the Oogoo surfaces that should not have release agent on them while building the mold.  I will have to learn how to deal with this for the next part.

Dimensional tolerances appear to be quite good, there is a firm friction fit between the patten and mold material.  In the photo immediately above, you can see that I wasn't able to get the Oogoo all the way into the slot of the part, but this could be easily fixed with a handsaw on the finished part.


With the pattern removed, the rods can be reinserted into the mold to create the impression for the mounting holds.  Provided the casting resin has low-enough viscosity, it will then easily flow around these posts to create the mounting holes.   I found it interesting to see that the acetic acid produced by the curing caulking actually corroded the post patterns, which you can see above by the discoloration of the steel posts where they were in contact with the Oogoo and by the rust-stains on the Oogoo itself.

Anyway, this process seems like it has great promise.  It's easy to work with, very cheap (less than $8 per pound) and seems to give quite accurate results.  The final molds are predominately silicone too, which has high heat resistance for the eventual polyester resin casting (I have seen large amounts of resin actually auto-ignite due to the exothermic reaction as the resin 'kicks').  I intend to keep playing with this stuff and hopefully end up with a reasonable method for performing casting of printed parts in the near future.



Saturday, November 3, 2012

Low Cost CNC Part VIII - Built-in electronics

In Part VII, I finally got the CNC cutting, but it was still a bit rough.  Cables ran everywhere, it was powered from a wall-wart and the electronics were sitting next to it on the table.  You can see the setup below:



Since finding the big C-channel piece that makes up the base of the machine, I've always planned to build the electronics into the base to give a nice, compact and clean machine.  I'd been waiting for a power-supply to arrive before doing this, but last week it came so I got to work.

The electronics are currently mounted to a piece of plywood that hooks on a key at one end and has bolt holes to bolt to some 3D printed plastic standoffs.  It fits into the base pretty nicely.


The plywood is a bit of an old shipping box; it had my address on it, hence the black tape.  Undoing two nuts on the right hand side allows the plywood to come loose:


Running approximately left-to-right, you can see the Arduino running GRBL, the three stepper driver carrier boards, a solid-state AC relay for spindle control as well as the power-supply and finally the E-stop button.  All the mains power is wired with 14 gauge household wiring connected with Marretts.  The E-stop button cuts all mains power to the power-supply and relay, thus rapidly immobilizing the machine.  The Arduino is unaffected by the E-stop, being powered from USB.  In this way, the E-stop also functions as an optional operator stop, allowing you to de-energize the machine, reposition it manually or change tools and then start it back up, knowing that it won't starting moving or cutting with your hands in there. 



Looking from the back, the USB socket for the Arduino is accessible, as is the scavenged spindle socket and power cable.  In a subsequent revision I plan to replace these with panel-mount components, but I couldn't locate them locally last week.


It's still a bit ghetto, but quite functional and self-contained at this point.  I intend to finalize the electronics layout and then make a sheet-metal replacement for the plywood with front and back panels and all panel-mount components.  But for the moment, it's working pretty well and I can easily pick it up and move it without rewiring the whole thing.


The other, straightforward, addition was cable drags on all the axes.  This has really helped to clean up the machine, giving me something that looks more like an actual tool than before.  Hopefully I will be able to start using it for projects soon.



Wednesday, October 24, 2012

Low Cost CNC Part VII - It LIVES!!!

So for the last, nearly two years, I've been working on building a low-cost, homemade, 3-axis CNC milling machine.  Today for the first time, I can say that I've done just that, having finally actually cut something.  As far as I know, it's the first milling machine build largely from 3D printed parts.

You can see the entire process, from the beginning, in the previous six posts:  Parts I, II, III, IV, V and VI.


The mill is shown below, along with the 3-axis CNC controller that I've posted about before. 


The spindle is a low-cost Dremel tool, currently attached with a Shapelock bracket.  It will no doubt get replaced with something less awful in the future (I am referring to both the Dremel and bracket, of course).


I have a Rotozip spiral-saw bit in the Dremel tool, to stand in for a proper milling bit.  It's a bit flexible, but more than up to tearing through MDF.


The whole setup is shown above and gives a good sense of scale.  My 3-Axis controller board is in the bottom right, controlled by my laptop.  The CNC itself has around a 6x6x4" working volume, although this is arbitrarily expandable in the X-direction.  The controller board runs GRBL, for which I have written a simple GUI for adjusting settings and jogging the machine. I plan to release the code for this when it stabilizes a bit, since GRBL needs a decent GUI.  I intend to add some basic features, like simple pocket/contour milling.  But for the time being, it's simply a software pendant.


So with everything set up, it was time to start cutting.  I attached my 3D printed iPhone mount to the XY table, started the Dremel, pressed record and began jogging the machine.  The result is pure awesomeness, for me anyway.



I had either the cutting depth or the feedrate too high for the spindle speed, since the bit began 'climbing', rounding the edges of the square when the feedrate was high.


But guess what? I don't care! 'Cause the CNC that I started nearly two years ago, which has been my preferred hobby while simultaneously being intensely frustrating has finally, finally, cut something. Praise Jebus!

Obviously there's refinement to be had.  For one thing, the ~10 mm long square sides should actually be one inch.  And I should make sure that the axes are actually square (I'd be shocked if they are).  Also I want to package the electronics in the base extrusion, provide a proper power-supply, perhaps some heat-sinks on the stepper drivers and maybe attach a real spindle.  And then there's ballscrews/belts.

But that's for later.  For the time being, my CNC actually cut something.

To my knowledge, this is also the first milling machine built substantially with 3D printed parts.  I hope in the not-to-distant future to get the feedrates up to the point of being able to 3D print with the mill itself. I also intend to build a tapping attachment to tap the holes used in the aluminum plates, which is -really- time-consuming and error-prone. This would make the machine as much of a RepRap as most 3D printers are, but considerably more solid.  But that's for later.

Monday, October 15, 2012

Low-Cost CNC Part VI - The Z-Axis

Following up on Parts I, II, III, IV, and V, the machine gets a Z-Axis!
 
With the addition of a Z-Axis the machine is starting to really come together.  The main supporting column is a 4x4" square extrusion with 1/4" walls, mounted at 30 degrees.  I'm hoping this will be stiff enough to be mounted cantilevered.  It mostly passes the 'grab-and-pull' test, but I can feel it deflect a bit when I yank on it.  Hopefully the forces during machining will be low enough for this to not be an issue.


I used two Newport 360-30 angle brackets to build the main support.  These normally retail for about $110 USD, but I was able to pick them up used for $38 each.  Here's a detailed shot of the lower mount.


This still makes them the most expensive part of the machine, but even with them, I think the mechanics could be built for about $100 per axis, including motors.  I looked at a number of options for the supporting column, including welding a custom bracket, but all quotes came back at $300+.  This cost less than $100 (including the column) and is pretty stiff. Plus I can re-use the brackets later.



I still need to tweak dimensions a bit.  As can be seen in the photo above, the Z-Axis is a bit low and doesn't provide much clearance over the XY table.  I will probably add a spacer on the angle mount and drill/tap a few new holes to get a bit more clearance.  I'm only shooting for about three extra inches.  This should give the final machine a few inches of working depth, including perhaps a small vise on the table and a cutter.  I may also flip the Z-Axis around and mount the plate rather than the carriage to the supporting column, which will give a bit more clearance but has other tradeoffs.


Sunday, September 16, 2012

Low Cost CNC Part V - A Redesign

I've been working on a homemade CNC now for quite some time.  My goal for the project was to produce something modular, where shaft-mounts, motor mounts and bearings were entirely separate parts using a standardized mounting pattern.  This would allow the machines to be put together like Legos and has a lot of advantages, like the ability to mix and match drive options, e.g. in the image below, one axis is screw-driver while the other is belt-driven.



I still think this concept has merit, however the part designs that I ended up using made for bulky and not particularly stiff machines. In the photo above, the top of the XY table is close to six inches from the base plate and the machine itself has considerable give.  Even worse, I didn't build enough slop into the designs to accommodate tolerances for manually built mounting plates, so it was actually quite difficult to get all the pieces to play nice.

I've since redesigned the machine to use more compact mounts, merged the shaft and motor supports into a single axis-end part and moved to half-inch shafting.  The switch to larger shafting results in a much stiffer machine, but unfortunately does require bushing style bearings.  Using the combined axis ends simplifies alignment, but unfortunately precludes belt drives.  I've also switched to Nema 17 steppers from Nema 23s, which leads to a more compact overall machine.  Surprisingly they don't seem to have much affect on the overall machine speed.  This makes for a much cleaner design:


Like the old parts, the new parts are 3D printed, but this time on my recently acquired RepRap.  The meshes were parametrically generated using my Python contructive solid geometry library, which is becoming quite usable.  This serves for prototypes, but final parts could be machines from plastic or aluminum.  The fixed-end for the leadscrew uses the same axis-end as the motor side, but with some bearing plates and Nylin nuts to take the axial loads:


After switching to acetal linear bushings, the bearings mounts can be made much more compact, reducing the height of the XY table from about six inches to a shade over 3.  These seem to run nicely on plain hardware store shafting, although hardened precision linear shafting would obviously be stiffer and smoother than cold-rolled bar stock.


Overall the machine is looking much cleaner.  I've decided to go with a knee-mill style machine rather than a gantry arrangement.  This means an XY table horizontally with a third axis mounted vertically. I bought a massive piece of aluminum channel to serve as the base of the machine, 10"x3"x20".  This provides a sturdy mount, and the controller and power-supply can be mounted to the underside.  Here's the thing midway through the build process yesterday:


And here's the finished-except-for-limit-switches XY table mounted to the channel.  The green duct tape is just there to stop it from gouging up my coffee table when I move it.  I will also probably replace the top cheese-plate with a thicker piece of flat-bar that's had all the holes drilled and tapped; I can't tell you how much I'm looking forward to doing that.


The mount for the Z-axis will be attached at the near-end of the machine.  I still have to design and build that mount, as well as print out the parts for the third axis.

After assembly, I simply had to try it out, missing limit switches or not.  I hooked it up to my 3-axis controller board with the Pololu A4988 carrier board carriers. The Arduino is flashed with GRBL, and wired to the carrier boards, which are in turn wired to the steppers.  I need to find some extra heatsinks for the X and Y axes, along with some thermal tape or epoxy, but they seem to run cool enough for testing anyway.


Here is it running, the first time I've gotten one of these CNC projects using actual GCode.  The leadscrew for the Y-axis is a bit loose in the fixed-end bearings, causing the knocking sounds, I have to look into the alignment, but otherwise it's working pretty well.  Speed is about one inch per second.


Next up will be designing a mount for the Z-axis.  I'm quite happy with the current set of printed parts and will probably continue to use them.

Saturday, August 25, 2012

Python Constructive Solid Geometry Update

In a earlier posts I've alluded to a Python Constructive Solid Geometry (CSG) library that I was working on to allow parametric design.  You can do this with OpenSCAD, which is great software, but in my opinion the language leaves a bit to be desired. I wanted a solution that worked with existing languages, specifically C, C++ and Python, so that the results could be integrated easily with other software such as remeshers or FEA packages.

Of course writing a robust CSG library is a daunting undertaking.  Fortunately there are existing libraries such as CGAL and Carve that handle this.  In my opinion CGAL is the more robust of the two, however it currently has compilation issues under OS-X and is substantially slower than Carve.

Regardless, neither have the interface that I'm looking for, like the ability to directly load meshes, affine transformations and minimal-code ways to perform boolean operations on meshes.  So I started work on a C++ wrapper for Carve that would give me the interface I wanted, with a wrapper for Python.

I'm pleased to say that it's coming along quite well and able to produce parts that are non-trivial.  The interface is considerably cleaned up from before and I'm now starting to use it for projects.  Here's two examples from (another) CNC project:



The code that generated these models is here:


from pyCSG import *

def inch_to_mm( inches ):
    return inches*25.4

def mm_to_inch( mm ):
    return mm/25.4

def hole_compensation( diameter ):
    return diameter+1.0

mounting_hole_radius = 0.5*hole_compensation( inch_to_mm( 5.0/16.0 ) )


def axis_end():
    obj = box( inch_to_mm( 4.5 ), inch_to_mm( 1.75 ), inch_to_mm( 0.75 ), True )
    
    screw_hole = cylinder( mounting_hole_radius, inch_to_mm( 3.0 ), True, 20 )
    
    shaft_hole = cylinder( 0.5*hole_compensation( inch_to_mm( 0.5 ) ), inch_to_mm(1.0), True, 20 ).rotate( 90.0, 0.0, 0.0 )

    center_hole = cylinder( 0.5*hole_compensation( inch_to_mm( 1.0 ) ), inch_to_mm(1.0), True, 20 ).rotate( 90.0, 0.0, 0.0 )
    mount_hole = cylinder( 0.5*hole_compensation( 4.0), inch_to_mm(1.0), True, 10 ).rotate( 90.0, 0.0, 0.0 )
    
    notch = box( inch_to_mm( 1.5 ), 2.0, inch_to_mm( 1.0 ), True )

    
    obj = obj - ( shaft_hole.translate( inch_to_mm( 1.5 ), 0.0, 0.0 ) + shaft_hole.translate( inch_to_mm( -1.5 ), 0.0, 0.0 ) )
    obj = obj - ( notch.translate( inch_to_mm( 2.25 ), 0.0, 0.0 ) + notch.translate( inch_to_mm( -2.25 ), 0.0, 0.0 ) )
    obj = obj - ( center_hole + mount_hole.translate( -15.5, -15.5, 0.0 ) + mount_hole.translate(  15.5, -15.5, 0.0 ) + mount_hole.translate( 15.5, 15.5, 0.0 ) + mount_hole.translate( -15.5, 15.5, 0.0 ) ) 
    
    obj = obj - ( screw_hole.translate( inch_to_mm(1.0), 0.0, 0.0 ) + screw_hole.translate( inch_to_mm(-1.0), 0.0, 0.0 ) )
    obj = obj - ( screw_hole.translate( inch_to_mm(2.0), 0.0, 0.0 ) + screw_hole.translate( inch_to_mm(-2.0), 0.0, 0.0 ) )
    
    return obj

def carriage():
    obj = box( inch_to_mm( 5 ), inch_to_mm( 5 ), inch_to_mm( 1.0 ), True )
    shaft_hole = cylinder( inch_to_mm( 0.75 )/2.0, inch_to_mm( 5.5 ), True )
    screw_hole = cylinder( inch_to_mm( 0.5 )/2.0, inch_to_mm( 5.5 ), True )
    
    leadnut_hole = cylinder( inch_to_mm(0.25)*0.5, inch_to_mm( 1.0 ), True );
    leadnut_access = box( inch_to_mm( 1.5 ), inch_to_mm( 3.0/8.0 ), inch_to_mm( 1.0 ), True )
    
    
    mhole = cylinder( mounting_hole_radius, inch_to_mm( 2.0 ), True ).rotate( 90.0, 0.0, 0.0 )
    
    obj = obj - ( shaft_hole.translate( inch_to_mm( 1.5 ), 0.0, 0.0 ) + shaft_hole.translate( inch_to_mm( -1.5 ), 0.0, 0.0 ) + screw_hole )
    obj = obj - ( leadnut_hole.translate( inch_to_mm( 0.5 ), inch_to_mm( -2.5 ), 0.0 ) + leadnut_hole.translate( inch_to_mm( -0.5 ), inch_to_mm( -2.5 ), 0.0 ) + leadnut_access.translate( 0.0, inch_to_mm( -2.0 ), inch_to_mm( 0.2 ) ) )
    
    for i in range( -2, 3 ):
        for j in range( -2, 3 ):
            if i != 0 and j != 0:
                obj = obj - ( mhole.translate( inch_to_mm( 1.0*i ), inch_to_mm( 1.0*j ), 0.0 ) )
    return obj
              

axis_end().save_mesh("axis_end.obj" )
carriage().save_mesh("carriage.obj" )

As you can see, this approach gives lots of flexibility in terms of manipulating and patterning objects using custom code.  The examples above are not great examples of parametric design, but I'm sure you can imagine the sort of stuff that can be done.

I still have to perform a bit of cleanup outside the library to get printable models.  I just run each model through MeshLab's planar edge-flipping optimizer. This is a pretty simple step and I plan to integrate it into the library shortly, along with the ability to extrude custom profiles and build surfaces of revolution.  When these features are finished I plan to release the code for the library and Python wrapper.

Friday, August 10, 2012

3-Axis CNC Controller

In a previous post, I showed the single axis stepper driver boards that I sent out to be made by OSH Park. These seemed to be electrically fine, although it was tricky to properly test without the connectors and other components.  After a quick order from DigiKey, I had the bits I needed.


I'm pleased to say that these work as expected, allowing the microstep mode to be chosen by DIP switch, breaking out all inputs and outputs with screw terminals, and providing the connections needed for high and low limit switches.  I've assembled three of these and screwed them to a piece of MDF to serve as the basis for a 3-Axis CNC controller board based on an Arduino Uno and GRBL.






The start of this board is shown above. Before it's complete I need to add the power connections for the high-power side, along with the limit switches.  I have the GRBL firmware flashed onto the Arduino and have connected a few motors to this setup and everything works great!


Shown below is a closeup of the boards.  The screw terminals in the front connect the limit switches for the high and low endstops.  These have pulldown resistors and are connected to two of the screw-terminal positions on the logic side of the board (the two un-wired stops).  The remaining pulldown resistors are connected to the microstep selection pins, which are set by the red DIP switch.  On the right side of the board are the motor connections (the 4-position terminal block) and the motor power connections (the two position terminals).  All connections are with 3.5mm terminal blocks, which actually meet the power requirements for multi-amp 24V operation.  They also allow multiple connections to be made which allows the daisy-chain type wiring shown above.  The low-power side also has these connections since even though they are not needed it's nice to only need one screwdriver to do the wiring.

I'm quite pleased with my first attempt at getting a board made.  It worked first try, the quality of the boards is excellent and I think these drivers can form the basis of a good many other projects.

Friday, July 27, 2012

A4988 Single Axis Carrier Board

I recently ordered some simple boards from OSH Park.  These are single-axis versions of my 3-axis carrier board for the Pololu A4988 stepper carriers and (will) include pulldown resistors for the microstepping pins (which can be set using DIP switches), as well power and pull-down resistors for high- and low-limit switches.  All connections are made using 3.5mm screw terminals and the boards have mounting holes for more permanent installation.  They also feature a diode for reverse voltage protection on the logic supply (but not on the motor supply).



A quick test seems to indicate that the boards are electrically sound, although I have yet to fully populate one and test it fully.  If they work properly, I plan to fix a silkscreen error where the logic supply voltage and ground connections are unlabeled.  I also plan to break out the enable pin on the driver and the large capacitor across the motor supply suggested by the Pololu site.  When I'm content with how the boards work, I'll release the Eagle files.

Sunday, June 10, 2012

3-Axis A4988 Stepper Driver Carrier

More on the perpetually in-progress CNC (see the mechanical stuff in parts I, II, III, IV)

I've recently built a prototype board for the Pololu A4988 stepper drivers carriers.  These little drivers are inexpensive (about $12) and fairly gutsy (2A per phase), but can blow fairly easily.  Using male headers they can easily be used as drop in modules for a larger CNC controller board.  My board breaks out the microstepping pins to DIP switches and adds screw terminal connections for the high-power side, with female headers for the TTL control inputs and low-power supply.


The two-pin set of female headers on the left is the low-power supply, the middle six-pin set of headers is the step/direction controls for each of the axes and the bottom six-pin header is for upper and lower limit switches for each axis.  The top set of two-pin screw-terminals is the motor power-supply and the remaining 3x6 pin connectors are the motor winding connections with the bottom two terminals for upper and lower limit inputs.  Unfortunately I ran out of space on the board to provide a 5V supply to the limit switches, so these will have to be wired externally to 5V.  I may also add some pull-down resistors to the backside of the board, since the switches are currently floating, although this does not seem to be a problem in practice for some reason.

There's a surprising number of solder joints needed for this simple board, largely due to using point-to-point wiring, but it seems to be electrically sound:


This board really cleans up my testing rig for the CNC, just a few jumper wires are all that's needed to connect my Arduino to the two mostly-finished translation stages.  Using screw-terminals instead of soldered on connectors is also much more convenient when rewiring the steppers as bipolar parallel/serial during testing:


The final machine will probably not end up using this board, I'm considering investing in a proper 3-axis board, since they can be found quite cheaply on Ebay.  That said, it's a nice tidy little package that allows the stepper drivers to be replaced as needed as well as preventing the inevitable wiring mistakes that happen when developing on breadboards.

Saturday, May 26, 2012

Python Involute Spur Gear Script

It's pretty difficult to find reasonably priced gears around and even if you can find them, they're often not exactly what you want. Finding a gear with the right number of teeth, width, pressure-angle and bore is often not possible.

To this end I have written a python script for generating involute gears. Other scripts are available, an OpenSCAD script thingiverse for example, however i) I like to do things myself, from scratch and ii) I'd prefer my setup work with a more mainstream programming language. The source-code is available at the bottom of this post.

The script generates involute spur gears, with pressure angles up to about 30 degrees. I hope to extend it to include racks and internal gears eventually, but it is quite useful now. Output is in SVG for easy editing in graphic-design/laser-cutting or DXF for use with OpenSCAD. I am currently working on a CGAL-based constructive solid geometry module for python that will allow CSG operations to be performed by python scripts. This would allow fully parameteric CSG design to be done in an open, mainstream language.

I've generated some examples using the following script:
# import the gears script
from gears import *

pa = 14.5   # pressure angle, in degrees
P  = 24     # pitch, teeth per unit distance

# generate three gears
ax, ay = gears_make_gear( pa, 12, P )
bx, by = gears_make_gear( pa, 24, P )
cx, cy = gears_make_gear( pa, 48, P )
dx, dy = gears_make_gear( 30.0, 8, P )

# write them out as svg files, scaled uniformly by 150 and 300
gears_svg_out( ax, ay, 'section_a.svg', 150 )
gears_svg_out( bx, by, 'section_b.svg', 150 )
gears_svg_out( cx, cy, 'section_c.svg', 150 )
gears_svg_out( dx, dy, 'section_d.svg', 300 )


The output is below:



14.5 degree pressure angle, 12 teeth




14.5 degree pressure angle, 24 teeth




14.5 degree pressure angle, 48 teeth




30 degree pressure angle, 8 teeth


Since I haven't finished the CGAL CSG python CSG library, I've been calling OpenSCAD from the command-line via python to generate actual gears. I 3D printed some of these on the Vancouver Hackspace (VHS) Makerbot, with reasonably good results:



These parts will eventually be used as part of a 1:4 drive for the leadscrews for my CNC. Currently I have ample torque, but cannot spin the motors fast enough to get fast rapid traversals. With a 1:4 drive, I will hopefully be able to drive the machine quite quickly.



Sourcecode for the script can be downloaded from: http://sites.google.com/site/jamesgregson/tmp/gears.py. Use it for whatever you'd like, but please don't redistribute it.

Saturday, April 21, 2012

Low Cost CNC Part IV - Cheeseplates and belt-drives

I started by making some cheese-plate, aka optical breadboards.  You can buy these--and they will be far more accurate than I can make--but they are expensive.  This is probably because they are precision ground, anodized and 1/2 inch thick.  Half-inch plates are too heavy for this application, so I set about making some from 1/4 inch plate.

I scored out a grid pattern and then center-punched the intersections.  Actually I center-punched them twice, once with a very fine punch and then again with a much larger punch.  I've found this results in better-centered holes.  I also purchased a 1/4-20 tap-drill.  This simultaneously drills and taps a hole and, when used in a drill-press, dramatically speeds the process of making these plates up, as well as results in more accurate holes.  You can see the grid of center-punched holes and tap-drill below:


It's kind of weird using these with a drill press.  First you have to lower the spindle to drill into the material, until the threads begin to catch.  At this point it pulls the spindle down at the correct speed, you basically have to just stay out of the way.  However it is critical to get the stop of the drill-press set at the right location, particularly with the 1/4 inch plate, since the tap-drill is only intended for thinner plate stock.  If the stop is set too low, it will drag the countersink portion of the bit most of the way through the plate, leaving almost no thread.  Too high and it will presumably either strip the hole or break the bit, since the drill-press will stop while the threaded portion of the bit is still in the plate.

After a few false-starts, I had it down and started cranking out the holes:


The tap-drill was a life-saver.  Previously each hole would take about 5 minutes by the time center-punching, clamping, drilling and hand-tapping was done.  With the tap-drill it is closer to one or two, and the results are way more accurate.  A catch is that with the non-reversing spindle of the VHS drill-press it is necessary to unscrew the spindle manually.  This is a bit of a workout for the wrists, but less so than tapping by hand.  A tap-head could fix this but costs more than I care to spend.

After a bit over an hour, I had my first cheese-plate:


Then I made another one.  Then added a few rows of holes to the existing plate I'd been using as a base for the first stage.

Following up on the previous post, I've printed out fixed-end bearing blocks for the leadscrews.  These support axial loads on the leadscrew and take the load off (flimsy) motor mount.  I also tested the completed stage at 30V, hoping to reach the 100 mm/s that I'd set as a target.  I was able to reach about 40 mm/s, far short of what I'd hoped.  Switching to a proper leadscrew would fix this, however would fly in the face of 'low-cost'.  I'm pretty sure that the current feedrates are more than any spindle I plan to use can handle, however they're way too low for 3D printing.

This is where the beauty of the design-concept comes in.  With all part mounting on 1" centered, 1/4-20 holes, it is trivial to modify the machine design just by unbolting the parts and shifting them around. So I rotated the motor and fixed-support by 90 degrees and shifted some of the linear bearings a bit and added some timing pulleys.  The end result was this:


The bottom axis is belt-driven, while the top-axis is still screw-driven. All the parts are common, except a few spacers that I pressed into service as clamps for the belt so I could tension it correctly. You can see the setup below:


The fixed-end screw supports were used to hold the timing pulley opposite the motor.  The picture below shows the support with the top-axis screw sticking through.  Backing nuts onto the bearings locks the axial position of the screw.  It currently uses 608 (i.e. skateboard) bearings but should be modified to use angular contact bearings:


So how fast is it?  Much faster.  Like, a lot faster.  It's not quite what I'd wanted (a design that handled everything), but I'll settle for a set of parts that can be CNC or 3D printer with only a wrench and some re-jiggering separating them.  And what's more, it's still pretty beefy.  I loaded up the stage with (in addition to the top-axis), 16 lbs (i.e. all) of my girlfriend's free-weights.  It had no problem flinging them back and forth, shaking my coffee table vigorously in the process.


That's all for now.  I'll probably convert the other long-axis to belt-drive since I think I'm more likely to get the full machine up and running as a 3D printer rather than as a CNC, at least in the initial stages.