Questions about Z Gantry squaring and Z_Tilt

I have a Ender3 V2 with dual Z motors/screws. I’m in the process of upgrading the electronics to support independent drives to the dual Z motors (to auto square/level the gantry X axis rail), and upgrading from the stock marlin to Klipper. I have already added a probe for auto bed leveling (which works fine under marlin).

It looks like the klipper Z_Tilt command will come close to what I want to do (more on that and a question later) but first a question about Z Tilt in general (something I don’t understand about the whole concept)…

The bed on my Ender3 is manually adjustable with spring loaded screws on each corner, so it can be quite level or quite tilted. It makes a lot of sense to use the probe on the moving head to move to each end of the X axis travel, probe the bed and use those positions, and the bed pivit points to calculate how much to independently move one or both Z motors to level the X rail (or synchronise the Z motors to make the X rail square to Z.

BUT, here is the part I don’t understand, so hopefully someone can explain what I’m missing here. This whole plan is based on the assumption? that the build plate is level and orthogonal to the Z axis? I would have thought that is a bad assumption. An auto bed level routine can fix this, but if the Z motors are not corrected for tilt, the leveled bed will be level to the X axis but not orthogonal to Z. Hence we need Z-Tilt to fix this, but isn’t there a chicken and egg problem here? Which one comes first.

That’s my puzzle, and now to my technical question about Z_Tilt and if it can do what I imagined was my solution to Z motor sync/tilt. I have a similar solution for gantry squaring on my home build CNC Y axis that has dual ballscrews.

I imagined 2 sensors (microswitch or prox sensors) at the top of each Z axis rail. The squaring routine (Z-Tilt?) just needs to move both motors UP to the sensors and stop each one interdependently based on the sensors, and perhaps apply an offset to allow for different triggering ranges and slightly different mounting positions of the sensors on each side.

Then you could have a perfectly square X rail (to the Z Axis), and proceed to probe the bed as usual to check / compensate for any tiny irregularities.

So the only thing I can’t see in the Z_Tilt to make this scheme work is the probing direction needs to be the opposite to normal bed leveling, and there needs to be support for independent probes for each motor.

Am I correct that these things really are not there? Is it possible to do what I did for my CNC, and develop fully custom code to drive the motors to do the probing/squaring?

Or is there a better way?

One workaround I thought of (to make the Z_Tilt work with a potentially out of square bed) is to make a alloy ‘bridge’ U shaped piece that sits on the Y axis rails and provides a perfectly square (to Z) flat rail that can be probed at each end using the bed leveling probe the way the Z_Tilt routine intends.

The only problem I see with that is it needs to be manually placed/removed.

A similar scheme with fixed probing pads on each end of the X travel limits could work too, and they could stay in place as long as they don’t get in the way of normal printing movements.

Anyhow, that’s a very long (1st) post here. Hopefully some folks made it to the end and have some comments/thoughts to share.



Hi Michael, and welcome.

The way I see it, if the bed needs to be levelled more than once per week at most, then that’s probably a problem that needs addressing fairly urgently (not suggesting your bed is like that. just setting up my scenario, bear with me).

About once every month or two, I will spend some time using the vernier calipers to match the height of each corner of the bed to the base of the frame, in an attempt to get the bed square to the z axis. That’s not super-accurate, but I find it close enough. and then every week or so, I do a quick scan of the bed using screws_tilt_calculate, and all that is needed is just small tweaks to get each screw within “5 minutes”. I don’t think that the bed needs much more attention than that, and if it does, then stiffer bed springs or silicone spacers can help keep it more stable.

When I switched over to dual (independent) z-axis though, I found that when the motors turn off after a print, that the x-gantry would settle unevenly, and not consistently. There are a few variables involved in how it settles, such as the friction on each leadscrew (they will rarely be equal), and the position of the toolhead when the motors turn off. So to ensure that the x-gantry always trams with the bed, I do a z_tilt_adjust before every print, immediately after homing (I have it built into my start_print macro).

After the z_tilt_adjust, I run a 5x5 mesh scan (bed_mesh_cabibrate) just of the print area (also built into the start_print macro), and I’m good to go. So under the scenario I described above, there is no chicken and egg problem, everything is done in a well defined sequence.

I’m sure that others will have their own routines, that’s just what works for me. Hope this helps :slight_smile:

Thanks for the reply. I was doing something similar in that on power up I’d move the Z up fairly high and measure the distance to the top, so manually square up the X rail to account for the kind of random and uneven motor movements when unpowered. If the distances were out, I’d just manually move a motor until it was all good.

What I’m wanting to do is automate that manual leveling process with a couple of sensors :slight_smile:
The same thing works great on my CNC, and checks/adjusts the Y dual motors at each homing. Usually the adjustment is less than the repeatability of the sensors, as unlike Z the motors don’t have any reason to move when de-powered.



So another way I can see forward (reading the Klipper docs) is to use the two Z axis sensors at the top of the Z travel as the home endstops, and with the stepper_z1 having it’s own end stop, it homes to that independently.

To account for different detection distances (using prox sensors) and slightly different mounting points, it looks like it’s possible in custom G28 code to apply a small correction to one axis if needed.

The only issue with this is it’s handy for the machine to ‘home’ with the nozzle near the bed (using the leveling sensor), not 200mm above it. Maybe there is another way around that by switching where the Z home is dynamically. e.g. home/square to the top once at power on, then home Z to the bed after that.

The klipper documentation suggests that having the endstop at the top of the z axis is more robust (not unlike the Benelli M4 :). I have my endstop at the top of my Z axis, about 350mm away from the bed, and it works great.

You might also want to take a look at endstop_phase_calibrate

That seems like what I’ll be doing, two sensors at the top of Z, with each motor homing to their own. If I can’t get them located exactly right to get the X rail square, then I think I can apply a small offset with a FORCE_MOVE on one motor to account for any slight sensor position or detection range variations.

I’d like to understand more about the implications (for Z values) with the endstops at the max Z position. Is the bed always assumed to be at Z=0 then? With small adjustments to level the bed, I can’t see how the Z min can be always 0 AND have the Z max fixed by the endstop sensor(s).

The z_tilt module is intended for use on printers with multiple Z lead screws. It adjusts the relative heights of the z screws based upon probe measurements.

If you want a tool to level bed screws with a probe, see screws_tilt_adjust. If you want a tool to level bed screws and don’t want to use (or don’t have) a probe, see bed_screws.

If you want to use multiple endstops to adjust the relative z heights of multiple z lead screws, then you can define the endstop in each stepper_z section.

Finally, in many cases one can use bed_mesh to account for beds that are not fully flat - it also works okay for beds that have a slight tilt.


Thanks Kevin, yes this is what I want to do.