probe: Unify mean and median code between run_probe() and PROBE_ACCURACY

Factor out _calc_mean() and _calc_median() functions and call from
both run_probe() and cmd_PROBE_ACCURACY().

This also fixes a subtle error in the run_probe() median function - on
some kinematics the x and y position can change on a z move so the x
and y should be taken from the z probe values actually used.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2019-05-31 14:16:59 -04:00 committed by KevinOConnor
parent 2b8dca5dbf
commit 74cc005ff3
1 changed files with 25 additions and 42 deletions

View File

@ -33,9 +33,9 @@ class PrinterProbe:
self.samples = config.getint('samples', 1, minval=1)
self.sample_retract_dist = config.getfloat(
'sample_retract_dist', 2., above=0.)
self.samples_result = config.getchoice('samples_result',
{'median': 0, 'average': 1},
default='average')
atypes = {'median': 'median', 'average': 'average'}
self.samples_result = config.getchoice('samples_result', atypes,
'average')
# Register z_virtual_endstop pin
self.printer.lookup_object('pins').register_chip('probe', self)
# Register PROBE/QUERY_PROBE commands
@ -86,6 +86,18 @@ class PrinterProbe:
toolhead.move(curpos, speed)
except homing.EndstopError as e:
raise self.gcode.error(str(e))
def _calc_mean(self, positions):
count = float(len(positions))
return [sum([pos[i] for pos in positions]) / count
for i in range(3)]
def _calc_median(self, positions):
z_sorted = sorted(positions, key=(lambda p: p[2]))
middle = len(positions) // 2
if (len(positions) & 1) == 1:
# odd number of samples
return z_sorted[middle]
# even number of samples
return self._calc_mean(z_sorted[middle-1:middle+1])
def run_probe(self):
positions = []
for i in range(self.samples):
@ -95,26 +107,9 @@ class PrinterProbe:
# retract
liftpos = [None, None, pos[2] + self.sample_retract_dist]
self._move(liftpos, self.speed)
if self.samples_result == 1:
# Calculate Average
calculated_value = [sum([pos[i] for pos in positions]) /
self.samples for i in range(3)]
else:
# Calculate Median
sorted_z_positions = sorted([position[2]
for position in positions])
middle = self.samples // 2
if (self.samples & 1) == 1:
# odd number of samples
median = sorted_z_positions[middle]
else:
# even number of samples
median = (sorted_z_positions[middle] +
sorted_z_positions[middle - 1]) / 2
calculated_value = [positions[0][0],
positions[0][1],
median]
return calculated_value
if self.samples_result == 'median':
return self._calc_median(positions)
return self._calc_mean(positions)
cmd_PROBE_help = "Probe Z-height at current XY position"
def cmd_PROBE(self, params):
pos = self.run_probe()
@ -130,7 +125,6 @@ class PrinterProbe:
cmd_PROBE_ACCURACY_help = "Probe Z-height accuracy at current XY position"
def cmd_PROBE_ACCURACY(self, params):
toolhead = self.printer.lookup_object('toolhead')
probes = []
pos = toolhead.get_position()
number_of_reads = self.gcode.get_int('REPEAT', params, default=10,
minval=4, maxval=50)
@ -147,36 +141,25 @@ class PrinterProbe:
x_start_position, y_start_position,
z_start_position, number_of_reads, speed))
# Probe bed "number_of_reads" times
sum_reads = 0
positions = []
for i in range(number_of_reads):
# Move Z to start reading position
self._move(start_pos, speed)
# Probe
pos = self._probe(speed)
# Get Z value, accumulate value to calculate average
# and save it to calculate standard deviation
sum_reads += pos[2]
probes.append(pos[2])
positions.append(pos)
# Move Z to start reading position
self._move(start_pos, speed)
# Calculate maximum, minimum and average values
max_value = max(probes)
min_value = min(probes)
avg_value = sum(probes) / number_of_reads
max_value = max([p[2] for p in positions])
min_value = min([p[2] for p in positions])
avg_value = self._calc_mean(positions)[2]
median = self._calc_median(positions)[2]
# calculate the standard deviation
deviation_sum = 0
for i in range(number_of_reads):
deviation_sum += pow(probes[i] - avg_value, 2)
deviation_sum += pow(positions[i][2] - avg_value, 2)
sigma = (deviation_sum / number_of_reads) ** 0.5
# Median
sorted_probes = sorted(probes)
middle = number_of_reads//2
if (number_of_reads & 1) == 1:
# odd number of reads
median = sorted_probes[middle]
else:
# even number of reads
median = (sorted_probes[middle]+sorted_probes[middle-1])/2
# Show information
self.gcode.respond_info(
"probe accuracy results: maximum %.6f, minimum %.6f, "