Week5: 3D Printing and Scanning

  • |
  • 27 February 2022
Post image

I am not new to 3d printing. When the rep-rap project started in around 2006 I didn’t have the means or the connections to get involved, but when the MakerBot Cupcake was sold as one of the first cheap commercially available 3d printers, I knew I had to get one. It’s still mostly in one piece, collecting dust on the top-shelf of my workplace. In time I have built some printers and I have now settled on an Ender 3, which has served me well over the last couple of years. I mostly use it for personal projects and to print little toys and replacement parts.

Think

Because I have some experience in 3d printing I wanted to challenge myself and see if I could use my 3d printer without using slicer software. I had read somewhere that there was an application that could help users to generate their own GCode scripts for 3d printing, but unfortunately I forgot the name of the application, so I started a GCode generator myself. I chose the Processing programming environment for this so I could very quickly get visual feedback from my work.

After I was well under way I found the GCode authoring tool. It is called FullControl and it it an Excel script with a lot of VBA macros. It allows the user to copy-paste rows in an Excel table which the macros then turn into GCode to copy paste into a file or printer. The paper was very inspiring.

  • Make the printer print something with custom written GCode
  • Make objects with generated gcode that would be hard to make with conventional slicing methods:
    • Quick mathematical shapes
    • Diagonal infill
    • Micro-structures
    • out-of-perimeter printing
    • layer-shifting

I was happy enough with the result I presented it voluntarily in the open review.

Make

3d Printer and development environment setup

I made use of a classic Ender-3 with BLTouch bed levelling sensor. For all the prints for this experiment I used PLA, mostly sourced from “Internet of Printing” and eSun. Most of the print jobs ran from an SD card but for some quick testing I had the printer hooked up to a PC running Ubuntu and Pronterface.

The GCode generator was written in Processing/Java for the following reasons:

  • I am familiar with the platform.
  • It allows for quick 3d visualizations
  • I can grow from hacky-hardcoded to semi-structured object oriented quite easily.

The final result

processing_screenshot_code processing_screenshot
The eventual processing code main sketch The output window.

GCode generation

I started out by creating a small, flat (1 layer) rectangle in openSCAD and slicing the STL with CURA. This gave me a simple example of what GCode form my printer should look like. The resulting file had thee distinct sections:

Section Purpose
Before printing
  • homing the printer
  • setting the temperature for bed and nozzle
  • creating a first line, to prime the nozzle
Printing
  • Clear distinctions between skin, wall and infill
  • GCode commands consist of G0 (travel) and G1(print) type commands
  • Possible parameters for these are:
    • F (feedrate)
    • X, Y and Z (the target position)
    • E (the amount to extrude)
After printing
  • put printhead in safe position
  • Turn off fans and heaters

In my code, I separated the gcode before printing and the gcode after printing in separate files. These strings are always attached before and after my generated gcode so I don’t have to write code for it to support those commands.

For the first generated gcode I wanted to try and do a “non-planar” path by making the z-axis move while moving the printhead in the XY-plane.

This snippet of code shows the trajectory of the printhead:

    x = bedSize/2 + r * cos(fi);
    y = bedSize/2 + r * sin(fi);
    z = abs(sin(fi*10))*5;
    r=fi*10;
The absolute sine wave makes the z-axis perform a kind of hopping motion.

The first results were interesting. It seemed that the printhead deposited a string of plastic, and periodically “glue” a little blob to the surface. This was exactly what I needed for printing micro-structures so I kept at it. But in subsequent prints there was no plastic fed through the extruder. Re-examining my example gcode again and watching a GCode tutorial video by CNC Kitchen I realized I made a mistake.

Absolute vs Relative extrusion
A command in the “front-matter.gcode”, M82, set the extrusion to “absolute” which meant it interprets the E parameters in the G1 commands as the absolute amount of material extruded since the beginning of the print. I interpreted the value as a relative value, i.e.: how much material was being added for this particular command. I chose to change M82 to M83 in the front-matter.gcode file to set it to relative extrusion.

Having fixed the extrusion and inspired by the hopping traces, I started to test more non-planar printing.

hoppy_lines_try print_mess
I printed several, what I now call, “hoppy” lines But eventually they all failed.

I started doing the advanced stuff way too early. First I decided to keep it simple.

Grids

A grid is simple. It’s just a bunch of straight lines. Creating gcode for a grid is less simple because you have to take the physical movement of the printhead into account. You want to print parallel, consecutive lines in opposite direction otherwise there is a big chance the already deposited line will be ripped off.

grid_on_bed grid_in_hand
Eventually I was able to print a grid. With some imagination this might become a way to print special types of fabric.

Now that the grid worked, it was a small step to print a stack if shrinking grids.

int nLayers = 50;
for (int iZ=0; iZ<nLayers; iZ++) {    
    float shrinkage = iZ*0.1;
    float z = 0.1 + iZ*0.2;
    // Grid pyramid    
    generateGrid(100+shrinkage, 100+shrinkage, // Left, bottom
        140-shrinkage, 140-shrinkage,          // Top, right
        10, 10,                                // nColumns, nRows
        z,                                     // zHeight
        0.05);                                 // extrusion
}
processing_stacked_grid_pyramid print_stacked_pyramid
This code resulted in half a pyramid The printed result

Although at first it looked like nothing you couldn’t make with a mesh and a slicer, this object had a cool property: the openings in the grid were diagonal, which meant you can only see light pass through if you hold the object at a certain angle.

Tilting the object makes for a cool effect

I was happy with the result and ready for more.

Spirals

Circular objects are quite stable so I made a function to create a spiral. Eventually I extended the function to make a spiral with a sine-wave superimposed, so I could make wobbly surfaces.

void generateWobblyHelix(float centerX, float centerY,  // center of the spiral
    float radius0, float radius1, // start and end radius
    int nSteps, // Amount of steps to approximate
    float frequency, float amplitude, float phase, // parameters for the superimposed sine wave
    float constrainMin, float constrainMax, // truncate values for the sine wave
    boolean absolute,  // sine wave absolute or not
    float zHeight, float flowRate) { 

  // Use polar coordinates for the helix
  float x, y, fi, radius, sineOffset;
  for (int iStep=0; iStep<nSteps; iStep++) {
    fi = (float)iStep/nSteps * 2 * PI;
    radius = map(iStep, 0, nSteps, radius0, radius1);
    // Superimpose the sinewave
    sineOffset = sin(frequency * fi + phase);
    sineOffset = absolute ? abs(sineOffset) : sineOffset;
    sineOffset *= amplitude;
    sineOffset = constrain(sineOffset, constrainMin, constrainMax);
    radius += sineOffset;

    x = centerX + radius * cos(fi);
    y = centerY + radius * sin(fi);

    // add the gcode command to the list
    gcode.addCommand(new PrintCommand(new PVector(x, y, zHeight), flowRate));
  }
}
In this interactive demo you can see the effects of the helix function Drag the sliders to see the effects.

Generating a stack of these helices with the following code:

for (int iZ=0; iZ<nLayers; iZ++) {    

    float shrinkage = iZ*0.1;
    float z = 0.1 + iZ*0.2;
    
    // WobblyCylinder
    generateWobblyHelix(120, 120,  //center
        20, 20, // radii
        100, // steps
        1+shrinkage/7, 2, -shrinkage/7, // sine parameters
        -100, 100, // unconstrained
        false, // not absolute
        z, 0.05); // z and flowrate
}
processing_wobbly_cylinder print_wobbly_cylinder
The code generated a kind of wobbly cylinder… …which looked like this when printed.

Then I proceeded to experiment with out-of-perimeter printing. This can be done by periodically have deviations from regular the perimeter of the cylinder.

// consider this code to be in a for-loop with increasing iZ and z
// XMasTree
float radius = map(iZ, 0, nLayers, 20, 1);
if (iZ % 10 == 0) { // change the superimposed sine wave parameters for every 10 layers
 generateWobblyHelix(120, 120, radius + 7, radius + 7, 100, 5, -7, shrinkage, -5, 5, true, z, 0.05);
} else {
 generateWobblyHelix(120, 120, radius, radius, 100, 0, 0, 0, 0, 0, true, z, 0.05);
}
processing_xmastree print_xmas_tree_blob
This resulted in an object resembling an xMas tree Having a roll of green PLA loaded was a total coincidence

Although it was quite my intention to have it look a little messy, I observed way too much material being extruded at the top of the cone shape. To investigate further I decided to print a simple cone shape and see what went wrong.

print_cone_blob
The cone got really messy

It seemed too much material was extruded approaching the top of the shape. I didn’t experience this problem with earlier tests though.

Extrusion depends on travel length
Again this came down to misinterpretation of the E parameter. Whatever comes after E is the amount of extruded material for the execution of that particular command. Because the radius of the cone is way smaller at the top, but the same number of commands are used to describe its circle, the printer tried to extrude the same amount of plastic for a small circle at the top as for a big circle at the bottom. I would eventually have had this same problem with my stacked grid pyramid. The problem could be solved to make the E parameter depend on the length of the path of the printhead. The parameter in my code would then act as a multiplier
print_cone_nice
Solving this gave me a very nicely extruded cone.
print_xmas_tree_nice
The xMas tree looked way better as well
Some other helix-based designs:
Code Processing shape 3d print
code_sinusoidal_cylinder code_sinusoidal_cylinder print_yellow_wobbly
code_gappy_cylinder code_sinusoidal_cylinder print_gappy
code_flower processing_flower print_gappy

Non-planar printing

After being successful in printing strange objects it was time to revisit non-planar printing. I’m really interested in prints where the printhead makes hopping motions in the z direction. If a printed line could have little, reproducible arcs of plastic, it would allow for prints that use less material and be more flexible.

As with the first attempt I used an absolute sine-wave to make the print-head “hop” in the z-direction. A gcode generator function called generateHoppyCircle was used to create gcode for a circle with a preset amount of “hops” in the z-direction. The function first creates a flat circle for support, followed by a circle for which the z-axis hops up and down.

The first attempt failed.

The z-axis hops but rips off the first layer. Putting down at least 5 layers underneath makes the structure stable enough.

After solving the bed-adhesion problem, the gcode actually created a sparse cylinder.

processing_hoppy_cylinder print_hoppy_cylinder
A “hoppy-cylinder” in processing The printed result
The resulting cylinder was way more flexible that it would have been without the hopping.

The printed arcs weren’t quite as high as they were designed to be in processing.

microscope_hops
Looking at the structure under a microscope, I could determine that the actual shape of the “hop” is reproducible, but no arc at all.

To actually make little arcs I will have to play with the hopHeight, the flowRate, the temperature and the hopDistance. Furthermore, I would probably need to know how to retract material to stop if from oozing when the flowRate is set lower momentarily. This would take too much time, so I continued with my suboptimal hops.

processing_hoppy_wall print_hoppy_wall
Using the function generateHoppyLine I stacked multiple arcs on top of each other to make a sparse vertical wall. At the turns, the result is a bit messy because the hops didn’t connect to the supporting surface very well.

The result was very flexible, although a bit prone to breaking.

microscope_hoppy_wall
Under the microscope it was very clear that the hops were asymmetrical. This might be corrected by using something other than abs(sin) to generate the hops. Perhaps using splines.

Of course there is no sense in creating 2D structures in the Z direction. Such a structure could be easily replicated in the XY-plane. The ultimate application would be to make a very sparse but strong and maybe flexible kind of infill material. To test this out I made a “hoppyGrid” and stacked multiple of those structures on top of each other to generate a sparse 3d structure.

processing_hoppy_grid print_hoppy_grid
The “hoppy-grid in Processing The printed result

The result was not as flexible as I hoped, but surprisingly light for its appearance.

microscope_hopfest
Under the microscope the result was pretty messy.

Because the printer is not designed for non-planar printing the hot-end got in the way of already printed structures, remelting them and making a bit of a mess. Still, the properties of the resulting material were quite different from what more traditional methods would yield. Lighter and slightly more flexible.

Conclusion

There is a lot more to research for such structures. Some ideas for future expansion and enhancement:

  • The software is fairly inflexible now. Every type of shape has to be programmed from scratch. For the helical designs I would like to implement a system to create custom surface and radius functions.
  • For the sparse structures, experimentation must be done to optimize the “hopping” shapes.
    • Different speeds, temperatures, flow-rates
    • Different arc-shapes: linear, Bezier curves, to compensate for asymmetry
    • Extruder retract movements to prevent material from oozing during a “hop”
  • Processing is entirely capable of sending the gcode to the printer itself. This might make the workflow a bit easier and making it possible to quickly experiment.

3D Scanning

Scanning 3d objects and people at the Waag is done with the 3d Systems “Sense” scanner.

It uses two RGB cameras and some image processing to compute the depth of objects in it’s field of view. "
3dscanner

This method has some limitations:

  • it’s hard to scan dark objects, as it needs contrasting colors on the surface to make out the surface texture
  • it can not detect transparent objects
  • it can not detect shiny objects as the reflected light is not proportional to the orientation of the camera with respect to the object

Ideal objects to be scanned have lots of geometrical and textural features. I have tried to scan a:

  • computer mouse: too dark and too shiny
  • a bosch drill: too dark
  • a hand vacuum cleaner: too shiny and some dark spots

Eventually I settled on scanning some bricks.

These bricks were lying around in the lab to be used for flattening sheet material in the lasercutter. The material properties of the bricks made them ideal for scanning. Not too dark, not too shiny
lasercutter_bricks scanning_the_bricks

The bricks came out pretty well. The scanner software allowed me to save the result as an OBJ file and therefore could include the textures and texture mapping information.

The result in the “Sense” software The polygon count is very high.
bricks_scanned blender_bricks_high_poly
Using the decimate modifier… …the polygon count was greatly reduced.
blender_decimate blender_brick_low_poly
The bricks, textured and rendered in Cycles on a sunny day A shiny piece of tape and the surface of contact with the glass the bricks were on, were not correctly scanned
blender_cycles_bricks blender_bricks_shiny_piece

I must admit there is not much sense in scanning bricks, but I liked doing it to experience the 3d scanning workflow. There was little sense in having a dense model of two stacked bricks, but still I decided to have some fun with them.

Using the bricks as a blender soft body object and a plane as a collider

If the video doesn’t work: watch on youtube.

Check

I set out to experiment with generating my own gcode and I was quite happy it turned out the way it did. Slicers are very complex pieces of software but with limited time and effort, it is entirely possible to create 3d-printed objects from scratch. With just a few parameters. I was actually amazed it was as accessible as it was. I performed a lot of experiments. To review my goals from the start:

  • Make the printer print something with customly written GCode

It worked. I programmed Processing/Java functions and classes to do just that.

  • Make objects with generated gcode that would be hard to make with conventional slicing methods:

  • Quick mathematical shapes

Using sinusoidal functions it’s possible to generate a wide range of interesting structures. Printing such a structure is fairly quick as it doesn’t need support structure. Mostly7 it is just one wall thick. The largest object took 1 hour to print.

  • Diagonal infill

Infill patterns are mostly generated without taking the shape of the object into account. By stacking the grids and shrinking the infill pattern as a function of the height, I managed to make the infill pattern a bit more interesting.

  • Micro structures

By making the z-axis “hop” I could make sparse structures.

  • out-of-perimeter printing

Using different parameters for single layers of a sinusoidal shape, I managed to make something that resembles an xmas-tree. Strange surface properties can be had when you don’t care about nice layer adhesion.

  • layer-shifting

The yellow flower was an experiment in shifting layers. If this is done too much, layer adhesion is so low, the material has a hard time keeping it’s shape in the xy-plane.

Group assignment

We found a test model on Thingiverse that could be used to determine how wel a printer performs. We tested the print on two different printers, the ultimaker 2+ and the Prusa i3 MK3. Both models were sliced with the default settings for both printers at medium quality. The model for the Prusa was sliced using Prusa Slicer and the model for the ultimaker was sliced using Cura.

Stringing

Prusa i3 MK3 Ultimaker 2+

The Prusa i3 MK3 displays more stringing in the region where multiple pillars have to be printed.

Overhangs

Prusa i3 MK3 Ultimaker 2+

It seems the default setting for the Prusa have larger layer heights. The details in the numbers on the models were hardly readable. The Ultimaker didn’t have much trouble with that.

On the bottom side of the overhangs, the Prusa displayed more sagging.

Expansion or contraction

Prusa i3 MK3 Ultimaker 2+

The size of 3d prints always have a slight deviation from the actual model. Two vertical holes in the model can be used to determine the expansion or contraction of the prints. The small hole should be 3mm and the large hole should be 5mm. Both holes were printed larger than the model would dictate. The expansion of the Prusa was slightly larger than that of the Ultimaker. Horizontal expansion is a setting in Ultimaker Cura to compensate for this effect.

Files

You May Also Like

Final Project

Final Project

Final project Initial conception (Febuary 2022) Learning to play any musical instrument takes a very long time and a lot of effort. To enjoy music …

Week18: Project development
  • 10 Jun, 2022

Week18: Project development

What I did. I created the enclosure and designed and milled all the PCBs. TODO Hardware Priority 1 20 x Button LED caps 8x Octave1 8x Octave2 4x chord …