Z-A coupling interlock: prevent collision between Z and A tools
The auxiliary A axis carries a tool that hangs below the Z spindle.
Beyond a small Z descent the two physically collide unless A drops
with Z. Enforce in machine coords:
A_machine - Z_machine <= K
K = (A_home_mm - z_home_mm) + couple_z_clearance_mm
With our setup K = (134 - 0) + 22 = 156. At rest A=134 Z=0, A-Z=134
which is fine. Z can descend 22mm before the rule starts forcing A
down with it.
Two complementary layers:
(1) AuxPreprocessor injection (auto-fix uploaded files)
Tracks modal Z, A and distance mode (G90/G91) while scanning the
file. When a line would put A above Z by more than the clearance
we emit a 'G0 A<safe>' BEFORE the line so A is already at the
safe position when Z descends. Endpoint check is sufficient
because Z moves monotonically along a single line.
Errors are raised (not silently auto-fixed) when:
- the line lifts A above the safe band while Z stays put
(would require auto-injecting a Z-up which could swing
through a fixture)
- the line endpoint targets an A above the safe band
G91 disables injection with a one-shot warning; the runtime
check still applies.
(2) Runtime check (ExternalAxis.check_coupling)
Single source of truth for live motion. Hooked into:
* Planner.__encode for every line block (covers MDI and
running programs - gplan emits machine-coord targets)
* ExternalAxis.execute_to_mm/enqueue_target_mm/enqueue_line
for direct A motion (covers UI jog/move and planner-A
dispatch)
Raises ExternalAxisError on violation; gplan and the API both
surface the message. Skipped when coupling is disabled or the
axis isn't homed (mirrors the soft-limit gate).
Continuous Z jog from the AVR is not gated - it's an active
operator action without a pre-known endpoint. Operator-driven
over-travel during continuous jog will be caught by the next
MDI/file-load attempt.
Configuration in aux.json:
couple_z_enabled bool default true (per agreed setup)
couple_z_clearance_mm float default 22.0
z_home_mm float default 0.0
Surfaced in the new Z-A Coupling section of the A Axis settings
page with a description of the rule. Existing aux.json files get
the new keys via the merged-defaults path on read.
Tested locally with synthetic gcode covering Z descent, combined
moves, A lift while Z deep, G92 reset, G91 mode, and combined
Z+A target violations.
This commit is contained in:
@@ -270,6 +270,27 @@ class Planner():
|
||||
if type != 'set': self.log.info('Cmd:' + log_json(block))
|
||||
|
||||
if type == 'line':
|
||||
# Z-A coupling check: every line block that touches Z (or
|
||||
# A) is validated against the projected (A,Z) machine
|
||||
# pair. The ExternalAxis check raises on violation so
|
||||
# gplan aborts the program with a useful error rather
|
||||
# than driving the gantry into the auxiliary tool. The
|
||||
# check is skipped when coupling is disabled or A isn't
|
||||
# homed (see ExternalAxis.check_coupling).
|
||||
ext_check = getattr(self.ctrl, 'ext_axis', None)
|
||||
if ext_check is not None:
|
||||
target = block.get('target') or {}
|
||||
z_target = target.get('z')
|
||||
if z_target is None: z_target = target.get('Z')
|
||||
a_letter = ext_check.axis_letter
|
||||
a_target = target.get(a_letter)
|
||||
if a_target is None:
|
||||
a_target = target.get(a_letter.upper())
|
||||
if z_target is not None or a_target is not None:
|
||||
ext_check.check_coupling(
|
||||
target_a_machine=a_target,
|
||||
target_z_machine=z_target)
|
||||
|
||||
ext = self._external_axis_for_line(block)
|
||||
if ext is not None:
|
||||
# Side effect: enqueue the ESP move on the external-
|
||||
@@ -476,8 +497,11 @@ class Planner():
|
||||
# should use A directly.
|
||||
try:
|
||||
from bbctrl.AuxPreprocessor import preprocess_file
|
||||
if preprocess_file(path, log = self.log):
|
||||
self.log.info('Rewrote ATC M-codes in %s' % path)
|
||||
ext = getattr(self.ctrl, 'ext_axis', None)
|
||||
coupling = (ext.coupling_for_preprocessor()
|
||||
if ext is not None else None)
|
||||
if preprocess_file(path, log=self.log, coupling=coupling):
|
||||
self.log.info('Rewrote (ATC / Z-A coupling) in %s' % path)
|
||||
except Exception:
|
||||
self.log.exception('Aux preprocess at load failed; '
|
||||
'attempting to load file unchanged')
|
||||
|
||||
Reference in New Issue
Block a user