aboutsummaryrefslogtreecommitdiffstats
path: root/klippy/kinematics
diff options
context:
space:
mode:
Diffstat (limited to 'klippy/kinematics')
-rw-r--r--klippy/kinematics/cartesian.py89
-rw-r--r--klippy/kinematics/corexy.py54
-rw-r--r--klippy/kinematics/corexz.py54
-rw-r--r--klippy/kinematics/delta.py283
-rw-r--r--klippy/kinematics/deltesian.py150
-rw-r--r--klippy/kinematics/extruder.py302
-rw-r--r--klippy/kinematics/generic_cartesian.py227
-rw-r--r--klippy/kinematics/hybrid_corexy.py88
-rw-r--r--klippy/kinematics/hybrid_corexz.py88
-rw-r--r--klippy/kinematics/idex_modes.py248
-rw-r--r--klippy/kinematics/kinematic_stepper.py65
-rw-r--r--klippy/kinematics/none.py17
-rw-r--r--klippy/kinematics/polar.py66
-rw-r--r--klippy/kinematics/rotary_delta.py277
-rw-r--r--klippy/kinematics/winch.py31
15 files changed, 1289 insertions, 750 deletions
diff --git a/klippy/kinematics/cartesian.py b/klippy/kinematics/cartesian.py
index f9030275..4d21c6e1 100644
--- a/klippy/kinematics/cartesian.py
+++ b/klippy/kinematics/cartesian.py
@@ -7,59 +7,70 @@ import logging
import stepper
from . import idex_modes
+
class CartKinematics:
def __init__(self, toolhead, config):
self.printer = config.get_printer()
# Setup axis rails
self.dual_carriage_axis = None
self.dual_carriage_rails = []
- self.rails = [stepper.LookupMultiRail(config.getsection('stepper_' + n))
- for n in 'xyz']
- for rail, axis in zip(self.rails, 'xyz'):
- rail.setup_itersolve('cartesian_stepper_alloc', axis.encode())
+ self.rails = [
+ stepper.LookupMultiRail(config.getsection("stepper_" + n)) for n in "xyz"
+ ]
+ for rail, axis in zip(self.rails, "xyz"):
+ rail.setup_itersolve("cartesian_stepper_alloc", axis.encode())
ranges = [r.get_range() for r in self.rails]
- self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.)
- self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.)
+ self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.0)
+ self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.0)
self.dc_module = None
- if config.has_section('dual_carriage'):
- dc_config = config.getsection('dual_carriage')
- dc_axis = dc_config.getchoice('axis', ['x', 'y'])
- self.dual_carriage_axis = {'x': 0, 'y': 1}[dc_axis]
+ if config.has_section("dual_carriage"):
+ dc_config = config.getsection("dual_carriage")
+ dc_axis = dc_config.getchoice("axis", ["x", "y"])
+ self.dual_carriage_axis = {"x": 0, "y": 1}[dc_axis]
# setup second dual carriage rail
self.rails.append(stepper.LookupMultiRail(dc_config))
- self.rails[3].setup_itersolve('cartesian_stepper_alloc',
- dc_axis.encode())
+ self.rails[3].setup_itersolve("cartesian_stepper_alloc", dc_axis.encode())
self.dc_module = idex_modes.DualCarriages(
- self.printer, [self.rails[self.dual_carriage_axis]],
- [self.rails[3]], axes=[self.dual_carriage_axis],
- safe_dist=dc_config.getfloat(
- 'safe_distance', None, minval=0.))
+ self.printer,
+ [self.rails[self.dual_carriage_axis]],
+ [self.rails[3]],
+ axes=[self.dual_carriage_axis],
+ safe_dist=dc_config.getfloat("safe_distance", None, minval=0.0),
+ )
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
- self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
- above=0., maxval=max_velocity)
- self.max_z_accel = config.getfloat('max_z_accel', max_accel,
- above=0., maxval=max_accel)
+ self.max_z_velocity = config.getfloat(
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
+ self.max_z_accel = config.getfloat(
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
rails = self.rails
if self.dc_module:
- primary_rail = self.dc_module.get_primary_rail(
- self.dual_carriage_axis)
- rails = (rails[:self.dual_carriage_axis] +
- [primary_rail] + rails[self.dual_carriage_axis+1:])
+ primary_rail = self.dc_module.get_primary_rail(self.dual_carriage_axis)
+ rails = (
+ rails[: self.dual_carriage_axis]
+ + [primary_rail]
+ + rails[self.dual_carriage_axis + 1 :]
+ )
return [stepper_positions[rail.get_name()] for rail in rails]
+
def update_limits(self, i, range):
l, h = self.limits[i]
# Only update limits if this axis was already homed,
# otherwise leave in un-homed state.
if l <= h:
self.limits[i] = range
+
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
@@ -70,10 +81,12 @@ class CartKinematics:
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home_axis(self, homing_state, axis, rail):
# Determine movement
position_min, position_max = rail.get_range()
@@ -87,6 +100,7 @@ class CartKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def home(self, homing_state):
# Each axis is homed independently and in order
for axis in homing_state.get_axes():
@@ -94,20 +108,26 @@ class CartKinematics:
self.dc_module.home(homing_state, self.dual_carriage_axis)
else:
self.home_axis(homing_state, axis, self.rails[axis])
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -115,15 +135,16 @@ class CartKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return CartKinematics(toolhead, config)
diff --git a/klippy/kinematics/corexy.py b/klippy/kinematics/corexy.py
index d68f8854..a571b2f6 100644
--- a/klippy/kinematics/corexy.py
+++ b/klippy/kinematics/corexy.py
@@ -6,45 +6,54 @@
import logging, math
import stepper
+
class CoreXYKinematics:
def __init__(self, toolhead, config):
# Setup axis rails
- self.rails = [stepper.LookupMultiRail(config.getsection('stepper_' + n))
- for n in 'xyz']
+ self.rails = [
+ stepper.LookupMultiRail(config.getsection("stepper_" + n)) for n in "xyz"
+ ]
for s in self.rails[1].get_steppers():
self.rails[0].get_endstops()[0][0].add_stepper(s)
for s in self.rails[0].get_steppers():
self.rails[1].get_endstops()[0][0].add_stepper(s)
- self.rails[0].setup_itersolve('corexy_stepper_alloc', b'+')
- self.rails[1].setup_itersolve('corexy_stepper_alloc', b'-')
- self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
+ self.rails[0].setup_itersolve("corexy_stepper_alloc", b"+")
+ self.rails[1].setup_itersolve("corexy_stepper_alloc", b"-")
+ self.rails[2].setup_itersolve("cartesian_stepper_alloc", b"z")
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
self.max_z_accel = config.getfloat(
- 'max_z_accel', max_accel, above=0., maxval=max_accel)
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
ranges = [r.get_range() for r in self.rails]
- self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.)
- self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.)
+ self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.0)
+ self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.0)
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
pos = [stepper_positions[rail.get_name()] for rail in self.rails]
return [0.5 * (pos[0] + pos[1]), 0.5 * (pos[0] - pos[1]), pos[2]]
+
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
if "xyz"[i] in homing_axes:
self.limits[i] = rail.get_range()
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home(self, homing_state):
# Each axis is homed independently and in order
for axis in homing_state.get_axes():
@@ -61,20 +70,26 @@ class CoreXYKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -82,15 +97,16 @@ class CoreXYKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return CoreXYKinematics(toolhead, config)
diff --git a/klippy/kinematics/corexz.py b/klippy/kinematics/corexz.py
index 8d3e027c..b75e00c0 100644
--- a/klippy/kinematics/corexz.py
+++ b/klippy/kinematics/corexz.py
@@ -6,45 +6,54 @@
import logging, math
import stepper
+
class CoreXZKinematics:
def __init__(self, toolhead, config):
# Setup axis rails
- self.rails = [stepper.LookupMultiRail(config.getsection('stepper_' + n))
- for n in 'xyz']
+ self.rails = [
+ stepper.LookupMultiRail(config.getsection("stepper_" + n)) for n in "xyz"
+ ]
for s in self.rails[0].get_steppers():
self.rails[2].get_endstops()[0][0].add_stepper(s)
for s in self.rails[2].get_steppers():
self.rails[0].get_endstops()[0][0].add_stepper(s)
- self.rails[0].setup_itersolve('corexz_stepper_alloc', b'+')
- self.rails[1].setup_itersolve('cartesian_stepper_alloc', b'y')
- self.rails[2].setup_itersolve('corexz_stepper_alloc', b'-')
+ self.rails[0].setup_itersolve("corexz_stepper_alloc", b"+")
+ self.rails[1].setup_itersolve("cartesian_stepper_alloc", b"y")
+ self.rails[2].setup_itersolve("corexz_stepper_alloc", b"-")
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
self.max_z_accel = config.getfloat(
- 'max_z_accel', max_accel, above=0., maxval=max_accel)
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
ranges = [r.get_range() for r in self.rails]
- self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.)
- self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.)
+ self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.0)
+ self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.0)
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
pos = [stepper_positions[rail.get_name()] for rail in self.rails]
return [0.5 * (pos[0] + pos[2]), pos[1], 0.5 * (pos[0] - pos[2])]
+
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
if "xyz"[i] in homing_axes:
self.limits[i] = rail.get_range()
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home(self, homing_state):
# Each axis is homed independently and in order
for axis in homing_state.get_axes():
@@ -61,20 +70,26 @@ class CoreXZKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -82,15 +97,16 @@ class CoreXZKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return CoreXZKinematics(toolhead, config)
diff --git a/klippy/kinematics/delta.py b/klippy/kinematics/delta.py
index aa244a8f..508a3331 100644
--- a/klippy/kinematics/delta.py
+++ b/klippy/kinematics/delta.py
@@ -7,116 +7,148 @@ import math, logging
import stepper, mathutil
# Slow moves once the ratio of tower to XY movement exceeds SLOW_RATIO
-SLOW_RATIO = 3.
+SLOW_RATIO = 3.0
+
class DeltaKinematics:
def __init__(self, toolhead, config):
# Setup tower rails
- stepper_configs = [config.getsection('stepper_' + a) for a in 'abc']
- rail_a = stepper.LookupMultiRail(
- stepper_configs[0], need_position_minmax = False)
+ stepper_configs = [config.getsection("stepper_" + a) for a in "abc"]
+ rail_a = stepper.LookupMultiRail(stepper_configs[0], need_position_minmax=False)
a_endstop = rail_a.get_homing_info().position_endstop
rail_b = stepper.LookupMultiRail(
- stepper_configs[1], need_position_minmax = False,
- default_position_endstop=a_endstop)
+ stepper_configs[1],
+ need_position_minmax=False,
+ default_position_endstop=a_endstop,
+ )
rail_c = stepper.LookupMultiRail(
- stepper_configs[2], need_position_minmax = False,
- default_position_endstop=a_endstop)
+ stepper_configs[2],
+ need_position_minmax=False,
+ default_position_endstop=a_endstop,
+ )
self.rails = [rail_a, rail_b, rail_c]
# Setup max velocity
self.max_velocity, self.max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', self.max_velocity,
- above=0., maxval=self.max_velocity)
- self.max_z_accel = config.getfloat('max_z_accel', self.max_accel,
- above=0., maxval=self.max_accel)
+ "max_z_velocity", self.max_velocity, above=0.0, maxval=self.max_velocity
+ )
+ self.max_z_accel = config.getfloat(
+ "max_z_accel", self.max_accel, above=0.0, maxval=self.max_accel
+ )
# Read radius and arm lengths
- self.radius = radius = config.getfloat('delta_radius', above=0.)
- print_radius = config.getfloat('print_radius', radius, above=0.)
- arm_length_a = stepper_configs[0].getfloat('arm_length', above=radius)
+ self.radius = radius = config.getfloat("delta_radius", above=0.0)
+ print_radius = config.getfloat("print_radius", radius, above=0.0)
+ arm_length_a = stepper_configs[0].getfloat("arm_length", above=radius)
self.arm_lengths = arm_lengths = [
- sconfig.getfloat('arm_length', arm_length_a, above=radius)
- for sconfig in stepper_configs]
+ sconfig.getfloat("arm_length", arm_length_a, above=radius)
+ for sconfig in stepper_configs
+ ]
self.arm2 = [arm**2 for arm in arm_lengths]
- self.abs_endstops = [(rail.get_homing_info().position_endstop
- + math.sqrt(arm2 - radius**2))
- for rail, arm2 in zip(self.rails, self.arm2)]
+ self.abs_endstops = [
+ (rail.get_homing_info().position_endstop + math.sqrt(arm2 - radius**2))
+ for rail, arm2 in zip(self.rails, self.arm2)
+ ]
# Determine tower locations in cartesian space
- self.angles = [sconfig.getfloat('angle', angle)
- for sconfig, angle in zip(stepper_configs,
- [210., 330., 90.])]
- self.towers = [(math.cos(math.radians(angle)) * radius,
- math.sin(math.radians(angle)) * radius)
- for angle in self.angles]
+ self.angles = [
+ sconfig.getfloat("angle", angle)
+ for sconfig, angle in zip(stepper_configs, [210.0, 330.0, 90.0])
+ ]
+ self.towers = [
+ (
+ math.cos(math.radians(angle)) * radius,
+ math.sin(math.radians(angle)) * radius,
+ )
+ for angle in self.angles
+ ]
for r, a, t in zip(self.rails, self.arm2, self.towers):
- r.setup_itersolve('delta_stepper_alloc', a, t[0], t[1])
+ r.setup_itersolve("delta_stepper_alloc", a, t[0], t[1])
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
self.need_home = True
- self.limit_xy2 = -1.
- self.home_position = tuple(
- self._actuator_to_cartesian(self.abs_endstops))
- self.max_z = min([rail.get_homing_info().position_endstop
- for rail in self.rails])
- self.min_z = config.getfloat('minimum_z_position', 0, maxval=self.max_z)
- self.limit_z = min([ep - arm
- for ep, arm in zip(self.abs_endstops, arm_lengths)])
+ self.limit_xy2 = -1.0
+ self.home_position = tuple(self._actuator_to_cartesian(self.abs_endstops))
+ self.max_z = min(
+ [rail.get_homing_info().position_endstop for rail in self.rails]
+ )
+ self.min_z = config.getfloat("minimum_z_position", 0, maxval=self.max_z)
+ self.limit_z = min(
+ [ep - arm for ep, arm in zip(self.abs_endstops, arm_lengths)]
+ )
self.min_arm_length = min_arm_length = min(arm_lengths)
self.min_arm2 = min_arm_length**2
logging.info(
"Delta max build height %.2fmm (radius tapered above %.2fmm)"
- % (self.max_z, self.limit_z))
+ % (self.max_z, self.limit_z)
+ )
# Find the point where an XY move could result in excessive
# tower movement
- half_min_step_dist = min([r.get_steppers()[0].get_step_dist()
- for r in self.rails]) * .5
+ half_min_step_dist = (
+ min([r.get_steppers()[0].get_step_dist() for r in self.rails]) * 0.5
+ )
min_arm_length = min(arm_lengths)
+
def ratio_to_xy(ratio):
- return (ratio * math.sqrt(min_arm_length**2 / (ratio**2 + 1.)
- - half_min_step_dist**2)
- + half_min_step_dist - radius)
- self.slow_xy2 = ratio_to_xy(SLOW_RATIO)**2
- self.very_slow_xy2 = ratio_to_xy(2. * SLOW_RATIO)**2
- self.max_xy2 = min(print_radius, min_arm_length - radius,
- ratio_to_xy(4. * SLOW_RATIO))**2
+ return (
+ ratio
+ * math.sqrt(
+ min_arm_length**2 / (ratio**2 + 1.0) - half_min_step_dist**2
+ )
+ + half_min_step_dist
+ - radius
+ )
+
+ self.slow_xy2 = ratio_to_xy(SLOW_RATIO) ** 2
+ self.very_slow_xy2 = ratio_to_xy(2.0 * SLOW_RATIO) ** 2
+ self.max_xy2 = (
+ min(print_radius, min_arm_length - radius, ratio_to_xy(4.0 * SLOW_RATIO))
+ ** 2
+ )
max_xy = math.sqrt(self.max_xy2)
- logging.info("Delta max build radius %.2fmm (moves slowed past %.2fmm"
- " and %.2fmm)"
- % (max_xy, math.sqrt(self.slow_xy2),
- math.sqrt(self.very_slow_xy2)))
- self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
- self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.)
- self.set_position([0., 0., 0.], "")
+ logging.info(
+ "Delta max build radius %.2fmm (moves slowed past %.2fmm"
+ " and %.2fmm)"
+ % (max_xy, math.sqrt(self.slow_xy2), math.sqrt(self.very_slow_xy2))
+ )
+ self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.0)
+ self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.0)
+ self.set_position([0.0, 0.0, 0.0], "")
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def _actuator_to_cartesian(self, spos):
sphere_coords = [(t[0], t[1], sp) for t, sp in zip(self.towers, spos)]
return mathutil.trilateration(sphere_coords, self.arm2)
+
def calc_position(self, stepper_positions):
spos = [stepper_positions[rail.get_name()] for rail in self.rails]
return self._actuator_to_cartesian(spos)
+
def set_position(self, newpos, homing_axes):
for rail in self.rails:
rail.set_position(newpos)
- self.limit_xy2 = -1.
+ self.limit_xy2 = -1.0
if homing_axes == "xyz":
self.need_home = False
+
def clear_homing_state(self, clear_axes):
# Clearing homing state for each axis individually is not implemented
if clear_axes:
self.limit_xy2 = -1
self.need_home = True
+
def home(self, homing_state):
# All axes are homed simultaneously
homing_state.set_axes([0, 1, 2])
forcepos = list(self.home_position)
- forcepos[2] = -1.5 * math.sqrt(max(self.arm2)-self.max_xy2)
+ forcepos[2] = -1.5 * math.sqrt(max(self.arm2) - self.max_xy2)
homing_state.home_rails(self.rails, forcepos, self.home_position)
+
def check_move(self, move):
end_pos = move.end_pos
- end_xy2 = end_pos[0]**2 + end_pos[1]**2
+ end_xy2 = end_pos[0] ** 2 + end_pos[1] ** 2
if end_xy2 <= self.limit_xy2 and not move.axes_d[2]:
# Normal XY move
return
@@ -127,44 +159,48 @@ class DeltaKinematics:
if end_z > self.limit_z:
above_z_limit = end_z - self.limit_z
allowed_radius = self.radius - math.sqrt(
- self.min_arm2 - (self.min_arm_length - above_z_limit)**2
+ self.min_arm2 - (self.min_arm_length - above_z_limit) ** 2
)
limit_xy2 = min(limit_xy2, allowed_radius**2)
if end_xy2 > limit_xy2 or end_z > self.max_z or end_z < self.min_z:
# Move out of range - verify not a homing move
- if (end_pos[:2] != self.home_position[:2]
- or end_z < self.min_z or end_z > self.home_position[2]):
+ if (
+ end_pos[:2] != self.home_position[:2]
+ or end_z < self.min_z
+ or end_z > self.home_position[2]
+ ):
raise move.move_error()
- limit_xy2 = -1.
+ limit_xy2 = -1.0
if move.axes_d[2]:
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(self.max_z_velocity * z_ratio,
- self.max_z_accel * z_ratio)
- limit_xy2 = -1.
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ limit_xy2 = -1.0
# Limit the speed/accel of this move if is is at the extreme
# end of the build envelope
- extreme_xy2 = max(end_xy2, move.start_pos[0]**2 + move.start_pos[1]**2)
+ extreme_xy2 = max(end_xy2, move.start_pos[0] ** 2 + move.start_pos[1] ** 2)
if extreme_xy2 > self.slow_xy2:
r = 0.5
if extreme_xy2 > self.very_slow_xy2:
r = 0.25
move.limit_speed(self.max_velocity * r, self.max_accel * r)
- limit_xy2 = -1.
+ limit_xy2 = -1.0
self.limit_xy2 = min(limit_xy2, self.slow_xy2)
+
def get_status(self, eventtime):
return {
- 'homed_axes': '' if self.need_home else 'xyz',
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
- 'cone_start_z': self.limit_z,
+ "homed_axes": "" if self.need_home else "xyz",
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
+ "cone_start_z": self.limit_z,
}
+
def get_calibration(self):
- endstops = [rail.get_homing_info().position_endstop
- for rail in self.rails]
- stepdists = [rail.get_steppers()[0].get_step_dist()
- for rail in self.rails]
- return DeltaCalibration(self.radius, self.angles, self.arm_lengths,
- endstops, stepdists)
+ endstops = [rail.get_homing_info().position_endstop for rail in self.rails]
+ stepdists = [rail.get_steppers()[0].get_step_dist() for rail in self.rails]
+ return DeltaCalibration(
+ self.radius, self.angles, self.arm_lengths, endstops, stepdists
+ )
+
# Delta parameter calibration for DELTA_CALIBRATE tool
class DeltaCalibration:
@@ -176,67 +212,94 @@ class DeltaCalibration:
self.stepdists = stepdists
# Calculate the XY cartesian coordinates of the delta towers
radian_angles = [math.radians(a) for a in angles]
- self.towers = [(math.cos(a) * radius, math.sin(a) * radius)
- for a in radian_angles]
+ self.towers = [
+ (math.cos(a) * radius, math.sin(a) * radius) for a in radian_angles
+ ]
# Calculate the absolute Z height of each tower endstop
radius2 = radius**2
- self.abs_endstops = [e + math.sqrt(a**2 - radius2)
- for e, a in zip(endstops, arms)]
+ self.abs_endstops = [
+ e + math.sqrt(a**2 - radius2) for e, a in zip(endstops, arms)
+ ]
+
def coordinate_descent_params(self, is_extended):
# Determine adjustment parameters (for use with coordinate_descent)
- adj_params = ('radius', 'angle_a', 'angle_b',
- 'endstop_a', 'endstop_b', 'endstop_c')
+ adj_params = (
+ "radius",
+ "angle_a",
+ "angle_b",
+ "endstop_a",
+ "endstop_b",
+ "endstop_c",
+ )
if is_extended:
- adj_params += ('arm_a', 'arm_b', 'arm_c')
- params = { 'radius': self.radius }
- for i, axis in enumerate('abc'):
- params['angle_'+axis] = self.angles[i]
- params['arm_'+axis] = self.arms[i]
- params['endstop_'+axis] = self.endstops[i]
- params['stepdist_'+axis] = self.stepdists[i]
+ adj_params += ("arm_a", "arm_b", "arm_c")
+ params = {"radius": self.radius}
+ for i, axis in enumerate("abc"):
+ params["angle_" + axis] = self.angles[i]
+ params["arm_" + axis] = self.arms[i]
+ params["endstop_" + axis] = self.endstops[i]
+ params["stepdist_" + axis] = self.stepdists[i]
return adj_params, params
+
def new_calibration(self, params):
# Create a new calibration object from coordinate_descent params
- radius = params['radius']
- angles = [params['angle_'+a] for a in 'abc']
- arms = [params['arm_'+a] for a in 'abc']
- endstops = [params['endstop_'+a] for a in 'abc']
- stepdists = [params['stepdist_'+a] for a in 'abc']
+ radius = params["radius"]
+ angles = [params["angle_" + a] for a in "abc"]
+ arms = [params["arm_" + a] for a in "abc"]
+ endstops = [params["endstop_" + a] for a in "abc"]
+ stepdists = [params["stepdist_" + a] for a in "abc"]
return DeltaCalibration(radius, angles, arms, endstops, stepdists)
+
def get_position_from_stable(self, stable_position):
# Return cartesian coordinates for the given stable_position
sphere_coords = [
(t[0], t[1], es - sp * sd)
- for sd, t, es, sp in zip(self.stepdists, self.towers,
- self.abs_endstops, stable_position) ]
+ for sd, t, es, sp in zip(
+ self.stepdists, self.towers, self.abs_endstops, stable_position
+ )
+ ]
return mathutil.trilateration(sphere_coords, [a**2 for a in self.arms])
+
def calc_stable_position(self, coord):
# Return a stable_position from a cartesian coordinate
steppos = [
- math.sqrt(a**2 - (t[0]-coord[0])**2 - (t[1]-coord[1])**2) + coord[2]
- for t, a in zip(self.towers, self.arms) ]
- return [(ep - sp) / sd
- for sd, ep, sp in zip(self.stepdists,
- self.abs_endstops, steppos)]
+ math.sqrt(a**2 - (t[0] - coord[0]) ** 2 - (t[1] - coord[1]) ** 2) + coord[2]
+ for t, a in zip(self.towers, self.arms)
+ ]
+ return [
+ (ep - sp) / sd
+ for sd, ep, sp in zip(self.stepdists, self.abs_endstops, steppos)
+ ]
+
def save_state(self, configfile):
# Save the current parameters (for use with SAVE_CONFIG)
- configfile.set('printer', 'delta_radius', "%.6f" % (self.radius,))
- for i, axis in enumerate('abc'):
- configfile.set('stepper_'+axis, 'angle', "%.6f" % (self.angles[i],))
- configfile.set('stepper_'+axis, 'arm_length',
- "%.6f" % (self.arms[i],))
- configfile.set('stepper_'+axis, 'position_endstop',
- "%.6f" % (self.endstops[i],))
+ configfile.set("printer", "delta_radius", "%.6f" % (self.radius,))
+ for i, axis in enumerate("abc"):
+ configfile.set("stepper_" + axis, "angle", "%.6f" % (self.angles[i],))
+ configfile.set("stepper_" + axis, "arm_length", "%.6f" % (self.arms[i],))
+ configfile.set(
+ "stepper_" + axis, "position_endstop", "%.6f" % (self.endstops[i],)
+ )
gcode = configfile.get_printer().lookup_object("gcode")
gcode.respond_info(
"stepper_a: position_endstop: %.6f angle: %.6f arm_length: %.6f\n"
"stepper_b: position_endstop: %.6f angle: %.6f arm_length: %.6f\n"
"stepper_c: position_endstop: %.6f angle: %.6f arm_length: %.6f\n"
"delta_radius: %.6f"
- % (self.endstops[0], self.angles[0], self.arms[0],
- self.endstops[1], self.angles[1], self.arms[1],
- self.endstops[2], self.angles[2], self.arms[2],
- self.radius))
+ % (
+ self.endstops[0],
+ self.angles[0],
+ self.arms[0],
+ self.endstops[1],
+ self.angles[1],
+ self.arms[1],
+ self.endstops[2],
+ self.angles[2],
+ self.arms[2],
+ self.radius,
+ )
+ )
+
def load_kinematics(toolhead, config):
return DeltaKinematics(toolhead, config)
diff --git a/klippy/kinematics/deltesian.py b/klippy/kinematics/deltesian.py
index 54b013a5..c2aec720 100644
--- a/klippy/kinematics/deltesian.py
+++ b/klippy/kinematics/deltesian.py
@@ -7,50 +7,51 @@ import math, logging
import stepper
# Slow moves once the ratio of tower to XY movement exceeds SLOW_RATIO
-SLOW_RATIO = 3.
+SLOW_RATIO = 3.0
# Minimum angle with the horizontal for the arm to not exceed - in degrees
-MIN_ANGLE = 5.
+MIN_ANGLE = 5.0
+
class DeltesianKinematics:
def __init__(self, toolhead, config):
self.rails = [None] * 3
- stepper_configs = [config.getsection('stepper_' + s)
- for s in ['left', 'right', 'y']]
+ stepper_configs = [
+ config.getsection("stepper_" + s) for s in ["left", "right", "y"]
+ ]
self.rails[0] = stepper.LookupRail(
- stepper_configs[0], need_position_minmax = False)
+ stepper_configs[0], need_position_minmax=False
+ )
def_pos_es = self.rails[0].get_homing_info().position_endstop
self.rails[1] = stepper.LookupRail(
- stepper_configs[1], need_position_minmax = False,
- default_position_endstop = def_pos_es)
+ stepper_configs[1],
+ need_position_minmax=False,
+ default_position_endstop=def_pos_es,
+ )
self.rails[2] = stepper.LookupMultiRail(stepper_configs[2])
arm_x = self.arm_x = [None] * 2
- arm_x[0] = stepper_configs[0].getfloat('arm_x_length', above=0.)
- arm_x[1] = stepper_configs[1].getfloat('arm_x_length', arm_x[0],
- above=0.)
+ arm_x[0] = stepper_configs[0].getfloat("arm_x_length", above=0.0)
+ arm_x[1] = stepper_configs[1].getfloat("arm_x_length", arm_x[0], above=0.0)
arm = [None] * 2
- arm[0] = stepper_configs[0].getfloat('arm_length', above=arm_x[0])
- arm[1] = stepper_configs[1].getfloat('arm_length', arm[0],
- above=arm_x[1])
+ arm[0] = stepper_configs[0].getfloat("arm_length", above=arm_x[0])
+ arm[1] = stepper_configs[1].getfloat("arm_length", arm[0], above=arm_x[1])
arm2 = self.arm2 = [a**2 for a in arm]
- self.rails[0].setup_itersolve(
- 'deltesian_stepper_alloc', arm2[0], -arm_x[0])
- self.rails[1].setup_itersolve(
- 'deltesian_stepper_alloc', arm2[1], arm_x[1])
- self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'y')
+ self.rails[0].setup_itersolve("deltesian_stepper_alloc", arm2[0], -arm_x[0])
+ self.rails[1].setup_itersolve("deltesian_stepper_alloc", arm2[1], arm_x[1])
+ self.rails[2].setup_itersolve("cartesian_stepper_alloc", b"y")
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
self.limits = [(1.0, -1.0)] * 3
# X axis limits
- min_angle = config.getfloat('min_angle', MIN_ANGLE,
- minval=0., maxval=90.)
+ min_angle = config.getfloat("min_angle", MIN_ANGLE, minval=0.0, maxval=90.0)
cos_angle = math.cos(math.radians(min_angle))
- x_kin_min = math.ceil( -min(arm_x[0], cos_angle * arm[1] - arm_x[1]))
- x_kin_max = math.floor( min(arm_x[1], cos_angle * arm[0] - arm_x[0]))
+ x_kin_min = math.ceil(-min(arm_x[0], cos_angle * arm[1] - arm_x[1]))
+ x_kin_max = math.floor(min(arm_x[1], cos_angle * arm[0] - arm_x[0]))
x_kin_range = min(x_kin_max - x_kin_min, x_kin_max * 2, -x_kin_min * 2)
- print_width = config.getfloat('print_width', None, minval=0.,
- maxval=x_kin_range)
+ print_width = config.getfloat(
+ "print_width", None, minval=0.0, maxval=x_kin_range
+ )
if print_width:
self.limits[0] = (-print_width * 0.5, print_width * 0.5)
else:
@@ -59,37 +60,56 @@ class DeltesianKinematics:
self.limits[1] = self.rails[2].get_range()
# Z axis limits
pmax = [r.get_homing_info().position_endstop for r in self.rails[:2]]
- self._abs_endstop = [p + math.sqrt(a2 - ax**2) for p, a2, ax
- in zip( pmax, arm2, arm_x )]
+ self._abs_endstop = [
+ p + math.sqrt(a2 - ax**2) for p, a2, ax in zip(pmax, arm2, arm_x)
+ ]
self.home_z = self._actuator_to_cartesian(self._abs_endstop)[1]
z_max = min([self._pillars_z_max(x) for x in self.limits[0]])
- z_min = config.getfloat('minimum_z_position', 0, maxval=z_max)
+ z_min = config.getfloat("minimum_z_position", 0, maxval=z_max)
self.limits[2] = (z_min, z_max)
# Limit the speed/accel if is is at the extreme values of the x axis
- slow_ratio = config.getfloat('slow_ratio', SLOW_RATIO, minval=0.)
+ slow_ratio = config.getfloat("slow_ratio", SLOW_RATIO, minval=0.0)
self.slow_x2 = self.very_slow_x2 = None
- if slow_ratio > 0.:
- sr2 = slow_ratio ** 2
- self.slow_x2 = min([math.sqrt((sr2 * a2) / (sr2 + 1))
- - axl for a2, axl in zip(arm2, arm_x)]) ** 2
- self.very_slow_x2 = min([math.sqrt((2 * sr2 * a2) / (2 * sr2 + 1))
- - axl for a2, axl in zip(arm2, arm_x)]) ** 2
- logging.info("Deltesian kinematics: moves slowed past %.2fmm"
- " and %.2fmm"
- % (math.sqrt(self.slow_x2),
- math.sqrt(self.very_slow_x2)))
+ if slow_ratio > 0.0:
+ sr2 = slow_ratio**2
+ self.slow_x2 = (
+ min(
+ [
+ math.sqrt((sr2 * a2) / (sr2 + 1)) - axl
+ for a2, axl in zip(arm2, arm_x)
+ ]
+ )
+ ** 2
+ )
+ self.very_slow_x2 = (
+ min(
+ [
+ math.sqrt((2 * sr2 * a2) / (2 * sr2 + 1)) - axl
+ for a2, axl in zip(arm2, arm_x)
+ ]
+ )
+ ** 2
+ )
+ logging.info(
+ "Deltesian kinematics: moves slowed past %.2fmm"
+ " and %.2fmm" % (math.sqrt(self.slow_x2), math.sqrt(self.very_slow_x2))
+ )
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
- self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
- above=0., maxval=max_velocity)
- self.max_z_accel = config.getfloat('max_z_accel', max_accel,
- above=0., maxval=max_accel)
- self.axes_min = toolhead.Coord(*[l[0] for l in self.limits], e=0.)
- self.axes_max = toolhead.Coord(*[l[1] for l in self.limits], e=0.)
+ self.max_z_velocity = config.getfloat(
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
+ self.max_z_accel = config.getfloat(
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
+ self.axes_min = toolhead.Coord(*[l[0] for l in self.limits], e=0.0)
+ self.axes_max = toolhead.Coord(*[l[1] for l in self.limits], e=0.0)
self.homed_axis = [False] * 3
- self.set_position([0., 0., 0.], "")
+ self.set_position([0.0, 0.0, 0.0], "")
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def _actuator_to_cartesian(self, sp):
arm_x, arm2 = self.arm_x, self.arm2
dx, dz = sum(arm_x), sp[1] - sp[0]
@@ -101,38 +121,48 @@ class DeltesianKinematics:
x = xt * dx / pivots + zt * dz / pivots - arm_x[0]
z = xt * dz / pivots - zt * dx / pivots + sp[0]
return [x, z]
+
def _pillars_z_max(self, x):
arm_x, arm2 = self.arm_x, self.arm2
- dz = (math.sqrt(arm2[0] - (arm_x[0] + x)**2),
- math.sqrt(arm2[1] - (arm_x[1] - x)**2))
+ dz = (
+ math.sqrt(arm2[0] - (arm_x[0] + x) ** 2),
+ math.sqrt(arm2[1] - (arm_x[1] - x) ** 2),
+ )
return min([o - z for o, z in zip(self._abs_endstop, dz)])
+
def calc_position(self, stepper_positions):
sp = [stepper_positions[rail.get_name()] for rail in self.rails]
x, z = self._actuator_to_cartesian(sp[:2])
return [x, sp[2], z]
+
def set_position(self, newpos, homing_axes):
for rail in self.rails:
rail.set_position(newpos)
for axis_name in homing_axes:
axis = "xyz".index(axis_name)
self.homed_axis[axis] = True
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.homed_axis[axis] = False
+
def home(self, homing_state):
homing_axes = homing_state.get_axes()
home_xz = 0 in homing_axes or 2 in homing_axes
home_y = 1 in homing_axes
- forceaxes = ([0, 1, 2] if (home_xz and home_y) else
- [0, 2] if home_xz else [1] if home_y else [])
+ forceaxes = (
+ [0, 1, 2]
+ if (home_xz and home_y)
+ else [0, 2] if home_xz else [1] if home_y else []
+ )
homing_state.set_axes(forceaxes)
homepos = [None] * 4
if home_xz:
homing_state.set_axes([0, 1, 2] if home_y else [0, 2])
- homepos[0], homepos[2] = 0., self.home_z
+ homepos[0], homepos[2] = 0.0, self.home_z
forcepos = list(homepos)
- dz2 = [(a2 - ax ** 2) for a2, ax in zip(self.arm2, self.arm_x)]
+ dz2 = [(a2 - ax**2) for a2, ax in zip(self.arm2, self.arm_x)]
forcepos[2] = -1.5 * math.sqrt(max(dz2))
homing_state.home_rails(self.rails[:2], forcepos, homepos)
if home_y:
@@ -145,11 +175,12 @@ class DeltesianKinematics:
else:
forcepos[1] += 1.5 * (position_max - hi.position_endstop)
homing_state.home_rails([self.rails[2]], forcepos, homepos)
+
def check_move(self, move):
limits = list(map(list, self.limits))
spos, epos = move.start_pos, move.end_pos
homing_move = False
- if epos[0] == 0. and epos[2] == self.home_z and not move.axes_d[1]:
+ if epos[0] == 0.0 and epos[2] == self.home_z and not move.axes_d[1]:
# Identify a homing move
homing_move = True
elif epos[2] > limits[2][1]:
@@ -164,22 +195,23 @@ class DeltesianKinematics:
raise move.move_error()
if move.axes_d[2]:
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
# Limit the speed/accel if is is at the extreme values of the x axis
if move.axes_d[0] and self.slow_x2 and self.very_slow_x2:
move_x2 = max(spos[0] ** 2, epos[0] ** 2)
if move_x2 > self.very_slow_x2:
- move.limit_speed(self.max_velocity *0.25, self.max_accel *0.25)
+ move.limit_speed(self.max_velocity * 0.25, self.max_accel * 0.25)
elif move_x2 > self.slow_x2:
- move.limit_speed(self.max_velocity *0.50, self.max_accel *0.50)
+ move.limit_speed(self.max_velocity * 0.50, self.max_accel * 0.50)
+
def get_status(self, eventtime):
axes = [a for a, b in zip("xyz", self.homed_axis) if b]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return DeltesianKinematics(toolhead, config)
diff --git a/klippy/kinematics/extruder.py b/klippy/kinematics/extruder.py
index 58ccdbec..25ac394d 100644
--- a/klippy/kinematics/extruder.py
+++ b/klippy/kinematics/extruder.py
@@ -6,51 +6,75 @@
import math, logging
import stepper, chelper
+
class ExtruderStepper:
def __init__(self, config):
self.printer = config.get_printer()
self.name = config.get_name().split()[-1]
- self.pressure_advance = self.pressure_advance_smooth_time = 0.
- self.config_pa = config.getfloat('pressure_advance', 0., minval=0.)
+ self.pressure_advance = self.pressure_advance_smooth_time = 0.0
+ self.config_pa = config.getfloat("pressure_advance", 0.0, minval=0.0)
self.config_smooth_time = config.getfloat(
- 'pressure_advance_smooth_time', 0.040, above=0., maxval=.200)
+ "pressure_advance_smooth_time", 0.040, above=0.0, maxval=0.200
+ )
# Setup stepper
self.stepper = stepper.PrinterStepper(config)
ffi_main, ffi_lib = chelper.get_ffi()
- self.sk_extruder = ffi_main.gc(ffi_lib.extruder_stepper_alloc(),
- ffi_lib.extruder_stepper_free)
+ self.sk_extruder = ffi_main.gc(
+ ffi_lib.extruder_stepper_alloc(), ffi_lib.extruder_stepper_free
+ )
self.stepper.set_stepper_kinematics(self.sk_extruder)
self.motion_queue = None
# Register commands
- self.printer.register_event_handler("klippy:connect",
- self._handle_connect)
- gcode = self.printer.lookup_object('gcode')
- if self.name == 'extruder':
- gcode.register_mux_command("SET_PRESSURE_ADVANCE", "EXTRUDER", None,
- self.cmd_default_SET_PRESSURE_ADVANCE,
- desc=self.cmd_SET_PRESSURE_ADVANCE_help)
- gcode.register_mux_command("SET_PRESSURE_ADVANCE", "EXTRUDER",
- self.name, self.cmd_SET_PRESSURE_ADVANCE,
- desc=self.cmd_SET_PRESSURE_ADVANCE_help)
- gcode.register_mux_command("SET_EXTRUDER_ROTATION_DISTANCE", "EXTRUDER",
- self.name, self.cmd_SET_E_ROTATION_DISTANCE,
- desc=self.cmd_SET_E_ROTATION_DISTANCE_help)
- gcode.register_mux_command("SYNC_EXTRUDER_MOTION", "EXTRUDER",
- self.name, self.cmd_SYNC_EXTRUDER_MOTION,
- desc=self.cmd_SYNC_EXTRUDER_MOTION_help)
+ self.printer.register_event_handler("klippy:connect", self._handle_connect)
+ gcode = self.printer.lookup_object("gcode")
+ if self.name == "extruder":
+ gcode.register_mux_command(
+ "SET_PRESSURE_ADVANCE",
+ "EXTRUDER",
+ None,
+ self.cmd_default_SET_PRESSURE_ADVANCE,
+ desc=self.cmd_SET_PRESSURE_ADVANCE_help,
+ )
+ gcode.register_mux_command(
+ "SET_PRESSURE_ADVANCE",
+ "EXTRUDER",
+ self.name,
+ self.cmd_SET_PRESSURE_ADVANCE,
+ desc=self.cmd_SET_PRESSURE_ADVANCE_help,
+ )
+ gcode.register_mux_command(
+ "SET_EXTRUDER_ROTATION_DISTANCE",
+ "EXTRUDER",
+ self.name,
+ self.cmd_SET_E_ROTATION_DISTANCE,
+ desc=self.cmd_SET_E_ROTATION_DISTANCE_help,
+ )
+ gcode.register_mux_command(
+ "SYNC_EXTRUDER_MOTION",
+ "EXTRUDER",
+ self.name,
+ self.cmd_SYNC_EXTRUDER_MOTION,
+ desc=self.cmd_SYNC_EXTRUDER_MOTION_help,
+ )
+
def _handle_connect(self):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.register_step_generator(self.stepper.generate_steps)
self._set_pressure_advance(self.config_pa, self.config_smooth_time)
+
def get_status(self, eventtime):
- return {'pressure_advance': self.pressure_advance,
- 'smooth_time': self.pressure_advance_smooth_time,
- 'motion_queue': self.motion_queue}
+ return {
+ "pressure_advance": self.pressure_advance,
+ "smooth_time": self.pressure_advance_smooth_time,
+ "motion_queue": self.motion_queue,
+ }
+
def find_past_position(self, print_time):
mcu_pos = self.stepper.get_past_mcu_position(print_time)
return self.stepper.mcu_to_commanded_position(mcu_pos)
+
def sync_to_extruder(self, extruder_name):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
if not extruder_name:
self.stepper.set_trapq(None)
@@ -58,62 +82,72 @@ class ExtruderStepper:
return
extruder = self.printer.lookup_object(extruder_name, None)
if extruder is None or not isinstance(extruder, PrinterExtruder):
- raise self.printer.command_error("'%s' is not a valid extruder."
- % (extruder_name,))
- self.stepper.set_position([extruder.last_position, 0., 0.])
+ raise self.printer.command_error(
+ "'%s' is not a valid extruder." % (extruder_name,)
+ )
+ self.stepper.set_position([extruder.last_position, 0.0, 0.0])
self.stepper.set_trapq(extruder.get_trapq())
self.motion_queue = extruder_name
+
def _set_pressure_advance(self, pressure_advance, smooth_time):
old_smooth_time = self.pressure_advance_smooth_time
if not self.pressure_advance:
- old_smooth_time = 0.
+ old_smooth_time = 0.0
new_smooth_time = smooth_time
if not pressure_advance:
- new_smooth_time = 0.
+ new_smooth_time = 0.0
toolhead = self.printer.lookup_object("toolhead")
if new_smooth_time != old_smooth_time:
toolhead.note_step_generation_scan_time(
- new_smooth_time * .5, old_delay=old_smooth_time * .5)
+ new_smooth_time * 0.5, old_delay=old_smooth_time * 0.5
+ )
ffi_main, ffi_lib = chelper.get_ffi()
espa = ffi_lib.extruder_set_pressure_advance
toolhead.register_lookahead_callback(
- lambda print_time: espa(self.sk_extruder, print_time,
- pressure_advance, new_smooth_time))
+ lambda print_time: espa(
+ self.sk_extruder, print_time, pressure_advance, new_smooth_time
+ )
+ )
self.pressure_advance = pressure_advance
self.pressure_advance_smooth_time = smooth_time
+
cmd_SET_PRESSURE_ADVANCE_help = "Set pressure advance parameters"
+
def cmd_default_SET_PRESSURE_ADVANCE(self, gcmd):
- extruder = self.printer.lookup_object('toolhead').get_extruder()
+ extruder = self.printer.lookup_object("toolhead").get_extruder()
if extruder.extruder_stepper is None:
raise gcmd.error("Active extruder does not have a stepper")
strapq = extruder.extruder_stepper.stepper.get_trapq()
if strapq is not extruder.get_trapq():
raise gcmd.error("Unable to infer active extruder stepper")
extruder.extruder_stepper.cmd_SET_PRESSURE_ADVANCE(gcmd)
+
def cmd_SET_PRESSURE_ADVANCE(self, gcmd):
- pressure_advance = gcmd.get_float('ADVANCE', self.pressure_advance,
- minval=0.)
- smooth_time = gcmd.get_float('SMOOTH_TIME',
- self.pressure_advance_smooth_time,
- minval=0., maxval=.200)
+ pressure_advance = gcmd.get_float("ADVANCE", self.pressure_advance, minval=0.0)
+ smooth_time = gcmd.get_float(
+ "SMOOTH_TIME", self.pressure_advance_smooth_time, minval=0.0, maxval=0.200
+ )
self._set_pressure_advance(pressure_advance, smooth_time)
- msg = ("pressure_advance: %.6f\n"
- "pressure_advance_smooth_time: %.6f"
- % (pressure_advance, smooth_time))
+ msg = "pressure_advance: %.6f\n" "pressure_advance_smooth_time: %.6f" % (
+ pressure_advance,
+ smooth_time,
+ )
self.printer.set_rollover_info(self.name, "%s: %s" % (self.name, msg))
gcmd.respond_info(msg, log=False)
+
cmd_SET_E_ROTATION_DISTANCE_help = "Set extruder rotation distance"
+
def cmd_SET_E_ROTATION_DISTANCE(self, gcmd):
- rotation_dist = gcmd.get_float('DISTANCE', None)
+ rotation_dist = gcmd.get_float("DISTANCE", None)
if rotation_dist is not None:
if not rotation_dist:
raise gcmd.error("Rotation distance can not be zero")
invert_dir, orig_invert_dir = self.stepper.get_dir_inverted()
next_invert_dir = orig_invert_dir
- if rotation_dist < 0.:
+ if rotation_dist < 0.0:
next_invert_dir = not orig_invert_dir
rotation_dist = -rotation_dist
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
self.stepper.set_rotation_distance(rotation_dist)
self.stepper.set_dir_inverted(next_invert_dir)
@@ -122,48 +156,53 @@ class ExtruderStepper:
invert_dir, orig_invert_dir = self.stepper.get_dir_inverted()
if invert_dir != orig_invert_dir:
rotation_dist = -rotation_dist
- gcmd.respond_info("Extruder '%s' rotation distance set to %0.6f"
- % (self.name, rotation_dist))
+ gcmd.respond_info(
+ "Extruder '%s' rotation distance set to %0.6f" % (self.name, rotation_dist)
+ )
+
cmd_SYNC_EXTRUDER_MOTION_help = "Set extruder stepper motion queue"
+
def cmd_SYNC_EXTRUDER_MOTION(self, gcmd):
- ename = gcmd.get('MOTION_QUEUE')
+ ename = gcmd.get("MOTION_QUEUE")
self.sync_to_extruder(ename)
- gcmd.respond_info("Extruder '%s' now syncing with '%s'"
- % (self.name, ename))
+ gcmd.respond_info("Extruder '%s' now syncing with '%s'" % (self.name, ename))
+
# Tracking for hotend heater, extrusion motion queue, and extruder stepper
class PrinterExtruder:
def __init__(self, config, extruder_num):
self.printer = config.get_printer()
self.name = config.get_name()
- self.last_position = 0.
+ self.last_position = 0.0
# Setup hotend heater
- pheaters = self.printer.load_object(config, 'heaters')
- gcode_id = 'T%d' % (extruder_num,)
+ pheaters = self.printer.load_object(config, "heaters")
+ gcode_id = "T%d" % (extruder_num,)
self.heater = pheaters.setup_heater(config, gcode_id)
# Setup kinematic checks
- self.nozzle_diameter = config.getfloat('nozzle_diameter', above=0.)
+ self.nozzle_diameter = config.getfloat("nozzle_diameter", above=0.0)
filament_diameter = config.getfloat(
- 'filament_diameter', minval=self.nozzle_diameter)
- self.filament_area = math.pi * (filament_diameter * .5)**2
- def_max_cross_section = 4. * self.nozzle_diameter**2
+ "filament_diameter", minval=self.nozzle_diameter
+ )
+ self.filament_area = math.pi * (filament_diameter * 0.5) ** 2
+ def_max_cross_section = 4.0 * self.nozzle_diameter**2
def_max_extrude_ratio = def_max_cross_section / self.filament_area
max_cross_section = config.getfloat(
- 'max_extrude_cross_section', def_max_cross_section, above=0.)
+ "max_extrude_cross_section", def_max_cross_section, above=0.0
+ )
self.max_extrude_ratio = max_cross_section / self.filament_area
logging.info("Extruder max_extrude_ratio=%.6f", self.max_extrude_ratio)
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_e_velocity = config.getfloat(
- 'max_extrude_only_velocity', max_velocity * def_max_extrude_ratio
- , above=0.)
+ "max_extrude_only_velocity", max_velocity * def_max_extrude_ratio, above=0.0
+ )
self.max_e_accel = config.getfloat(
- 'max_extrude_only_accel', max_accel * def_max_extrude_ratio
- , above=0.)
- self.max_e_dist = config.getfloat(
- 'max_extrude_only_distance', 50., minval=0.)
+ "max_extrude_only_accel", max_accel * def_max_extrude_ratio, above=0.0
+ )
+ self.max_e_dist = config.getfloat("max_extrude_only_distance", 50.0, minval=0.0)
self.instant_corner_v = config.getfloat(
- 'instantaneous_corner_velocity', 1., minval=0.)
+ "instantaneous_corner_velocity", 1.0, minval=0.0
+ )
# Setup extruder trapq (trapezoidal motion queue)
ffi_main, ffi_lib = chelper.get_ffi()
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
@@ -171,111 +210,151 @@ class PrinterExtruder:
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
# Setup extruder stepper
self.extruder_stepper = None
- if (config.get('step_pin', None) is not None
- or config.get('dir_pin', None) is not None
- or config.get('rotation_distance', None) is not None):
+ if (
+ config.get("step_pin", None) is not None
+ or config.get("dir_pin", None) is not None
+ or config.get("rotation_distance", None) is not None
+ ):
self.extruder_stepper = ExtruderStepper(config)
self.extruder_stepper.stepper.set_trapq(self.trapq)
# Register commands
- gcode = self.printer.lookup_object('gcode')
- if self.name == 'extruder':
- toolhead.set_extruder(self, 0.)
+ gcode = self.printer.lookup_object("gcode")
+ if self.name == "extruder":
+ toolhead.set_extruder(self, 0.0)
gcode.register_command("M104", self.cmd_M104)
gcode.register_command("M109", self.cmd_M109)
- gcode.register_mux_command("ACTIVATE_EXTRUDER", "EXTRUDER",
- self.name, self.cmd_ACTIVATE_EXTRUDER,
- desc=self.cmd_ACTIVATE_EXTRUDER_help)
+ gcode.register_mux_command(
+ "ACTIVATE_EXTRUDER",
+ "EXTRUDER",
+ self.name,
+ self.cmd_ACTIVATE_EXTRUDER,
+ desc=self.cmd_ACTIVATE_EXTRUDER_help,
+ )
+
def get_status(self, eventtime):
sts = self.heater.get_status(eventtime)
- sts['can_extrude'] = self.heater.can_extrude
+ sts["can_extrude"] = self.heater.can_extrude
if self.extruder_stepper is not None:
sts.update(self.extruder_stepper.get_status(eventtime))
return sts
+
def get_name(self):
return self.name
+
def get_heater(self):
return self.heater
+
def get_trapq(self):
return self.trapq
+
def get_axis_gcode_id(self):
- return 'E'
+ return "E"
+
def stats(self, eventtime):
return self.heater.stats(eventtime)
+
def check_move(self, move, ea_index):
if not self.heater.can_extrude:
raise self.printer.command_error(
"Extrude below minimum temp\n"
- "See the 'min_extrude_temp' config option for details")
+ "See the 'min_extrude_temp' config option for details"
+ )
axis_r = move.axes_r[ea_index]
axis_d = move.axes_d[ea_index]
- if (not move.axes_d[0] and not move.axes_d[1]) or axis_r < 0.:
+ if (not move.axes_d[0] and not move.axes_d[1]) or axis_r < 0.0:
# Extrude only move (or retraction move) - limit accel and velocity
if abs(axis_d) > self.max_e_dist:
raise self.printer.command_error(
"Extrude only move too long (%.3fmm vs %.3fmm)\n"
"See the 'max_extrude_only_distance' config"
- " option for details" % (axis_d, self.max_e_dist))
- inv_extrude_r = 1. / abs(axis_r)
- move.limit_speed(self.max_e_velocity * inv_extrude_r,
- self.max_e_accel * inv_extrude_r)
+ " option for details" % (axis_d, self.max_e_dist)
+ )
+ inv_extrude_r = 1.0 / abs(axis_r)
+ move.limit_speed(
+ self.max_e_velocity * inv_extrude_r, self.max_e_accel * inv_extrude_r
+ )
elif axis_r > self.max_extrude_ratio:
if axis_d <= self.nozzle_diameter * self.max_extrude_ratio:
# Permit extrusion if amount extruded is tiny
return
area = axis_r * self.filament_area
- logging.debug("Overextrude: %s vs %s (area=%.3f dist=%.3f)",
- axis_r, self.max_extrude_ratio, area, move.move_d)
+ logging.debug(
+ "Overextrude: %s vs %s (area=%.3f dist=%.3f)",
+ axis_r,
+ self.max_extrude_ratio,
+ area,
+ move.move_d,
+ )
raise self.printer.command_error(
"Move exceeds maximum extrusion (%.3fmm^2 vs %.3fmm^2)\n"
"See the 'max_extrude_cross_section' config option for details"
- % (area, self.max_extrude_ratio * self.filament_area))
+ % (area, self.max_extrude_ratio * self.filament_area)
+ )
+
def calc_junction(self, prev_move, move, ea_index):
diff_r = move.axes_r[ea_index] - prev_move.axes_r[ea_index]
if diff_r:
- return (self.instant_corner_v / abs(diff_r))**2
+ return (self.instant_corner_v / abs(diff_r)) ** 2
return move.max_cruise_v2
+
def process_move(self, print_time, move, ea_index):
axis_r = move.axes_r[ea_index]
accel = move.accel * axis_r
start_v = move.start_v * axis_r
cruise_v = move.cruise_v * axis_r
can_pressure_advance = False
- if axis_r > 0. and (move.axes_d[0] or move.axes_d[1]):
+ if axis_r > 0.0 and (move.axes_d[0] or move.axes_d[1]):
can_pressure_advance = True
# Queue movement (x is extruder movement, y is pressure advance flag)
- self.trapq_append(self.trapq, print_time,
- move.accel_t, move.cruise_t, move.decel_t,
- move.start_pos[ea_index], 0., 0.,
- 1., can_pressure_advance, 0.,
- start_v, cruise_v, accel)
+ self.trapq_append(
+ self.trapq,
+ print_time,
+ move.accel_t,
+ move.cruise_t,
+ move.decel_t,
+ move.start_pos[ea_index],
+ 0.0,
+ 0.0,
+ 1.0,
+ can_pressure_advance,
+ 0.0,
+ start_v,
+ cruise_v,
+ accel,
+ )
self.last_position = move.end_pos[ea_index]
+
def find_past_position(self, print_time):
if self.extruder_stepper is None:
- return 0.
+ return 0.0
return self.extruder_stepper.find_past_position(print_time)
+
def cmd_M104(self, gcmd, wait=False):
# Set Extruder Temperature
- temp = gcmd.get_float('S', 0.)
- index = gcmd.get_int('T', None, minval=0)
+ temp = gcmd.get_float("S", 0.0)
+ index = gcmd.get_int("T", None, minval=0)
if index is not None:
- section = 'extruder'
+ section = "extruder"
if index:
- section = 'extruder%d' % (index,)
+ section = "extruder%d" % (index,)
extruder = self.printer.lookup_object(section, None)
if extruder is None:
- if temp <= 0.:
+ if temp <= 0.0:
return
raise gcmd.error("Extruder not configured")
else:
- extruder = self.printer.lookup_object('toolhead').get_extruder()
- pheaters = self.printer.lookup_object('heaters')
+ extruder = self.printer.lookup_object("toolhead").get_extruder()
+ pheaters = self.printer.lookup_object("heaters")
pheaters.set_temperature(extruder.get_heater(), temp, wait)
+
def cmd_M109(self, gcmd):
# Set Extruder Temperature and Wait
self.cmd_M104(gcmd, wait=True)
+
cmd_ACTIVATE_EXTRUDER_help = "Change the active extruder"
+
def cmd_ACTIVATE_EXTRUDER(self, gcmd):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
if toolhead.get_extruder() is self:
gcmd.respond_info("Extruder %s already active" % (self.name,))
return
@@ -284,31 +363,40 @@ class PrinterExtruder:
toolhead.set_extruder(self, self.last_position)
self.printer.send_event("extruder:activate_extruder")
+
# Dummy extruder class used when a printer has no extruder at all
class DummyExtruder:
def __init__(self, printer):
self.printer = printer
+
def check_move(self, move, ea_index):
raise move.move_error("Extrude when no extruder present")
+
def find_past_position(self, print_time):
- return 0.
+ return 0.0
+
def calc_junction(self, prev_move, move, ea_index):
return move.max_cruise_v2
+
def get_name(self):
return ""
+
def get_heater(self):
raise self.printer.command_error("Extruder not configured")
+
def get_trapq(self):
return None
+
def get_axis_gcode_id(self):
- return 'E'
+ return "E"
+
def add_printer_objects(config):
printer = config.get_printer()
for i in range(99):
- section = 'extruder'
+ section = "extruder"
if i:
- section = 'extruder%d' % (i,)
+ section = "extruder%d" % (i,)
if not config.has_section(section):
break
pe = PrinterExtruder(config.getsection(section), i)
diff --git a/klippy/kinematics/generic_cartesian.py b/klippy/kinematics/generic_cartesian.py
index b8cabb77..a76e4a21 100644
--- a/klippy/kinematics/generic_cartesian.py
+++ b/klippy/kinematics/generic_cartesian.py
@@ -9,6 +9,7 @@ import gcode, mathutil, stepper
from . import idex_modes
from . import kinematic_stepper as ks
+
def mat_mul(a, b):
if len(a[0]) != len(b):
return None
@@ -19,89 +20,118 @@ def mat_mul(a, b):
res[i].append(sum(a[i][k] * b[k][j] for k in range(len(b))))
return res
+
def mat_transp(a):
res = []
for i in range(len(a[0])):
res.append([a[j][i] for j in range(len(a))])
return res
+
def mat_pseudo_inverse(m):
mt = mat_transp(m)
mtm = mat_mul(mt, m)
pinv = mat_mul(mathutil.matrix_inv(mtm), mt)
return pinv
+
class MainCarriage:
def __init__(self, config, axis):
self.rail = stepper.GenericPrinterRail(config)
- self.axis = ord(axis) - ord('x')
+ self.axis = ord(axis) - ord("x")
self.axis_name = axis
self.dual_carriage = None
+
def get_name(self):
return self.rail.get_name(short=True)
+
def get_axis(self):
return self.axis
+
def get_rail(self):
return self.rail
+
def add_stepper(self, kin_stepper):
self.rail.add_stepper(kin_stepper.get_stepper())
+
def is_active(self):
if self.dual_carriage is None:
return True
return self.dual_carriage.get_dc_module().is_active(self.rail)
+
def set_dual_carriage(self, carriage):
old_dc = self.dual_carriage
self.dual_carriage = carriage
return old_dc
+
def get_dual_carriage(self):
return self.dual_carriage
+
class ExtraCarriage:
def __init__(self, config, carriages):
self.name = config.get_name().split()[-1]
- self.primary_carriage = config.getchoice('primary_carriage', carriages)
- self.endstop_pin = config.get('endstop_pin')
+ self.primary_carriage = config.getchoice("primary_carriage", carriages)
+ self.endstop_pin = config.get("endstop_pin")
+
def get_name(self):
return self.name
+
def get_axis(self):
return self.primary_carriage.get_axis()
+
def get_rail(self):
return self.primary_carriage.get_rail()
+
def add_stepper(self, kin_stepper):
- self.get_rail().add_stepper(kin_stepper.get_stepper(),
- self.endstop_pin, self.name)
+ self.get_rail().add_stepper(
+ kin_stepper.get_stepper(), self.endstop_pin, self.name
+ )
+
class DualCarriage:
def __init__(self, config, carriages):
self.printer = config.get_printer()
self.rail = stepper.GenericPrinterRail(config)
- self.primary_carriage = config.getchoice('primary_carriage', carriages)
+ self.primary_carriage = config.getchoice("primary_carriage", carriages)
if self.primary_carriage.set_dual_carriage(self) is not None:
raise config.error(
- "Redefinition of dual_carriage for carriage '%s'" %
- self.primary_carriage.get_name())
+ "Redefinition of dual_carriage for carriage '%s'"
+ % self.primary_carriage.get_name()
+ )
self.axis = self.primary_carriage.get_axis()
if self.axis > 1:
- raise config.error("Invalid axis '%s' for dual_carriage" %
- self.primary_carriage.get_axis_name())
- self.safe_dist = config.getfloat('safe_distance', None, minval=0.)
+ raise config.error(
+ "Invalid axis '%s' for dual_carriage"
+ % self.primary_carriage.get_axis_name()
+ )
+ self.safe_dist = config.getfloat("safe_distance", None, minval=0.0)
+
def get_name(self):
return self.rail.get_name(short=True)
+
def get_axis(self):
return self.primary_carriage.get_axis()
+
def get_rail(self):
return self.rail
+
def get_safe_dist(self):
return self.safe_dist
+
def get_dc_module(self):
- return self.printer.lookup_object('dual_carriage')
+ return self.printer.lookup_object("dual_carriage")
+
def is_active(self):
return self.get_dc_module().is_active(self.rail)
+
def get_dual_carriage(self):
return self.primary_carriage
+
def add_stepper(self, kin_stepper):
self.rail.add_stepper(kin_stepper.get_stepper())
+
class GenericCartesianKinematics:
def __init__(self, toolhead, config):
self.printer = config.get_printer()
@@ -115,42 +145,50 @@ class GenericCartesianKinematics:
primary_rails = [pc.get_rail() for pc in pcs]
dual_rails = [dc.get_rail() for dc in self.dc_carriages]
axes = [dc.get_axis() for dc in self.dc_carriages]
- safe_dist = {dc.get_axis() : dc.get_safe_dist()
- for dc in self.dc_carriages}
+ safe_dist = {dc.get_axis(): dc.get_safe_dist() for dc in self.dc_carriages}
self.dc_module = dc_module = idex_modes.DualCarriages(
- self.printer, primary_rails, dual_rails, axes, safe_dist)
- zero_pos = (0., 0.)
+ self.printer, primary_rails, dual_rails, axes, safe_dist
+ )
+ zero_pos = (0.0, 0.0)
for acs in itertools.product(*zip(pcs, self.dc_carriages)):
for c in acs:
dc_module.get_dc_rail_wrapper(c.get_rail()).activate(
- idex_modes.PRIMARY, zero_pos)
+ idex_modes.PRIMARY, zero_pos
+ )
dc_rail = c.get_dual_carriage().get_rail()
dc_module.get_dc_rail_wrapper(dc_rail).inactivate(zero_pos)
self._check_kinematics(config.error)
for c in pcs:
dc_module.get_dc_rail_wrapper(c.get_rail()).activate(
- idex_modes.PRIMARY, zero_pos)
+ idex_modes.PRIMARY, zero_pos
+ )
dc_rail = c.get_dual_carriage().get_rail()
dc_module.get_dc_rail_wrapper(dc_rail).inactivate(zero_pos)
else:
self._check_kinematics(config.error)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
- self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
- above=0., maxval=max_velocity)
- self.max_z_accel = config.getfloat('max_z_accel', max_accel,
- above=0., maxval=max_accel)
+ self.max_z_velocity = config.getfloat(
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
+ self.max_z_accel = config.getfloat(
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
# Register gcode commands
- gcode = self.printer.lookup_object('gcode')
- gcode.register_command("SET_STEPPER_CARRIAGES",
- self.cmd_SET_STEPPER_CARRIAGES,
- desc=self.cmd_SET_STEPPER_CARRIAGES_help)
+ gcode = self.printer.lookup_object("gcode")
+ gcode.register_command(
+ "SET_STEPPER_CARRIAGES",
+ self.cmd_SET_STEPPER_CARRIAGES,
+ desc=self.cmd_SET_STEPPER_CARRIAGES_help,
+ )
+
def _load_kinematics(self, config):
- carriages = {a : MainCarriage(config.getsection('carriage ' + a), a)
- for a in 'xyz'}
+ carriages = {
+ a: MainCarriage(config.getsection("carriage " + a), a) for a in "xyz"
+ }
dc_carriages = []
- for c in config.get_prefix_sections('dual_carriage '):
+ for c in config.get_prefix_sections("dual_carriage "):
dc_carriages.append(DualCarriage(c, carriages))
for dc in dc_carriages:
name = dc.get_name()
@@ -160,7 +198,7 @@ class GenericCartesianKinematics:
self.carriages = dict(carriages)
self.dc_carriages = dc_carriages
ec_carriages = []
- for c in config.get_prefix_sections('extra_carriage '):
+ for c in config.get_prefix_sections("extra_carriage "):
ec_carriages.append(ExtraCarriage(c, carriages))
for ec in ec_carriages:
name = ec.get_name()
@@ -171,6 +209,7 @@ class GenericCartesianKinematics:
self.all_carriages = carriages
self._check_carriages_references(config.error)
self._check_multi_mcu_homing(config.error)
+
def _check_carriages_references(self, report_error):
carriages = dict(self.all_carriages)
for s in self.kin_steppers:
@@ -178,21 +217,35 @@ class GenericCartesianKinematics:
carriages.pop(c.get_name(), None)
if carriages:
raise report_error(
- "Carriage(s) %s must be referenced by some "
- "stepper(s)" % (", ".join(carriages),))
+ "Carriage(s) %s must be referenced by some "
+ "stepper(s)" % (", ".join(carriages),)
+ )
+
def _check_multi_mcu_homing(self, report_error):
for carriage in self.carriages.values():
for es in carriage.get_rail().get_endstops():
- stepper_mcus = set([s.get_mcu() for s in es[0].get_steppers()
- if s in carriage.get_rail().get_steppers()])
+ stepper_mcus = set(
+ [
+ s.get_mcu()
+ for s in es[0].get_steppers()
+ if s in carriage.get_rail().get_steppers()
+ ]
+ )
if len(stepper_mcus) > 1:
- raise report_error("Multi-mcu homing not supported on"
- " multi-mcu shared carriage %s" % es[1])
+ raise report_error(
+ "Multi-mcu homing not supported on"
+ " multi-mcu shared carriage %s" % es[1]
+ )
+
def _load_steppers(self, config, carriages):
- return [ks.KinematicStepper(c, carriages)
- for c in config.get_prefix_sections('stepper ')]
+ return [
+ ks.KinematicStepper(c, carriages)
+ for c in config.get_prefix_sections("stepper ")
+ ]
+
def get_steppers(self):
return [s.get_stepper() for s in self.kin_steppers]
+
def get_primary_carriages(self):
carriages = [self.carriages[a] for a in "xyz"]
if self.dc_module:
@@ -202,13 +255,15 @@ class GenericCartesianKinematics:
if c.get_rail() == primary_rail:
carriages[a] = c
return carriages
+
def _get_kinematics_coeffs(self):
- matr = {s.get_name() : list(s.get_kin_coeffs())
- for s in self.kin_steppers}
- offs = {s.get_name() : [0.] * 3 for s in self.kin_steppers}
+ matr = {s.get_name(): list(s.get_kin_coeffs()) for s in self.kin_steppers}
+ offs = {s.get_name(): [0.0] * 3 for s in self.kin_steppers}
if self.dc_module is None:
- return ([matr[s.get_name()] for s in self.kin_steppers],
- [0. for s in self.kin_steppers])
+ return (
+ [matr[s.get_name()] for s in self.kin_steppers],
+ [0.0 for s in self.kin_steppers],
+ )
axes = [dc.get_axis() for dc in self.dc_carriages]
orig_matr = copy.deepcopy(matr)
for dc in self.dc_carriages:
@@ -218,33 +273,41 @@ class GenericCartesianKinematics:
for s in c.get_rail().get_steppers():
matr[s.get_name()][axis] *= m
offs[s.get_name()][axis] += o
- return ([matr[s.get_name()] for s in self.kin_steppers],
- [mathutil.matrix_dot(orig_matr[s.get_name()],
- offs[s.get_name()])
- for s in self.kin_steppers])
+ return (
+ [matr[s.get_name()] for s in self.kin_steppers],
+ [
+ mathutil.matrix_dot(orig_matr[s.get_name()], offs[s.get_name()])
+ for s in self.kin_steppers
+ ],
+ )
+
def _check_kinematics(self, report_error):
matr, _ = self._get_kinematics_coeffs()
det = mathutil.matrix_det(mat_mul(mat_transp(matr), matr))
if abs(det) < 0.00001:
raise report_error(
- "Verify configured stepper(s) and their 'carriages' "
- "specifications, the current configuration does not "
- "allow independent movements of all printer axes.")
+ "Verify configured stepper(s) and their 'carriages' "
+ "specifications, the current configuration does not "
+ "allow independent movements of all printer axes."
+ )
+
def calc_position(self, stepper_positions):
matr, offs = self._get_kinematics_coeffs()
spos = [stepper_positions[s.get_name()] for s in self.kin_steppers]
pinv = mat_pseudo_inverse(matr)
- pos = mat_mul([[sp-o for sp, o in zip(spos, offs)]], mat_transp(pinv))
+ pos = mat_mul([[sp - o for sp, o in zip(spos, offs)]], mat_transp(pinv))
for i in range(3):
if not any(pinv[i]):
pos[0][i] = None
return pos[0]
+
def update_limits(self, i, range):
l, h = self.limits[i]
# Only update limits if this axis was already homed,
# otherwise leave in un-homed state.
if l <= h:
self.limits[i] = range
+
def set_position(self, newpos, homing_axes):
for s in self.kin_steppers:
s.set_position(newpos)
@@ -254,10 +317,12 @@ class GenericCartesianKinematics:
if c.get_axis() == axis and c.is_active():
self.limits[axis] = c.get_rail().get_range()
break
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home_axis(self, homing_state, axis, rail):
# Determine movement
position_min, position_max = rail.get_range()
@@ -271,6 +336,7 @@ class GenericCartesianKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def home(self, homing_state):
self._check_kinematics(self.printer.command_error)
# Each axis is homed independently and in order
@@ -280,20 +346,26 @@ class GenericCartesianKinematics:
self.dc_module.home(homing_state, axis)
else:
self.home_axis(homing_state, axis, carriage.get_rail())
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -301,27 +373,31 @@ class GenericCartesianKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
- ranges = [c.get_rail().get_range()
- for c in self.get_primary_carriages()]
- axes_min = gcode.Coord(*[r[0] for r in ranges], e=0.)
- axes_max = gcode.Coord(*[r[1] for r in ranges], e=0.)
+ ranges = [c.get_rail().get_range() for c in self.get_primary_carriages()]
+ axes_min = gcode.Coord(*[r[0] for r in ranges], e=0.0)
+ axes_max = gcode.Coord(*[r[1] for r in ranges], e=0.0)
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': axes_min,
- 'axis_maximum': axes_max,
+ "homed_axes": "".join(axes),
+ "axis_minimum": axes_min,
+ "axis_maximum": axes_max,
}
+
cmd_SET_STEPPER_CARRIAGES_help = "Set stepper carriages"
+
def cmd_SET_STEPPER_CARRIAGES(self, gcmd):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
stepper_name = gcmd.get("STEPPER")
- steppers = [stepper for stepper in self.kin_steppers
- if stepper.get_name() == stepper_name
- or stepper.get_name(short=True) == stepper_name]
+ steppers = [
+ stepper
+ for stepper in self.kin_steppers
+ if stepper.get_name() == stepper_name
+ or stepper.get_name(short=True) == stepper_name
+ ]
if len(steppers) != 1:
raise gcmd.error("Invalid STEPPER '%s' specified" % stepper_name)
stepper = steppers[0]
@@ -333,8 +409,10 @@ class GenericCartesianKinematics:
new_carriages = stepper.get_carriages()
if new_carriages != old_carriages:
stepper.update_kin_coeffs(old_kin_coeffs)
- raise gcmd.error("SET_STEPPER_CARRIAGES cannot add or remove "
- "carriages that the stepper controls")
+ raise gcmd.error(
+ "SET_STEPPER_CARRIAGES cannot add or remove "
+ "carriages that the stepper controls"
+ )
pos = toolhead.get_position()
stepper.set_position(pos)
if not validate:
@@ -346,13 +424,16 @@ class GenericCartesianKinematics:
for acs in itertools.product(*zip(pcs, self.dc_carriages)):
for c in acs:
self.dc_module.get_dc_rail_wrapper(c.get_rail()).activate(
- idex_modes.PRIMARY, pos)
+ idex_modes.PRIMARY, pos
+ )
self.dc_module.get_dc_rail_wrapper(
- c.get_dual_carriage().get_rail()).inactivate(pos)
+ c.get_dual_carriage().get_rail()
+ ).inactivate(pos)
self._check_kinematics(gcmd.error)
self.dc_module.restore_dual_carriage_state(dc_state, move=0)
else:
self._check_kinematics(gcmd.error)
+
def load_kinematics(toolhead, config):
return GenericCartesianKinematics(toolhead, config)
diff --git a/klippy/kinematics/hybrid_corexy.py b/klippy/kinematics/hybrid_corexy.py
index fd2621d5..2562ba33 100644
--- a/klippy/kinematics/hybrid_corexy.py
+++ b/klippy/kinematics/hybrid_corexy.py
@@ -7,61 +7,75 @@ import logging
import stepper
from . import idex_modes
+
# The hybrid-corexy kinematic is also known as Markforged kinematics
class HybridCoreXYKinematics:
def __init__(self, toolhead, config):
self.printer = config.get_printer()
# itersolve parameters
- self.rails = [ stepper.LookupRail(config.getsection('stepper_x')),
- stepper.LookupMultiRail(config.getsection('stepper_y')),
- stepper.LookupMultiRail(config.getsection('stepper_z'))]
- self.rails[1].get_endstops()[0][0].add_stepper(
- self.rails[0].get_steppers()[0])
- self.rails[0].setup_itersolve('corexy_stepper_alloc', b'-')
- self.rails[1].setup_itersolve('cartesian_stepper_alloc', b'y')
- self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
+ self.rails = [
+ stepper.LookupRail(config.getsection("stepper_x")),
+ stepper.LookupMultiRail(config.getsection("stepper_y")),
+ stepper.LookupMultiRail(config.getsection("stepper_z")),
+ ]
+ self.rails[1].get_endstops()[0][0].add_stepper(self.rails[0].get_steppers()[0])
+ self.rails[0].setup_itersolve("corexy_stepper_alloc", b"-")
+ self.rails[1].setup_itersolve("cartesian_stepper_alloc", b"y")
+ self.rails[2].setup_itersolve("cartesian_stepper_alloc", b"z")
ranges = [r.get_range() for r in self.rails]
- self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.)
- self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.)
+ self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.0)
+ self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.0)
self.dc_module = None
- if config.has_section('dual_carriage'):
- dc_config = config.getsection('dual_carriage')
+ if config.has_section("dual_carriage"):
+ dc_config = config.getsection("dual_carriage")
# dummy for cartesian config users
- dc_config.getchoice('axis', ['x'], default='x')
+ dc_config.getchoice("axis", ["x"], default="x")
# setup second dual carriage rail
self.rails.append(stepper.LookupRail(dc_config))
self.rails[1].get_endstops()[0][0].add_stepper(
- self.rails[3].get_steppers()[0])
- self.rails[3].setup_itersolve('corexy_stepper_alloc', b'+')
+ self.rails[3].get_steppers()[0]
+ )
+ self.rails[3].setup_itersolve("corexy_stepper_alloc", b"+")
self.dc_module = idex_modes.DualCarriages(
- self.printer, [self.rails[0]], [self.rails[3]], axes=[0],
- safe_dist=dc_config.getfloat(
- 'safe_distance', None, minval=0.))
+ self.printer,
+ [self.rails[0]],
+ [self.rails[3]],
+ axes=[0],
+ safe_dist=dc_config.getfloat("safe_distance", None, minval=0.0),
+ )
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
self.max_z_accel = config.getfloat(
- 'max_z_accel', max_accel, above=0., maxval=max_accel)
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
pos = [stepper_positions[rail.get_name()] for rail in self.rails]
- if (self.dc_module is not None and 'PRIMARY' == \
- self.dc_module.get_status()['carriage_1']):
+ if (
+ self.dc_module is not None
+ and "PRIMARY" == self.dc_module.get_status()["carriage_1"]
+ ):
return [pos[3] - pos[1], pos[1], pos[2]]
else:
return [pos[0] + pos[1], pos[1], pos[2]]
+
def update_limits(self, i, range):
l, h = self.limits[i]
# Only update limits if this axis was already homed,
# otherwise leave in un-homed state.
if l <= h:
self.limits[i] = range
+
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
@@ -72,10 +86,12 @@ class HybridCoreXYKinematics:
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home_axis(self, homing_state, axis, rail):
position_min, position_max = rail.get_range()
hi = rail.get_homing_info()
@@ -88,26 +104,33 @@ class HybridCoreXYKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def home(self, homing_state):
for axis in homing_state.get_axes():
if self.dc_module is not None and axis == 0:
self.dc_module.home(homing_state, axis)
else:
self.home_axis(homing_state, axis, self.rails[axis])
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -115,15 +138,16 @@ class HybridCoreXYKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return HybridCoreXYKinematics(toolhead, config)
diff --git a/klippy/kinematics/hybrid_corexz.py b/klippy/kinematics/hybrid_corexz.py
index b9699982..6c3ae57d 100644
--- a/klippy/kinematics/hybrid_corexz.py
+++ b/klippy/kinematics/hybrid_corexz.py
@@ -7,61 +7,75 @@ import logging
import stepper
from . import idex_modes
+
# The hybrid-corexz kinematic is also known as Markforged kinematics
class HybridCoreXZKinematics:
def __init__(self, toolhead, config):
self.printer = config.get_printer()
# itersolve parameters
- self.rails = [ stepper.LookupRail(config.getsection('stepper_x')),
- stepper.LookupMultiRail(config.getsection('stepper_y')),
- stepper.LookupMultiRail(config.getsection('stepper_z'))]
- self.rails[2].get_endstops()[0][0].add_stepper(
- self.rails[0].get_steppers()[0])
- self.rails[0].setup_itersolve('corexz_stepper_alloc', b'-')
- self.rails[1].setup_itersolve('cartesian_stepper_alloc', b'y')
- self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
+ self.rails = [
+ stepper.LookupRail(config.getsection("stepper_x")),
+ stepper.LookupMultiRail(config.getsection("stepper_y")),
+ stepper.LookupMultiRail(config.getsection("stepper_z")),
+ ]
+ self.rails[2].get_endstops()[0][0].add_stepper(self.rails[0].get_steppers()[0])
+ self.rails[0].setup_itersolve("corexz_stepper_alloc", b"-")
+ self.rails[1].setup_itersolve("cartesian_stepper_alloc", b"y")
+ self.rails[2].setup_itersolve("cartesian_stepper_alloc", b"z")
ranges = [r.get_range() for r in self.rails]
- self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.)
- self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.)
+ self.axes_min = toolhead.Coord(*[r[0] for r in ranges], e=0.0)
+ self.axes_max = toolhead.Coord(*[r[1] for r in ranges], e=0.0)
self.dc_module = None
- if config.has_section('dual_carriage'):
- dc_config = config.getsection('dual_carriage')
+ if config.has_section("dual_carriage"):
+ dc_config = config.getsection("dual_carriage")
# dummy for cartesian config users
- dc_config.getchoice('axis', ['x'], default='x')
+ dc_config.getchoice("axis", ["x"], default="x")
# setup second dual carriage rail
self.rails.append(stepper.LookupRail(dc_config))
self.rails[2].get_endstops()[0][0].add_stepper(
- self.rails[3].get_steppers()[0])
- self.rails[3].setup_itersolve('corexz_stepper_alloc', b'+')
+ self.rails[3].get_steppers()[0]
+ )
+ self.rails[3].setup_itersolve("corexz_stepper_alloc", b"+")
self.dc_module = idex_modes.DualCarriages(
- self.printer, [self.rails[0]], [self.rails[3]], axes=[0],
- safe_dist=dc_config.getfloat(
- 'safe_distance', None, minval=0.))
+ self.printer,
+ [self.rails[0]],
+ [self.rails[3]],
+ axes=[0],
+ safe_dist=dc_config.getfloat("safe_distance", None, minval=0.0),
+ )
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
self.max_z_accel = config.getfloat(
- 'max_z_accel', max_accel, above=0., maxval=max_accel)
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limits = [(1.0, -1.0)] * 3
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
pos = [stepper_positions[rail.get_name()] for rail in self.rails]
- if (self.dc_module is not None and 'PRIMARY' == \
- self.dc_module.get_status()['carriage_1']):
+ if (
+ self.dc_module is not None
+ and "PRIMARY" == self.dc_module.get_status()["carriage_1"]
+ ):
return [pos[3] - pos[2], pos[1], pos[2]]
else:
return [pos[0] + pos[2], pos[1], pos[2]]
+
def update_limits(self, i, range):
l, h = self.limits[i]
# Only update limits if this axis was already homed,
# otherwise leave in un-homed state.
if l <= h:
self.limits[i] = range
+
def set_position(self, newpos, homing_axes):
for i, rail in enumerate(self.rails):
rail.set_position(newpos)
@@ -72,10 +86,12 @@ class HybridCoreXZKinematics:
else:
rail = self.rails[axis]
self.limits[axis] = rail.get_range()
+
def clear_homing_state(self, clear_axes):
for axis, axis_name in enumerate("xyz"):
if axis_name in clear_axes:
self.limits[axis] = (1.0, -1.0)
+
def home_axis(self, homing_state, axis, rail):
position_min, position_max = rail.get_range()
hi = rail.get_homing_info()
@@ -88,26 +104,33 @@ class HybridCoreXZKinematics:
forcepos[axis] += 1.5 * (position_max - hi.position_endstop)
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def home(self, homing_state):
for axis in homing_state.get_axes():
if self.dc_module is not None and axis == 0:
self.dc_module.home(homing_state, axis)
else:
self.home_axis(homing_state, axis, self.rails[axis])
+
def _check_endstops(self, move):
end_pos = move.end_pos
for i in (0, 1, 2):
- if (move.axes_d[i]
- and (end_pos[i] < self.limits[i][0]
- or end_pos[i] > self.limits[i][1])):
+ if move.axes_d[i] and (
+ end_pos[i] < self.limits[i][0] or end_pos[i] > self.limits[i][1]
+ ):
if self.limits[i][0] > self.limits[i][1]:
raise move.move_error("Must home axis first")
raise move.move_error()
+
def check_move(self, move):
limits = self.limits
xpos, ypos = move.end_pos[:2]
- if (xpos < limits[0][0] or xpos > limits[0][1]
- or ypos < limits[1][0] or ypos > limits[1][1]):
+ if (
+ xpos < limits[0][0]
+ or xpos > limits[0][1]
+ or ypos < limits[1][0]
+ or ypos > limits[1][1]
+ ):
self._check_endstops(move)
if not move.axes_d[2]:
# Normal XY move - use defaults
@@ -115,15 +138,16 @@ class HybridCoreXZKinematics:
# Move with Z - update velocity and accel for slower Z axis
self._check_endstops(move)
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(
- self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
axes = [a for a, (l, h) in zip("xyz", self.limits) if l <= h]
return {
- 'homed_axes': "".join(axes),
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max
+ "homed_axes": "".join(axes),
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return HybridCoreXZKinematics(toolhead, config)
diff --git a/klippy/kinematics/idex_modes.py b/klippy/kinematics/idex_modes.py
index 19d5a655..5910d5c0 100644
--- a/klippy/kinematics/idex_modes.py
+++ b/klippy/kinematics/idex_modes.py
@@ -7,29 +7,33 @@
import collections, logging, math
import chelper
-INACTIVE = 'INACTIVE'
-PRIMARY = 'PRIMARY'
-COPY = 'COPY'
-MIRROR = 'MIRROR'
+INACTIVE = "INACTIVE"
+PRIMARY = "PRIMARY"
+COPY = "COPY"
+MIRROR = "MIRROR"
+
class DualCarriages:
VALID_MODES = [PRIMARY, COPY, MIRROR]
- def __init__(self, printer, primary_rails, dual_rails, axes,
- safe_dist={}):
+
+ def __init__(self, printer, primary_rails, dual_rails, axes, safe_dist={}):
self.printer = printer
self.axes = axes
self._init_steppers(primary_rails + dual_rails)
self.primary_rails = [
- DualCarriagesRail(printer, c, dual_rails[i],
- axes[i], active=True)
- for i, c in enumerate(primary_rails)]
+ DualCarriagesRail(printer, c, dual_rails[i], axes[i], active=True)
+ for i, c in enumerate(primary_rails)
+ ]
self.dual_rails = [
- DualCarriagesRail(printer, c, primary_rails[i],
- axes[i], active=False)
- for i, c in enumerate(dual_rails)]
+ DualCarriagesRail(printer, c, primary_rails[i], axes[i], active=False)
+ for i, c in enumerate(dual_rails)
+ ]
self.dc_rails = collections.OrderedDict(
- [(c.rail.get_name(short=True), c)
- for c in self.primary_rails + self.dual_rails])
+ [
+ (c.rail.get_name(short=True), c)
+ for c in self.primary_rails + self.dual_rails
+ ]
+ )
self.saved_states = {}
self.safe_dist = {}
for i, dc in enumerate(dual_rails):
@@ -42,22 +46,29 @@ class DualCarriages:
self.safe_dist[axis] = safe_dist
continue
pc = primary_rails[i]
- self.safe_dist[axis] = min(abs(pc.position_min - dc.position_min),
- abs(pc.position_max - dc.position_max))
- self.printer.add_object('dual_carriage', self)
+ self.safe_dist[axis] = min(
+ abs(pc.position_min - dc.position_min),
+ abs(pc.position_max - dc.position_max),
+ )
+ self.printer.add_object("dual_carriage", self)
self.printer.register_event_handler("klippy:ready", self._handle_ready)
- gcode = self.printer.lookup_object('gcode')
+ gcode = self.printer.lookup_object("gcode")
gcode.register_command(
- 'SET_DUAL_CARRIAGE', self.cmd_SET_DUAL_CARRIAGE,
- desc=self.cmd_SET_DUAL_CARRIAGE_help)
+ "SET_DUAL_CARRIAGE",
+ self.cmd_SET_DUAL_CARRIAGE,
+ desc=self.cmd_SET_DUAL_CARRIAGE_help,
+ )
gcode.register_command(
- 'SAVE_DUAL_CARRIAGE_STATE',
- self.cmd_SAVE_DUAL_CARRIAGE_STATE,
- desc=self.cmd_SAVE_DUAL_CARRIAGE_STATE_help)
+ "SAVE_DUAL_CARRIAGE_STATE",
+ self.cmd_SAVE_DUAL_CARRIAGE_STATE,
+ desc=self.cmd_SAVE_DUAL_CARRIAGE_STATE_help,
+ )
gcode.register_command(
- 'RESTORE_DUAL_CARRIAGE_STATE',
- self.cmd_RESTORE_DUAL_CARRIAGE_STATE,
- desc=self.cmd_RESTORE_DUAL_CARRIAGE_STATE_help)
+ "RESTORE_DUAL_CARRIAGE_STATE",
+ self.cmd_RESTORE_DUAL_CARRIAGE_STATE,
+ desc=self.cmd_RESTORE_DUAL_CARRIAGE_STATE_help,
+ )
+
def _init_steppers(self, rails):
ffi_main, ffi_lib = chelper.get_ffi()
self.dc_stepper_kinematics = []
@@ -67,8 +78,9 @@ class DualCarriages:
c_steppers = rail.get_steppers()
if not c_steppers:
raise self.printer.config_error(
- "At least one stepper must be "
- "associated with carriage: %s" % rail.get_name())
+ "At least one stepper must be "
+ "associated with carriage: %s" % rail.get_name()
+ )
steppers.update(c_steppers)
for s in steppers:
sk = ffi_main.gc(ffi_lib.dual_carriage_alloc(), ffi_lib.free)
@@ -77,28 +89,34 @@ class DualCarriages:
self.dc_stepper_kinematics.append(sk)
self.orig_stepper_kinematics.append(orig_sk)
s.set_stepper_kinematics(sk)
+
def get_axes(self):
return self.axes
+
def get_primary_rail(self, axis):
for dc_rail in self.dc_rails.values():
if dc_rail.mode == PRIMARY and dc_rail.axis == axis:
return dc_rail.rail
return None
+
def get_dc_rail_wrapper(self, rail):
for dc_rail in self.dc_rails.values():
if dc_rail.rail == rail:
return dc_rail
return None
+
def get_transform(self, rail):
dc_rail = self.get_dc_rail_wrapper(rail)
if dc_rail is not None:
return (dc_rail.scale, dc_rail.offset)
- return (0., 0.)
+ return (0.0, 0.0)
+
def is_active(self, rail):
dc_rail = self.get_dc_rail_wrapper(rail)
return dc_rail.is_active() if dc_rail is not None else False
+
def toggle_active_dc_rail(self, target_dc):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
pos = toolhead.get_position()
kin = toolhead.get_kinematics()
@@ -107,16 +125,17 @@ class DualCarriages:
if dc != target_dc and dc.axis == axis and dc.is_active():
dc.inactivate(pos)
if target_dc.mode != PRIMARY:
- newpos = pos[:axis] + [target_dc.get_axis_position(pos)] \
- + pos[axis+1:]
+ newpos = pos[:axis] + [target_dc.get_axis_position(pos)] + pos[axis + 1 :]
target_dc.activate(PRIMARY, newpos, old_position=pos)
toolhead.set_position(newpos)
kin.update_limits(axis, target_dc.rail.get_range())
+
def home(self, homing_state, axis):
- kin = self.printer.lookup_object('toolhead').get_kinematics()
+ kin = self.printer.lookup_object("toolhead").get_kinematics()
dcs = [dc for dc in self.dc_rails.values() if dc.axis == axis]
- if (self.get_dc_order(dcs[0], dcs[1]) > 0) != \
- dcs[0].rail.get_homing_info().positive_dir:
+ if (self.get_dc_order(dcs[0], dcs[1]) > 0) != dcs[
+ 0
+ ].rail.get_homing_info().positive_dir:
# The second carriage must home first, because the carriages home in
# the same direction and the first carriage homes on the second one
dcs.reverse()
@@ -125,13 +144,20 @@ class DualCarriages:
kin.home_axis(homing_state, axis, dc.rail)
# Restore the original rails ordering
self.toggle_active_dc_rail(dcs[0])
+
def get_status(self, eventtime=None):
- status = {'carriages' : {dc.get_name() : dc.mode
- for dc in self.dc_rails.values()}}
+ status = {
+ "carriages": {dc.get_name(): dc.mode for dc in self.dc_rails.values()}
+ }
if len(self.dc_rails) == 2:
- status.update({('carriage_%d' % (i,)) : dc.mode
- for i, dc in enumerate(self.dc_rails.values())})
+ status.update(
+ {
+ ("carriage_%d" % (i,)): dc.mode
+ for i, dc in enumerate(self.dc_rails.values())
+ }
+ )
return status
+
def get_kin_range(self, toolhead, mode, axis):
pos = toolhead.get_position()
dcs = [dc for dc in self.dc_rails.values() if dc.axis == axis]
@@ -149,21 +175,19 @@ class DualCarriages:
return (range_min, range_max)
if mode == COPY:
- range_min = max(range_min,
- axes_pos[0] - axes_pos[1] + dc1_rail.position_min)
- range_max = min(range_max,
- axes_pos[0] - axes_pos[1] + dc1_rail.position_max)
+ range_min = max(
+ range_min, axes_pos[0] - axes_pos[1] + dc1_rail.position_min
+ )
+ range_max = min(
+ range_max, axes_pos[0] - axes_pos[1] + dc1_rail.position_max
+ )
elif mode == MIRROR:
if self.get_dc_order(dcs[0], dcs[1]) > 0:
- range_min = max(range_min,
- 0.5 * (sum(axes_pos) + safe_dist))
- range_max = min(range_max,
- sum(axes_pos) - dc1_rail.position_min)
+ range_min = max(range_min, 0.5 * (sum(axes_pos) + safe_dist))
+ range_max = min(range_max, sum(axes_pos) - dc1_rail.position_min)
else:
- range_max = min(range_max,
- 0.5 * (sum(axes_pos) - safe_dist))
- range_min = max(range_min,
- sum(axes_pos) - dc1_rail.position_max)
+ range_max = min(range_max, 0.5 * (sum(axes_pos) - safe_dist))
+ range_min = max(range_min, sum(axes_pos) - dc1_rail.position_max)
else:
# mode == PRIMARY
active_idx = 1 if dcs[1].is_active() else 0
@@ -184,6 +208,7 @@ class DualCarriages:
# which actually permits carriage motion.
return (range_min, range_min)
return (range_min, range_max)
+
def get_dc_order(self, first_dc, second_dc):
if first_dc == second_dc:
return 0
@@ -201,8 +226,9 @@ class DualCarriages:
if first_rail.position_endstop > second_rail.position_endstop:
return 1
return -1
+
def activate_dc_mode(self, dc, mode):
- toolhead = self.printer.lookup_object('toolhead')
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
kin = toolhead.get_kinematics()
axis = dc.axis
@@ -214,14 +240,17 @@ class DualCarriages:
self.toggle_active_dc_rail(self.get_dc_rail_wrapper(dc.dual_rail))
dc.activate(mode, toolhead.get_position())
kin.update_limits(axis, self.get_kin_range(toolhead, mode, axis))
+
def _handle_ready(self):
for dc_rail in self.dc_rails.values():
dc_rail.apply_transform()
+
cmd_SET_DUAL_CARRIAGE_help = "Configure the dual carriages mode"
+
def cmd_SET_DUAL_CARRIAGE(self, gcmd):
- carriage_str = gcmd.get('CARRIAGE', None)
+ carriage_str = gcmd.get("CARRIAGE", None)
if carriage_str is None:
- raise gcmd.error('CARRIAGE must be specified')
+ raise gcmd.error("CARRIAGE must be specified")
if carriage_str in self.dc_rails:
dc_rail = self.dc_rails[carriage_str]
else:
@@ -230,65 +259,75 @@ class DualCarriages:
try:
index = int(carriage_str.strip())
if index < 0 or index > 1:
- raise gcmd.error('Invalid CARRIAGE=%d index' % index)
- dc_rail = (self.dual_rails if index
- else self.primary_rails)[0]
+ raise gcmd.error("Invalid CARRIAGE=%d index" % index)
+ dc_rail = (self.dual_rails if index else self.primary_rails)[0]
except ValueError:
pass
if dc_rail is None:
- raise gcmd.error('Invalid CARRIAGE=%s specified' % carriage_str)
- mode = gcmd.get('MODE', PRIMARY).upper()
+ raise gcmd.error("Invalid CARRIAGE=%s specified" % carriage_str)
+ mode = gcmd.get("MODE", PRIMARY).upper()
if mode not in self.VALID_MODES:
raise gcmd.error("Invalid mode=%s specified" % (mode,))
if mode in [COPY, MIRROR]:
if dc_rail in self.primary_rails:
raise gcmd.error(
- "Mode=%s is not supported for carriage=%s" % (
- mode, dc_rail.get_name()))
+ "Mode=%s is not supported for carriage=%s"
+ % (mode, dc_rail.get_name())
+ )
curtime = self.printer.get_reactor().monotonic()
- kin = self.printer.lookup_object('toolhead').get_kinematics()
- axis = 'xyz'[dc_rail.axis]
- if axis not in kin.get_status(curtime)['homed_axes']:
+ kin = self.printer.lookup_object("toolhead").get_kinematics()
+ axis = "xyz"[dc_rail.axis]
+ if axis not in kin.get_status(curtime)["homed_axes"]:
raise gcmd.error(
- "Axis %s must be homed prior to enabling mode=%s" %
- (axis.upper(), mode))
+ "Axis %s must be homed prior to enabling mode=%s"
+ % (axis.upper(), mode)
+ )
self.activate_dc_mode(dc_rail, mode)
- cmd_SAVE_DUAL_CARRIAGE_STATE_help = \
- "Save dual carriages modes and positions"
+
+ cmd_SAVE_DUAL_CARRIAGE_STATE_help = "Save dual carriages modes and positions"
+
def cmd_SAVE_DUAL_CARRIAGE_STATE(self, gcmd):
- state_name = gcmd.get('NAME', 'default')
+ state_name = gcmd.get("NAME", "default")
self.saved_states[state_name] = self.save_dual_carriage_state()
+
def save_dual_carriage_state(self):
- pos = self.printer.lookup_object('toolhead').get_position()
- return {'carriage_modes': {dc.get_name() : dc.mode
- for dc in self.dc_rails.values()},
- 'carriage_positions': {dc.get_name() : dc.get_axis_position(pos)
- for dc in self.dc_rails.values()}}
- cmd_RESTORE_DUAL_CARRIAGE_STATE_help = \
- "Restore dual carriages modes and positions"
+ pos = self.printer.lookup_object("toolhead").get_position()
+ return {
+ "carriage_modes": {dc.get_name(): dc.mode for dc in self.dc_rails.values()},
+ "carriage_positions": {
+ dc.get_name(): dc.get_axis_position(pos)
+ for dc in self.dc_rails.values()
+ },
+ }
+
+ cmd_RESTORE_DUAL_CARRIAGE_STATE_help = "Restore dual carriages modes and positions"
+
def cmd_RESTORE_DUAL_CARRIAGE_STATE(self, gcmd):
- state_name = gcmd.get('NAME', 'default')
+ state_name = gcmd.get("NAME", "default")
saved_state = self.saved_states.get(state_name)
if saved_state is None:
raise gcmd.error("Unknown DUAL_CARRIAGE state: %s" % (state_name,))
- move_speed = gcmd.get_float('MOVE_SPEED', 0., above=0.)
- move = gcmd.get_int('MOVE', 1)
+ move_speed = gcmd.get_float("MOVE_SPEED", 0.0, above=0.0)
+ move = gcmd.get_int("MOVE", 1)
self.restore_dual_carriage_state(saved_state, move, move_speed)
- def restore_dual_carriage_state(self, saved_state, move, move_speed=0.):
- toolhead = self.printer.lookup_object('toolhead')
+
+ def restore_dual_carriage_state(self, saved_state, move, move_speed=0.0):
+ toolhead = self.printer.lookup_object("toolhead")
toolhead.flush_step_generation()
if move:
- homing_speed = 99999999.
+ homing_speed = 99999999.0
move_pos = list(toolhead.get_position())
cur_pos = []
- carriage_positions = saved_state['carriage_positions']
+ carriage_positions = saved_state["carriage_positions"]
dcs = list(self.dc_rails.values())
for dc in dcs:
self.toggle_active_dc_rail(dc)
homing_speed = min(homing_speed, dc.rail.homing_speed)
cur_pos.append(toolhead.get_position())
- dl = [carriage_positions[dc.get_name()] - cur_pos[i][dc.axis]
- for i, dc in enumerate(dcs)]
+ dl = [
+ carriage_positions[dc.get_name()] - cur_pos[i][dc.axis]
+ for i, dc in enumerate(dcs)
+ ]
for axis in self.axes:
dc_ind = [i for i, dc in enumerate(dcs) if dc.axis == axis]
if abs(dl[dc_ind[0]]) >= abs(dl[dc_ind[1]]):
@@ -298,26 +337,29 @@ class DualCarriages:
primary_dc = dcs[primary_ind]
self.toggle_active_dc_rail(primary_dc)
move_pos[axis] = carriage_positions[primary_dc.get_name()]
- dc_mode = INACTIVE if min(abs(dl[primary_ind]),
- abs(dl[secondary_ind])) < .000000001 \
- else COPY if dl[primary_ind] * dl[secondary_ind] > 0 \
- else MIRROR
+ dc_mode = (
+ INACTIVE
+ if min(abs(dl[primary_ind]), abs(dl[secondary_ind])) < 0.000000001
+ else COPY if dl[primary_ind] * dl[secondary_ind] > 0 else MIRROR
+ )
if dc_mode != INACTIVE:
dcs[secondary_ind].activate(dc_mode, cur_pos[primary_ind])
dcs[secondary_ind].override_axis_scaling(
- abs(dl[secondary_ind] / dl[primary_ind]),
- cur_pos[primary_ind])
+ abs(dl[secondary_ind] / dl[primary_ind]), cur_pos[primary_ind]
+ )
toolhead.manual_move(move_pos, move_speed or homing_speed)
toolhead.flush_step_generation()
# Make sure the scaling coefficients are restored with the mode
for dc in dcs:
dc.inactivate(move_pos)
for dc in self.dc_rails.values():
- saved_mode = saved_state['carriage_modes'][dc.get_name()]
+ saved_mode = saved_state["carriage_modes"][dc.get_name()]
self.activate_dc_mode(dc, saved_mode)
+
class DualCarriagesRail:
- ENC_AXES = [b'x', b'y']
+ ENC_AXES = [b"x", b"y"]
+
def __init__(self, printer, rail, dual_rail, axis, active):
self.printer = printer
self.rail = rail
@@ -325,31 +367,39 @@ class DualCarriagesRail:
self.sks = [s.get_stepper_kinematics() for s in rail.get_steppers()]
self.axis = axis
self.mode = (INACTIVE, PRIMARY)[active]
- self.offset = 0.
- self.scale = 1. if active else 0.
+ self.offset = 0.0
+ self.scale = 1.0 if active else 0.0
+
def get_name(self):
return self.rail.get_name()
+
def is_active(self):
return self.mode != INACTIVE
+
def get_axis_position(self, position):
return position[self.axis] * self.scale + self.offset
+
def apply_transform(self):
ffi_main, ffi_lib = chelper.get_ffi()
for sk in self.sks:
ffi_lib.dual_carriage_set_transform(
- sk, self.ENC_AXES[self.axis], self.scale, self.offset)
- self.printer.send_event('dual_carriage:update_kinematics')
+ sk, self.ENC_AXES[self.axis], self.scale, self.offset
+ )
+ self.printer.send_event("dual_carriage:update_kinematics")
+
def activate(self, mode, position, old_position=None):
old_axis_position = self.get_axis_position(old_position or position)
- self.scale = -1. if mode == MIRROR else 1.
+ self.scale = -1.0 if mode == MIRROR else 1.0
self.offset = old_axis_position - position[self.axis] * self.scale
self.apply_transform()
self.mode = mode
+
def inactivate(self, position):
self.offset = self.get_axis_position(position)
- self.scale = 0.
+ self.scale = 0.0
self.apply_transform()
self.mode = INACTIVE
+
def override_axis_scaling(self, new_scale, position):
old_axis_position = self.get_axis_position(position)
self.scale = math.copysign(new_scale, self.scale)
diff --git a/klippy/kinematics/kinematic_stepper.py b/klippy/kinematics/kinematic_stepper.py
index c82f0855..7d983ed1 100644
--- a/klippy/kinematics/kinematic_stepper.py
+++ b/klippy/kinematics/kinematic_stepper.py
@@ -7,86 +7,105 @@
import logging, re
import stepper, chelper
+
def parse_carriages_string(carriages_str, printer_carriages, parse_error):
nxt = 0
- pat = re.compile('[+-]')
- coeffs = [0.] * 3
+ pat = re.compile("[+-]")
+ coeffs = [0.0] * 3
ref_carriages = []
while nxt < len(carriages_str):
- match = pat.search(carriages_str, nxt+1)
+ match = pat.search(carriages_str, nxt + 1)
end = len(carriages_str) if match is None else match.start()
term = carriages_str[nxt:end].strip()
- term_lst = term.split('*')
+ term_lst = term.split("*")
if len(term_lst) not in [1, 2]:
- raise parse_error(
- "Invalid term '%s' in '%s'" % (term, carriages_str))
+ raise parse_error("Invalid term '%s' in '%s'" % (term, carriages_str))
if len(term_lst) == 2:
try:
coeff = float(term_lst[0])
except ValueError:
raise error("Invalid float '%s'" % term_lst[0])
else:
- coeff = -1. if term_lst[0].startswith('-') else 1.
- if term_lst[0].startswith('-') or term_lst[0].startswith('+'):
+ coeff = -1.0 if term_lst[0].startswith("-") else 1.0
+ if term_lst[0].startswith("-") or term_lst[0].startswith("+"):
term_lst[0] = term_lst[0][1:]
c = term_lst[-1]
if c not in printer_carriages:
- raise parse_error("Invalid '%s' carriage referenced in '%s'" %
- (c, carriages_str))
+ raise parse_error(
+ "Invalid '%s' carriage referenced in '%s'" % (c, carriages_str)
+ )
carriage = printer_carriages[c]
j = carriage.get_axis()
if coeffs[j]:
- raise error("Carriage '%s' was referenced multiple times in '%s'" %
- (c, carriages_str))
+ raise error(
+ "Carriage '%s' was referenced multiple times in '%s'"
+ % (c, carriages_str)
+ )
coeffs[j] = coeff
ref_carriages.append(carriage)
nxt = end
return coeffs, ref_carriages
+
class KinematicStepper:
def __init__(self, config, printer_carriages):
self.printer = config.get_printer()
self.stepper = stepper.PrinterStepper(config)
self.kin_coeffs, self.carriages = parse_carriages_string(
- config.get('carriages'), printer_carriages, config.error)
+ config.get("carriages"), printer_carriages, config.error
+ )
if not any(self.kin_coeffs):
raise config.error(
- "'%s' must provide a valid 'carriages' configuration" %
- self.stepper.get_name())
+ "'%s' must provide a valid 'carriages' configuration"
+ % self.stepper.get_name()
+ )
self.stepper.setup_itersolve(
- 'generic_cartesian_stepper_alloc',
- self.kin_coeffs[0], self.kin_coeffs[1], self.kin_coeffs[2])
+ "generic_cartesian_stepper_alloc",
+ self.kin_coeffs[0],
+ self.kin_coeffs[1],
+ self.kin_coeffs[2],
+ )
self.stepper_sk = self.stepper.get_stepper_kinematics()
# Add stepper to the carriages it references
for sc in self.carriages:
sc.add_stepper(self)
+
def get_name(self, short=False):
name = self.stepper.get_name(short)
- if short and name.startswith('stepper '):
+ if short and name.startswith("stepper "):
return name[8:]
return name
+
def get_stepper(self):
return self.stepper
+
def get_kin_coeffs(self):
return tuple(self.kin_coeffs)
+
def get_active_axes(self):
return [i for i, c in enumerate(self.kin_coeffs) if c]
+
def get_carriages(self):
return self.carriages
+
def update_kin_coeffs(self, kin_coeffs):
self.kin_coeffs = kin_coeffs
ffi_main, ffi_lib = chelper.get_ffi()
ffi_lib.generic_cartesian_stepper_set_coeffs(
- self.stepper_sk, kin_coeffs[0], kin_coeffs[1], kin_coeffs[2])
+ self.stepper_sk, kin_coeffs[0], kin_coeffs[1], kin_coeffs[2]
+ )
+
def update_carriages(self, carriages_str, printer_carriages, report_error):
kin_coeffs, carriages = parse_carriages_string(
- carriages_str, printer_carriages,
- report_error or self.printer.command_error)
+ carriages_str, printer_carriages, report_error or self.printer.command_error
+ )
if report_error is not None and not any(kin_coeffs):
raise report_error(
- "A valid string that references at least one carriage"
- " must be provided for '%s'" % self.get_name())
+ "A valid string that references at least one carriage"
+ " must be provided for '%s'" % self.get_name()
+ )
self.carriages = carriages
self.update_kin_coeffs(kin_coeffs)
+
def set_position(self, coord):
self.stepper.set_position(coord)
diff --git a/klippy/kinematics/none.py b/klippy/kinematics/none.py
index d9f9d21d..3c37217c 100644
--- a/klippy/kinematics/none.py
+++ b/klippy/kinematics/none.py
@@ -4,27 +4,36 @@
#
# This file may be distributed under the terms of the GNU GPLv3 license.
+
class NoneKinematics:
def __init__(self, toolhead, config):
- self.axes_minmax = toolhead.Coord(0., 0., 0., 0.)
+ self.axes_minmax = toolhead.Coord(0.0, 0.0, 0.0, 0.0)
+
def get_steppers(self):
return []
+
def calc_position(self, stepper_positions):
return [0, 0, 0]
+
def set_position(self, newpos, homing_axes):
pass
+
def clear_homing_state(self, clear_axes):
pass
+
def home(self, homing_state):
pass
+
def check_move(self, move):
pass
+
def get_status(self, eventtime):
return {
- 'homed_axes': '',
- 'axis_minimum': self.axes_minmax,
- 'axis_maximum': self.axes_minmax,
+ "homed_axes": "",
+ "axis_minimum": self.axes_minmax,
+ "axis_maximum": self.axes_minmax,
}
+
def load_kinematics(toolhead, config):
return NoneKinematics(toolhead, config)
diff --git a/klippy/kinematics/polar.py b/klippy/kinematics/polar.py
index ffa15d83..cd480a42 100644
--- a/klippy/kinematics/polar.py
+++ b/klippy/kinematics/polar.py
@@ -6,55 +6,64 @@
import logging, math
import stepper
+
class PolarKinematics:
def __init__(self, toolhead, config):
# Setup axis steppers
- stepper_bed = stepper.PrinterStepper(config.getsection('stepper_bed'),
- units_in_radians=True)
- rail_arm = stepper.LookupRail(config.getsection('stepper_arm'))
- rail_z = stepper.LookupMultiRail(config.getsection('stepper_z'))
- stepper_bed.setup_itersolve('polar_stepper_alloc', b'a')
- rail_arm.setup_itersolve('polar_stepper_alloc', b'r')
- rail_z.setup_itersolve('cartesian_stepper_alloc', b'z')
+ stepper_bed = stepper.PrinterStepper(
+ config.getsection("stepper_bed"), units_in_radians=True
+ )
+ rail_arm = stepper.LookupRail(config.getsection("stepper_arm"))
+ rail_z = stepper.LookupMultiRail(config.getsection("stepper_z"))
+ stepper_bed.setup_itersolve("polar_stepper_alloc", b"a")
+ rail_arm.setup_itersolve("polar_stepper_alloc", b"r")
+ rail_z.setup_itersolve("cartesian_stepper_alloc", b"z")
self.rails = [rail_arm, rail_z]
- self.steppers = [stepper_bed] + [ s for r in self.rails
- for s in r.get_steppers() ]
+ self.steppers = [stepper_bed] + [
+ s for r in self.rails for s in r.get_steppers()
+ ]
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
max_velocity, max_accel = toolhead.get_max_velocity()
self.max_z_velocity = config.getfloat(
- 'max_z_velocity', max_velocity, above=0., maxval=max_velocity)
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
self.max_z_accel = config.getfloat(
- 'max_z_accel', max_accel, above=0., maxval=max_accel)
+ "max_z_accel", max_accel, above=0.0, maxval=max_accel
+ )
self.limit_z = (1.0, -1.0)
- self.limit_xy2 = -1.
+ self.limit_xy2 = -1.0
max_xy = self.rails[0].get_range()[1]
min_z, max_z = self.rails[1].get_range()
- self.axes_min = toolhead.Coord(-max_xy, -max_xy, min_z, 0.)
- self.axes_max = toolhead.Coord(max_xy, max_xy, max_z, 0.)
+ self.axes_min = toolhead.Coord(-max_xy, -max_xy, min_z, 0.0)
+ self.axes_max = toolhead.Coord(max_xy, max_xy, max_z, 0.0)
+
def get_steppers(self):
return list(self.steppers)
+
def calc_position(self, stepper_positions):
bed_angle = stepper_positions[self.steppers[0].get_name()]
arm_pos = stepper_positions[self.rails[0].get_name()]
z_pos = stepper_positions[self.rails[1].get_name()]
- return [math.cos(bed_angle) * arm_pos, math.sin(bed_angle) * arm_pos,
- z_pos]
+ return [math.cos(bed_angle) * arm_pos, math.sin(bed_angle) * arm_pos, z_pos]
+
def set_position(self, newpos, homing_axes):
for s in self.steppers:
s.set_position(newpos)
if "z" in homing_axes:
self.limit_z = self.rails[1].get_range()
if "x" in homing_axes and "y" in homing_axes:
- self.limit_xy2 = self.rails[0].get_range()[1]**2
+ self.limit_xy2 = self.rails[0].get_range()[1] ** 2
+
def clear_homing_state(self, clear_axes):
if "x" in clear_axes or "y" in clear_axes:
# X and Y cannot be cleared separately
- self.limit_xy2 = -1.
+ self.limit_xy2 = -1.0
if "z" in clear_axes:
self.limit_z = (1.0, -1.0)
+
def _home_axis(self, homing_state, axis, rail):
# Determine movement
position_min, position_max = rail.get_range()
@@ -62,7 +71,7 @@ class PolarKinematics:
homepos = [None, None, None, None]
homepos[axis] = hi.position_endstop
if axis == 0:
- homepos[1] = 0.
+ homepos[1] = 0.0
forcepos = list(homepos)
if hi.positive_dir:
forcepos[axis] -= hi.position_endstop - position_min
@@ -70,6 +79,7 @@ class PolarKinematics:
forcepos[axis] += position_max - hi.position_endstop
# Perform homing
homing_state.home_rails([rail], forcepos, homepos)
+
def home(self, homing_state):
# Always home XY together
homing_axes = homing_state.get_axes()
@@ -86,11 +96,12 @@ class PolarKinematics:
self._home_axis(homing_state, 0, self.rails[0])
if home_z:
self._home_axis(homing_state, 2, self.rails[1])
+
def check_move(self, move):
end_pos = move.end_pos
- xy2 = end_pos[0]**2 + end_pos[1]**2
+ xy2 = end_pos[0] ** 2 + end_pos[1] ** 2
if xy2 > self.limit_xy2:
- if self.limit_xy2 < 0.:
+ if self.limit_xy2 < 0.0:
raise move.move_error("Must home axis first")
raise move.move_error()
if move.axes_d[2]:
@@ -100,16 +111,17 @@ class PolarKinematics:
raise move.move_error()
# Move with Z - update velocity and accel for slower Z axis
z_ratio = move.move_d / abs(move.axes_d[2])
- move.limit_speed(self.max_z_velocity * z_ratio,
- self.max_z_accel * z_ratio)
+ move.limit_speed(self.max_z_velocity * z_ratio, self.max_z_accel * z_ratio)
+
def get_status(self, eventtime):
- xy_home = "xy" if self.limit_xy2 >= 0. else ""
+ xy_home = "xy" if self.limit_xy2 >= 0.0 else ""
z_home = "z" if self.limit_z[0] <= self.limit_z[1] else ""
return {
- 'homed_axes': xy_home + z_home,
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": xy_home + z_home,
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return PolarKinematics(toolhead, config)
diff --git a/klippy/kinematics/rotary_delta.py b/klippy/kinematics/rotary_delta.py
index 732a89a8..7bc3a4d8 100644
--- a/klippy/kinematics/rotary_delta.py
+++ b/klippy/kinematics/rotary_delta.py
@@ -6,102 +6,130 @@
import math, logging
import stepper, mathutil, chelper
+
class RotaryDeltaKinematics:
def __init__(self, toolhead, config):
# Setup tower rails
- stepper_configs = [config.getsection('stepper_' + a) for a in 'abc']
+ stepper_configs = [config.getsection("stepper_" + a) for a in "abc"]
rail_a = stepper.LookupRail(
- stepper_configs[0], need_position_minmax=False,
- units_in_radians=True)
+ stepper_configs[0], need_position_minmax=False, units_in_radians=True
+ )
a_endstop = rail_a.get_homing_info().position_endstop
rail_b = stepper.LookupRail(
- stepper_configs[1], need_position_minmax=False,
- default_position_endstop=a_endstop, units_in_radians=True)
+ stepper_configs[1],
+ need_position_minmax=False,
+ default_position_endstop=a_endstop,
+ units_in_radians=True,
+ )
rail_c = stepper.LookupRail(
- stepper_configs[2], need_position_minmax=False,
- default_position_endstop=a_endstop, units_in_radians=True)
+ stepper_configs[2],
+ need_position_minmax=False,
+ default_position_endstop=a_endstop,
+ units_in_radians=True,
+ )
self.rails = [rail_a, rail_b, rail_c]
# Read config
max_velocity, max_accel = toolhead.get_max_velocity()
- self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
- above=0., maxval=max_velocity)
- shoulder_radius = config.getfloat('shoulder_radius', above=0.)
- shoulder_height = config.getfloat('shoulder_height', above=0.)
- a_upper_arm = stepper_configs[0].getfloat('upper_arm_length', above=0.)
+ self.max_z_velocity = config.getfloat(
+ "max_z_velocity", max_velocity, above=0.0, maxval=max_velocity
+ )
+ shoulder_radius = config.getfloat("shoulder_radius", above=0.0)
+ shoulder_height = config.getfloat("shoulder_height", above=0.0)
+ a_upper_arm = stepper_configs[0].getfloat("upper_arm_length", above=0.0)
upper_arms = [
- sconfig.getfloat('upper_arm_length', a_upper_arm, above=0.)
- for sconfig in stepper_configs]
- a_lower_arm = stepper_configs[0].getfloat('lower_arm_length', above=0.)
+ sconfig.getfloat("upper_arm_length", a_upper_arm, above=0.0)
+ for sconfig in stepper_configs
+ ]
+ a_lower_arm = stepper_configs[0].getfloat("lower_arm_length", above=0.0)
lower_arms = [
- sconfig.getfloat('lower_arm_length', a_lower_arm, above=0.)
- for sconfig in stepper_configs]
- angles = [sconfig.getfloat('angle', angle)
- for sconfig, angle in zip(stepper_configs, [30., 150., 270.])]
+ sconfig.getfloat("lower_arm_length", a_lower_arm, above=0.0)
+ for sconfig in stepper_configs
+ ]
+ angles = [
+ sconfig.getfloat("angle", angle)
+ for sconfig, angle in zip(stepper_configs, [30.0, 150.0, 270.0])
+ ]
# Setup rotary delta calibration helper
- endstops = [rail.get_homing_info().position_endstop
- for rail in self.rails]
- stepdists = [rail.get_steppers()[0].get_step_dist()
- for rail in self.rails]
+ endstops = [rail.get_homing_info().position_endstop for rail in self.rails]
+ stepdists = [rail.get_steppers()[0].get_step_dist() for rail in self.rails]
self.calibration = RotaryDeltaCalibration(
- shoulder_radius, shoulder_height, angles, upper_arms, lower_arms,
- endstops, stepdists)
+ shoulder_radius,
+ shoulder_height,
+ angles,
+ upper_arms,
+ lower_arms,
+ endstops,
+ stepdists,
+ )
# Setup iterative solver
for r, a, ua, la in zip(self.rails, angles, upper_arms, lower_arms):
- r.setup_itersolve('rotary_delta_stepper_alloc',
- shoulder_radius, shoulder_height,
- math.radians(a), ua, la)
+ r.setup_itersolve(
+ "rotary_delta_stepper_alloc",
+ shoulder_radius,
+ shoulder_height,
+ math.radians(a),
+ ua,
+ la,
+ )
for s in self.get_steppers():
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
self.need_home = True
- self.limit_xy2 = -1.
- eangles = [r.calc_position_from_coord([0., 0., ep])
- for r, ep in zip(self.rails, endstops)]
- self.home_position = tuple(
- self.calibration.actuator_to_cartesian(eangles))
+ self.limit_xy2 = -1.0
+ eangles = [
+ r.calc_position_from_coord([0.0, 0.0, ep])
+ for r, ep in zip(self.rails, endstops)
+ ]
+ self.home_position = tuple(self.calibration.actuator_to_cartesian(eangles))
self.max_z = min(endstops)
- self.min_z = config.getfloat('minimum_z_position', 0, maxval=self.max_z)
+ self.min_z = config.getfloat("minimum_z_position", 0, maxval=self.max_z)
min_ua = min([shoulder_radius + ua for ua in upper_arms])
min_la = min([la - shoulder_radius for la in lower_arms])
- self.max_xy2 = min(min_ua, min_la)**2
- arm_z = [self.calibration.elbow_coord(i, ea)[2]
- for i, ea in enumerate(eangles)]
+ self.max_xy2 = min(min_ua, min_la) ** 2
+ arm_z = [self.calibration.elbow_coord(i, ea)[2] for i, ea in enumerate(eangles)]
self.limit_z = min([az - la for az, la in zip(arm_z, lower_arms)])
logging.info(
"Delta max build height %.2fmm (radius tapered above %.2fmm)"
- % (self.max_z, self.limit_z))
+ % (self.max_z, self.limit_z)
+ )
max_xy = math.sqrt(self.max_xy2)
- self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.)
- self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.)
- self.set_position([0., 0., 0.], "")
+ self.axes_min = toolhead.Coord(-max_xy, -max_xy, self.min_z, 0.0)
+ self.axes_max = toolhead.Coord(max_xy, max_xy, self.max_z, 0.0)
+ self.set_position([0.0, 0.0, 0.0], "")
+
def get_steppers(self):
return [s for rail in self.rails for s in rail.get_steppers()]
+
def calc_position(self, stepper_positions):
spos = [stepper_positions[rail.get_name()] for rail in self.rails]
return self.calibration.actuator_to_cartesian(spos)
+
def set_position(self, newpos, homing_axes):
for rail in self.rails:
rail.set_position(newpos)
- self.limit_xy2 = -1.
+ self.limit_xy2 = -1.0
if homing_axes == "xyz":
self.need_home = False
+
def clear_homing_state(self, clear_axes):
# Clearing homing state for each axis individually is not implemented
if clear_axes:
self.limit_xy2 = -1
self.need_home = True
+
def home(self, homing_state):
# All axes are homed simultaneously
homing_state.set_axes([0, 1, 2])
forcepos = list(self.home_position)
- #min_angles = [-.5 * math.pi] * 3
- #forcepos[2] = self.calibration.actuator_to_cartesian(min_angles)[2]
- forcepos[2] = -1.
+ # min_angles = [-.5 * math.pi] * 3
+ # forcepos[2] = self.calibration.actuator_to_cartesian(min_angles)[2]
+ forcepos[2] = -1.0
homing_state.home_rails(self.rails, forcepos, self.home_position)
+
def check_move(self, move):
end_pos = move.end_pos
- end_xy2 = end_pos[0]**2 + end_pos[1]**2
+ end_xy2 = end_pos[0] ** 2 + end_pos[1] ** 2
if end_xy2 <= self.limit_xy2 and not move.axes_d[2]:
# Normal XY move
return
@@ -110,30 +138,44 @@ class RotaryDeltaKinematics:
end_z = end_pos[2]
limit_xy2 = self.max_xy2
if end_z > self.limit_z:
- limit_xy2 = min(limit_xy2, (self.max_z - end_z)**2)
+ limit_xy2 = min(limit_xy2, (self.max_z - end_z) ** 2)
if end_xy2 > limit_xy2 or end_z > self.max_z or end_z < self.min_z:
# Move out of range - verify not a homing move
- if (end_pos[:2] != self.home_position[:2]
- or end_z < self.min_z or end_z > self.home_position[2]):
+ if (
+ end_pos[:2] != self.home_position[:2]
+ or end_z < self.min_z
+ or end_z > self.home_position[2]
+ ):
raise move.move_error()
- limit_xy2 = -1.
+ limit_xy2 = -1.0
if move.axes_d[2]:
move.limit_speed(self.max_z_velocity, move.accel)
- limit_xy2 = -1.
+ limit_xy2 = -1.0
self.limit_xy2 = limit_xy2
+
def get_status(self, eventtime):
return {
- 'homed_axes': '' if self.need_home else 'xyz',
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "" if self.need_home else "xyz",
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def get_calibration(self):
return self.calibration
+
# Rotary delta parameter calibration for DELTA_CALIBRATE tool
class RotaryDeltaCalibration:
- def __init__(self, shoulder_radius, shoulder_height, angles,
- upper_arms, lower_arms, endstops, stepdists):
+ def __init__(
+ self,
+ shoulder_radius,
+ shoulder_height,
+ angles,
+ upper_arms,
+ lower_arms,
+ endstops,
+ stepdists,
+ ):
self.shoulder_radius = shoulder_radius
self.shoulder_height = shoulder_height
self.angles = angles
@@ -143,39 +185,56 @@ class RotaryDeltaCalibration:
self.stepdists = stepdists
# Calculate the absolute angle of each endstop
ffi_main, self.ffi_lib = chelper.get_ffi()
- self.sks = [ffi_main.gc(self.ffi_lib.rotary_delta_stepper_alloc(
- shoulder_radius, shoulder_height, math.radians(a), ua, la),
- self.ffi_lib.free)
- for a, ua, la in zip(angles, upper_arms, lower_arms)]
+ self.sks = [
+ ffi_main.gc(
+ self.ffi_lib.rotary_delta_stepper_alloc(
+ shoulder_radius, shoulder_height, math.radians(a), ua, la
+ ),
+ self.ffi_lib.free,
+ )
+ for a, ua, la in zip(angles, upper_arms, lower_arms)
+ ]
self.abs_endstops = [
- self.ffi_lib.itersolve_calc_position_from_coord(sk, 0., 0., es)
- for sk, es in zip(self.sks, endstops)]
+ self.ffi_lib.itersolve_calc_position_from_coord(sk, 0.0, 0.0, es)
+ for sk, es in zip(self.sks, endstops)
+ ]
+
def coordinate_descent_params(self, is_extended):
# Determine adjustment parameters (for use with coordinate_descent)
- adj_params = ('shoulder_height', 'endstop_a', 'endstop_b', 'endstop_c')
+ adj_params = ("shoulder_height", "endstop_a", "endstop_b", "endstop_c")
if is_extended:
- adj_params += ('shoulder_radius', 'angle_a', 'angle_b')
- params = { 'shoulder_radius': self.shoulder_radius,
- 'shoulder_height': self.shoulder_height }
- for i, axis in enumerate('abc'):
- params['angle_'+axis] = self.angles[i]
- params['upper_arm_'+axis] = self.upper_arms[i]
- params['lower_arm_'+axis] = self.lower_arms[i]
- params['endstop_'+axis] = self.endstops[i]
- params['stepdist_'+axis] = self.stepdists[i]
+ adj_params += ("shoulder_radius", "angle_a", "angle_b")
+ params = {
+ "shoulder_radius": self.shoulder_radius,
+ "shoulder_height": self.shoulder_height,
+ }
+ for i, axis in enumerate("abc"):
+ params["angle_" + axis] = self.angles[i]
+ params["upper_arm_" + axis] = self.upper_arms[i]
+ params["lower_arm_" + axis] = self.lower_arms[i]
+ params["endstop_" + axis] = self.endstops[i]
+ params["stepdist_" + axis] = self.stepdists[i]
return adj_params, params
+
def new_calibration(self, params):
# Create a new calibration object from coordinate_descent params
- shoulder_radius = params['shoulder_radius']
- shoulder_height = params['shoulder_height']
- angles = [params['angle_'+a] for a in 'abc']
- upper_arms = [params['upper_arm_'+a] for a in 'abc']
- lower_arms = [params['lower_arm_'+a] for a in 'abc']
- endstops = [params['endstop_'+a] for a in 'abc']
- stepdists = [params['stepdist_'+a] for a in 'abc']
+ shoulder_radius = params["shoulder_radius"]
+ shoulder_height = params["shoulder_height"]
+ angles = [params["angle_" + a] for a in "abc"]
+ upper_arms = [params["upper_arm_" + a] for a in "abc"]
+ lower_arms = [params["lower_arm_" + a] for a in "abc"]
+ endstops = [params["endstop_" + a] for a in "abc"]
+ stepdists = [params["stepdist_" + a] for a in "abc"]
return RotaryDeltaCalibration(
- shoulder_radius, shoulder_height, angles, upper_arms, lower_arms,
- endstops, stepdists)
+ shoulder_radius,
+ shoulder_height,
+ angles,
+ upper_arms,
+ lower_arms,
+ endstops,
+ stepdists,
+ )
+
def elbow_coord(self, elbow_id, spos):
# Calculate elbow position in coordinate system at shoulder joint
sj_elbow_x = self.upper_arms[elbow_id] * math.cos(spos)
@@ -186,43 +245,59 @@ class RotaryDeltaCalibration:
y = (sj_elbow_x + self.shoulder_radius) * math.sin(angle)
z = sj_elbow_y + self.shoulder_height
return (x, y, z)
+
def actuator_to_cartesian(self, spos):
sphere_coords = [self.elbow_coord(i, sp) for i, sp in enumerate(spos)]
lower_arm2 = [la**2 for la in self.lower_arms]
return mathutil.trilateration(sphere_coords, lower_arm2)
+
def get_position_from_stable(self, stable_position):
# Return cartesian coordinates for the given stable_position
- spos = [ea - sp * sd
- for ea, sp, sd in zip(self.abs_endstops, stable_position,
- self.stepdists)]
+ spos = [
+ ea - sp * sd
+ for ea, sp, sd in zip(self.abs_endstops, stable_position, self.stepdists)
+ ]
return self.actuator_to_cartesian(spos)
+
def calc_stable_position(self, coord):
# Return a stable_position from a cartesian coordinate
- pos = [ self.ffi_lib.itersolve_calc_position_from_coord(
- sk, coord[0], coord[1], coord[2])
- for sk in self.sks ]
- return [(ep - sp) / sd
- for sd, ep, sp in zip(self.stepdists, self.abs_endstops, pos)]
+ pos = [
+ self.ffi_lib.itersolve_calc_position_from_coord(
+ sk, coord[0], coord[1], coord[2]
+ )
+ for sk in self.sks
+ ]
+ return [
+ (ep - sp) / sd for sd, ep, sp in zip(self.stepdists, self.abs_endstops, pos)
+ ]
+
def save_state(self, configfile):
# Save the current parameters (for use with SAVE_CONFIG)
- configfile.set('printer', 'shoulder_radius', "%.6f"
- % (self.shoulder_radius,))
- configfile.set('printer', 'shoulder_height', "%.6f"
- % (self.shoulder_height,))
- for i, axis in enumerate('abc'):
- configfile.set('stepper_'+axis, 'angle', "%.6f" % (self.angles[i],))
- configfile.set('stepper_'+axis, 'position_endstop',
- "%.6f" % (self.endstops[i],))
+ configfile.set("printer", "shoulder_radius", "%.6f" % (self.shoulder_radius,))
+ configfile.set("printer", "shoulder_height", "%.6f" % (self.shoulder_height,))
+ for i, axis in enumerate("abc"):
+ configfile.set("stepper_" + axis, "angle", "%.6f" % (self.angles[i],))
+ configfile.set(
+ "stepper_" + axis, "position_endstop", "%.6f" % (self.endstops[i],)
+ )
gcode = configfile.get_printer().lookup_object("gcode")
gcode.respond_info(
"stepper_a: position_endstop: %.6f angle: %.6f\n"
"stepper_b: position_endstop: %.6f angle: %.6f\n"
"stepper_c: position_endstop: %.6f angle: %.6f\n"
"shoulder_radius: %.6f shoulder_height: %.6f"
- % (self.endstops[0], self.angles[0],
- self.endstops[1], self.angles[1],
- self.endstops[2], self.angles[2],
- self.shoulder_radius, self.shoulder_height))
+ % (
+ self.endstops[0],
+ self.angles[0],
+ self.endstops[1],
+ self.angles[1],
+ self.endstops[2],
+ self.angles[2],
+ self.shoulder_radius,
+ self.shoulder_height,
+ )
+ )
+
def load_kinematics(toolhead, config):
return RotaryDeltaKinematics(toolhead, config)
diff --git a/klippy/kinematics/winch.py b/klippy/kinematics/winch.py
index 47bc6855..290a7f27 100644
--- a/klippy/kinematics/winch.py
+++ b/klippy/kinematics/winch.py
@@ -5,54 +5,63 @@
# This file may be distributed under the terms of the GNU GPLv3 license.
import stepper, mathutil
+
class WinchKinematics:
def __init__(self, toolhead, config):
# Setup steppers at each anchor
self.steppers = []
self.anchors = []
for i in range(26):
- name = 'stepper_' + chr(ord('a') + i)
+ name = "stepper_" + chr(ord("a") + i)
if i >= 3 and not config.has_section(name):
break
stepper_config = config.getsection(name)
s = stepper.PrinterStepper(stepper_config)
self.steppers.append(s)
- a = tuple([stepper_config.getfloat('anchor_' + n) for n in 'xyz'])
+ a = tuple([stepper_config.getfloat("anchor_" + n) for n in "xyz"])
self.anchors.append(a)
- s.setup_itersolve('winch_stepper_alloc', *a)
+ s.setup_itersolve("winch_stepper_alloc", *a)
s.set_trapq(toolhead.get_trapq())
toolhead.register_step_generator(s.generate_steps)
# Setup boundary checks
acoords = list(zip(*self.anchors))
- self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
- self.axes_max = toolhead.Coord(*[max(a) for a in acoords], e=0.)
- self.set_position([0., 0., 0.], "")
+ self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.0)
+ self.axes_max = toolhead.Coord(*[max(a) for a in acoords], e=0.0)
+ self.set_position([0.0, 0.0, 0.0], "")
+
def get_steppers(self):
return list(self.steppers)
+
def calc_position(self, stepper_positions):
# Use only first three steppers to calculate cartesian position
pos = [stepper_positions[rail.get_name()] for rail in self.steppers[:3]]
- return mathutil.trilateration(self.anchors[:3], [sp*sp for sp in pos])
+ return mathutil.trilateration(self.anchors[:3], [sp * sp for sp in pos])
+
def set_position(self, newpos, homing_axes):
for s in self.steppers:
s.set_position(newpos)
+
def clear_homing_state(self, clear_axes):
# XXX - homing not implemented
pass
+
def home(self, homing_state):
# XXX - homing not implemented
homing_state.set_axes([0, 1, 2])
- homing_state.set_homed_position([0., 0., 0.])
+ homing_state.set_homed_position([0.0, 0.0, 0.0])
+
def check_move(self, move):
# XXX - boundary checks and speed limits not implemented
pass
+
def get_status(self, eventtime):
# XXX - homed_checks and rail limits not implemented
return {
- 'homed_axes': 'xyz',
- 'axis_minimum': self.axes_min,
- 'axis_maximum': self.axes_max,
+ "homed_axes": "xyz",
+ "axis_minimum": self.axes_min,
+ "axis_maximum": self.axes_max,
}
+
def load_kinematics(toolhead, config):
return WinchKinematics(toolhead, config)