Skip to content

Commit 6b7868d

Browse files
authored
🐛 Fix backlash applied steps when config changes (MarlinFirmware#23826)
Followup to MarlinFirmware#23814
1 parent 15de14d commit 6b7868d

File tree

8 files changed

+167
-117
lines changed

8 files changed

+167
-117
lines changed

Marlin/src/feature/backlash.cpp

+46-30
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@
3030
#include "../module/planner.h"
3131

3232
axis_bits_t Backlash::last_direction_bits;
33-
#ifdef BACKLASH_SMOOTHING_MM
34-
xyz_long_t Backlash::residual_error{0};
35-
#endif
33+
xyz_long_t Backlash::residual_error{0};
3634

3735
#ifdef BACKLASH_DISTANCE_MM
3836
#if ENABLED(BACKLASH_GCODE)
@@ -43,7 +41,7 @@ axis_bits_t Backlash::last_direction_bits;
4341
#endif
4442

4543
#if ENABLED(BACKLASH_GCODE)
46-
uint8_t Backlash::correction = (BACKLASH_CORRECTION) * 0xFF;
44+
uint8_t Backlash::correction = (BACKLASH_CORRECTION) * all_on;
4745
#ifdef BACKLASH_SMOOTHING_MM
4846
float Backlash::smoothing_mm = BACKLASH_SMOOTHING_MM;
4947
#endif
@@ -87,43 +85,36 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
8785
#endif
8886
last_direction_bits ^= changed_dir;
8987

90-
if (correction == 0) return;
88+
if (!correction && !residual_error) return;
9189

9290
#ifdef BACKLASH_SMOOTHING_MM
9391
// The segment proportion is a value greater than 0.0 indicating how much residual_error
9492
// is corrected for in this segment. The contribution is based on segment length and the
9593
// smoothing distance. Since the computation of this proportion involves a floating point
9694
// division, defer computation until needed.
9795
float segment_proportion = 0;
98-
#else
99-
// No direction change, no correction.
100-
if (!changed_dir) return;
101-
// No leftover residual error from segment to segment
102-
xyz_long_t residual_error{0};
10396
#endif
10497

105-
const float f_corr = float(correction) / 255.0f;
98+
const float f_corr = float(correction) / all_on;
10699

107100
LOOP_LINEAR_AXES(axis) {
108101
if (distance_mm[axis]) {
109-
const bool reversing = TEST(dm,axis);
102+
const bool reverse = TEST(dm, axis);
110103

111104
// When an axis changes direction, add axis backlash to the residual error
112105
if (TEST(changed_dir, axis))
113-
residual_error[axis] += (reversing ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
106+
residual_error[axis] += (reverse ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
114107

115108
// Decide how much of the residual error to correct in this segment
116109
int32_t error_correction = residual_error[axis];
110+
if (reverse != (error_correction < 0))
111+
error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps
112+
117113
#ifdef BACKLASH_SMOOTHING_MM
118114
if (error_correction && smoothing_mm != 0) {
119-
// Take up a portion of the residual_error in this segment, but only when
120-
// the current segment travels in the same direction as the correction
121-
if (reversing == (error_correction < 0)) {
122-
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
123-
error_correction = CEIL(segment_proportion * error_correction);
124-
}
125-
else
126-
error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps
115+
// Take up a portion of the residual_error in this segment
116+
if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm);
117+
error_correction = CEIL(segment_proportion * error_correction);
127118
}
128119
#endif
129120

@@ -153,27 +144,52 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const
153144
}
154145
}
155146

156-
int32_t Backlash::applied_steps(const AxisEnum axis) {
147+
int32_t Backlash::get_applied_steps(const AxisEnum axis) {
157148
if (axis >= LINEAR_AXES) return 0;
158149

159-
const bool reversing = TEST(last_direction_bits, axis);
150+
const bool reverse = TEST(last_direction_bits, axis);
160151

161-
#ifdef BACKLASH_SMOOTHING_MM
162-
const int32_t residual_error_axis = residual_error[axis];
163-
#else
164-
constexpr int32_t residual_error_axis = 0;
165-
#endif
152+
const int32_t residual_error_axis = residual_error[axis];
166153

167154
// At startup it is assumed the last move was forwards. So the applied
168155
// steps will always be a non-positive number.
169156

170-
if (!reversing) return -residual_error_axis;
157+
if (!reverse) return -residual_error_axis;
171158

172-
const float f_corr = float(correction) / 255.0f;
159+
const float f_corr = float(correction) / all_on;
173160
const int32_t full_error_axis = -f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis];
174161
return full_error_axis - residual_error_axis;
175162
}
176163

164+
class Backlash::StepAdjuster {
165+
xyz_long_t applied_steps;
166+
public:
167+
StepAdjuster() {
168+
LOOP_LINEAR_AXES(axis) applied_steps[axis] = backlash.get_applied_steps((AxisEnum)axis);
169+
}
170+
~StepAdjuster() {
171+
// after backlash compensation parameter changes, ensure applied step count does not change
172+
LOOP_LINEAR_AXES(axis) residual_error[axis] += backlash.get_applied_steps((AxisEnum)axis) - applied_steps[axis];
173+
}
174+
};
175+
176+
void Backlash::set_correction_uint8(const uint8_t v) {
177+
StepAdjuster adjuster;
178+
correction = v;
179+
}
180+
181+
void Backlash::set_distance_mm(const AxisEnum axis, const float v) {
182+
StepAdjuster adjuster;
183+
distance_mm[axis] = v;
184+
}
185+
186+
#ifdef BACKLASH_SMOOTHING_MM
187+
void Backlash::set_smoothing_mm(const float v) {
188+
StepAdjuster adjuster;
189+
smoothing_mm = v;
190+
}
191+
#endif
192+
177193
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
178194

179195
#include "../module/probe.h"

Marlin/src/feature/backlash.h

+29-17
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,36 @@
2424
#include "../inc/MarlinConfigPre.h"
2525
#include "../module/planner.h"
2626

27-
constexpr uint8_t all_on = 0xFF, all_off = 0x00;
28-
2927
class Backlash {
28+
public:
29+
static constexpr uint8_t all_on = 0xFF, all_off = 0x00;
30+
3031
private:
3132
static axis_bits_t last_direction_bits;
32-
#ifdef BACKLASH_SMOOTHING_MM
33-
static xyz_long_t residual_error;
34-
#endif
33+
static xyz_long_t residual_error;
3534

36-
public:
3735
#if ENABLED(BACKLASH_GCODE)
38-
static xyz_float_t distance_mm;
3936
static uint8_t correction;
37+
static xyz_float_t distance_mm;
4038
#ifdef BACKLASH_SMOOTHING_MM
4139
static float smoothing_mm;
4240
#endif
43-
44-
static void set_correction(const_float_t v) { correction = _MAX(0, _MIN(1.0, v)) * all_on; }
45-
static float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; }
4641
#else
47-
static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF;
42+
static constexpr uint8_t correction = (BACKLASH_CORRECTION) * all_on;
4843
static const xyz_float_t distance_mm;
4944
#ifdef BACKLASH_SMOOTHING_MM
5045
static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM;
5146
#endif
5247
#endif
5348

5449
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
55-
private:
56-
static xyz_float_t measured_mm;
57-
static xyz_uint8_t measured_count;
58-
public:
59-
static void measure_with_probe();
50+
static xyz_float_t measured_mm;
51+
static xyz_uint8_t measured_count;
6052
#endif
6153

54+
class StepAdjuster;
55+
56+
public:
6257
static float get_measurement(const AxisEnum a) {
6358
UNUSED(a);
6459
// Return the measurement averaged over all readings
@@ -78,7 +73,24 @@ class Backlash {
7873
}
7974

8075
static void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block);
81-
static int32_t applied_steps(const AxisEnum axis);
76+
static int32_t get_applied_steps(const AxisEnum axis);
77+
78+
#if ENABLED(BACKLASH_GCODE)
79+
static void set_correction_uint8(const uint8_t v);
80+
static uint8_t get_correction_uint8() { return correction; }
81+
static void set_correction(const float v) { set_correction_uint8(_MAX(0, _MIN(1.0, v)) * all_on + 0.5f); }
82+
static float get_correction() { return float(get_correction_uint8()) / all_on; }
83+
static void set_distance_mm(const AxisEnum axis, const float v);
84+
static float get_distance_mm(const AxisEnum axis) {return distance_mm[axis];}
85+
#ifdef BACKLASH_SMOOTHING_MM
86+
static void set_smoothing_mm(const float v);
87+
static float get_smoothing_mm() {return smoothing_mm;}
88+
#endif
89+
#endif
90+
91+
#if ENABLED(MEASURE_BACKLASH_WHEN_PROBING)
92+
static void measure_with_probe();
93+
#endif
8294
};
8395

8496
extern Backlash backlash;

Marlin/src/gcode/calibrate/G425.cpp

+37-23
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,27 @@ struct measurements_t {
105105
};
106106

107107
#if ENABLED(BACKLASH_GCODE)
108-
#define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value)
108+
class restorer_correction {
109+
const uint8_t val_;
110+
public:
111+
restorer_correction(const uint8_t temp_val) : val_(backlash.get_correction_uint8()) { backlash.set_correction_uint8(temp_val); }
112+
~restorer_correction() { backlash.set_correction_uint8(val_); }
113+
};
114+
115+
#define TEMPORARY_BACKLASH_CORRECTION(value) restorer_correction restorer_tbst(value)
109116
#else
110117
#define TEMPORARY_BACKLASH_CORRECTION(value)
111118
#endif
112119

113120
#if ENABLED(BACKLASH_GCODE) && defined(BACKLASH_SMOOTHING_MM)
114-
#define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash.smoothing_mm, value)
121+
class restorer_smoothing {
122+
const float val_;
123+
public:
124+
restorer_smoothing(const float temp_val) : val_(backlash.get_smoothing_mm()) { backlash.set_smoothing_mm(temp_val); }
125+
~restorer_smoothing() { backlash.set_smoothing_mm(val_); }
126+
};
127+
128+
#define TEMPORARY_BACKLASH_SMOOTHING(value) restorer_smoothing restorer_tbsm(value)
115129
#else
116130
#define TEMPORARY_BACKLASH_SMOOTHING(value)
117131
#endif
@@ -524,53 +538,53 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
524538

525539
{
526540
// New scope for TEMPORARY_BACKLASH_CORRECTION
527-
TEMPORARY_BACKLASH_CORRECTION(all_off);
541+
TEMPORARY_BACKLASH_CORRECTION(backlash.all_off);
528542
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
529543

530544
probe_sides(m, uncertainty);
531545

532546
#if ENABLED(BACKLASH_GCODE)
533547

534548
#if HAS_X_CENTER
535-
backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
549+
backlash.set_distance_mm(X_AXIS, (m.backlash[LEFT] + m.backlash[RIGHT]) / 2);
536550
#elif ENABLED(CALIBRATION_MEASURE_LEFT)
537-
backlash.distance_mm.x = m.backlash[LEFT];
551+
backlash.set_distance_mm(X_AXIS, m.backlash[LEFT]);
538552
#elif ENABLED(CALIBRATION_MEASURE_RIGHT)
539-
backlash.distance_mm.x = m.backlash[RIGHT];
553+
backlash.set_distance_mm(X_AXIS, m.backlash[RIGHT]);
540554
#endif
541555

542556
#if HAS_Y_CENTER
543-
backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2;
557+
backlash.set_distance_mm(Y_AXIS, (m.backlash[FRONT] + m.backlash[BACK]) / 2);
544558
#elif ENABLED(CALIBRATION_MEASURE_FRONT)
545-
backlash.distance_mm.y = m.backlash[FRONT];
559+
backlash.set_distance_mm(Y_AXIS, m.backlash[FRONT]);
546560
#elif ENABLED(CALIBRATION_MEASURE_BACK)
547-
backlash.distance_mm.y = m.backlash[BACK];
561+
backlash.set_distance_mm(Y_AXIS, m.backlash[BACK]);
548562
#endif
549563

550-
TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]);
564+
TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.set_distance_mm(Z_AXIS, m.backlash[TOP]));
551565

552566
#if HAS_I_CENTER
553-
backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2;
567+
backlash.set_distance_mm(I_AXIS, (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2);
554568
#elif ENABLED(CALIBRATION_MEASURE_IMIN)
555-
backlash.distance_mm.i = m.backlash[IMINIMUM];
569+
backlash.set_distance_mm(I_AXIS, m.backlash[IMINIMUM]);
556570
#elif ENABLED(CALIBRATION_MEASURE_IMAX)
557-
backlash.distance_mm.i = m.backlash[IMAXIMUM];
571+
backlash.set_distance_mm(I_AXIS, m.backlash[IMAXIMUM]);
558572
#endif
559573

560574
#if HAS_J_CENTER
561-
backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2;
575+
backlash.set_distance_mm(J_AXIS, (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2);
562576
#elif ENABLED(CALIBRATION_MEASURE_JMIN)
563-
backlash.distance_mm.j = m.backlash[JMINIMUM];
577+
backlash.set_distance_mm(J_AXIS, m.backlash[JMINIMUM]);
564578
#elif ENABLED(CALIBRATION_MEASURE_JMAX)
565-
backlash.distance_mm.j = m.backlash[JMAXIMUM];
579+
backlash.set_distance_mm(J_AXIS, m.backlash[JMAXIMUM]);
566580
#endif
567581

568582
#if HAS_K_CENTER
569-
backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2;
583+
backlash.set_distance_mm(K_AXIS, (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2);
570584
#elif ENABLED(CALIBRATION_MEASURE_KMIN)
571-
backlash.distance_mm.k = m.backlash[KMINIMUM];
585+
backlash.set_distance_mm(K_AXIS, m.backlash[KMINIMUM]);
572586
#elif ENABLED(CALIBRATION_MEASURE_KMAX)
573-
backlash.distance_mm.k = m.backlash[KMAXIMUM];
587+
backlash.set_distance_mm(K_AXIS, m.backlash[KMAXIMUM]);
574588
#endif
575589

576590
#endif // BACKLASH_GCODE
@@ -581,7 +595,7 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
581595
// allowed directions to take up any backlash
582596
{
583597
// New scope for TEMPORARY_BACKLASH_CORRECTION
584-
TEMPORARY_BACKLASH_CORRECTION(all_on);
598+
TEMPORARY_BACKLASH_CORRECTION(backlash.all_on);
585599
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
586600
const xyz_float_t move = LINEAR_AXIS_ARRAY(
587601
AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3,
@@ -611,7 +625,7 @@ inline void update_measurements(measurements_t &m, const AxisEnum axis) {
611625
* - Call calibrate_backlash() beforehand for best accuracy
612626
*/
613627
inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const uint8_t extruder) {
614-
TEMPORARY_BACKLASH_CORRECTION(all_on);
628+
TEMPORARY_BACKLASH_CORRECTION(backlash.all_on);
615629
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
616630

617631
TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder));
@@ -648,7 +662,7 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const
648662
* uncertainty in - How far away from the object to begin probing
649663
*/
650664
inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty) {
651-
TEMPORARY_BACKLASH_CORRECTION(all_on);
665+
TEMPORARY_BACKLASH_CORRECTION(backlash.all_on);
652666
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
653667

654668
HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e);
@@ -674,7 +688,7 @@ inline void calibrate_all() {
674688

675689
TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
676690

677-
TEMPORARY_BACKLASH_CORRECTION(all_on);
691+
TEMPORARY_BACKLASH_CORRECTION(backlash.all_on);
678692
TEMPORARY_BACKLASH_SMOOTHING(0.0f);
679693

680694
// Do a fast and rough calibration of the toolheads

0 commit comments

Comments
 (0)