Skip to content

axis_twist_compensation has no effect #266

Description

@boviex

[axis_twist_compensation] produces zero visible effect on bed mesh output regardless of the configured z_compensations values.

Observed behaviour is that even with extreme z_compensation values of 1,0,-1 and -1,0,1 the bed mesh results are near identical:

#*# [bed_mesh twist_a]
#*# version = 1
#*# points =
#*# 	0.260111, 0.200111, 0.150111, 0.140111, 0.130111, 0.120111, 0.090111, 0.100111, 0.140111, 0.200111, 0.250111
#*# 	0.250111, 0.160111, 0.140111, 0.120111, 0.100111, 0.080111, 0.080111, 0.090111, 0.100111, 0.150111, 0.190111
#*# 	0.260111, 0.170111, 0.130111, 0.100111, 0.090111, 0.090111, 0.040111, 0.060111, 0.060111, 0.090111, 0.160111
#*# 	0.230111, 0.160111, 0.100111, 0.070111, 0.060111, 0.040111, 0.040111, 0.030111, 0.050111, 0.090111, 0.150111
#*# 	0.230111, 0.140111, 0.090111, 0.060111, 0.040111, 0.000111, -0.019889, 0.020111, 0.040111, 0.060111, 0.120111
#*# 	0.220111, 0.120111, 0.090111, 0.050111, 0.000111, 0.000111, -0.009889, 0.040111, 0.050111, 0.070111, 0.130111
#*# 	0.200111, 0.150111, 0.130111, 0.050111, 0.020111, -0.009889, -0.029889, -0.009889, 0.050111, 0.060111, 0.130111
#*# 	0.240111, 0.130111, 0.070111, 0.040111, 0.040111, 0.020111, 0.030111, 0.040111, 0.070111, 0.090111, 0.140111
#*# 	0.250111, 0.160111, 0.100111, 0.060111, 0.050111, 0.040111, 0.030111, 0.050111, 0.070111, 0.100111, 0.170111
#*# 	0.260111, 0.150111, 0.080111, 0.080111, 0.060111, 0.060111, 0.060111, 0.090111, 0.090111, 0.130111, 0.200111
#*# 	0.330111, 0.240111, 0.140111, 0.130111, 0.120111, 0.100111, 0.080111, 0.140111, 0.160111, 0.190111, 0.260111
#*# x_count = 11
#*# y_count = 11
#*# mesh_x_pps = 2
#*# mesh_y_pps = 2
#*# algo = bicubic
#*# tension = 0.2
#*# min_x = 5.0
#*# max_x = 218.0
#*# min_y = 10.0
#*# max_y = 210.0
#*#
#*# [bed_mesh twist_b]
#*# version = 1
#*# points =
#*# 	0.279639, 0.209639, 0.169639, 0.149639, 0.149639, 0.139639, 0.099639, 0.119639, 0.159639, 0.209639, 0.269639
#*# 	0.269639, 0.179639, 0.149639, 0.129639, 0.119639, 0.089639, 0.089639, 0.099639, 0.109639, 0.159639, 0.209639
#*# 	0.259639, 0.179639, 0.139639, 0.099639, 0.099639, 0.069639, 0.059639, 0.069639, 0.069639, 0.089639, 0.179639
#*# 	0.239639, 0.149639, 0.099639, 0.079639, 0.049639, 0.049639, 0.039639, 0.049639, 0.059639, 0.099639, 0.149639
#*# 	0.229639, 0.149639, 0.089639, 0.069639, 0.049639, 0.009639, -0.010361, 0.009639, 0.039639, 0.069639, 0.129639
#*# 	0.209639, 0.149639, 0.099639, 0.059639, 0.029639, -0.000361, -0.000361, 0.039639, 0.059639, 0.079639, 0.129639
#*# 	0.209639, 0.179639, 0.129639, 0.069639, 0.039639, 0.039639, -0.020361, 0.009639, 0.059639, 0.069639, 0.149639
#*# 	0.239639, 0.149639, 0.079639, 0.049639, 0.039639, 0.029639, 0.049639, 0.049639, 0.079639, 0.099639, 0.149639
#*# 	0.259639, 0.169639, 0.089639, 0.069639, 0.069639, 0.049639, 0.039639, 0.059639, 0.079639, 0.099639, 0.179639
#*# 	0.269639, 0.149639, 0.099639, 0.079639, 0.069639, 0.069639, 0.069639, 0.109639, 0.099639, 0.139639, 0.199639
#*# 	0.339639, 0.229639, 0.149639, 0.139639, 0.119639, 0.099639, 0.089639, 0.139639, 0.169639, 0.189639, 0.269639
#*# x_count = 11
#*# y_count = 11
#*# mesh_x_pps = 2
#*# mesh_y_pps = 2
#*# algo = bicubic
#*# tension = 0.2
#*# min_x = 5.0
#*# max_x = 218.0
#*# min_y = 10.0
#*# max_y = 210.0

LLM generated diagnosis below which may or may not be accurate:

Root cause: manual_probe.ProbeResult is an immutable namedtuple:

ProbeResult = collections.namedtuple('probe_result', ['bed_x', 'bed_y', 'bed_z', 'test_x', 'test_y', 'test_z'])

axis_twist_compensation's event handler applies the correction by replacing the list element, not by mutating the tuple in place (since it can't):

def _update_z_compensation_value(self, poslist):
    for i in range(len(poslist)):
        pos = poslist[i]
        zo = ...
        pos = manual_probe.ProbeResult(pos.bed_x, pos.bed_y, pos.bed_z + zo, ...)
        poslist[i] = pos

This only works if the caller re-reads the corrected value back out of that same list. However, every probing function in BDsensor.py builds a throwaway list, passes it to the event, and then keeps using the original (pre-event) local variable:

epos = self.probe_offsets.create_probe_result(pos)
# Allow axis_twist_compensation to update results
self.printer.send_event("probe:update_results", [epos])   # temp list, discarded after this line
gcode.respond_info("... %.6f" % (epos.bed_x, epos.bed_y, epos.bed_z))  # still uncompensated
positions.append(epos)   # still uncompensated

epos never gets reassigned from the list that was passed to send_event, so the compensation computed by axis_twist_compensation is silently thrown away every time. This pattern appears in at least:

BDPrinterProbe._probe
BDPrinterProbe._probe_external_endstop
The BED_MESH_CALIBRATE / QUAD_GANTRY_LEVEL / Z_TILT_ADJUST inline branch in run_probe

Additionally, scan_sample_event (used when no_stop_probe is set) never calls the event at all — it's commented out:

self.mcu_probe.results.append(epos)
# Allow axis_twist_compensation to update results
#self.printer.send_event("probe:update_results", [epos])

To Reproduce

Configure [axis_twist_compensation] with a clearly nonzero z_compensations (e.g. 1, 0, -1) spanning the bed's X range.
Run BED_MESH_CALIBRATE twice — once with those values, once with the sign flipped (e.g. -1, 0, 1) — with a full FIRMWARE_RESTART and SAVE_CONFIG between runs.
Compare the two [bed_mesh] outputs.

Expected behavior

The two meshes should differ by roughly the shape of the compensation ramp — up to several mm at the X extremes for a 1mm-magnitude z_compensations array, tapering to ~0 near the center of the calibrated range.

Actual behavior

The two meshes are nearly identical (sub-0.02mm differences, consistent with ordinary probe repeatability noise), with no correlation to X position, in both the no_stop_probe (rapid scan) and normal stop-and-probe modes.

Suggested fix

Re-read the corrected value from the list passed to send_event in each affected function, e.g.:

poslist = [epos]
self.printer.send_event("probe:update_results", poslist)
epos = poslist[0]

And uncomment the equivalent call in scan_sample_event, applying the same fix there.

Environment

Klipper version: v0.13.0-743-gac2a7f8b
BDsensor.py commit: v0.0.0-556-g087257f0-inferred
Config: [BDsensor] x_offset: 0, y_offset: 10

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions