1 #include "DeltaCalibrationStrategy.h"
5 #include "StreamOutputPool.h"
8 #include "ConfigValue.h"
9 #include "PublicDataRequest.h"
10 #include "EndstopsPublicAccess.h"
11 #include "PublicData.h"
14 #include "BaseSolution.h"
15 #include "StepperMotor.h"
20 #define radius_checksum CHECKSUM("radius")
21 #define initial_height_checksum CHECKSUM("initial_height")
24 #define probe_radius_checksum CHECKSUM("probe_radius")
26 bool DeltaCalibrationStrategy::handleConfig()
28 // default is probably wrong
29 float r
= THEKERNEL
->config
->value(leveling_strategy_checksum
, delta_calibration_strategy_checksum
, radius_checksum
)->by_default(-1)->as_number();
31 // deprecated config syntax]
32 r
= THEKERNEL
->config
->value(zprobe_checksum
, probe_radius_checksum
)->by_default(100.0F
)->as_number();
34 this->probe_radius
= r
;
36 // the initial height above the bed we stop the intial move down after home to find the bed
37 // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset)
38 this->initial_height
= THEKERNEL
->config
->value(leveling_strategy_checksum
, delta_calibration_strategy_checksum
, initial_height_checksum
)->by_default(10)->as_number();
42 bool DeltaCalibrationStrategy::handleGcode(Gcode
*gcode
)
46 if( gcode
->g
== 32 ) { // auto calibration for delta, Z bed mapping for cartesian
47 // first wait for an empty queue i.e. no moves left
48 THEKERNEL
->conveyor
->wait_for_idle();
50 // turn off any compensation transform as it will be invalidated anyway by this
51 THEROBOT
->compensationTransform
= nullptr;
53 if(!gcode
->has_letter('R')) {
54 if(!calibrate_delta_endstops(gcode
)) {
55 gcode
->stream
->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n");
59 if(!gcode
->has_letter('E')) {
60 if(!calibrate_delta_radius(gcode
)) {
61 gcode
->stream
->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n");
65 gcode
->stream
->printf("Calibration complete, save settings with M500\n");
68 }else if (gcode
->g
== 29) {
70 if(!probe_delta_points(gcode
)) {
71 gcode
->stream
->printf("Calibration failed to complete, check the initial probe height and/or initial_height settings\n");
76 } else if(gcode
->has_m
) {
83 // calculate the X and Y positions for the three towers given the radius from the center
84 static std::tuple
<float, float, float, float, float, float> getCoordinates(float radius
)
86 float px
= 0.866F
* radius
; // ~sin(60)
87 float py
= 0.5F
* radius
; // cos(60)
88 float t1x
= -px
, t1y
= -py
; // X Tower
89 float t2x
= px
, t2y
= -py
; // Y Tower
90 float t3x
= 0.0F
, t3y
= radius
; // Z Tower
91 return std::make_tuple(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
);
95 // Probes the 7 points on a delta can be used for off board calibration
96 bool DeltaCalibrationStrategy::probe_delta_points(Gcode
*gcode
)
98 float bedht
= findBed();
99 if(isnan(bedht
)) return false;
101 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
105 if(!zprobe
->doProbeAt(mm
, 0, 0)) return false;
106 float dz
= zprobe
->getProbeHeight() - mm
;
107 gcode
->stream
->printf("center probe: %1.4f\n", dz
);
110 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
111 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
113 // gather probe points
114 float pp
[][2] {{t1x
, t1y
}, {t2x
, t2y
}, {t3x
, t3y
}, {0, 0}, {-t1x
, -t1y
}, {-t2x
, -t2y
}, {-t3x
, -t3y
}};
118 float start_z
= THEROBOT
->actuators
[2]->get_current_position();
122 if(!zprobe
->doProbeAt(mm
, i
[0], i
[1])) return false;
124 if(gcode
->subcode
== 0) {
125 // prints the delta Z moved at the XY coordinates given
126 gcode
->stream
->printf("X:%1.4f Y:%1.4f Z:%1.4f\n", i
[0], i
[1], z
);
128 }else if(gcode
->subcode
== 1) {
129 // format that can be pasted here http://escher3d.com/pages/wizards/wizarddelta.php
130 gcode
->stream
->printf("X%1.4f Y%1.4f Z%1.4f\n", i
[0], i
[1], start_z
- z
); // actual Z of bed at probe point
136 max_delta
= std::max(max_delta
, fabsf(z
-last_z
));
140 gcode
->stream
->printf("max delta: %f\n", max_delta
);
145 float DeltaCalibrationStrategy::findBed()
150 // move to an initial position fast so as to not take all day, we move down max_z - initial_height, which is set in config, default 10mm
151 float deltaz
= zprobe
->getMaxZ() - initial_height
;
152 zprobe
->coordinated_move(NAN
, NAN
, -deltaz
, zprobe
->getFastFeedrate(), true);
154 // find bed, run at slow rate so as to not hit bed hard
156 if(!zprobe
->run_probe(mm
, false)) return NAN
;
157 zprobe
->return_probe(mm
);
159 // leave the probe zprobe->getProbeHeight() above bed
160 float dz
= zprobe
->getProbeHeight() - mm
;
161 zprobe
->coordinated_move(NAN
, NAN
, dz
, zprobe
->getFastFeedrate(), true); // relative move
163 return mm
+ deltaz
- zprobe
->getProbeHeight(); // distance to move from home to 5mm above bed
166 /* Run a calibration routine for a delta
169 3. probe initial tower positions
170 4. set initial trims such that trims will be minimal negative values
171 5. home, probe three towers again
172 6. calculate trim offset and apply to all trims
173 7. repeat 5, 6 until it converges on a solution
176 bool DeltaCalibrationStrategy::calibrate_delta_endstops(Gcode
*gcode
)
178 float target
= 0.03F
;
179 if(gcode
->has_letter('I')) target
= gcode
->get_value('I'); // override default target
180 if(gcode
->has_letter('J')) this->probe_radius
= gcode
->get_value('J'); // override default probe radius
183 if(gcode
->has_letter('K')) keep
= true; // keep current settings
185 gcode
->stream
->printf("Calibrating Endstops: target %fmm, radius %fmm\n", target
, this->probe_radius
);
188 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
189 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
191 float trimx
= 0.0F
, trimy
= 0.0F
, trimz
= 0.0F
;
194 if(!set_trim(0, 0, 0, gcode
->stream
)) return false;
197 // get current trim, and continue from that
198 if (get_trim(trimx
, trimy
, trimz
)) {
199 gcode
->stream
->printf("Current Trim X: %f, Y: %f, Z: %f\r\n", trimx
, trimy
, trimz
);
202 gcode
->stream
->printf("Could not get current trim, are endstops enabled?\n");
207 // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is
208 // so we need to find the initial place that the probe triggers when it hits the bed
209 float bedht
= findBed();
210 if(isnan(bedht
)) return false;
211 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
215 if(!zprobe
->doProbeAt(mm
, 0, 0)) return false;
216 float dz
= zprobe
->getProbeHeight() - mm
;
217 gcode
->stream
->printf("center probe: %1.4f\n", dz
);
218 if(fabsf(dz
) > target
) {
219 gcode
->stream
->printf("Probe was not repeatable to %f mm, (%f)\n", target
, dz
);
223 // get initial probes
224 // probe the base of the X tower
225 if(!zprobe
->doProbeAt(mm
, t1x
, t1y
)) return false;
227 gcode
->stream
->printf("T1-0 Z:%1.4f\n", t1z
);
229 // probe the base of the Y tower
230 if(!zprobe
->doProbeAt(mm
, t2x
, t2y
)) return false;
232 gcode
->stream
->printf("T2-0 Z:%1.4f\n", t2z
);
234 // probe the base of the Z tower
235 if(!zprobe
->doProbeAt(mm
, t3x
, t3y
)) return false;
237 gcode
->stream
->printf("T3-0 Z:%1.4f\n", t3z
);
239 float trimscale
= 1.2522F
; // empirically determined
241 auto mmx
= std::minmax({t1z
, t2z
, t3z
});
242 if((mmx
.second
- mmx
.first
) <= target
) {
243 gcode
->stream
->printf("trim already set within required parameters: delta %f\n", mmx
.second
- mmx
.first
);
247 // set trims to worst case so we always have a negative trim
248 trimx
+= (mmx
.first
- t1z
) * trimscale
;
249 trimy
+= (mmx
.first
- t2z
) * trimscale
;
250 trimz
+= (mmx
.first
- t3z
) * trimscale
;
252 for (int i
= 1; i
<= 10; ++i
) {
254 if(!set_trim(trimx
, trimy
, trimz
, gcode
->stream
)) return false;
256 // home and move probe to start position just above the bed
258 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // do a relative move from home to the point above the bed
260 // probe the base of the X tower
261 if(!zprobe
->doProbeAt(mm
, t1x
, t1y
)) return false;
263 gcode
->stream
->printf("T1-%d Z:%1.4f\n", i
, t1z
);
265 // probe the base of the Y tower
266 if(!zprobe
->doProbeAt(mm
, t2x
, t2y
)) return false;
268 gcode
->stream
->printf("T2-%d Z:%1.4f\n", i
, t2z
);
270 // probe the base of the Z tower
271 if(!zprobe
->doProbeAt(mm
, t3x
, t3y
)) return false;
273 gcode
->stream
->printf("T3-%d Z:%1.4f\n", i
, t3z
);
275 mmx
= std::minmax({t1z
, t2z
, t3z
});
276 if((mmx
.second
- mmx
.first
) <= target
) {
277 gcode
->stream
->printf("trim set to within required parameters: delta %f\n", mmx
.second
- mmx
.first
);
281 // set new trim values based on min difference
282 trimx
+= (mmx
.first
- t1z
) * trimscale
;
283 trimy
+= (mmx
.first
- t2z
) * trimscale
;
284 trimz
+= (mmx
.first
- t3z
) * trimscale
;
287 THEKERNEL
->call_event(ON_IDLE
);
290 if((mmx
.second
- mmx
.first
) > target
) {
291 gcode
->stream
->printf("WARNING: trim did not resolve to within required parameters: delta %f\n", mmx
.second
- mmx
.first
);
298 probe edges to get outer positions, then probe center
299 modify the delta radius until center and X converge
302 bool DeltaCalibrationStrategy::calibrate_delta_radius(Gcode
*gcode
)
304 float target
= 0.03F
;
305 if(gcode
->has_letter('I')) target
= gcode
->get_value('I'); // override default target
306 if(gcode
->has_letter('J')) this->probe_radius
= gcode
->get_value('J'); // override default probe radius
308 gcode
->stream
->printf("Calibrating delta radius: target %f, radius %f\n", target
, this->probe_radius
);
311 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
312 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
314 // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is
315 // so we need to find thr initial place that the probe triggers when it hits the bed
316 float bedht
= findBed();
317 if(isnan(bedht
)) return false;
318 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
322 if(!zprobe
->doProbeAt(mm
, 0, 0)) return false;
323 float dz
= zprobe
->getProbeHeight() - mm
;
324 gcode
->stream
->printf("center probe: %1.4f\n", dz
);
325 if(fabsf(dz
) > target
) {
326 gcode
->stream
->printf("Probe was not repeatable to %f mm, (%f)\n", target
, dz
);
330 // probe center to get reference point at this Z height
332 if(!zprobe
->doProbeAt(dc
, 0, 0)) return false;
333 gcode
->stream
->printf("CT Z:%1.3f\n", dc
);
336 // get current delta radius
337 float delta_radius
= 0.0F
;
338 BaseSolution::arm_options_t options
;
339 if(THEROBOT
->arm_solution
->get_optional(options
)) {
340 delta_radius
= options
['R'];
342 if(delta_radius
== 0.0F
) {
343 gcode
->stream
->printf("This appears to not be a delta arm solution\n");
349 float drinc
= 2.5F
; // approx
350 for (int i
= 1; i
<= 10; ++i
) {
351 // probe t1, t2, t3 and get average, but use coordinated moves, probing center won't change
353 if(!zprobe
->doProbeAt(dx
, t1x
, t1y
)) return false;
354 gcode
->stream
->printf("T1-%d Z:%1.3f\n", i
, dx
);
355 if(!zprobe
->doProbeAt(dy
, t2x
, t2y
)) return false;
356 gcode
->stream
->printf("T2-%d Z:%1.3f\n", i
, dy
);
357 if(!zprobe
->doProbeAt(dz
, t3x
, t3y
)) return false;
358 gcode
->stream
->printf("T3-%d Z:%1.3f\n", i
, dz
);
360 // now look at the difference and reduce it by adjusting delta radius
361 float m
= (dx
+ dy
+ dz
) / 3.0F
;
363 gcode
->stream
->printf("C-%d Z-ave:%1.4f delta: %1.3f\n", i
, m
, d
);
365 if(fabsf(d
) <= target
){
367 break; // resolution of success
370 // increase delta radius to adjust for low center
371 // decrease delta radius to adjust for high center
372 delta_radius
+= (d
* drinc
);
374 // set the new delta radius
375 options
['R'] = delta_radius
;
376 THEROBOT
->arm_solution
->set_optional(options
);
377 gcode
->stream
->printf("Setting delta radius to: %1.4f\n", delta_radius
);
380 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // needs to be a relative coordinated move
383 THEKERNEL
->call_event(ON_IDLE
);
387 gcode
->stream
->printf("WARNING: delta radius did not resolve to within required parameters: %f\n", target
);
393 bool DeltaCalibrationStrategy::set_trim(float x
, float y
, float z
, StreamOutput
*stream
)
395 float t
[3] {x
, y
, z
};
396 bool ok
= PublicData::set_value( endstops_checksum
, trim_checksum
, t
);
399 stream
->printf("set trim to X:%f Y:%f Z:%f\n", x
, y
, z
);
401 stream
->printf("unable to set trim, is endstops enabled?\n");
407 bool DeltaCalibrationStrategy::get_trim(float &x
, float &y
, float &z
)
410 bool ok
= PublicData::get_value( endstops_checksum
, trim_checksum
, &returned_data
);
413 float *trim
= static_cast<float *>(returned_data
);