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_empty_queue();
50 if(!gcode
->has_letter('R')) {
51 if(!calibrate_delta_endstops(gcode
)) {
52 gcode
->stream
->printf("Calibration failed to complete, probe not triggered\n");
56 if(!gcode
->has_letter('E')) {
57 if(!calibrate_delta_radius(gcode
)) {
58 gcode
->stream
->printf("Calibration failed to complete, probe not triggered\n");
62 gcode
->stream
->printf("Calibration complete, save settings with M500\n");
65 }else if (gcode
->g
== 29) {
67 if(!probe_delta_points(gcode
)) {
68 gcode
->stream
->printf("Calibration failed to complete, probe not triggered\n");
73 } else if(gcode
->has_m
) {
80 // calculate the X and Y positions for the three towers given the radius from the center
81 static std::tuple
<float, float, float, float, float, float> getCoordinates(float radius
)
83 float px
= 0.866F
* radius
; // ~sin(60)
84 float py
= 0.5F
* radius
; // cos(60)
85 float t1x
= -px
, t1y
= -py
; // X Tower
86 float t2x
= px
, t2y
= -py
; // Y Tower
87 float t3x
= 0.0F
, t3y
= radius
; // Z Tower
88 return std::make_tuple(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
);
92 // Probes the 7 points on a delta can be used for off board calibration
93 bool DeltaCalibrationStrategy::probe_delta_points(Gcode
*gcode
)
95 float bedht
= findBed();
96 if(isnan(bedht
)) return false;
98 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
100 // move to start position
102 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // do a relative move from home to the point above the bed
105 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
106 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
108 // gather probe points
109 float pp
[][2] {{t1x
, t1y
}, {t2x
, t2y
}, {t3x
, t3y
}, {0, 0}, {-t1x
, -t1y
}, {-t2x
, -t2y
}, {-t3x
, -t3y
}};
114 std::tie(std::ignore
, std::ignore
, start_z
)= THEKERNEL
->robot
->get_axis_position();
118 if(!zprobe
->doProbeAt(s
, i
[0], i
[1])) return false;
119 float z
= zprobe
->zsteps_to_mm(s
);
120 if(gcode
->subcode
== 0) {
121 gcode
->stream
->printf("X:%1.4f Y:%1.4f Z:%1.4f (%d) A:%1.4f B:%1.4f C:%1.4f\n",
123 THEKERNEL
->robot
->actuators
[0]->get_current_position()+z
,
124 THEKERNEL
->robot
->actuators
[1]->get_current_position()+z
,
125 THEKERNEL
->robot
->actuators
[2]->get_current_position()+z
);
127 }else if(gcode
->subcode
== 1) {
128 // format that can be pasted here http://escher3d.com/pages/wizards/wizarddelta.php
129 gcode
->stream
->printf("X%1.4f Y%1.4f Z%1.4f\n", i
[0], i
[1], start_z
- z
);
135 max_delta
= std::max(max_delta
, fabsf(z
-last_z
));
139 gcode
->stream
->printf("max delta: %f\n", max_delta
);
144 float DeltaCalibrationStrategy::findBed()
149 // 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
150 float deltaz
= zprobe
->getMaxZ() - initial_height
;
151 zprobe
->coordinated_move(NAN
, NAN
, -deltaz
, zprobe
->getFastFeedrate(), true);
153 // find bed, run at slow rate so as to not hit bed hard
155 if(!zprobe
->run_probe(s
, false)) return NAN
;
157 return zprobe
->zsteps_to_mm(s
) + deltaz
- zprobe
->getProbeHeight(); // distance to move from home to 5mm above bed
160 /* Run a calibration routine for a delta
163 3. probe initial tower positions
164 4. set initial trims such that trims will be minimal negative values
165 5. home, probe three towers again
166 6. calculate trim offset and apply to all trims
167 7. repeat 5, 6 until it converges on a solution
170 bool DeltaCalibrationStrategy::calibrate_delta_endstops(Gcode
*gcode
)
172 float target
= 0.03F
;
173 if(gcode
->has_letter('I')) target
= gcode
->get_value('I'); // override default target
174 if(gcode
->has_letter('J')) this->probe_radius
= gcode
->get_value('J'); // override default probe radius
177 if(gcode
->has_letter('K')) keep
= true; // keep current settings
179 gcode
->stream
->printf("Calibrating Endstops: target %fmm, radius %fmm\n", target
, this->probe_radius
);
182 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
183 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
185 float trimx
= 0.0F
, trimy
= 0.0F
, trimz
= 0.0F
;
188 if(!set_trim(0, 0, 0, gcode
->stream
)) return false;
191 // get current trim, and continue from that
192 if (get_trim(trimx
, trimy
, trimz
)) {
193 gcode
->stream
->printf("Current Trim X: %f, Y: %f, Z: %f\r\n", trimx
, trimy
, trimz
);
196 gcode
->stream
->printf("Could not get current trim, are endstops enabled?\n");
201 // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is
202 // so we need to find the initial place that the probe triggers when it hits the bed
203 float bedht
= findBed();
204 if(isnan(bedht
)) return false;
205 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
207 // move to start position
209 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // do a relative move from home to the point above the bed
211 // get initial probes
212 // probe the base of the X tower
214 if(!zprobe
->doProbeAt(s
, t1x
, t1y
)) return false;
215 float t1z
= zprobe
->zsteps_to_mm(s
);
216 gcode
->stream
->printf("T1-0 Z:%1.4f C:%d\n", t1z
, s
);
218 // probe the base of the Y tower
219 if(!zprobe
->doProbeAt(s
, t2x
, t2y
)) return false;
220 float t2z
= zprobe
->zsteps_to_mm(s
);
221 gcode
->stream
->printf("T2-0 Z:%1.4f C:%d\n", t2z
, s
);
223 // probe the base of the Z tower
224 if(!zprobe
->doProbeAt(s
, t3x
, t3y
)) return false;
225 float t3z
= zprobe
->zsteps_to_mm(s
);
226 gcode
->stream
->printf("T3-0 Z:%1.4f C:%d\n", t3z
, s
);
228 float trimscale
= 1.2522F
; // empirically determined
230 auto mm
= std::minmax({t1z
, t2z
, t3z
});
231 if((mm
.second
- mm
.first
) <= target
) {
232 gcode
->stream
->printf("trim already set within required parameters: delta %f\n", mm
.second
- mm
.first
);
236 // set trims to worst case so we always have a negative trim
237 trimx
+= (mm
.first
- t1z
) * trimscale
;
238 trimy
+= (mm
.first
- t2z
) * trimscale
;
239 trimz
+= (mm
.first
- t3z
) * trimscale
;
241 for (int i
= 1; i
<= 10; ++i
) {
243 if(!set_trim(trimx
, trimy
, trimz
, gcode
->stream
)) return false;
245 // home and move probe to start position just above the bed
247 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // do a relative move from home to the point above the bed
249 // probe the base of the X tower
250 if(!zprobe
->doProbeAt(s
, t1x
, t1y
)) return false;
251 t1z
= zprobe
->zsteps_to_mm(s
);
252 gcode
->stream
->printf("T1-%d Z:%1.4f C:%d\n", i
, t1z
, s
);
254 // probe the base of the Y tower
255 if(!zprobe
->doProbeAt(s
, t2x
, t2y
)) return false;
256 t2z
= zprobe
->zsteps_to_mm(s
);
257 gcode
->stream
->printf("T2-%d Z:%1.4f C:%d\n", i
, t2z
, s
);
259 // probe the base of the Z tower
260 if(!zprobe
->doProbeAt(s
, t3x
, t3y
)) return false;
261 t3z
= zprobe
->zsteps_to_mm(s
);
262 gcode
->stream
->printf("T3-%d Z:%1.4f C:%d\n", i
, t3z
, s
);
264 mm
= std::minmax({t1z
, t2z
, t3z
});
265 if((mm
.second
- mm
.first
) <= target
) {
266 gcode
->stream
->printf("trim set to within required parameters: delta %f\n", mm
.second
- mm
.first
);
270 // set new trim values based on min difference
271 trimx
+= (mm
.first
- t1z
) * trimscale
;
272 trimy
+= (mm
.first
- t2z
) * trimscale
;
273 trimz
+= (mm
.first
- t3z
) * trimscale
;
276 THEKERNEL
->call_event(ON_IDLE
);
279 if((mm
.second
- mm
.first
) > target
) {
280 gcode
->stream
->printf("WARNING: trim did not resolve to within required parameters: delta %f\n", mm
.second
- mm
.first
);
287 probe edges to get outer positions, then probe center
288 modify the delta radius until center and X converge
291 bool DeltaCalibrationStrategy::calibrate_delta_radius(Gcode
*gcode
)
293 float target
= 0.03F
;
294 if(gcode
->has_letter('I')) target
= gcode
->get_value('I'); // override default target
295 if(gcode
->has_letter('J')) this->probe_radius
= gcode
->get_value('J'); // override default probe radius
297 gcode
->stream
->printf("Calibrating delta radius: target %f, radius %f\n", target
, this->probe_radius
);
300 float t1x
, t1y
, t2x
, t2y
, t3x
, t3y
;
301 std::tie(t1x
, t1y
, t2x
, t2y
, t3x
, t3y
) = getCoordinates(this->probe_radius
);
303 // find the bed, as we potentially have a temporary z probe we don't know how low under the nozzle it is
304 // so we need to find thr initial place that the probe triggers when it hits the bed
305 float bedht
= findBed();
306 if(isnan(bedht
)) return false;
307 gcode
->stream
->printf("initial Bed ht is %f mm\n", bedht
);
310 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // do a relative move from home to the point above the bed
312 // probe center to get reference point at this Z height
314 if(!zprobe
->doProbeAt(dc
, 0, 0)) return false;
315 gcode
->stream
->printf("CT Z:%1.3f C:%d\n", zprobe
->zsteps_to_mm(dc
), dc
);
316 float cmm
= zprobe
->zsteps_to_mm(dc
);
318 // get current delta radius
319 float delta_radius
= 0.0F
;
320 BaseSolution::arm_options_t options
;
321 if(THEKERNEL
->robot
->arm_solution
->get_optional(options
)) {
322 delta_radius
= options
['R'];
324 if(delta_radius
== 0.0F
) {
325 gcode
->stream
->printf("This appears to not be a delta arm solution\n");
331 float drinc
= 2.5F
; // approx
332 for (int i
= 1; i
<= 10; ++i
) {
333 // probe t1, t2, t3 and get average, but use coordinated moves, probing center won't change
335 if(!zprobe
->doProbeAt(dx
, t1x
, t1y
)) return false;
336 gcode
->stream
->printf("T1-%d Z:%1.3f C:%d\n", i
, zprobe
->zsteps_to_mm(dx
), dx
);
337 if(!zprobe
->doProbeAt(dy
, t2x
, t2y
)) return false;
338 gcode
->stream
->printf("T2-%d Z:%1.3f C:%d\n", i
, zprobe
->zsteps_to_mm(dy
), dy
);
339 if(!zprobe
->doProbeAt(dz
, t3x
, t3y
)) return false;
340 gcode
->stream
->printf("T3-%d Z:%1.3f C:%d\n", i
, zprobe
->zsteps_to_mm(dz
), dz
);
342 // now look at the difference and reduce it by adjusting delta radius
343 float m
= zprobe
->zsteps_to_mm((dx
+ dy
+ dz
) / 3.0F
);
345 gcode
->stream
->printf("C-%d Z-ave:%1.4f delta: %1.3f\n", i
, m
, d
);
347 if(abs(d
) <= target
){
349 break; // resolution of success
352 // increase delta radius to adjust for low center
353 // decrease delta radius to adjust for high center
354 delta_radius
+= (d
* drinc
);
356 // set the new delta radius
357 options
['R'] = delta_radius
;
358 THEKERNEL
->robot
->arm_solution
->set_optional(options
);
359 gcode
->stream
->printf("Setting delta radius to: %1.4f\n", delta_radius
);
362 zprobe
->coordinated_move(NAN
, NAN
, -bedht
, zprobe
->getFastFeedrate(), true); // needs to be a relative coordinated move
365 THEKERNEL
->call_event(ON_IDLE
);
369 gcode
->stream
->printf("WARNING: delta radius did not resolve to within required parameters: %f\n", target
);
375 bool DeltaCalibrationStrategy::set_trim(float x
, float y
, float z
, StreamOutput
*stream
)
377 float t
[3] {x
, y
, z
};
378 bool ok
= PublicData::set_value( endstops_checksum
, trim_checksum
, t
);
381 stream
->printf("set trim to X:%f Y:%f Z:%f\n", x
, y
, z
);
383 stream
->printf("unable to set trim, is endstops enabled?\n");
389 bool DeltaCalibrationStrategy::get_trim(float &x
, float &y
, float &z
)
392 bool ok
= PublicData::get_value( endstops_checksum
, trim_checksum
, &returned_data
);
395 float *trim
= static_cast<float *>(returned_data
);