Parametric OpenSCAD mill stand model

mcdanlj

Registered
Registered
Joined
Aug 24, 2018
Messages
321
Probably OpenSCAD isn't what most folks here think of when they hear "CAD" but I'm a programmer by day so for me it can be faster than dragging things around with a mouse and thinking about how to express constraints parsimoniously so constrain-driven CAD doesn't complain about over-constrained models.

For my PM-30MV, I need a stand that holds a tool chest and won't tip over if I have to run a load well out to one end. Since anchoring to the floor isn't a great option for me, cantilevered feet and leveling pads are a better idea. So I fired up OpenSCAD and came up with the following model. Before I weld it up or even buy stock I'd love thoughts on the design. I think the doubled supports in back are probably overkill but are a better-safe-than-sorry thing. I'll take a tool chest without handles or casters installed and slide it in the front, all the way back to the back supports.

mill_stand_low_front_view.png

The BOM for the image shown is:
2 48" 2"x2" tube (feet)
4 32.55" 1"x2" tube (pillars)
2 29.25" 1"x2" tube (transverse top support)
2 14.3" 1"x2" tube (top mill support)
2 14.3" 1"x1" angle (chest base support)
1 29.25"x18.3" plate (1/8" or thicker)
2 29.25" 1"x4" channel
2 14.3" 1"x4" channel

I didn't specify web/thickness generally, though I put something into the model. I don't have FEA tools and haven't done more than pondered with no training on how this might fail. Basically all the joints would be welded. Large holes drilled/milled in the ends of the feet to mount the leveling pads, and holes (not shown or modeled) drilled and tapped through the top plate and top mill support tubes for attaching the mill.

mill_stand_upper_rear_view.png

I would attach the OpenSCAD model but it's not an allowed type, so I guess I'll just paste it inline....
C-like:
// Copyright © 2018 Michael K Johnson
// CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/
// dimensions are inches not mm because that's what all the components use
// Example dimensions are for holding this chest (without casters installed):
// https://www.homedepot.com/p/Husky-27-in-5-Drawer-Roller-Cabinet-Tool-Chest-in-Textured-Black-UAT-H-26051/303764045
// Expected to be used with leveling pads, such as
// https://www.precisionmatthews.com/shop/levelingpads/
// http://www.grizzly.com/products/Grizzly-Machine-Mount-3-1600-lb-Capacity
// prints calculated lengths to cut; weld all joints, drill as necessary
// no parts shown for filling tubes with sand to damp vibration but feel free
chest_w = 27;
chest_d = 18.3;
chest_h = 31.3;
web=0.125;
w_space = 0.125;
head_space = 0.25;
angle_w = 1;
angle_h = angle_w;
channel_w = 4;
channel_h = 1;
tube_w = 2;
tube_h = 1;
//foot_w = tube_w;
//foot_l = foot_w;
//foot_t = 3/16;
foot_w = 48;
foot_h = 2;
toe_h = max(2, foot_h);
foot_mount_d = 3/8;
pillar_l = chest_h+toe_h+head_space-tube_h;
echo(pillar_length=pillar_l);
transverse_l = chest_w + tube_h*2 + w_space*2;
echo(transverse_length=transverse_l);
support_l = chest_d-2*tube_w;
echo(support_length=support_l);
mill_x_offset = 17.125/2; // measure your mill: half X distance between center of mounting holes
back_support_l = transverse_l;
side_support_l = support_l;
top_t = 1/8;
top_w = chest_w+2*tube_h+2*w_space;
echo(top_width = top_w);
module chest() {
    translate([-chest_w/2, -chest_d/2, 0])
        cube([chest_w, chest_d, chest_h]);
}
module angle(width=angle_w, height=angle_h, web=web, length=4) {
    translate([-width/2, -length/2, -height/2]) {
        cube([width, length, web]);
        cube([web, length, height]);
    }
}
module channel(width=channel_w, height=channel_h, web=web, length=4) {
    angle(width, height, web, length);
    translate([width-(web+width/2), -length/2, -height/2])
        cube([web, length, height]);
}
module tube(width=tube_w, height=tube_h, web=web, length=4) {
    channel(width, height, web, length);
    translate([-width/2, -length/2, height-(web+height/2)])
        cube([width, length, web]);
}
module chest_support() {
    toe_top = toe_h-web-angle_h/2;
    foot_top = foot_h-angle_h/2;
    h = max(toe_top, foot_top);
    translate([angle_w/2-(chest_w/2+w_space), 0, h])
        rotate([0, 180, 180]) {
            if (h == foot_top) {
                angle(length = chest_d - 2*tube_w);
            } else {
                angle(length = chest_d);
            }
        }
}
module pillar() {
    translate([-(tube_h/2+chest_w/2+w_space), -chest_d/2+tube_w/2, pillar_l/2+tube_h])
        rotate([90, 0, 90])
        tube(length=pillar_l);
    // foot — this would work for wide enough chest if no cantilever needed
    /*
    translate([-(tube_h+chest_w/2+w_space), -chest_d/2+tube_w/2, foot_t/2]) difference() {
        cube([foot_w, foot_l, foot_t], center=true);
        translate([-foot_w/4, 0, 0]) cylinder(d=foot_mount_d, h=foot_t*2, center=true, $fn=12);
    }
    */
}
module foot_hole() {
    translate([0, 0, -foot_h/2])
        cylinder(d=foot_mount_d, h=foot_h, center=true, $fn=12);
    translate([0, 0, foot_h/2])
        cylinder(d=tube_w/4*3, h=foot_h, center=true, $fn=24);
}
module foot() {
    translate([0, chest_d/2-tube_w/2, foot_h/2])
        rotate([0, 0, 90])
        difference() {
            tube(height=foot_h, length=foot_w);
            union() {
                translate([0, foot_w/2-tube_w/2]) foot_hole();
                translate([0, -(foot_w/2-tube_w/2)]) foot_hole();
            }
        }
}
module top_transverse() {
    translate([0, chest_d/2-tube_w/2, chest_h+toe_h+head_space+tube_h/2])
        rotate([0, 0, 90])
        tube(length=transverse_l);
}
module top_support() {
    translate([-mill_x_offset, 0, chest_h+toe_h+head_space+tube_h/2])
        tube(length=support_l);
}
module back_support() {
    translate([0, chest_d/2+channel_h/2, 0])
        rotate([0, 90, 90])
        channel(length=back_support_l);
}
module side_support() {
    translate([-(chest_w/2+w_space+channel_h/2), 0, (chest_h+toe_h)/4*3 + channel_w/2])
        rotate([0, 90, 0])
        channel(length=side_support_l);
}
module top() {
    translate([-top_w/2, -chest_d/2, pillar_l+tube_h+tube_h])
        cube([top_w, chest_d, top_t]);
}
module assembly() {
    //color("Gray", alpha=0.2) translate([0, 0, toe_h]) chest();

    // angle supports for chest to sit on
    chest_support();
    mirror([1, 0, 0]) chest_support();

    // pillars up the sides
    pillar(); // LF
    mirror([1, 0, 0]) pillar(); // RF
    mirror([0, 1, 0]) pillar(); // LR
    mirror([1, 0, 0]) mirror([0, 1, 0]) pillar(); // RR
    
    // feet
    foot();
    mirror([0, 1, 0]) foot();
    
    // transverse top supports
    top_transverse();
    mirror([0, 1, 0]) top_transverse();
    
    // top supports under mill to be drilled for supports
    top_support(); // left
    mirror([1, 0, 0]) top_support(); // right
    
    translate([0, 0, (chest_h+toe_h)/3 + channel_w/2]) back_support();
    translate([0, 0, (chest_h+toe_h)/3*2 + channel_w/2]) back_support();
    
    side_support();
    mirror([1, 0, 0]) side_support();
    
    top();
}
assembly();
 
I think I've convinced myself that triangular gussets in the back will be better than channel across the back:

mill_stand_low_rear_view.png

C-like:
// Copyright © 2018 Michael K Johnson
// CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/
// dimensions are inches not mm because that's what all the components use
// Example dimensions are for holding this chest (without casters installed):
// https://www.homedepot.com/p/Husky-27-in-5-Drawer-Roller-Cabinet-Tool-Chest-in-Textured-Black-UAT-H-26051/303764045
// Expected to be used with leveling pads, such as
// https://www.precisionmatthews.com/shop/levelingpads/
// http://www.grizzly.com/products/Grizzly-Machine-Mount-3-1600-lb-Capacity
// prints calculated lengths to cut; weld all joints, drill as necessary
// no parts shown for filling tubes with sand to damp vibration but feel free
chest_w = 27;
chest_d = 18.3;
chest_h = 31.3;
web=0.125;
w_space = 0.125;
head_space = 0.25;
angle_w = 1;
angle_h = angle_w;
channel_w = 4;
channel_h = 1;
tube_w = 2;
tube_h = 1;
//foot_w = tube_w;
//foot_l = foot_w;
//foot_t = 3/16;
foot_w = 48;
foot_h = 2;
toe_h = max(2, foot_h);
foot_mount_d = 3/8;
pillar_l = chest_h+toe_h+head_space-tube_h;
echo(pillar_length=pillar_l);
transverse_l = chest_w + tube_h*2 + w_space*2;
echo(transverse_length=transverse_l);
support_l = chest_d-2*tube_w;
echo(support_length=support_l);
mill_x_offset = 17.125/2; // measure your mill: half X distance between center of mounting holes
back_support_l = transverse_l;
side_support_l = support_l;
gusset_len = 8;
gusset_thickness = 1/8;
top_t = 1/8;
top_w = chest_w+2*tube_h+2*w_space;
echo(top_width = top_w);
module chest() {
    translate([-chest_w/2, -chest_d/2, 0])
        cube([chest_w, chest_d, chest_h]);
}
module angle(width=angle_w, height=angle_h, web=web, length=4) {
    translate([-width/2, -length/2, -height/2]) {
        cube([width, length, web]);
        cube([web, length, height]);
    }
}
module channel(width=channel_w, height=channel_h, web=web, length=4) {
    angle(width, height, web, length);
    translate([width-(web+width/2), -length/2, -height/2])
        cube([web, length, height]);
}
module tube(width=tube_w, height=tube_h, web=web, length=4) {
    channel(width, height, web, length);
    translate([-width/2, -length/2, height-(web+height/2)])
        cube([width, length, web]);
}
module chest_support() {
    toe_top = toe_h-web-angle_h/2;
    foot_top = foot_h-angle_h/2;
    h = max(toe_top, foot_top);
    translate([angle_w/2-(chest_w/2+w_space), 0, h])
        rotate([0, 180, 180]) {
            if (h == foot_top) {
                angle(length = chest_d - 2*tube_w);
            } else {
                angle(length = chest_d);
            }
        }
}
module pillar() {
    translate([-(tube_h/2+chest_w/2+w_space), -chest_d/2+tube_w/2, pillar_l/2+tube_h])
        rotate([90, 0, 90])
        tube(length=pillar_l);
    // foot — this would work for wide enough chest if no cantilever needed
    /*
    translate([-(tube_h+chest_w/2+w_space), -chest_d/2+tube_w/2, foot_t/2]) difference() {
        cube([foot_w, foot_l, foot_t], center=true);
        translate([-foot_w/4, 0, 0]) cylinder(d=foot_mount_d, h=foot_t*2, center=true, $fn=12);
    }
    */
}
module foot_hole() {
    translate([0, 0, -foot_h/2])
        cylinder(d=foot_mount_d, h=foot_h, center=true, $fn=12);
    translate([0, 0, foot_h/2])
        cylinder(d=tube_w/4*3, h=foot_h, center=true, $fn=24);
}
module foot() {
    translate([0, chest_d/2-tube_w/2, foot_h/2])
        rotate([0, 0, 90])
        difference() {
            tube(height=foot_h, length=foot_w);
            union() {
                translate([0, foot_w/2-tube_w/2]) foot_hole();
                translate([0, -(foot_w/2-tube_w/2)]) foot_hole();
            }
        }
}
module gusset(length=1, height=gusset_thickness) {
    linear_extrude(height=height)
        polygon([[0, 0], [0, length], [length, 0]]);
}
module top_transverse() {
    translate([0, chest_d/2-tube_w/2, chest_h+toe_h+head_space+tube_h/2])
        rotate([0, 0, 90])
        tube(length=transverse_l);
}
module top_support() {
    translate([-mill_x_offset, 0, chest_h+toe_h+head_space+tube_h/2])
        tube(length=support_l);
}
module back_support() {
    translate([0, chest_d/2+channel_h/2, 0])
        rotate([0, 90, 90])
        channel(length=back_support_l);
}
module side_support() {
    translate([-(chest_w/2+w_space+channel_h/2), 0, (chest_h+toe_h)/4*3 + channel_w/2])
        rotate([0, 90, 0])
        channel(length=side_support_l);
}
module top() {
    translate([-top_w/2, -chest_d/2, pillar_l+tube_h+tube_h])
        cube([top_w, chest_d, top_t]);
}
module back_top_gusset() {
    translate([-top_w/2, chest_d/2, pillar_l+tube_h+tube_h])
        rotate([-90, 0, 0])
        gusset(length=gusset_len);
}
module back_bottom_gusset() {
    translate([-top_w/2, chest_d/2+gusset_thickness, 0])
        rotate([90, 0, 0])
        gusset(length=gusset_len);
}
module assembly() {
    //color("Gray", alpha=0.2) translate([0, 0, toe_h]) chest();

    // angle supports for chest to sit on
    chest_support();
    mirror([1, 0, 0]) chest_support();

    // pillars up the sides
    pillar(); // LF
    mirror([1, 0, 0]) pillar(); // RF
    mirror([0, 1, 0]) pillar(); // LR
    mirror([1, 0, 0]) mirror([0, 1, 0]) pillar(); // RR
    
    // feet
    foot();
    mirror([0, 1, 0]) foot();
    
    // transverse top supports
    top_transverse();
    mirror([0, 1, 0]) top_transverse();
    
    // top supports under mill to be drilled for supports
    top_support(); // left
    mirror([1, 0, 0]) top_support(); // right
    
    //translate([0, 0, (chest_h+toe_h)/3 + channel_w/2]) back_support();
    //translate([0, 0, (chest_h+toe_h)/3*2 + channel_w/2]) back_support();
    
    side_support();
    mirror([1, 0, 0]) side_support();
    
    back_top_gusset();
    mirror([1, 0, 0]) back_top_gusset();
    back_bottom_gusset();
    mirror([1, 0, 0]) back_bottom_gusset();
    
    top();
}
assembly();
 
This is how I learn how new I am to this: I find that 1"x4" channel is not trivially available, and I don't have a brake let alone a brake capable of bending heavy-gauge sheet from which I could make appropriate channel. So gussets on the side, too! But then I see that running tube instead of angle for the bottom support will give bottom gussets something to attach to. I think I'll quit posting models until I've actually built something, unless someone asks to see changes.
 
...And then I finally get it through my thick skull that the mill is deeper than the chest. :p

This makes me think that I should first decide exactly how high I want the mill table, design for that, and then maybe end up with a mill table that could accommodate different tool chest sizes should I ever decide I want a different/better tool chest under it.
 
Just a suggestion. You want the mill support to be as solid as possible, a solid block won't allow for a tool box, as you've already concluded. Your design removes all the solid except at the corners, I'd suggest keeping the sides and back solid, or at least one sheet to keep from 'swaying' as work progresses. Your gussets are a beginning, but since there will be no needed access on the back, cover it with a fairly stout sheet of steel, perhaps with embossed (or welded on) cross bars.
 
I actually want access to the sides for sure, and possibly the back, so I think I'll start with gussets and if it's a problem I can tack a sheet on later. I do respect your experience, and recognize that this is definitely a possibility. I added more of them thanks to your prompting, and also made the ones in back larger (because X transit will be heaviest dynamic load, and the new wider design needs more help with the static load as well.

I also changed my mind (again) about which chest I want under the mill, because I realized that the wider chest is also a little shorter; the shortest I can reasonably build the stand I previously posted here will not lower the mill at all, whereas I would like to lower it about 2 inches. If I use the chest that the mill is currently perched on instead, I can lower the mill 1.75" which will be about the right height.

wide_mill_stand_low_front.png
 
I finally realized that the tool chests have side and back already, and that instead of building a stand into which I can put a tool chest, I should reinforce a tool chest to make it into a stand. It's been fun, but probably time to re-think this design and assume that I can fasten steel to the tool chest to make a single thing that is a stand, so first to determine what real problems I have with the current setup.
 
Back
Top