Skip to content

Commit 93c8ad8

Browse files
committed
bed_mesh: fix cpu hogging when processing large meshes
1 parent 8c94072 commit 93c8ad8

1 file changed

Lines changed: 52 additions & 15 deletions

File tree

klippy/extras/bed_mesh.py

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ def handle_connect(self):
138138
self.toolhead = self.printer.lookup_object('toolhead')
139139
self.bmc.print_generated_points(logging.info)
140140
def set_mesh(self, mesh):
141+
if mesh is not None:
142+
mesh.reactor = self.printer.get_reactor()
141143
if mesh is not None and self.fade_end != self.FADE_DISABLE:
142144
self.log_fade_complete = True
143145
if self.base_fade_target is None:
@@ -231,7 +233,7 @@ def move(self, newpos, speed):
231233
def get_status(self, eventtime=None):
232234
return self.status
233235
def update_status(self):
234-
self.status = {
236+
new_status = {
235237
"profile_name": "",
236238
"mesh_min": (0., 0.),
237239
"mesh_max": (0., 0.),
@@ -245,11 +247,12 @@ def update_status(self):
245247
mesh_max = (params['max_x'], params['max_y'])
246248
probed_matrix = self.z_mesh.get_probed_matrix()
247249
mesh_matrix = self.z_mesh.get_mesh_matrix()
248-
self.status['profile_name'] = self.z_mesh.get_profile_name()
249-
self.status['mesh_min'] = mesh_min
250-
self.status['mesh_max'] = mesh_max
251-
self.status['probed_matrix'] = probed_matrix
252-
self.status['mesh_matrix'] = mesh_matrix
250+
new_status['profile_name'] = self.z_mesh.get_profile_name()
251+
new_status['mesh_min'] = mesh_min
252+
new_status['mesh_max'] = mesh_max
253+
new_status['probed_matrix'] = probed_matrix
254+
new_status['mesh_matrix'] = mesh_matrix
255+
self.status = new_status
253256
def get_mesh(self):
254257
return self.z_mesh
255258
cmd_BED_MESH_OUTPUT_help = "Retrieve interpolated grid of probed z-points"
@@ -327,6 +330,7 @@ class BedMeshCalibrate:
327330
ALGOS = ['lagrange', 'bicubic']
328331
def __init__(self, config, bedmesh):
329332
self.printer = config.get_printer()
333+
self.reactor = self.printer.get_reactor()
330334
self.orig_config = {'radius': None, 'origin': None}
331335
self.radius = self.origin = None
332336
self.mesh_min = self.mesh_max = (0., 0.)
@@ -349,7 +353,15 @@ def __init__(self, config, bedmesh):
349353
self.gcode.register_command(
350354
'BED_MESH_CALIBRATE', self.cmd_BED_MESH_CALIBRATE,
351355
desc=self.cmd_BED_MESH_CALIBRATE_help)
352-
def print_generated_points(self, print_func):
356+
def _reactor_yield(self, return_value=None):
357+
self.reactor.pause(self.reactor.NOW)
358+
return return_value
359+
def _reactor_yield_interval(self, sequence, interval=100):
360+
for i, v in enumerate(sequence):
361+
if i % interval == interval - 1:
362+
self.reactor.pause(self.reactor.NOW)
363+
yield v
364+
def print_generated_points(self, print_func, force=False):
353365
x_offset = y_offset = 0.
354366
probe = self.printer.lookup_object('probe', None)
355367
if probe is not None:
@@ -658,7 +670,10 @@ def cmd_BED_MESH_CALIBRATE(self, gcmd):
658670
def probe_finalize(self, offsets, positions):
659671
z_offset = offsets[2]
660672
positions = [[round(p[0], 2), round(p[1], 2), p[2]]
661-
for p in positions]
673+
for p in self._reactor_yield_interval(positions, 500)]
674+
675+
self._reactor_yield()
676+
662677
if self.probe_mgr.get_zero_ref_mode() == ZrefMode.PROBE:
663678
ref_pos = positions.pop()
664679
logging.info(
@@ -667,14 +682,18 @@ def probe_finalize(self, offsets, positions):
667682
% (ref_pos[0], ref_pos[1], ref_pos[2])
668683
)
669684
z_offset = ref_pos[2]
685+
670686
base_points = self.probe_mgr.get_base_points()
671687
params = dict(self.mesh_config)
672688
params['min_x'] = min(base_points, key=lambda p: p[0])[0]
673689
params['max_x'] = max(base_points, key=lambda p: p[0])[0]
690+
self._reactor_yield()
674691
params['min_y'] = min(base_points, key=lambda p: p[1])[1]
675692
params['max_y'] = max(base_points, key=lambda p: p[1])[1]
676693
x_cnt = params['x_count']
677694
y_cnt = params['y_count']
695+
696+
self._reactor_yield()
678697

679698
substitutes = self.probe_mgr.get_substitutes()
680699
probed_pts = positions
@@ -703,6 +722,7 @@ def probe_finalize(self, offsets, positions):
703722
corrected_pts.append(fpt)
704723
corrected_pts.extend(positions[start_idx:])
705724
positions = corrected_pts
725+
self._reactor_yield()
706726

707727
# validate length of result
708728
if len(base_points) != len(positions):
@@ -716,7 +736,7 @@ def probe_finalize(self, offsets, positions):
716736
probed_matrix = []
717737
row = []
718738
prev_pos = base_points[0]
719-
for pos, result in zip(base_points, positions):
739+
for i, (pos, result) in enumerate(zip(base_points, positions)):
720740
offset_pos = [p - o for p, o in zip(pos, offsets[:2])]
721741
if (
722742
not isclose(offset_pos[0], result[0], abs_tol=.5) or
@@ -739,6 +759,8 @@ def probe_finalize(self, offsets, positions):
739759
# probed in the negative direction
740760
row.insert(0, z_pos)
741761
prev_pos = pos
762+
if i % 500 == 499:
763+
self._reactor_yield()
742764
# append last row
743765
probed_matrix.append(row)
744766

@@ -774,12 +796,14 @@ def probe_finalize(self, offsets, positions):
774796
("bed_mesh: invalid x-axis table length\n"
775797
"Probed table length: %d Probed Table:\n%s") %
776798
(len(probed_matrix), str(probed_matrix)))
777-
778-
z_mesh = ZMesh(params, self._profile_name)
799+
800+
z_mesh = ZMesh(params, self._profile_name, self.reactor)
801+
779802
try:
780803
z_mesh.build_mesh(probed_matrix)
781804
except BedMeshError as e:
782805
raise self.gcode.error(str(e))
806+
783807
if self.probe_mgr.get_zero_ref_mode() == ZrefMode.IN_MESH:
784808
# The reference can be anywhere in the mesh, therefore
785809
# it is necessary to set the reference after the initial mesh
@@ -788,6 +812,7 @@ def probe_finalize(self, offsets, positions):
788812
z_mesh.set_zero_reference(*zero_ref_pos)
789813
self.bedmesh.set_mesh(z_mesh)
790814
self.gcode.respond_info("Mesh Bed Leveling Complete")
815+
791816
if self._profile_name is not None:
792817
self.bedmesh.save_profile(self._profile_name)
793818
def _dump_points(self, probed_pts, corrected_pts, offsets):
@@ -1323,7 +1348,8 @@ def split(self):
13231348

13241349

13251350
class ZMesh:
1326-
def __init__(self, params, name):
1351+
def __init__(self, params, name, reactor = None):
1352+
self.reactor = reactor
13271353
self.profile_name = name or "adaptive-%X" % (id(self),)
13281354
self.probed_matrix = self.mesh_matrix = None
13291355
self.mesh_params = params
@@ -1361,14 +1387,18 @@ def __init__(self, params, name):
13611387
(self.mesh_x_count - 1)
13621388
self.mesh_y_dist = (self.mesh_y_max - self.mesh_y_min) / \
13631389
(self.mesh_y_count - 1)
1390+
def _reactor_yield(self, return_value=None):
1391+
if self.reactor:
1392+
self.reactor.pause(self.reactor.NOW)
1393+
return return_value
13641394
def get_mesh_matrix(self):
13651395
if self.mesh_matrix is not None:
1366-
return [[round(z, 6) for z in line]
1396+
return [self._reactor_yield([round(z, 6) for z in line])
13671397
for line in self.mesh_matrix]
13681398
return [[]]
13691399
def get_probed_matrix(self):
13701400
if self.probed_matrix is not None:
1371-
return [[round(z, 6) for z in line]
1401+
return [self._reactor_yield([round(z, 6) for z in line])
13721402
for line in self.probed_matrix]
13731403
return [[]]
13741404
def get_mesh_params(self):
@@ -1387,6 +1417,7 @@ def print_probed_matrix(self, print_func):
13871417
print_func("bed_mesh: bed has not been probed")
13881418
def print_mesh(self, print_func, move_z=None):
13891419
matrix = self.get_mesh_matrix()
1420+
self._reactor_yield()
13901421
if matrix is not None:
13911422
msg = "Mesh X,Y: %d,%d\n" % (self.mesh_x_count, self.mesh_y_count)
13921423
if move_z is not None:
@@ -1399,17 +1430,20 @@ def print_mesh(self, print_func, move_z=None):
13991430
msg += "Interpolation Algorithm: %s\n" \
14001431
% (self.mesh_params['algo'])
14011432
msg += "Measured points:\n"
1433+
self._reactor_yield()
14021434
for y_line in range(self.mesh_y_count - 1, -1, -1):
14031435
for z in matrix[y_line]:
14041436
msg += " %f" % (z)
14051437
msg += "\n"
1438+
self._reactor_yield()
14061439
print_func(msg)
14071440
else:
14081441
print_func("bed_mesh: Z Mesh not generated")
14091442
def build_mesh(self, z_matrix):
14101443
self.probed_matrix = z_matrix
14111444
self._sample(z_matrix)
14121445
self.print_mesh(logging.debug)
1446+
self._reactor_yield()
14131447
def set_zero_reference(self, xpos, ypos):
14141448
offset = self.calc_z(xpos, ypos)
14151449
logging.info(
@@ -1539,6 +1573,7 @@ def _sample_bicubic(self, z_matrix):
15391573
else z_matrix[j//y_mult][i//x_mult]
15401574
for i in range(self.mesh_x_count)]
15411575
for j in range(self.mesh_y_count)]
1576+
self._reactor_yield()
15421577
# Interpolate X values
15431578
for y in range(self.mesh_y_count):
15441579
if y % y_mult != 0:
@@ -1548,13 +1583,15 @@ def _sample_bicubic(self, z_matrix):
15481583
continue
15491584
pts = self._get_x_ctl_pts(x, y)
15501585
self.mesh_matrix[y][x] = self._cardinal_spline(pts, c)
1586+
self._reactor_yield()
15511587
# Interpolate Y values
15521588
for x in range(self.mesh_x_count):
15531589
for y in range(self.mesh_y_count):
15541590
if y % y_mult == 0:
15551591
continue
15561592
pts = self._get_y_ctl_pts(x, y)
15571593
self.mesh_matrix[y][x] = self._cardinal_spline(pts, c)
1594+
self._reactor_yield()
15581595
def _get_x_ctl_pts(self, x, y):
15591596
# Fetch control points and t for a X value in the mesh
15601597
x_mult = self.x_mult
@@ -1721,7 +1758,7 @@ def load_profile(self, prof_name):
17211758
"bed_mesh: Unknown profile [%s]" % prof_name)
17221759
probed_matrix = profile['points']
17231760
mesh_params = profile['mesh_params']
1724-
z_mesh = ZMesh(mesh_params, prof_name)
1761+
z_mesh = ZMesh(mesh_params, prof_name, self.printer.get_reactor())
17251762
try:
17261763
z_mesh.build_mesh(probed_matrix)
17271764
except BedMeshError as e:

0 commit comments

Comments
 (0)