Showing posts with label holes. Show all posts
Showing posts with label holes. Show all posts

Saturday, 12 December 2020

Sinkholes

Continuing my series of adventures in 3D printing holes the correct size, I turned my attention to countersink holes. 

I don't normally use countersunk screws in 3D printed parts because they create lateral stress, whereas other forms of screw exert a purely compressive force, which can be spread by a washer. However, I am currently designing a part that needs flush screws in its base that is only 4mm thick, so there is no room for a counter-bore to bury a flat head.

First I had to improve my countersunk screw model because I previously just used a 45° cone with a sharp edge. In practice the edge has a nominal thickness of 1/10th of the screw diameter. That means if you countersink the hole with a normal conical countersink drill bit it has to be oversized to sink the head flush, and then leaves a gap around the circumference.



So the ideal shape for a 3D printed hole is an improvement because it can have a straight section at the top.

The complications are that, like horizontal holes, we have to take into account the rounded staircase produced by the filament layers and also, like vertical holes, we need to use polyholes to get the correct diameter when printed.

The cone makes contact with the rounded filament edge above, rather than the centre of the extrusion, where the slicer samples the model, as shown below:

In order for the extrusion to not overlap the cone it has to be offset outwards by a distance $x$, which is simply $(\sqrt2-1) r$ where $r$ is half the layer height. Applying that to the line equation of the cone and limiting the result to be in the range between the screw clearance radius and the screw head radius gives the correct placement for the rings of filament that just touch the cone without overlapping.


To make each of the rings print the correct size I make a stack of polyholes to subtract from the model.


These are double sided so they can be subtracted from the either the top or the bottom of a hole. As the slope is 45° they should be printable either way up.

This is what it looks like subtracted from a part:



I have added an OpenSCAD module called screw_polysink() to NopSCADlib to make printed countersinks. I haven't tried printing it yet but it should certainly work on a top surface. It might get a bit scrappy printed as an overhang without support.

Monday, 20 July 2020

Horiholes 2

As whosawhatsis pointed out in a comment on my last post, the edges of the filament staircase are actually semicircular and that makes a big difference as to where they should be to meet the circle tangentially. Not sure why I missed that as I have done several posts about extruded filament shape, I must be losing it!

This is what my previous shape actually produces.


The circle only touches at four points. The correct shape is obtained by calculating where the semicircles meet the circle.

The centres of the ends of the filament lie on a circle with a radius of the hole plus half the filament height. The end of the filament is then offset inwards horizontally by half the filament height. I.e. the slicer samples the layer at the central tip but the filament touches the circle on a tangent.


To make the shape geometrically I make a teardrop with a radius of the hole plus half the filament width, split it in half and shift the two halves together by half the filament width. As can be seen here that goes through all the filament tips, i.e., where the slicer samples.


This is what it looks like relative to the target hole.


Interestingly it is the same as my previous attempt at the top, bottom and sides, i.e. the only four points it touched before.

I made a test with the new formula.


The plug gauges all fit, but more snugly than before, so this is definitely a better solution for supporting a bearing in a pocket. At every layer it should have a tangential support from the rounded edge of an extrusion.


Here is a close up of the 6mm hole that is aligned on a layer boundary and the 1mm hole above it.



I have updated NopSCADlib on Github to use this method.

Tuesday, 11 March 2014

Buried nuts and hanging holes

I needed to make some M4 nuts that could be finger tightened but I didn't have room for a standard wing-nut, so I decided to embed nuts in a printed plastic knob. I knocked up a simple design in OpenScad :-


M4 nuts are nominally 3.2mm thick. I made the base and lid 2.4mm and sliced it with 0.4mm layers. That meant the top of the nut would be flush with a layer boundary at 5.6mm and I confirmed that the first covering layer was at 6.0mm in Skeinlayer. So I needed to pause the build before the start of the layer at Z=6.0 and insert the nuts.

I run my USB machines using Raspberry PIs and OctoPrint (so that all my machines are connected via Ethernet) and noticed a post by the author, Gina Häußge, that said OctoPrint interprets an M0 in the gcode as a pause command. The host stops sending gcode until you press the pause button to un-pause it again. I believe other hosts use @PAUSE to do the same thing.

So M0 is exactly what I needed. The only problem is that up until then I mistakenly thought M0 meant end of program and placed it at the end of the PLA profiles that I distribute. Fortunately the version of Marlin I use ignores it but if you want to use the latest version, or OctoPrint, then you need to remove it from end.gcode, otherwise either the host or the firmware will pause at the end of the print and wait for a button press. Harmless but a bit confusing.

So, armed with a new appreciation of what M0 is, I searched my gcode for the first instance of Z6.0 which looks like this:

F12000.0
G1 X-9.082 Y3.907 Z6.0 F12000.0
G1 X-5.457 Y-3.937 Z6.0 F12000.0
G1 X-7.05 Y-3.803 Z6.0 F12000.0
G1 X-11.486 Y-4.991 Z6.0 F12000.0
G1 X-13.721 Y-10.229 Z6.0 F12000.0
G1 F1800.0
G1 E1.0
G1 F12000.0
M101
G1 X-12.65 Y-10.848 Z6.0 F1837.1615 E0.036

What we have is a sequence of non-extruding moves followed by an un-retract and the first extrusion. The moves are the result of the comb module and not really relevant if we are restarting after a pause, so I removed all but the last move and inserted my pause code:

M104 S100
G1 Z6.0
G1 X-100 Y-100 F9000
M0
G1 X10.0 Y98.0 F9000
G1 Z0.05
M109 S250
G92 E0
G1 E3 F50
G1 E-1 F1200
G1 X40.0 F4000
G1 Z6.0 F9000

G1 X-13.721 Y-10.229 Z6.0 F12000.0
G1 F1800.0
G1 E1.0
G1 F12000.0
M101
G1 X-12.65 Y-10.848 Z6.0 F1837.1615 E0.036

I set the extruder temperature to 100°C to minimise ooze and stop it discolouring while waiting for me to insert the nuts. The bed is left on so the half printed objects don't detach. It then moves up to Z = 6.0 to clear the objects before going to X = -100, Y =-100. That moves the bed to the front and the extruder to the far right on a Mendel90, giving the best access to the partially printed objects. M0 then pauses the program.

I threaded the nuts onto a screw to insert them easily without touching the hot plastic. 



After pressing the pause button to make OctoPrint resume, the print head moves to the front of the bed to do another ooze free warmup. The only difference from the start of the print is it parks the nozzle 10mm further left to avoid the blob it has already made and it moves to Z = 6.0 before resuming the print.

This all worked very well except for a slight snag. ABS does not stick to steel, so when it extruded the circular holes on top of the nuts it made a bit of a mess.



Normally I would use a one layer support diaphragm when printing suspended holes and drill it out afterwards. In this case it can't be drilled because the nut is in the way, so I developed a method of printing holes in mid air. 

The last layer of the nut trap looks like this: 



You can't print a smaller hole on the next layer as the outline would be printed in mid air. The infill is also only attached at one end. After a few layers it does sort itself out but leaves a mess. However, what you can do is print two bridges over the large hole with a gap between them equal to the diameter of the small hole:



This is done by cutting out a one layer rectangle clipped to the hexagon. It is rotated to match the layer's infill direction because Skeinforge fails to detect it as a bridge, probably because the bridged area is tiny.

On the next layer we can bridge in the opposite direction and close down the hole to a square:



Two sides are supported by the edges of the rectangle below and the other two span the gap. 

On the next layer we can approximate the hole with an octagon. Four edges are coincident with the square and the other four span small gaps:



It is now a good enough approximation to a circle for such a small hole so it continues up to the top as an octagon. The resulting print is much neater:



The cavity for the nut is made by subtracting a shape like this: 


Here is the OpenScad code. It needs various functions from the Mendel90 source tree.

//
// Smaller alternative to a wingnut
//
include <conf config.scad>

module hanging_hole(or, ir, ofn = 0) {
    union() {
        intersection() {
            if(ofn)
                cylinder(r = or, h = 3 * layer_height, center = true, $fn = ofn);
            else
                poly_cylinder(r = or, h = 3 * layer_height, center = true);
            rotate([0, 0, 90])
                cube([2 * or + 1, 2 * ir, 2 * layer_height], center = true);
        }
        rotate([0, 0, 90])
            cube([ir * 2, ir * 2, 4 * layer_height + 4 * eta], center = true);

        rotate([0, 0, 22.5])
            translate([0, 0, 2 * layer_height])
                cylinder(r = corrected_radius(ir, 8), h = 100, $fn = 8);
    }
}

base_thickness = 2.4;
lid_thickness = 2.4;

function nut_knob_height(nut) = base_thickness + nut_thickness(nut) + lid_thickness;

module nut_knob_stl(screw = M4_hex_screw, d = 14) {
    nut = screw_nut(screw);
    h = nut_knob_height(nut);
    flutes = 3;

    stl("nut_knob");
    rotate([0, 0, -45])
        difference() {
            cylinder(r = d / 2, h = h);                                                 // basic shape

            for(i = [0 : flutes - 1])                                                   // flutes for finger grip
                rotate([0, 0, i * 360 / flutes + 30])
                    translate([d * cos(90 / flutes), 0, base_thickness])
                        cylinder(r = d / 2, h = 100);

            union() {                                                                   // nut cavity
                difference() {
                    translate([0, 0, base_thickness + nut_thickness(nut)])
                        nut_trap(screw_clearance_radius(screw), nut_radius(nut), nut_thickness(nut));

                    translate([0, 0, base_thickness + nut_thickness(nut)])              // remove top of nut trap
                        cylinder(r = 20, h = 110);
                }

                translate([0, 0, base_thickness + nut_thickness(nut)])
                    hanging_hole(nut_radius(nut), screw_clearance_radius(screw), 6);    // replace with hanging hole
            }

        }
}

So this seems to be a general solution to printing holes in mid air without any support material. The only downside is that it is a bit weaker than using a membrane and drilling it out. In this case no strength above the nut was required. In general you can just make it two layers thicker.

Saturday, 5 February 2011

Polyholes

When Reprap machines print holes they tend to come out undersized, even if the linear dimensions of an object are spot on. There are several effects that all make holes smaller than they should be: -

Faceting error
When CAD systems convert cylinders to triangles they produce a polygonal prism, so holes represented in an STL file are polygons with their vertices on the circumference of the original circle. That means the sides of the polygon are inside the circle, shrinking it by cos(π / n).
You need 10 vertices to reduce the error to 5% and 22 for 1%. So this error quickly becomes small as n increases but that creates another error:-

Segment pausing
When a circle is broken into a lot of little segments the start up time for a segment becomes significant. Reprap in the past has suffered from this really badly and I am unsure what the current status is. Slow serial comms and complex floating point firmware add pauses where extra filament can ooze from the nozzle.

I have never suffered from pausing because I use a 100Mbit Ethernet connection, which has a very low latency, and the data is transmitted in binary and in the units my firmware works in. This means that no further processing is required other than calculating which of the three axes has to go the furthest. However, I use trapezoidal acceleration on each segment, so for very short segments the average speed will be a little lower.

Arc shrinkage
When a flat strip of filament is bent into an arc there is too much plastic on the inside of the curve and too little on the outside. That makes both the inside and outside edges a smaller diameter than they should be. Adrian calculated a formula for it here: http://reprap.org/wiki/ArcCompensation. The formula comes out with a figure that is too small though. I think there is a secondary effect:

Corner cutting
When filament is dragged round a corner it likes to take a short-cut. This depends on how elastic the filament is and how much it is being stretched. I think when the nozzle moves in a circle the filament is continually trying to cut the corner and ends up forming a smaller diameter circle. I think this is the dominant effect on my machines.

Obviously, if you lie to Skeinforge about how wide your filament is that will make holes even smaller, but that is just a calibration problem.

Ideally all these effects should be compensated for in the slicing software but what has happened instead recently is that people are using parametric values in OpenScad to tweak the holes to come out right on their machines. That is the wrong approach because when the holes comes out smaller than they should be, without the slicing software compensating for it, then the infill doesn't meet it as tightly as it should do.

When I started printing Prusa Mendel parts I found the values in the configuration file far too big. I have also noticed this when downloading some designs from Thingiverse. That implies that my holes shrink less than a lot of other peoples, which is odd because all the effects above don't depend on the machine, apart from segment  pausing.

Some of the holes in Josef's parts are octagonal. That made me realise that polygons with low vertex counts don't shrink. The inside of the hole is defined by straight lines and they get extruded in the correct place. What does happen though is that the corners of the polygon are rounded. As long as the polygon has a small number of vertices, the corners are far enough from the circle that they can be rounded without impinging on it. The ideal number of vertices is when the corner cutting just meets the circle.

I decided to investigate this using OpenScad. I made a script that generates holes from 1 to 10mm with vertex counts from 3 to 8, 10, 16 and 32. The diameter of the holes is increased to make the polygon edges tangential to the circular hole. I.e. removing the faceting error by dividing by cos(π / n).

difference() {
    cube(size = [95,125,3]);
    for(i = [1:9]) {
        assign(v=[3,4,5,6,7,8,10,16,32][i - 1]) {
     assign(shrink =  cos (180 / v)) {
                echo(v,shrink);
                for(d = [1:9]) {
                    translate([d * d + 5 - ((v == 3) ? 3 : 0), 13 * i, 0]) 
                         cylinder(h= 20, r = (d/2)/shrink, $fn= v);
                }
            }
        }
    }
}


I printed the resulting shape on HydraRaptor and used drill shanks to gauge the hole sizes. Not terribly accurate as the shanks tend to be a little smaller than the tip. I inserted the drills in the highest vertex count hole that it would fit in.


A pattern emerged that the seemed to indicate the maximum number of vertices you can have before the hole shrinks is twice the hole size in mm. The only drill I couldn't fit was the 1mm drill because you can't have a polygon with only two sides. The "1mm" triangular hole did at least leave a hole though, whereas higher polygon counts fill in completely.

To test this simple rule I made a new shape with holes from 1mm to 10.5mm in 0.5mm steps with the number of vertices set to twice the diameter and the diameter increased by cos(π / n).

module polyhole(h, d) {
    n = max(round(2 * d),3);
    rotate([0,0,180])
        cylinder(h = h, r = (d / 2) / cos (180 / n), $fn = n);
}


difference() {
 cube(size = [100,27,3]);
    union() {
     for(i = [1:10]) {
            translate([(i * i + i)/2 + 3 * i , 8,-1])
                polyhole(h = 5, d = i);
                
            assign(d = i + 0.5)
                translate([(d * d + d)/2 + 3 * d, 19,-1])
                    polyhole(h = 5, d = d);
     }
    }
}


I found that all my drills bigger than 1mm fit. The large ones are a snug fit and the smaller ones a little loose, probably because with only a few tangential points touching there is little friction.


These two tests where done on HydraRaptor extruding 0.375mm filament from a 0.4mm nozzle. I printed this the test again on my Mendel with 0.6mm filament through a 0.5mm nozzle and the drills still fit, so it seems universal, at least amongst my machines. It would be interesting to see if others get the same result, so I have put the files on Thingiverse.

My goal is to work out how to print circular holes the correct size, but this seems like a good hack for OpenScad designs to allow holes to come out the right size, regardless of the printer or whether it compensates hole diameters. For example, one would expect circular holes to come out right on a professional printer, so if you have oversized circular holes in your model they will come out too big. However, if you use these low vertex count polygonal holes they should still come out the right size as one would also expect a professional printer to print polygons at least as accurately.