Z_TILT_ADJUST results in incorrect z offset

Basic Information:

Printer Model: ULTrong Origee (dont bother looking, might as well be custom)
MCU / Printerboard: Mellow Fly C8 (not using onboard host)
Host / SBC: RPi4 MainsailOS (up to date)
klippy.log.2025-04-02.log.zip (2.2 MB)
klippy (14).log (290.7 KB)

After running G28 and Z_TILT_ADJUST the Z PROBE command returns approximately 0.8 to 0.9

Before running Z_TILT_ADJUST, z=0.31 and z1=0.68 and afterwards, its 0.9

Adjusting [stepper_z] position_endstop has no effect on the outcome. For example, I added 0.3 to position_endstop so z=0.003 & z1=0.35 but after running Z_TILT_ADJUST, it’s still z=0.89

This printer has dual Z and dual Z endstops. The XY gantry is heavy and drops when Z motors are disabled, so you can often see a very obvious leveling step on a normal G28 Z command.

However, due to the placement of endstops, adjusting the endstop position is not possible. And Klipper won’t accept an additional endstop offset for z1…

Option ā€˜position_endstop’ is not valid in section ā€˜stepper_z1’

  • (I think this should be allowed, but understand it would require more dev work to accomplish)

And since it has dual z endstops, running G28 Z after a Z_TILT_ADJUST will just undo the tilt compensation. (based on recommendation seen on reddit)

The only workarounds I currently have are to use a bed mesh or manually set z_offset before I start a print. Mesh is OK, but if the offset changes (which it does when the screws get turned sometimes), then the relative bed mesh measurements are worthless and still require a manual z_offset.

So, I think I’m missing something… there should be an offset that will compensate for this without manual intervention.


My existing process:

To start with, I level my bed with the frame. This is the most important prerequisite of Z_TILT_ADJUST in my opinion. I just make sure the bed-to-frame measurement is equal on both sides before starting. It’s not a bed slinger, so I put the probe points in the middle of Y nearest the Z screws. And have to turn both front and back bed screws equally as I adjust the entire left, then right sides.

Then I run G28 and Z_TILT_ADJUST

Then I run the Bed Leveling macro from Klipper Screen (SCREWS_TILT_CALCULATE)

Then I check the bed-frame distances again. But it’s not usually much different from before.

At this point, the PROBE command returns approximately 0.8 to 0.9

I’ve tried adjusting [stepper_z] position_endstop and even [bltouch] z_offset but neither have any effect on the outcome after Z_TILT_ADJUST

PROBE_ACCURACY returns a standard deviation of 0.005 to 0.01

Am I missing something or is Z_TILT_ADJUST just making up its own absolute Z?

Should it not be seeking z=0 as it adjusts both steppers?


Printer: ULTrong Origee (don’t bother looking, its a chinese ghost printer our parent company bought… and nearly impossible to google due to the similarity to an MCU character))

600x600x800 printable area

XY Gantry with independent dual Z leadscrews in middle of Y axis

dual z endstops

bltouch only used for Z_TILT and BED_MESH

600x600 bed on 4 springs with 4 screw adjustments

Came with Mellow Fly C8 but onboard host OS was unreliable, disabled and replaced with RPi 4 running MainsailOS (will be upgrading to BTT Octopus later this year)

mcu (stm32f407xx)

Version: v0.12.0-439-g1fc6d214f


Host (aarch64, 64bit)

Version: v0.12.0-474-g4b9add2fc

OS: Debian GNU/Linux 11 (bullseye)

Distro: MainsailOS 1.3.2 (bullseye)

Also, I think this is the Z issue I’ve noticed on a dual z printer I have at home…

I have a bed slinger (former Anycubic i3 MEGA S) at home I upgraded to Dual Z and do Z_TILT. And I think it suffers from this issue as I’ve noticed the z offset is wildly off sometimes…

It only has the one Z end stop but the gantry is often skewed before running the tilt macro.

Probe accuracy is similar if not better… but I regularly have to add or remove +/- 0.7~ to the z_offset…

Subsequently changing the nozzle offset only makes the issue worse since it wants to reboot afterwards… and then has to do the z_tilt all over again.

My theory is that the amount of z_offset error is based on the amount of difference between the two z measurements. May try this when I get home

I have an Anycubic Mega Pro and I remember having a similar issue when initially setting up Klipper with the stock ends tops. For some reason, I couldn’t get the endstops to trigger correctly after I installed Klipper. I ended up adding a BL-touch on the printhead via the Anycubic i3 Mega X-Carriage [MK4] mod.

I got rid of the stock end-stops and now let the BL-touch take care of homing the Z-Axis, Z-Titlt Adjustments and Bed Meshing. Not sure if it’s what you’re looking for or if you absolutely have to make it work with the current endstops, but if you’re flexible and have already got a probe on there for Z-tilt and bedmeshing then you should be able use that same probe to home the Z-axis.

Also, make sure you re-home your Z-axis after the Z-tilt procedure is done. When I turn on my printer the first thing I usually do is heat up the nozzle and heatbed to the temperatures I’m going to print at → let the bed temperature stabilize for a few minutes → home all axes → z-tilt adjust → home z-axis again to recacluate ā€œz=0ā€ based on the z-tilt adjustmets. Then I start the print.

Not an expert answer but hope that helps.

1 Like

I might not have fully understood @calley479’s description, but this does not seem like a valid approach. When you home against the Z-min endstop, this endstop is supposed to define your Z=0. With all the steps you take afterward, you are invalidating this homing, and even worse, the defined distance between your nozzle tip and the bed.

The Z-offset for the probing is only used or needed for homing with the probe. It has no meaning for SCREWS_TILT_CALCULATE or Z_TILT_ADJUST, as far as I am aware.

1 Like

I think you’re right that the typical approach is to use probe leveling if available.

Our big printer came with 2 hardware endstops… but we wanted a bl-touch since the offsets are not easily adjustable… plus the print area is so big we wanted to do a bed mesh. Then we just left it using the physical endstops because that’s the way it came.

However, on my home printer that uses bltouch exclusively, its ended up having weirdly changing probe offset numbers and its finally occurred to me that it may be the z_tilt function that causes this.

So, I came here to post this as a potential bug. The way z_tilt adjusts the dual z seems to come up to a random arbitrary z height that is not properly offset from the nozzle height.

I haven’t tried to test my theory yet… but I postulate that a higher discrepancy at startup leads to a higher likelihood of an arbitrary final z.

I’m not sure how this would be fixed… so I’m hoping it gets the attention it needs here.

But also, even without using a probe, I think Klipper should allow a 2nd z offset for dual z setups to properly align the gantry when you cant adjust the endstops.

I saw that mentioned in a reddit post… doing a G28 Z after z-tilt. And I’m going to try that on my home printer (the Anycubic). Seems like I had issues with the endstop but was already using the bltouch.

And I’ll probably drop the hardware endstops from the config on the ULTrong and do the same.

However, I wanted to post this here as a potential bug. I’m thinking z-tilt shouldn’t move one of the axis or should change the final z based on how much z0 moved, not z1. The way it moves both seems prone to error.

Also, I like that x-carriage mod. Did a similar one for a StealthBurner style mount on my Anycubic… but I like the bltouch placement better on that one.

1 Like

The Z_TILT_ADJUST is always going to have a different value to G28 Z 0 because the probe and the z endstop switch are different points on a machine.

The fact that Z_TILT is reporting 0.9 just means that your end stop is 0.9mm vertically different from the probe.

From your point of view, it does not matter. The only things that affect the nozzle to bed adjustment are the stored z_offset in the probe and the baby step adjustment you might make during a print.

Your comment about adding a G28 Z before starting a print is a good idea.

Doing a G28 Z(or even a full G28) is, in my opinion, required after a bed tramming/leveling adjustment. These function are not perfect, thats why they do not do it in one go, so the resulting Z will be different from before.

As indicated above and also mentioned by the other contributors:

  • A G28 is mandatory after adjustments.
  • Depending on the range of the adjustments, it might even affect the defined X and Y offsets between the nozzle and the probe tip.
  • I’d strongly advise against combining SCREWS_TILT_CALCULATE with Z_TILT_ADJUST.
  • I’d also strongly advise against combining these adjustments with homing towards an endstop.

To make the situation clear, here are two simple illustrations that show what is happening in an exaggerated way:

First Example:

Initial Position: Gantry is leveled, but the Bed / Bed-Screws are misaligned:

After Z_TILT_ADJUST:

  • In one axis (probably X), your bed is now leveled, BUT only in its middle axis and NOT in the axis of the screws.
  • The gantry is severely distorted.
  • Now you come with SCREWS_TILT_CALCULATE on top, and all the following compensation will relate to the probe WITHOUT any knowledge of the nozzle.
  • Now you home again against the endstops, which know nothing about all the distortions of either the gantry or the bed.
  • The result will be a totally undefined position between the nozzle tip and the bed surface.

Second Example:

Initial Position: Gantry and Bed / Bed-Screws are both misaligned:

After Z_TILT_ADJUST:

  • In one axis (probably X), your bed is now leveled, BUT only in its middle axis and NOT in the axis of the screws.
  • The gantry is severely distorted.
  • Now you come with SCREWS_TILT_CALCULATE on top, and all the following compensation will relate to the probe WITHOUT any knowledge of the nozzle.
  • Now you home again against the endstops, which know nothing about all the distortions of either the gantry or the bed.
  • The result will be a totally undefined position between the nozzle tip and the bed surface.

In my opinion, there are only two valid solutions:

Solution 1

  • Remove the hardware endstops.
  • Use the probe as a z-endstop (carefully define the z-offset).
  • Mechanically align the gantry as well as possible so that the lead-screws have minimal friction.
  • Do not use Z_TILT_ADJUST.
  • Do a fine leveling of the bed with the probe and the SCREWS_TILT_CALCULATE.
  • Perform a G28.

Solution 2

  • Keep the hardware endstops.
  • Mechanically align the gantry as well as possible so that the lead-screws have minimal friction.
  • Do not use Z_TILT_ADJUST.
  • Instead of using the SCREWS_TILT_CALCULATE command, use the BED_SCREWS_ADJUST command along with the ā€œpaper methodā€ to ensure a constant distance to the nozzle.
  • Perform a G28.

IMO, all other approaches will cause a mess in the alignment between the nozzle and the bed.

5 Likes

Thanks for the detailed reply. Though I don’t understand why you say not to use Z_TILT_ADJUST in either scenario as it is the point of this post. Ultimately, I think you covered everything except why that function is a problem.

If the macro is flawed, then I want to make sure the bug is reported. But if my methodology is flawed, I want to remedy that as well.

Solution 1 seems to be where we’re headed. Though we plan to use Z_TILT_ADJUST but with the additional G28 Z after. Initial testing seems to be good.

This was my process before to make sure the gantry was straight.

  1. manually level the bed against the frame using caliper
  2. G28 + Z_TILT_ADJUST
  3. SCREWS_TILT_CALCULATE

But now that I’m using probe:z_virtual_endstop we can do a final G28 Z at the end to make sure any discrepancy is accounted for. And then both the bed and gantry are trammed correctly.

Am I missing any subtext or history here?

The issues I’m up against on the big ULTrong printer:

  1. dual z gantry is not easy to adjust
    a) looks like the frame has to be disassembled with the unit upside down to access the endstops and were not even sure if there’s any adjustment possible
    b) there are set-screws above the endstops but neither want to budge and we stripped one out trying to get it to turn
  2. dual z gantry falls and rests in uneven position
    a) may end up printing some shims that stop it from falling further on one side
  3. this thing is huge… and while it seems like an uneven gantry would be ok as long as it’s even with the bed, I expect to have gantry binding and other related print quality issues if we go that way

Again though, I reiterate: z_tilt seems to be fundamentally flawed since it A) adjusts both simultaneously and fails to compensate the original z measurement and B) is not documented that you need a subsequent G28 Z

So something happened yesterday that I think explains the issue.

This was on my home printer (Anycubic Mega S heavily upgraded) and I hadn’t yet integrated the subsequent G28 Z into the Z_TILT_ADJUST macro.

The printer has been mostly dialed in for a while… so usually I just hit print with faith it will start correctly… but occasionally something throws it off.

Yesterday I left my caliper on the right side of the bed right where it probes for z1.

The calipers added about 4mm of skew. I saw it on the webcam so I ran over to remove the calipers… but I let it finish Z_TILT_ADJUSTing anyway. Came back to watch the first layer… and as I suspected, the nozzle was 2-3mm up in the air.

Now, a G28 Z should fix the offset… but I want to understand why the original macro is doing this. So, more experimentation and testing is needed.

But for now, this is the modification I’m adding to fix my dual z bltouch enabled printers:

[gcode_macro Z_TILT_ADJUST]
rename_existing: OG_Z_TILT_ADJUST
gcode:
    {% if printer.toolhead.homed_axes == "xyz" %}
        M118 Printer is already homed
    {% else %}
        M118 Printer needs homed...
        G28
    {% endif %}
  OG_Z_TILT_ADJUST
  # final G28 Z to fix tilt error
  G28 Z

I too have an independant dual z, and found, Z_TILT_ADJUST tries to make both sides equal the current z_offset up or down… in so doing it changes the z_offset during the run.
Thereby you have to re-run G28 Z after, to change it back to bed center.

So my start procedure includes this routine, (see script)

FYI, I happen to have enough room on both sides of the bed, to level the X gantry to the Frame. To explain:
I have installed a pair of 123blocks on each side. (see video) and have the Z_tilt_adjust probe the 123blocks to square the gantry to the frame. And finish with a G28 Z to reset the z_offset.

15:32:41 // Retries: 1/10 Probed points range: 0.002500 tolerance: 0.010000
15:32:41 // Making the following Z adjustments:
// stepper_z = -0.011520
// stepper_z1 = -0.008745
15:32:41 // probe at 400.000,175.000 is z=2.372245
15:32:37 // probe at 400.000,175.000 is z=2.372245
...

I then run SCREWS_TILT_ADJUST, and using the bed screws to adjust each corner to match the center height, I am able to level the bed to the now square gantry.( see pic.) I used the bed center as base in config. (I dont need to run the Screws Adjust utility very often, as I have silicone standoffs they rarely change, But I do have the Z_tilt and g28 Z in start print macro.)

G28 # Home_all
Z_TILT_ADJUST # Square Gantry to Frame
G28 Z  #Re-Home Z to Bed Center

1 Like