syntax-highlighter

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.

Thursday, April 19, 2012

Simple Command-Line Argument Handling

Much of the code I write is run from the command-line and often has a large number of input parameters.  I quickly got tired of handling command-line arguments and of having to remember the ordering of parameters.

The following code makes this easier by allowing parameters to be specified as key-value pairs.  You then call command_line_get_argument(.), with the desired argument name and type.  It searches through the list of arguments supplied to the program and attempts to read them into the correct type.  If the argument doesn't exist or doesn't convert to the correct type, the function returns false.

e.g.
// some variables declared here...
bool ok = true;
ok &= command_line_get_argument( argc, argv, "-input_file",      ARGUMENT_STRING, input_filename );
ok &= command_line_get_argument( argc, argv, "-output_basename", ARGUMENT_STRING, output_basename );
ok &= command_line_get_argument( argc, argv, "-num_iterations",  ARGUMENT_INT,    &num_iterations );
ok &= command_line_get_argument( argc, argv, "-prior_weight",    ARGUMENT_FLOAT,  &prior_weight );
if( !ok ){
 std::cout << "Usage:" << argv[0] << " [arguments]" << std::endl;
 std::cout << "arguments:" << std::endl;
 std::cout << "\t-input_file       [filename]         REQUIRED - input ground-truth image" << std::endl;
 std::cout << "\t-output_basename  [partial filename] REQUIRED - base name for output files, e.g. 'test01/image0_'" << std::endl;
 std::cout << "\t-num_iterations   [integer]          REQUIRED - number of iterations to perform, each is width*height mutations" << std::endl;
 std::cout << "\t-prior_weight     [real]             REQUIRED - weight for regularizer" << std::endl;
 return 1;
}

The 20 or so lines above replace what was initially closer to one hundred with error checking and ends up being much more readable. The programs are also nicer to work with, since they don't rely on a specific argument order.

The code for this can be downloaded from https://sites.google.com/site/jamesgregson/tmp/command_line_arguments.h. It can be used for whatever you'd like, provided it's attributed as mine.

Sunday, April 8, 2012

Low Cost CNC Machine Part III - 1 1/2 Half-Finished Translation Stages

A continuation of Parts I and II....

I went on a bit of a tear today, building another four of the CNC shaft mounts, plus printing two hex-nut mounts and another motor mount.  As a result I have one nearly completed stage that just needs end-stops added, plus another that's about halfway done and still needs bearing mounts and end-stops.

The newly created hex-nut mounts follow the trend of the shaft and bearing mounts, except that the frame size of the Keling NEMA23s that I'm using don't match that well with shaft centers at 1" height, so the threaded rod ends up a few millimeters above the linear shafts.  Consequently the nut has to be moved a little closer to the carriage to take up the difference.



Nothing really novel here, but having everything mount on standard hole-spacings with standard hardware really makes the design and assembly process easier.

With the hex-nuts in hand, I can now test the stages to see how they perform.  The assembled stage is shown below, with the half-finished stage partially assembled on the carriage.  The extra pillow-block bearings were the ones I'd bought from VXB, I will probably not actually end up using them.  Note that I'm really short on 2" 1/4-20 machine screws, so every mount only has one screw on the clamp side. 


I also tested out a simple anti-backlash nut idea.  It involves backing an additional nut onto a stack of rubber washers.  This is pretty cheap, with adjustable preload and surprisingly enough, seems to stay in place just by friction, although I think a proper mount should probably be designed.  A closeup is below:


For testing I'm using the standard Pololu A4988 drivers, driven by an Arduino Uno.  After some mishaps a few months ago, I fried two of my drivers so I'll have to pick up some new ones. In the photo below you can see the Arduino connected to the 4-axis driver board I've kludged together, connected to the stepper in bipolar parallel, which should give about 180 oz-in of torque.


Early tests showed that even unloaded, it was important to control acceleration of the motors to make sure they didn't skip steps.  I wrote a simple sketch that ramps the step rate up from a low rate where the motors can slam from forward to reverse without stuttering up to a much higher rate, about 2500 steps/sec with no microstepping.  The code is below:

// step pin is #3, direction is pin #2
void setup(){
 Serial.begin(115200); 
 pinMode(2, OUTPUT);
 pinMode(3, OUTPUT);
}

inline void advance_steps( int dir, int steps ){
  int increment  = 4;
  int delay_init = 1000;        // 1/2 initial step delay, in us
  int delay_final = 200;        // 1/2 final step delay, in us
  int delay_curr = delay_init;  // current delay
  
  // compute the end of the ramp up and the start of the ramp down
  int ramp_up   = min( steps/2, (delay_init-delay_final)/increment );
  int ramp_down = max( steps/2, steps-(delay_init-delay_final)/increment );
  
  // set the direction
  digitalWrite( 2, dir );
  
  // start stepping
  int i=0; // step counter
  
  // ramp up
  for( ; i<ramp_up; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );  
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );
    delay_curr-=increment;
  }
  
  // constant feed-rate
  for( ; i<ramp_down; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );  
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );    
  }
  
  // ramp down
  for( ; i<steps; i++ ){
    digitalWrite( 3, HIGH );
    delayMicroseconds( delay_curr );
    digitalWrite( 3, LOW );
    delayMicroseconds( delay_curr );
    delay_curr+=increment;
  }
}

void loop(){
  // move carriage one way
  advance_steps( LOW, 15000 );
  
  // then reverse
  advance_steps( HIGH, 15000 );
}

So I tried it out, and it worked! Moves the carriage back and forth like all-get-out.

video

Pretty cool to see something you've taken from initial idea to built prototype actually perform.  Doesn't hit the 100 mm/s feedrate that I'd like, but I didn't really expect that from the 1/4-20 rod.  With a proper leadscrew it should be no problem to get to that rate.  Yes, that is a T-Pain microphone on the chair in the background.

Next I wanted to see how loaded down the motors were.  To test this I added 16 pounds of freeweights.  It still ran smooth as silk, and after 15 minutes, the Pololu driver was only slightly warm.

I'm really pleased with how this turned out.  The stage is actually pretty quiet, much quieter than I'd expected.  On top of that, it seems to have power to spare even from a 12V supply.  Since the Pololu drivers are rated for up to 35V, I think I should be able to run it at 24V and get even faster feedrates. I haven't checked the accuracy yet, but if the 1/4-20 rod was perfect (Ha!) and there was no backlash (Ha!) then it would have a resolution of 6.4 um/step.  It might be wishful thinking, but I'm hoping to get it to within a few thousands of an inch (0.025-0.05mm).

Next steps are to finish the z-axis and install endstops.  Then I'll make the components for the y-axis and start thinking about a frame.




Wednesday, April 4, 2012

Low Cost CNC Machine Part II - A half-finished translation stage

Following up on my previous post regarding a low-cost cnc machine I have begun the design process and have a half-completed linear stage.  This will form the x-axis for my machine which the z-axis will be mounted to gantry-style.  The y-axis will mount to the machine bed, keeping the total amount of moving metal minimized in the interests of stiffness and acceleration.


Here is the (half-finished) result.  The carriage will run on 8mm linear shafting.  This is pretty light-duty, but cheap and easy for prototyping.  It will probably need to be replaced on the final machine with 12mm or 20mm shafting.  The shaft mounts are made from 1/2"x1.5" bar stock and clamp using one of the two mounting bolts.  This allows the shafts to be removed without totally loosening the mounts.  I started by prototyping on the Makerbot at VHS, and finally got the bar-stock cut at Metal Supermarkets, doing the drilling with the drill-press at VHS.


I have been very happy with my choice of mounting all components with 1/4-20 bolts on 1" centers.  The base stock is 1/4" aluminum plate, with drilled and tapped mounting holes.  It would also be possible, and much more accurate, to use optical breadboards, however these would severely limit the acceleration and feedrates attainable, since they are generally 1/2" thick and twice as heavy.  For now I'm sticking with the 1/4" plate.I



The bearing mounts are 3D printed for now.  They will eventually be made from 1.5" square stock cut in 1" lengths, which is just long enough to hold the LM8UU linear bearings.  The bearing is mounted 1" from the base, following the convention for the shaft-mounts, in order to provide a half-inch of clearance for bolt-heads. I had originally intended to make these out of aluminum, buying the square stock and having it cut, however when I got home I had a terrible surprise:


The prototype mount is shown with the cut aluminum, guess the pieces were cut incorrectly.  In the bag are the other 11 cut pieces that won't fit.  I don't know if it's my mistake or Metal Supermarket's, but either way the pieces are useless to me and I have a lot of them.

I may eventually switch to brass bushings similar to my Shapelock linear bearings to keep the noise down when 3D printing, but will use the LM8UU's until this is actually a problem.  The bearing mounts clamp to the bearings the same way that the shaft-supports do, and as always, mount to the carriage with 1/4-20 bolts on 1" centers.



The motor-mount breaks from the mold a bit.  I will probably make the final mounts from angle or C-channel, but for now just have the 3D printed part.  This fits a NEMA23 stepper very nicely, but is not particularly stiff.  The stiffness is not so big a concern, since I will be adding support bearing to keep the threaded rod in tension, which should handle any axial stresses, however I'm worried that the less than rugged mount might break during repeated trips to VHS in my bicycle paniers.

All that remains to finish the prototype stage is to get a piece of plate for the carriage, print the (already designed) lead-screw support bearings and lead-nut mount and drill/tap about a dozen holes.  The result should be a reasonably stiff, reasonably accurate linear stage.  If all goes well, I can then look into sourcing reasonably priced leadscrews, since the stand-in 1/4-20 rod I'm using won't allow the 100 mm/s feeds I'm looking for.  Currently I can only achieve about 30 RPS (~38mm/s) with the steppers, which needs quite gradual acceleration. Switching to a 1/4", 2- or 3- start leadscrew would give me the feeds I'm looking for, but sourcing these in Vancouver is troublesome.

Low Cost CNC Machine Part I

I am currently in the midst of trying to build a low-cost 3-axis CNC milling machine.  I have no basis for making one, I don't even have a project in mind that needs one; I just want one, and specifically one that I've made myself.  This means that I will invariably (a) spend way more time/money building it than needed if I just ordered one, (b) will probably end up with a sub-par machine and (c) will be heartbroken when it has to be taken apart because it is absolutely deafening when running. Oh well.

I've seen a number of DIY machines online, and while I find them interesting, none really do it for me.  I would like to end up with a mill that can handle a variety of materials: wood, plastic, protoboard and ideally, aluminum.  Milling hard materials at sensible feedrates will require a stiff machine, so plywood and MDF framed machines are out, as are (in my opinion) belt and chain drives since they can be backdriven.

Beyond having just a plain CNC, I would also like the ability to add an extruder head to make a 3D extuder.  This means that the feedrates must be reasonably fast; based on the Makerbot at the Vancouver Hack Space, (VHS). I would like feedrates of about 50 mm/s, with a maximum feed of over 100 mm/s so the motors aren't running full-out all the time and so the machine has decent acceleration which will keep it responsive.  This means that I will also need a fairly light machine, which is completely at odds with the stiffness requirement.  Beyond that, I would like to be able to build the machine with a minimum of machining, in order to keep costs down.

Nevertheless I plunge ahead.  Compromising between the stiffness and lightness requirements, I have decided to make the machine from aluminum.  To give maximum flexibility in the design and to maximize component reuse when the design inevitably changes, I have decided to make all frame components from stock sections of aluminum, i.e. bar and plate stock.  These have good tolerances (relatively speaking) to begin with and nice factory edges from which to reference other features.  I've further decided that all components should mount with 1/4-20 UNC bolts on 1" centers.  These bolts are cheap in a wide range of lengths, and using this spacing allows me to prototype on the optical table at the lab.  This will mean a lot of tapping, but it's time I learned how to tap anyway.  The frame components themselves will be drilled with 5/16" holes to allow a sloppy fit, which will allow minor offsets and angular misalignments to be corrected before tightening everything down.

I've also decided to take advantage of my excellent access to the Makerbot at VHS to prototype the various parts before machining them from aluminum.  As conceived, everything can be made with a cutoff saw (which Metal Supermarkets has, at 1$ per cut) and a drill-press (which the hackspace has).  However the machining is a time-consuming and error-prone process for me, so having a design without interference or other gotchas will cut down on frustration.

That's all for now....

Hex-Meshing Paper Published!

Quite delayed in posting this, but my paper, All-Hex Mesh Generation via Volumetric PolyCube Deformation (joint work with Alla Sheffer and Eugene Zhange) was published in SGP2011! This paper dealt with how to automatically generate a lego-like PolyCube version of an object that could then be meshed with hexahedral elements (distorted cubes).  This is a very difficult problem with big application in industry, although this paper is just a stepping stone in the road to automatic all-hex mesh generation.


The image above shows a PolyCube automatically generated from the geometry that is hex-meshed on the right.  Only hexahedral elements are present in the output mesh and the minimum element quality is often very high, although this is not guaranteed by the method.

Uniform Grid Interpolation in 1D, 2D and 3D

Recently I had need for code to interpolate data stored on regular grids in 1D, 2D and 3D.  This code is straightforward but time-consuming to write and I often find myself re-implementing it for each project that I work on.

The linked header file includes templated interpolation routines for 1D, 2D and 3D interpolation on uniformly gridded data with linear, cosine and cubic (Catmull-Rom) interpolants.  It is fully documented with DOXYGEN and provides a simple fairly consistent API.  The code works by interpolating values that are passed in either as individual values or as arrays allowing it to work with arbitrarily defined grids and also optionally returns the weights associated with each value.  I have used the code to implement

The following code snippet taken from one of the 2D test functions shows the use of the API. In all API functions, points are ordered lexographically by axis, i.e. p[0] = [x, y], p[1] = [x+1, y], p[2] = [x, y+1], p[3] = [x+1, y+1].

/**
 @brief test function for the 2D linear interpolation routines, currently just
 checks that the methods are consistent, i.e. that they all produce the same
 output for the same input
 @return true if test passes false otherwise
*/
static bool interpolation_test_lerp_2D(){
 const double t0=0.325f, t1=0.675, v[] = { 1.0, 2.0, 2.0, 5.0 };
 double o0, o1, o2, o3, w[4];
 bool result = true;
 
 // check that API functions are consistent
 o0 = interpolation_lerp_2D( v[0], v[1], v[2], v[3], t0, t1 );
 o1 = interpolation_lerp_2D( v[0], v[1], v[2], v[3], t0, t1, w[0], w[1], w[2], w[3] );
 o2 = interpolation_lerp_2D( v, t0, t1 );
 o3 = interpolation_lerp_2D( v, t0, t1, w );
 result &= fabs(o0-o1) < 1e-9 && fabs(o0-o2) < 1e-9 && fabs(o0-o3) < 1e-9;
 
 // check that input values are reproduced
 result &= fabs( interpolation_lerp_2D( v[0], v[1], v[2], v[3], 0.0, 0.0 )-v[0] ) < 1e-9; 
 result &= fabs( interpolation_lerp_2D( v[0], v[1], v[2], v[3], 1.0, 0.0 )-v[1] ) < 1e-9;
 result &= fabs( interpolation_lerp_2D( v[0], v[1], v[2], v[3], 0.0, 1.0 )-v[2] ) < 1e-9;
 result &= fabs( interpolation_lerp_2D( v[0], v[1], v[2], v[3], 1.0, 1.0 )-v[3] ) < 1e-9; 

 return result;
}

The code is freely available for all purposes and can be downloaded below:

https://sites.google.com/site/jamesgregson/tmp/interpolation.h

Please contact me if you find bugs, questions or find the code useful.