05. 3D printing and scanning#
1. Summary#
Hero Shot#
Learning Outcomes#
What did I learn this week ?
- 3D print :
- Clean a nozzle
- Watch if the first layers work fine before going away
- Printed pump :
- A lot of iterations are required to make something work
Lecture Content#
To do : document Natural Science Museum visit
2. Assignment#
This fifth week's assignments are :
- Group :
- Test the design rules for your 3D printer
- Individual :
- Design, document, and 3D print an object that could not be made subtractively (small, few cm3, limited by printer time)
- 3D scan an object (and optionally print it)
Below you will find the achievement of the latter.
2.1. Group Assignment#
In Fab Lab ULB, there are 16 Prusa i3 MK3S printer (and one resin 3D printer).
This week I will only use the Prusa ones therefore I will only test the design rules of the latter. To test the design rules, Patrick showed us a quite convenient object to print that looks like a small board containing different design rule tests.
The 3D file can be found on Thingiverse and its description says that one should print it without support and with 100% infill. There is no indication about the nozzle diameter nor the layers thickness therefore I chose the following parameters :
Here is the result :
- Bridge test passes for all the length (25 mm max on the design)
- Diameter test shows 0.1 mm mistake for the 8mm circle and the 0.2 mm test for the 6mm circle
- Stringing test fails
- Scale test shows no measurable mistake (0.01 mm resolution)
- Support test passes
- Angle test shows small artifacts starting at 60° and large ones starting at 70°
Below you can see a closer view on the angle test :
I then compared my result with Michel's one who used a Prusa Mini. Check his webpage to see his result but using a smaller nozzle and a finer layer thickness seem to solve the problems.
2.2. Individual Assignment#
2.2.1. Peristaltic Pump#
This week is the right time to start testing 3D printed peristaltic pumps for my final project. There are a lot of different models available but I found one on Nicolas webpage that is single piece and hence highlighting the strengths of a 3D printer. Indeed such a model could not be made substractively since the gears are nested in the circular part :
The original file can be found on the creator personal website and its code is coppied below.
Pump OpenScad Code
// Planetary peristaltic pump (customizable)
// by Drmn4ea (drmn4ea at google's mail)
//
// Released under the Creative Commons - Attribution - Share Alike license (http://creativecommons.org/licenses/by-sa/3.0/)
//
// Adapted from Emmett Lalish's Planetary Gear Bearing at http://www.thingiverse.com/thing:53451
// -------- Printer-related settings ------------
// Clearance to generate between non-connected parts. If the gears print 'stuck together' or are difficult to separate, try increasing this value. If there is excessive play between them, try lowering it. (default: 0.15mm)
tol=0.10;
// Allowed overhang for overhang removal; between 0 and 0.999 (0 = none, 0.5 = 45 degrees, 1 = infinite)
allowed_overhang = 0.75;
// -------- Details of the tubing used in the pump, in mm ------------
// Outer diameter of your tubing in mm
tubing_od = 6;
// Wall thickness of your tubing
tubing_wall_thickness = 1;
// Amount the tubing should be compressed by the rollers, as a proportion of total thickness (0 = no squish, 1.0 = complete squish)
tubing_squish_ratio = 0.4;
// -------- Part geometry settings ------------
// Approximate outer diameter of ring in mm
D=51.7;
// Thickness i.e. height in mm
T=17.5;
// Number of planet gears
number_of_planets=3;
// Number of teeth on planet gears
number_of_teeth_on_planets=7;
// Number of teeth on sun gear (approximate)
approximate_number_of_teeth_on_sun=9;
// pressure angle
P=45;//[30:60]
// number of teeth to twist across
nTwist=1;
// width of hexagonal hole
w=6.7;
DR=0.5*1;// maximum depth ratio of teeth
// ----------------End of customizable values -----------------
m=round(number_of_planets);
np=round(number_of_teeth_on_planets);
ns1=approximate_number_of_teeth_on_sun;
k1=round(2/m*(ns1+np));
k= k1*m%2!=0 ? k1+1 : k1;
ns=k*m/2-np;
echo(ns);
nr=ns+2*np;
pitchD=0.9*D/(1+min(PI/(2*nr*tan(P)),PI*DR/nr));
pitch=pitchD*PI/nr;
echo(pitch);
helix_angle=atan(2*nTwist*pitch/T);
echo(helix_angle);
phi=$t*360/m;
// compute some parameters related to the tubing
tubing_squished_width = tubing_od * (PI/2);
tubing_depth_clearance = 2*(tubing_wall_thickness*(1-tubing_squish_ratio));
// temporary variables for computing the outer radius of the outer ring gear teeth
// used to make the clearance for the peristaltic squeezer feature on the planets
outerring_pitch_radius = nr*pitch/(2*PI);
outerring_depth=pitch/(2*tan(P));
outerring_outer_radius = tol<0 ? outerring_pitch_radius+outerring_depth/2-tol : outerring_pitch_radius+outerring_depth/2;
// temporary variables for computing the outer radius of the planet gear teeth
// used to make the peristaltic squeezer feature on the planets
planet_pitch_radius = np*pitch/(2*PI);
planet_depth=pitch/(2*tan(P));
planet_outer_radius = tol<0 ? planet_pitch_radius+planet_depth/2-tol : planet_pitch_radius+planet_depth/2;
// temporary variables for computing the inside & outside radius of the sun gear teeth
// used to make clearance for planet squeezers
sun_pitch_radius = ns*pitch/(2*PI);
sun_base_radius = sun_pitch_radius*cos(P);
echo(sun_base_radius);
sun_depth=pitch/(2*tan(P));
sun_outer_radius = tol<0 ? sun_pitch_radius+sun_depth/2-tol : sun_pitch_radius+sun_depth/2;
sun_root_radius1 = sun_pitch_radius-sun_depth/2-tol/2;
sun_root_radius = (tol<0 && sun_root_radius1<sun_base_radius) ? sun_base_radius : sun_root_radius1;
sun_min_radius = max (sun_base_radius,sun_root_radius);
// debug raw gear shape for generating overhang removal
//translate([0,0,5])
//{
// //halftooth (pitch_angle=5,base_radius=1, min_radius=0.1, outer_radius=5, half_thick_angle=3);
// gear2D(number_of_teeth=number_of_teeth_on_planets, circular_pitch=pitch, pressure_angle=P, depth_ratio=DR, clearance=tol);
//}
translate([0,0,T/2])
{
// outer ring
difference()
{
// HACK: Add tubing depth clearance value to the total OD, otherwise the outer part may be too thin. FIXME: This is a quick n dirty way and makes the actual OD not match what the user entered...
cylinder(r=D/2 + tubing_depth_clearance,h=T,center=true,$fn=100);
exitholes(outerring_outer_radius-tubing_od/4,tubing_od, len=100);
union()
{
// HACK: On my printer, it seems to need extra clearance for the outside gear, trying double...
herringbone(nr,pitch,P,DR,-2*tol,helix_angle,T+0.2);
cylinder(r=outerring_outer_radius+tubing_depth_clearance,h=tubing_squished_width,center=true,$fn=100);
// overhang removal for top teeth of outer ring: create a frustum starting at the top surface of the "roller" cylinder
// (which will actually be cut out of the outer ring) and shrinking inward at the allowed overhang angle until it reaches the
// gear root diameter.
translate([0, 0, tubing_squished_width/2])
{
cylinder(r1=outerring_outer_radius+tubing_depth_clearance, r2=outerring_depth,h=abs(outerring_outer_radius+tubing_depth_clearance - outerring_depth)/tan(allowed_overhang*90),center=false,$fn=100);
}
}
}
// sun gear
rotate([0,0,(np+1)*180/ns+phi*(ns+np)*2/ns])
difference()
{
// the gear with band cut out of the middle
difference()
{
mirror([0,1,0])
herringbone(ns,pitch,P,DR,tol,helix_angle,T);
// center hole
cylinder(r=w/sqrt(3),h=T+1,center=true,$fn=6);
// gap for planet squeezer surface
difference()
{
cylinder(r=sun_outer_radius,h=tubing_squished_width,center=true,$fn=100);
cylinder(r=sun_min_radius-tol,h=tubing_squished_width,center=true,$fn=100);
}
}
// on the top part, cut an angle on the underside of the gear teeth to keep the overhang to a feasible amount
translate([0, 0, tubing_squished_width/2])
{
difference()
{
// in height, numeric constant sets the amount of allowed overhang after trim.
//h=abs((sun_min_radius-tol)-sun_outer_radius)*(1-allowed_overhang)
// h=tan(allowed_overhang*90)
cylinder(r=sun_outer_radius,h=abs((sun_min_radius-tol)-sun_outer_radius)/tan(allowed_overhang*90),center=false,$fn=100);
cylinder(r1=sun_min_radius-tol, r2=sun_outer_radius,h=abs((sun_min_radius-tol)-sun_outer_radius)/tan(allowed_overhang*90),center=false,$fn=100);
}
}
}
// planet gears
for(i=[1:m])
{
rotate([0,0,i*360/m+phi])
{
translate([pitchD/2*(ns+np)/nr,0,0])
{
rotate([0,0,i*ns/m*360/np-phi*(ns+np)/np-phi])
{
union()
{
herringbone(np,pitch,P,DR,tol,helix_angle,T);
// Add a roller cylinder in the center of the planet gears.
// But also constrain overhangs to a sane level, so this is kind of a mess...
intersection()
{
// the cylinder itself
cylinder(r=planet_outer_radius,h=tubing_squished_width-tol,center=true,$fn=100);
// Now deal with overhang on the underside of the planets' roller cylinders.
// create the outline of a gear where the herringbone meets the cylinder;
// make its angle match the twist at this point.
// Then difference this flat gear from a slightly larger cylinder, extrude it with an
// outward-growing angle, and cut the result from the cylinder.
planet_overhangfix(np, pitch, P, DR, tol, helix_angle, T, tubing_squished_width, allowed_overhang);
}
}
}
}
}
}
}
module planet_overhangfix(
number_of_teeth=15,
circular_pitch=10,
pressure_angle=28,
depth_ratio=1,
clearance=0,
helix_angle=0,
gear_thickness=5,
tubing_squished_width,
allowed_overhang)
{
height_from_bottom = (gear_thickness/2) - (tubing_squished_width/2);
pitch_radius = number_of_teeth*circular_pitch/(2*PI);
twist=tan(helix_angle)*height_from_bottom/pitch_radius*180/PI; // the total rotation angle at that point - should match that of the gear itself
translate([0,0, -tubing_squished_width/2]) // relative to center height, where this is used
{
// FIXME: This calculation is most likely wrong...
//rotate([0, 0, helix_angle * ((tubing_squished_width-(2*tol))/2)])
rotate([0, 0, twist])
{
// want to extrude to a height proportional to the distance between the root of the gear teeth
// and the outer edge of the cylinder
linear_extrude(height=tubing_squished_width-clearance,twist=0,slices=6, scale=1+(1/(1-allowed_overhang)))
{
gear2D(number_of_teeth=number_of_teeth, circular_pitch=circular_pitch, pressure_angle=pressure_angle, depth_ratio=depth_ratio, clearance=clearance);
}
}
}
}
module exitholes(distance_apart, hole_diameter)
{
translate([distance_apart, len/2, 0])
{
rotate([90, 0, 0])
{
cylinder(r=hole_diameter/2,h=len,center=true,$fn=100);
}
}
mirror([1,0,0])
{
translate([distance_apart, len/2, 0])
{
rotate([90, 0, 0])
{
cylinder(r=hole_diameter/2,h=len,center=true,$fn=100);
}
}
}
}
module rack(
number_of_teeth=15,
circular_pitch=10,
pressure_angle=28,
helix_angle=0,
clearance=0,
gear_thickness=5,
flat=false){
addendum=circular_pitch/(4*tan(pressure_angle));
flat_extrude(h=gear_thickness,flat=flat)translate([0,-clearance*cos(pressure_angle)/2])
union(){
translate([0,-0.5-addendum])square([number_of_teeth*circular_pitch,1],center=true);
for(i=[1:number_of_teeth])
translate([circular_pitch*(i-number_of_teeth/2-0.5),0])
polygon(points=[[-circular_pitch/2,-addendum],[circular_pitch/2,-addendum],[0,addendum]]);
}
}
//module monogram(h=1)
//linear_extrude(height=h,center=true)
//translate(-[3,2.5])union(){
// difference(){
// square([4,5]);
// translate([1,1])square([2,3]);
// }
// square([6,1]);
// translate([0,2])square([2,1]);
//}
module herringbone(
number_of_teeth=15,
circular_pitch=10,
pressure_angle=28,
depth_ratio=1,
clearance=0,
helix_angle=0,
gear_thickness=5){
union(){
//translate([0,0,10])
gear(number_of_teeth,
circular_pitch,
pressure_angle,
depth_ratio,
clearance,
helix_angle,
gear_thickness/2);
mirror([0,0,1])
gear(number_of_teeth,
circular_pitch,
pressure_angle,
depth_ratio,
clearance,
helix_angle,
gear_thickness/2);
}}
module gear (
number_of_teeth=15,
circular_pitch=10,
pressure_angle=28,
depth_ratio=1,
clearance=0,
helix_angle=0,
gear_thickness=5,
flat=false){
pitch_radius = number_of_teeth*circular_pitch/(2*PI);
twist=tan(helix_angle)*gear_thickness/pitch_radius*180/PI;
flat_extrude(h=gear_thickness,twist=twist,flat=flat)
gear2D (
number_of_teeth,
circular_pitch,
pressure_angle,
depth_ratio,
clearance);
}
module flat_extrude(h,twist,flat){
if(flat==false)
linear_extrude(height=h,twist=twist,slices=twist/6, scale=1)child(0);
else
child(0);
}
module gear2D (
number_of_teeth,
circular_pitch,
pressure_angle,
depth_ratio,
clearance){
pitch_radius = number_of_teeth*circular_pitch/(2*PI);
base_radius = pitch_radius*cos(pressure_angle);
depth=circular_pitch/(2*tan(pressure_angle));
outer_radius = clearance<0 ? pitch_radius+depth/2-clearance : pitch_radius+depth/2;
root_radius1 = pitch_radius-depth/2-clearance/2;
root_radius = (clearance<0 && root_radius1<base_radius) ? base_radius : root_radius1;
backlash_angle = clearance/(pitch_radius*cos(pressure_angle)) * 180 / PI;
half_thick_angle = 90/number_of_teeth - backlash_angle/2;
pitch_point = involute (base_radius, involute_intersect_angle (base_radius, pitch_radius));
pitch_angle = atan2 (pitch_point[1], pitch_point[0]);
min_radius = max (base_radius,root_radius);
intersection(){
rotate(90/number_of_teeth)
circle($fn=number_of_teeth*3,r=pitch_radius+depth_ratio*circular_pitch/2-clearance/2);
union(){
rotate(90/number_of_teeth)
circle($fn=number_of_teeth*2,r=max(root_radius,pitch_radius-depth_ratio*circular_pitch/2-clearance/2));
for (i = [1:number_of_teeth])rotate(i*360/number_of_teeth){
halftooth (
pitch_angle,
base_radius,
min_radius,
outer_radius,
half_thick_angle);
mirror([0,1])halftooth (
pitch_angle,
base_radius,
min_radius,
outer_radius,
half_thick_angle);
}
}
}}
module halftooth (
pitch_angle,
base_radius,
min_radius,
outer_radius,
half_thick_angle){
index=[0,1,2,3,4,5];
start_angle = max(involute_intersect_angle (base_radius, min_radius)-5,0);
stop_angle = involute_intersect_angle (base_radius, outer_radius);
angle=index*(stop_angle-start_angle)/index[len(index)-1];
p=[[0,0],
involute(base_radius,angle[0]+start_angle),
involute(base_radius,angle[1]+start_angle),
involute(base_radius,angle[2]+start_angle),
involute(base_radius,angle[3]+start_angle),
involute(base_radius,angle[4]+start_angle),
involute(base_radius,angle[5]+start_angle)];
difference(){
rotate(-pitch_angle-half_thick_angle)polygon(points=p);
square(2*outer_radius);
}}
// Mathematical Functions
//===============
// Finds the angle of the involute about the base radius at the given distance (radius) from it's center.
//source: http://www.mathhelpforum.com/math-help/geometry/136011-circle-involute-solving-y-any-given-x.html
function involute_intersect_angle (base_radius, radius) = sqrt (pow (radius/base_radius, 2) - 1) * 180 / PI;
// Calculate the involute position for a given base radius and involute angle.
function involute (base_radius, involute_angle) =
[
base_radius*(cos (involute_angle) + involute_angle*PI/180*sin (involute_angle)),
base_radius*(sin (involute_angle) - involute_angle*PI/180*cos (involute_angle))
];
The customizable parameters of the model are :
| Symbol | Description | Default Value |
|---|---|---|
tol |
Clearance | 0.15 |
allowed_overhang |
- | 0.75 |
tubing_od |
Outer diameter of the tube [mm] | 5 |
tubing_wall_thickness |
Wall thickness of the tube [mm] | 1 |
tubing_squish_ratio |
Ratio of the tube squished | 0.5 |
D |
Ring outer diameter [mm] | 51.7 |
T |
Height [mm] | 15 |
number_of_planets |
- | 3 |
number_of_teeth_on_planets |
- | 7 |
approximate_number_of_teeh_on_sun |
- | 9 |
P |
Pressure angle | 45 |
nTwist |
Number of teeth to twist accross | 1 |
w |
Width of middle hole | 6.7 |
DR |
Maximum depth ratio of teeth | 0.5 |
I did a first print with the default parameters to see if the model was indeed working correctly.
The stars are rolling correctly, not too tight, not too loose. I inserted a small tube but I knew it would not work since I did not care about modifying the tube dimensions parameters. I then modified the tube diameter and thickness parameters to fit the one I have :
However the 3D printer failed. I think the plate was not clean enough and the PLA did not fix to it.
I had to clean the nozzle because it was full a melted plastic (Fabio helped me a lot, it was my first time doing it) and then I carefully cleaned the plate and restarted the print, and it worked.
One can see that the hole is indeed wider but the teeth are to thin hence the print seems really fragile. Indeed I tried to insert the tube and it directly dislocated the gears.
I then modified the global height so the teeth would be thicker. I also picked another tube and modified the corresponding parameters because the previous one seemed to thick even with thicker teeth.
The rotation required to much strength and it was really hard to put the tube inside. I, once again dislocated the gears while inserting the tube. I then modified the squishing ratio and the clearance values.
I had to use a screwdriver to rotate the central wheel since it still required a high torque. I also had to fill the tube with water to initialise the pump because the squishing ratio (that I diminished to reduce the required force) was too low and let air go back after pushing it forward.
However, with a screwdriver and filled tube, the pump worked (slowly) !
2.2.2. 3D Scanning#
For this assignment I used a CR-Scan Ferret Pro 3D Scanner by Creality. I first had to download the software on Creality website. Once installed I plugged the scanner to my laptop with a USB cable and started a New project on the software. I decided to scan my Week 3 assignment :
I started the scanning on the software and moved around the object. When I feeled I had enough points I stopped the scanning on the software and I got a preview. The scan was pretty clean at first (it just had a big hole on the part of the object that was in contact with the table) :
I then started the postprocessing with the Fusion operation and got a lot of artifacts (maybe there was some points I did not see previously) :
I cleaned the artifacts manually with the selection tool and I got :
I then generated a mesh and filled the mesh holes manually :
The scan shows pretty clean angles but there are a lot of surface artifacts that look like paint. Maybe I should retry with a more homogeneous lighting.