gcode_arcs: support XY, XZ and YZ planes
add G17, G18 and G19 commands to select arc planes enhance G2/G3 to support arc moves in XY, XZ and YZ planes Signed-off-by: Andrew Mirsky <andrew@mirsky.net>
This commit is contained in:
parent
c7f323e863
commit
0c5c87d7c0
|
@ -549,8 +549,9 @@ clears any error state from the micro-controller.
|
||||||
The following standard G-Code commands are available if a
|
The following standard G-Code commands are available if a
|
||||||
[gcode_arcs config section](Config_Reference.md#gcode_arcs) is
|
[gcode_arcs config section](Config_Reference.md#gcode_arcs) is
|
||||||
enabled:
|
enabled:
|
||||||
- Controlled Arc Move (G2 or G3): `G2 [X<pos>] [Y<pos>] [Z<pos>]
|
- Arc Move Clockwise (G2), Arc Move Counter-clockwise (G3): `G2|G3 [X<pos>] [Y<pos>] [Z<pos>]
|
||||||
[E<pos>] [F<speed>] I<value> J<value>`
|
[E<pos>] [F<speed>] I<value> J<value>|I<value> K<value>|J<value> K<value>`
|
||||||
|
- Arc Plane Select: G17 (XY plane), G18 (XZ plane), G19 (YZ plane)
|
||||||
|
|
||||||
### [gcode_macro]
|
### [gcode_macro]
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,25 @@
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import math
|
import math
|
||||||
|
from gcode import Coord
|
||||||
# Coordinates created by this are converted into G1 commands.
|
# Coordinates created by this are converted into G1 commands.
|
||||||
#
|
#
|
||||||
# note: only IJ version available
|
# supports XY, XZ & YZ planes with remaining axis as helical
|
||||||
|
|
||||||
|
# Enum
|
||||||
|
ARC_PLANE_X_Y = 0
|
||||||
|
ARC_PLANE_X_Z = 1
|
||||||
|
ARC_PLANE_Y_Z = 2
|
||||||
|
|
||||||
|
# Enum
|
||||||
|
X_AXIS = 0
|
||||||
|
Y_AXIS = 1
|
||||||
|
Z_AXIS = 2
|
||||||
|
E_AXIS = 3
|
||||||
|
|
||||||
|
|
||||||
class ArcSupport:
|
class ArcSupport:
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.mm_per_arc_segment = config.getfloat('resolution', 1., above=0.0)
|
self.mm_per_arc_segment = config.getfloat('resolution', 1., above=0.0)
|
||||||
|
@ -22,12 +35,28 @@ class ArcSupport:
|
||||||
self.gcode.register_command("G2", self.cmd_G2)
|
self.gcode.register_command("G2", self.cmd_G2)
|
||||||
self.gcode.register_command("G3", self.cmd_G3)
|
self.gcode.register_command("G3", self.cmd_G3)
|
||||||
|
|
||||||
|
self.gcode.register_command("G17", self.cmd_G17)
|
||||||
|
self.gcode.register_command("G18", self.cmd_G18)
|
||||||
|
self.gcode.register_command("G19", self.cmd_G19)
|
||||||
|
|
||||||
|
# backwards compatibility, prior implementation only supported XY
|
||||||
|
self.plane = ARC_PLANE_X_Y
|
||||||
|
|
||||||
def cmd_G2(self, gcmd):
|
def cmd_G2(self, gcmd):
|
||||||
self._cmd_inner(gcmd, True)
|
self._cmd_inner(gcmd, True)
|
||||||
|
|
||||||
def cmd_G3(self, gcmd):
|
def cmd_G3(self, gcmd):
|
||||||
self._cmd_inner(gcmd, False)
|
self._cmd_inner(gcmd, False)
|
||||||
|
|
||||||
|
def cmd_G17(self, gcmd):
|
||||||
|
self.plane = ARC_PLANE_X_Y
|
||||||
|
|
||||||
|
def cmd_G18(self, gcmd):
|
||||||
|
self.plane = ARC_PLANE_X_Z
|
||||||
|
|
||||||
|
def cmd_G19(self, gcmd):
|
||||||
|
self.plane = ARC_PLANE_Y_Z
|
||||||
|
|
||||||
def _cmd_inner(self, gcmd, clockwise):
|
def _cmd_inner(self, gcmd, clockwise):
|
||||||
gcodestatus = self.gcode_move.get_status()
|
gcodestatus = self.gcode_move.get_status()
|
||||||
if not gcodestatus['absolute_coordinates']:
|
if not gcodestatus['absolute_coordinates']:
|
||||||
|
@ -35,21 +64,33 @@ class ArcSupport:
|
||||||
currentPos = gcodestatus['gcode_position']
|
currentPos = gcodestatus['gcode_position']
|
||||||
|
|
||||||
# Parse parameters
|
# Parse parameters
|
||||||
asX = gcmd.get_float("X", currentPos[0])
|
asTarget = Coord(x=gcmd.get_float("X", currentPos[0]),
|
||||||
asY = gcmd.get_float("Y", currentPos[1])
|
y=gcmd.get_float("Y", currentPos[1]),
|
||||||
asZ = gcmd.get_float("Z", currentPos[2])
|
z=gcmd.get_float("Z", currentPos[2]),
|
||||||
|
e=None)
|
||||||
|
|
||||||
if gcmd.get_float("R", None) is not None:
|
if gcmd.get_float("R", None) is not None:
|
||||||
raise gcmd.error("G2/G3 does not support R moves")
|
raise gcmd.error("G2/G3 does not support R moves")
|
||||||
asI = gcmd.get_float("I", 0.)
|
|
||||||
asJ = gcmd.get_float("J", 0.)
|
# determine the plane coordinates and the helical axis
|
||||||
if not asI and not asJ:
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IJ') ]
|
||||||
raise gcmd.error("G2/G3 neither I nor J given")
|
axes = (X_AXIS, Y_AXIS, Z_AXIS)
|
||||||
|
if self.plane == ARC_PLANE_X_Z:
|
||||||
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('IK') ]
|
||||||
|
axes = (X_AXIS, Z_AXIS, Y_AXIS)
|
||||||
|
elif self.plane == ARC_PLANE_Y_Z:
|
||||||
|
asPlanar = [ gcmd.get_float(a, 0.) for i,a in enumerate('JK') ]
|
||||||
|
axes = (Y_AXIS, Z_AXIS, X_AXIS)
|
||||||
|
|
||||||
|
if not asPlanar[0] or not asPlanar[1]:
|
||||||
|
raise gcmd.error("G2/G3 requires IJ, IK or JK parameters")
|
||||||
|
|
||||||
asE = gcmd.get_float("E", None)
|
asE = gcmd.get_float("E", None)
|
||||||
asF = gcmd.get_float("F", None)
|
asF = gcmd.get_float("F", None)
|
||||||
|
|
||||||
# Build list of linear coordinates to move to
|
# Build list of linear coordinates to move
|
||||||
coords = self.planArc(currentPos, [asX, asY, asZ], [asI, asJ],
|
coords = self.planArc(currentPos, asTarget, asPlanar,
|
||||||
clockwise)
|
clockwise, *axes)
|
||||||
e_per_move = e_base = 0.
|
e_per_move = e_base = 0.
|
||||||
if asE is not None:
|
if asE is not None:
|
||||||
if gcodestatus['absolute_extrude']:
|
if gcodestatus['absolute_extrude']:
|
||||||
|
@ -74,37 +115,37 @@ class ArcSupport:
|
||||||
# The arc is approximated by generating many small linear segments.
|
# The arc is approximated by generating many small linear segments.
|
||||||
# The length of each segment is configured in MM_PER_ARC_SEGMENT
|
# The length of each segment is configured in MM_PER_ARC_SEGMENT
|
||||||
# Arcs smaller then this value, will be a Line only
|
# Arcs smaller then this value, will be a Line only
|
||||||
def planArc(self, currentPos, targetPos, offset, clockwise):
|
#
|
||||||
|
# alpha and beta axes are the current plane, helical axis is linear travel
|
||||||
|
def planArc(self, currentPos, targetPos, offset, clockwise,
|
||||||
|
alpha_axis, beta_axis, helical_axis):
|
||||||
# todo: sometimes produces full circles
|
# todo: sometimes produces full circles
|
||||||
X_AXIS = 0
|
|
||||||
Y_AXIS = 1
|
|
||||||
Z_AXIS = 2
|
|
||||||
|
|
||||||
# Radius vector from center to current location
|
# Radius vector from center to current location
|
||||||
r_P = -offset[0]
|
r_P = -offset[0]
|
||||||
r_Q = -offset[1]
|
r_Q = -offset[1]
|
||||||
|
|
||||||
# Determine angular travel
|
# Determine angular travel
|
||||||
center_P = currentPos[X_AXIS] - r_P
|
center_P = currentPos[alpha_axis] - r_P
|
||||||
center_Q = currentPos[Y_AXIS] - r_Q
|
center_Q = currentPos[beta_axis] - r_Q
|
||||||
rt_X = targetPos[X_AXIS] - center_P
|
rt_Alpha = targetPos[alpha_axis] - center_P
|
||||||
rt_Y = targetPos[Y_AXIS] - center_Q
|
rt_Beta = targetPos[beta_axis] - center_Q
|
||||||
angular_travel = math.atan2(r_P * rt_Y - r_Q * rt_X,
|
angular_travel = math.atan2(r_P * rt_Beta - r_Q * rt_Alpha,
|
||||||
r_P * rt_X + r_Q * rt_Y)
|
r_P * rt_Alpha + r_Q * rt_Beta)
|
||||||
if angular_travel < 0.:
|
if angular_travel < 0.:
|
||||||
angular_travel += 2. * math.pi
|
angular_travel += 2. * math.pi
|
||||||
if clockwise:
|
if clockwise:
|
||||||
angular_travel -= 2. * math.pi
|
angular_travel -= 2. * math.pi
|
||||||
|
|
||||||
if (angular_travel == 0.
|
if (angular_travel == 0.
|
||||||
and currentPos[X_AXIS] == targetPos[X_AXIS]
|
and currentPos[alpha_axis] == targetPos[alpha_axis]
|
||||||
and currentPos[Y_AXIS] == targetPos[Y_AXIS]):
|
and currentPos[beta_axis] == targetPos[beta_axis]):
|
||||||
# Make a circle if the angular rotation is 0 and the
|
# Make a circle if the angular rotation is 0 and the
|
||||||
# target is current position
|
# target is current position
|
||||||
angular_travel = 2. * math.pi
|
angular_travel = 2. * math.pi
|
||||||
|
|
||||||
# Determine number of segments
|
# Determine number of segments
|
||||||
linear_travel = targetPos[Z_AXIS] - currentPos[Z_AXIS]
|
linear_travel = targetPos[helical_axis] - currentPos[helical_axis]
|
||||||
radius = math.hypot(r_P, r_Q)
|
radius = math.hypot(r_P, r_Q)
|
||||||
flat_mm = radius * angular_travel
|
flat_mm = radius * angular_travel
|
||||||
if linear_travel:
|
if linear_travel:
|
||||||
|
@ -118,14 +159,18 @@ class ArcSupport:
|
||||||
linear_per_segment = linear_travel / segments
|
linear_per_segment = linear_travel / segments
|
||||||
coords = []
|
coords = []
|
||||||
for i in range(1, int(segments)):
|
for i in range(1, int(segments)):
|
||||||
dist_Z = i * linear_per_segment
|
dist_Helical = i * linear_per_segment
|
||||||
cos_Ti = math.cos(i * theta_per_segment)
|
cos_Ti = math.cos(i * theta_per_segment)
|
||||||
sin_Ti = math.sin(i * theta_per_segment)
|
sin_Ti = math.sin(i * theta_per_segment)
|
||||||
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
|
r_P = -offset[0] * cos_Ti + offset[1] * sin_Ti
|
||||||
r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti
|
r_Q = -offset[0] * sin_Ti - offset[1] * cos_Ti
|
||||||
|
|
||||||
c = [center_P + r_P, center_Q + r_Q, currentPos[Z_AXIS] + dist_Z]
|
# Coord doesn't support index assignment, create list
|
||||||
coords.append(c)
|
c = [None, None, None, None]
|
||||||
|
c[alpha_axis] = center_P + r_P
|
||||||
|
c[beta_axis] = center_Q + r_Q
|
||||||
|
c[helical_axis] = currentPos[helical_axis] + dist_Helical
|
||||||
|
coords.append(Coord(*c))
|
||||||
|
|
||||||
coords.append(targetPos)
|
coords.append(targetPos)
|
||||||
return coords
|
return coords
|
||||||
|
|
|
@ -2,10 +2,31 @@
|
||||||
DICTIONARY atmega2560.dict
|
DICTIONARY atmega2560.dict
|
||||||
CONFIG gcode_arcs.cfg
|
CONFIG gcode_arcs.cfg
|
||||||
|
|
||||||
# Home and move in arcs
|
# Home and move in XY arc
|
||||||
G28
|
G28
|
||||||
|
G90
|
||||||
G1 X20 Y20 Z20
|
G1 X20 Y20 Z20
|
||||||
G2 X125 Y32 Z20 E1 I10.5 J10.5
|
G2 X125 Y32 Z20 E1 I10.5 J10.5
|
||||||
|
|
||||||
# XY+Z arc move
|
# XY+Z arc move
|
||||||
G2 X20 Y20 Z10 E1 I10.5 J10.5
|
G2 X20 Y20 Z10 E1 I10.5 J10.5
|
||||||
|
|
||||||
|
# Home and move in XZ arc
|
||||||
|
G28
|
||||||
|
G90
|
||||||
|
G1 X20 Y20 Z20
|
||||||
|
G18
|
||||||
|
G2 X125 Y20 Z32 E1 I10.5 K10.5
|
||||||
|
|
||||||
|
# XZ+Y arc move
|
||||||
|
G2 X20 Y10 Z20 E1 I10.5 K10.5
|
||||||
|
|
||||||
|
# Home and move in YZ arc
|
||||||
|
G28
|
||||||
|
G90
|
||||||
|
G1 X20 Y20 Z20
|
||||||
|
G19
|
||||||
|
G2 X20 Y125 Z32 E1 J10.5 K10.5
|
||||||
|
|
||||||
|
# YZ+X arc move
|
||||||
|
G2 X10 Y20 Z20 E1 J10.5 K10.5
|
||||||
|
|
Loading…
Reference in New Issue