Autoleveling the bed[]
Bed autoleveling greatly simplifies 3D printer operation, especially in the cases (like mine) when the printer seats deep inside a (fireproof) enclosure, making manual bed leveling hard or impossible.
As I am using a thin (0.8mm) sheet of PEI as my bed surface, I use a convenient and accurate inductive proximity sensor (8mm type) for autoleveling. But most of the following applies to any type of bed surface and proximity sensor.
Shortly after installing my inductive proximity sensor I discovered that I cannot produce consistent autoleveling results - some parts of the bed would have inaccurate first layer thickness. There was some randomness, and some persistent patterns to it. I spent some time and I eventually figured out and fixed all the issues with my printer which made autoleveling inaccurate. They are broadly in two main categories:
Random effect issues[]
Bed attachment[]
Springs used to attached the bed to the frame are convenient but also can be a source of some random autoleveling issues. Getting rid of the springs, drilling out the threads in the bed, and using three nuts per bolt resulted in a much more reliable and rigid connection between the bed and the frame. I suspect this also improved the quality of my prints, especially for large and massive prints (when the springs would result in the printed part wobbling slightly after each sharp move, producing the infamous "ghosting" effect near sharp corners).
Loose bearings for the hot end[]
I discovered that the bearings which allow the hot end assembly to move along the "X" axis were a bit wobbly. This is especially bad in my case as my inductive sensor is pretty far (65mm) from the nozzle in the "X" direction, so even small wobbles along that axis would result in noticeable (and mostly random) change in the sensor-to-nozzle distance, affecting the autoleveling accuracy.
My fix was to loosen up the screws used to attach the hot end to the bearings, and insert a thin (~1.5mm I think) plastic wedge from the top into the space between the two top bearings, then fasten up the screws. I had to experiment with a few different thickness wedges to get the effect I wanted - to minimize the wobbliness to effectively zero, but at the same time not to make the hot end sliding along the X rods noticeably harder.
Proximity sensor adapter[]
Initially I printed an adapter from thingiverse to attach my inductive sensor to the hot end assembly, but only to discover that this approach is not reliable - attachment is not very rigid, plus I didn't trust the plastic adapter not to change its shape when inside a hot enclosure. I ended up making my own adapters from 2.5mm thick steel (first for my original, 4mm type inductive sensor, then for the much bulkier 8mm type sensor):
These adapters are attached to the bottom of the hot end assembly, for which purpose I drilled two holes there and cut there M4 thread:
They are very reliable, would survive any accident (like, being driven into the bed by mistake), have zero wobbliness, and wouldn't change shape in a hot enclosure. Totally worth the 1-2 hours spent on making them!
Bed PID[]
Bed PID heating (off by default in Marlin) can be very important to improve quality of your prints, specifically when addressing a regular Z-wobble pattern resulting from the heating bed expansion/contraction cycles when the default heating method (bang-bang) is used. Unfortunately, bed PID messes up badly with autoleveling, as it produces strong electromagnetic interference which screws up the low current signal wiring for proximity sensors. I have used the cheapest induction sensor for 2 years, and it never rammed into the bed during autoleveling (so it was rock solid in terms of reliability) when using the "bang-bang" heating method, but once I enabled bed PID heating in the Marlin firmware, within minutes of trying I had my first bad "hot nozzle running into the bed" incident, leaving a permanent mark in the PEI bed surface. Ouch!
After some experimenting, I came up with a reliable way the bed PID heating and autoleveling can co-exist. One has to do the following two tricks:
1) Disable bed heating when proximity sensor is used to detect the bed surface (that happens a fraction of the autoleveling time, when the printer head is slowly descending towards the bed at each autoleveling point). For this one has to enable (uncomment) the following line in Marlin's Configuration.h file:
#define PROBING_HEATERS_OFF // Turn heaters off when probing
2) The above trick fixes the electromagnetic interference factor, but introduces another serious issue: now bed cools down significantly during autoleveling procedure. In my case (3x3 points autoleveling) bed temperature fluctuates wildly between points, with the general trend to colder temps; at the end, the temperature was 8-9C degrees below the target one (110C), which I am sure messes up autoleveling accuracy big time.
The solution is to slow down the travel speed during the G29 command execution to the point that bed heats up to your target temperature before reaching each autoleveling point. Also, you should insert the standard "M190 S{material_bed_temperature}" commands in your Cura's startup code before and after each command which is using the proximity sensor (G28 and G29). Also, insert the equivalent command for the hot end temperature ("M109 S{material_print_temperature}"), after G29. This is how the relevant part of my Cura startup code looks like:
M190 S{material_bed_temperature} ; wait until the bed is hot G12 P1 S1 T4 ; nozzle clean G28 Z ; recalibrate Z, with bed and hotend being hot M190 S{material_bed_temperature} G29 S350 ; autoleveling with slow travel speed, to let the bed heat up between moves M190 S{material_bed_temperature} M109 S{material_print_temperature} G92 E0 ;zero the extruded length M117 Printing...
The critical part here is to use the optional switch "S" with the G29 command. In my case, speed 350 mm/min was sufficient for the bed to heat up back to 110C before each autoleveling point. Of course, the speed will strongly depend on how fast your bed heats up (in my case it's pretty fast as I am using a separate 16V PSU for my 12V-type bed), and how high is your target temperature. You'll have to experiment with different speeds to find the one which works for you setup.
With the above two tricks, my autoleveling slows down (takes ~2 minutes for 3x3 points - not terrible), but the bed temperature fluctuates no more than 0.6C degrees during bed surface sensing, so autoleveling accuracy is very good. And I enjoy the print quality improvements thanks to the bed PID heating - a win-win!
Systematic issues[]
Non-straight rods[]
Once I got rid of all the effects affecting the autoleveling accuracy in a random fashion, I was left with a systematic (reproducible) issue when for some "X" coordinates the first layer would always be too thin, and on the other end of the bed - too thick (poor adhesion). I believe this comes from the fact that my X and Y rods are not perfectly straight. The following diagram demonstrates (in a very exaggerated fashion) how this would affect the proximity distance measuring:
As you can see here, the sensor-to-nozzle vertical distance will depend on the position of the hot end on the rod, because of the rod being non-straight.
Replacing the rods wasn't an option, so I ended up correcting for this persistent problem by making slight changes to the Marlin code.
First, for each of the nine points I always use during autoleveling (I always do 3x3 bi-linear autoleveling), I experimentally measured the optimal nozzle-to-sensor distance (also called "z-offset"), when both the bed and hot end were hot (just in case; perhaps not important). First I ran "G28" command (but no "G29"!). Then say for the point X=40,Y=110 I did:
M48 P10 X40 Y110
This gave me the Z-coordinate for the sensor triggering (let's call it Z_sensor), averaged over 10 tries. I then moved the nozzle to the same position, and tuned the Z value to my liking (so the nozzle scratches slightly the piece of paper I put on the bed). Let's say, the perfect Z coordinate was Z_nozzle=0.15:
G1 X40 Y110 Z0.15
I repeated this for all 9 points (in my case they have coordinates X=40,110,180; Y=40,110,180), and created a 3x3 table where each cell corresponds to one of the points (the top left one corresponding to smallest X and Y coordinates), and the values there being the Z_sensor-Z_nozzle differences for each point. The table looked like this:
X\Y | 40 | 110 | 180 |
---|---|---|---|
40 | -0.19 | -0.21 | -0.22 |
110 | -0.03 | -0.07 | -0.07 |
180 | 0.03 | 0.01 | 0.05 |
Then from each value in the above table I subtracted the value from the cell corresponding to the point were the G28 command measures the distance to the bed. Usually this is done at the center, but I modified Marlin to do it in the left top corner for certain reasons (not related to this topic), so from all the table values I subtracted the value "-0.19" corresponding to the X=40,Y=40 point (my G28 position). The table now looks like this:
X\Y | 40 | 110 | 180 |
---|---|---|---|
40 | 0.00 | -0.02 | -0.03 |
110 | 0.16 | 0.12 | 0.12 |
180 | 0.22 | 0.20 | 0.24 |
The new table shows how the Z-offset changes relative to the G28 measurement point. As you can see, the change is pretty large - the full magnitude is 0.27mm (going from the top right corner to the bottom right corner), which would definitely ruin my first layer if left uncorrected.
Now that we have the correction table, we can modify the Marlin code to apply this correction during autoleveling.
First - the changes to the Marlin_main.cpp code. Right after the existing line
float measured_z = run_z_probe();
insert the following lines:
// Printing the raw z value to the serial interface: SERIAL_PROTOCOLPGM("Raw bed Z: "); SERIAL_PROTOCOL_F(measured_z, 3); SERIAL_EOL; //Correcting for variable z_offset as a function of x,y (caused by some rod bending) // (At this point, x and y are X/Y coordinates of the probe.) // Using the 3x3 correction matrix to compensate for curved rods: int ix = (int)((x-5.0)/70.0); // Matrix index ix will have values 0, 1, 2 for the left, middle, and right probe position in 3x3 autoleveling if (ix < 0) ix = 0; if (ix > 2) ix = 2; int iy = (int)((y-5.0)/70.0); // Matrix index iy will have values 0, 1, 2 for the front, middle, and rear probe position in 3x3 autoleveling if (iy < 0) iy = 0; if (iy > 2) iy = 2; // The correction matrix CORR (z_probe_raw - z_nozzle for the nine 3x3 autoleveling points) is defined in Configuration.h measured_z = measured_z - CORR[ix][iy];
You'll have to modify the lines starting with "int ix = " and "int iy = " for your specific setup. You want these lines to return the values 0,1,2 when the probe is at the first, second, and third point during the 3x3 autoleveling, both for the X and Y dimensions.
The second addition is to the file Configuration.h (not important where):
// z probe corrections matrix for the 3x3 points used in autoleveling (z_probe_raw-z_nozzle), set to 0 at the first point // (which is used for z probing in G28); used in Marlin_Main.cpp const float CORR[3][3] = { { 0.00, -0.02, -0.03}, { 0.16, 0.12, 0.12}, { 0.22, 0.20, 0.24} };
You'll have to use your own values (computed as described above) in this table.
Voila - now you'd only have to upload the new firmware to your Anet A8 printer. Next time you do the usual G28, G29 sequence at the beginning of your print, the appropriate corrections will be applied automatically, and the first layer should be very consistent across the entire bed.
You still can change the global Z_offset if needed, in the usual fashion. This is the way I do it:
Using thin ruled paper (0.08mm), with both hot end and bed hot:
G28 G29 G1 X40 Y40 Z10 F4000 G1 Z0.08
(Put your own coordinates - corresponding to the point where the G28 measures the distance to the bed - for the G1 command.) Let's say the current value of z-offset (M851) is -0.44. If after the above commands the space is too tight, provide a larger (smaller in absolute terms) value to M851. E.g. if you want to raise the nozzle by 0.1mm, do
M851 Z-0.34 M500
Run M500 to save the value.