cartesian: Do acceleration and lookahead on requested coordinates
Perform all acceleration calculations and lookahead checks in millimeters using the cartesian coordinate system of the request. The conversion to step coordinates is now done at the time of the step timing creation. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
ae8d57e650
commit
b3e8b430e5
|
@ -13,48 +13,30 @@ import lookahead, stepper, homing
|
||||||
StepList = (0, 1, 2, 3)
|
StepList = (0, 1, 2, 3)
|
||||||
|
|
||||||
class Move:
|
class Move:
|
||||||
def __init__(self, kin, relsteps, speed):
|
def __init__(self, kin, pos, move_d, axes_d, speed, accel):
|
||||||
self.kin = kin
|
self.kin = kin
|
||||||
self.relsteps = relsteps
|
self.pos = tuple(pos)
|
||||||
self.junction_max = self.junction_start_max = self.junction_delta = 0.
|
self.axes_d = axes_d
|
||||||
# Calculate requested distance to travel (in mm)
|
self.move_d = move_d
|
||||||
steppers = self.kin.steppers
|
self.junction_max = speed**2
|
||||||
absrelsteps = [abs(relsteps[i]) for i in StepList]
|
self.junction_delta = 2.0 * move_d * accel
|
||||||
stepper_d = [absrelsteps[i] * steppers[i].step_dist
|
self.junction_start_max = 0.
|
||||||
for i in StepList]
|
|
||||||
self.move_d = math.sqrt(sum([d*d for d in stepper_d[:3]]))
|
|
||||||
if not self.move_d:
|
|
||||||
self.move_d = stepper_d[3]
|
|
||||||
if not self.move_d:
|
|
||||||
return
|
|
||||||
# Limit velocity to max for each stepper
|
|
||||||
velocity_factor = min([steppers[i].max_step_velocity / absrelsteps[i]
|
|
||||||
for i in StepList if absrelsteps[i]])
|
|
||||||
move_v = min(speed, velocity_factor * self.move_d)
|
|
||||||
self.junction_max = move_v**2
|
|
||||||
# Find max acceleration factor
|
|
||||||
accel_factor = min([steppers[i].max_step_accel / absrelsteps[i]
|
|
||||||
for i in StepList if absrelsteps[i]])
|
|
||||||
accel = min(self.kin.max_accel, accel_factor * self.move_d)
|
|
||||||
self.junction_delta = 2.0 * self.move_d * accel
|
|
||||||
def calc_junction(self, prev_move):
|
def calc_junction(self, prev_move):
|
||||||
# Find max start junction velocity using approximated
|
# Find max start junction velocity using approximated
|
||||||
# centripetal velocity as described at:
|
# centripetal velocity as described at:
|
||||||
# https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/
|
# https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/
|
||||||
if not prev_move.move_d or self.relsteps[2] or prev_move.relsteps[2]:
|
if not prev_move.move_d:
|
||||||
return
|
return
|
||||||
steppers = self.kin.steppers
|
junction_cos_theta = -((self.axes_d[0] * prev_move.axes_d[0]
|
||||||
junction_cos_theta = -sum([
|
+ self.axes_d[1] * prev_move.axes_d[1])
|
||||||
self.relsteps[i] * prev_move.relsteps[i] * steppers[i].step_dist**2
|
/ (self.move_d * prev_move.move_d))
|
||||||
for i in range(2)]) / (self.move_d * prev_move.move_d)
|
|
||||||
if junction_cos_theta > 0.999999:
|
if junction_cos_theta > 0.999999:
|
||||||
return
|
return
|
||||||
junction_cos_theta = max(junction_cos_theta, -0.999999)
|
junction_cos_theta = max(junction_cos_theta, -0.999999)
|
||||||
sin_theta_d2 = math.sqrt(0.5*(1.0-junction_cos_theta));
|
sin_theta_d2 = math.sqrt(0.5*(1.0-junction_cos_theta));
|
||||||
R = self.kin.junction_deviation * sin_theta_d2 / (1.0 - sin_theta_d2)
|
R = self.kin.junction_deviation * sin_theta_d2 / (1.0 - sin_theta_d2)
|
||||||
accel = self.junction_delta / (2.0 * self.move_d)
|
|
||||||
self.junction_start_max = min(
|
self.junction_start_max = min(
|
||||||
accel * R, self.junction_max, prev_move.junction_max)
|
R * self.kin.max_xy_accel, self.junction_max, prev_move.junction_max)
|
||||||
def process(self, junction_start, junction_end):
|
def process(self, junction_start, junction_end):
|
||||||
# Determine accel, cruise, and decel portions of the move
|
# Determine accel, cruise, and decel portions of the move
|
||||||
junction_cruise = self.junction_max
|
junction_cruise = self.junction_max
|
||||||
|
@ -84,9 +66,12 @@ class Move:
|
||||||
# Calculate step times for the move
|
# Calculate step times for the move
|
||||||
next_move_time = self.kin.get_next_move_time()
|
next_move_time = self.kin.get_next_move_time()
|
||||||
for i in StepList:
|
for i in StepList:
|
||||||
steps = self.relsteps[i]
|
new_step_pos = int(self.pos[i]*self.kin.steppers[i].inv_step_dist
|
||||||
|
+ 0.5)
|
||||||
|
steps = new_step_pos - self.kin.stepper_pos[i]
|
||||||
if not steps:
|
if not steps:
|
||||||
continue
|
continue
|
||||||
|
self.kin.stepper_pos[i] = new_step_pos
|
||||||
sdir = 0
|
sdir = 0
|
||||||
if steps < 0:
|
if steps < 0:
|
||||||
sdir = 1
|
sdir = 1
|
||||||
|
@ -133,13 +118,15 @@ class CartKinematics:
|
||||||
steppers = ['stepper_x', 'stepper_y', 'stepper_z', 'stepper_e']
|
steppers = ['stepper_x', 'stepper_y', 'stepper_z', 'stepper_e']
|
||||||
self.steppers = [stepper.PrinterStepper(printer, config.getsection(n))
|
self.steppers = [stepper.PrinterStepper(printer, config.getsection(n))
|
||||||
for n in steppers]
|
for n in steppers]
|
||||||
self.max_accel = min(s.max_step_accel*s.step_dist
|
self.max_xy_speed = min(s.max_step_velocity*s.step_dist
|
||||||
for s in self.steppers[:2]) # XXX
|
for s in self.steppers[:2])
|
||||||
dummy_move = Move(self, [0]*len(self.steppers), 0.)
|
self.max_xy_accel = min(s.max_step_accel*s.step_dist
|
||||||
dummy_move.junction_max = 0.
|
for s in self.steppers[:2])
|
||||||
self.junction_deviation = config.getfloat('junction_deviation', 0.02)
|
self.junction_deviation = config.getfloat('junction_deviation', 0.02)
|
||||||
|
dummy_move = Move(self, [0.]*4, 0., [0.]*4, 0., 0.)
|
||||||
self.move_queue = lookahead.MoveQueue(dummy_move)
|
self.move_queue = lookahead.MoveQueue(dummy_move)
|
||||||
self.pos = [0, 0, 0, 0]
|
self.commanded_pos = [0., 0., 0., 0.]
|
||||||
|
self.stepper_pos = [0, 0, 0, 0]
|
||||||
# Print time tracking
|
# Print time tracking
|
||||||
self.buffer_time_high = config.getfloat('buffer_time_high', 5.000)
|
self.buffer_time_high = config.getfloat('buffer_time_high', 5.000)
|
||||||
self.buffer_time_low = config.getfloat('buffer_time_low', 0.150)
|
self.buffer_time_low = config.getfloat('buffer_time_low', 0.150)
|
||||||
|
@ -214,24 +201,50 @@ class CartKinematics:
|
||||||
self.print_time, buffer_time, self.print_time_stall)
|
self.print_time, buffer_time, self.print_time_stall)
|
||||||
# Movement commands
|
# Movement commands
|
||||||
def get_position(self):
|
def get_position(self):
|
||||||
return [self.pos[i] * self.steppers[i].step_dist
|
return list(self.commanded_pos)
|
||||||
for i in StepList]
|
|
||||||
def set_position(self, newpos):
|
def set_position(self, newpos):
|
||||||
self.pos = [int(newpos[i]*self.steppers[i].inv_step_dist + 0.5)
|
self.move_queue.flush()
|
||||||
for i in StepList]
|
self.commanded_pos[:] = newpos
|
||||||
|
self.stepper_pos = [int(newpos[i]*self.steppers[i].inv_step_dist + 0.5)
|
||||||
|
for i in StepList]
|
||||||
|
def _move_with_z(self, newpos, axes_d, speed):
|
||||||
|
self.move_queue.flush()
|
||||||
|
# Limit velocity to max for each stepper
|
||||||
|
move_d = math.sqrt(sum([d*d for d in axes_d[:3]]))
|
||||||
|
velocity_factor = min(
|
||||||
|
[self.steppers[i].max_step_velocity
|
||||||
|
* self.steppers[i].step_dist / abs(axes_d[i])
|
||||||
|
for i in StepList if axes_d[i]])
|
||||||
|
speed = min(speed, self.max_xy_speed, velocity_factor * move_d)
|
||||||
|
# Find max acceleration factor
|
||||||
|
accel_factor = min(
|
||||||
|
[self.steppers[i].max_step_accel
|
||||||
|
* self.steppers[i].step_dist / abs(axes_d[i])
|
||||||
|
for i in StepList if axes_d[i]])
|
||||||
|
accel = min(self.max_xy_accel, accel_factor * move_d)
|
||||||
|
move = Move(self, newpos, move_d, axes_d, speed, accel)
|
||||||
|
move.process(0., 0.)
|
||||||
|
def _move_only_e(self, newpos, axes_d, speed):
|
||||||
|
self.move_queue.flush()
|
||||||
|
s = self.steppers[3]
|
||||||
|
speed = min(speed, self.max_xy_speed, s.max_step_velocity * s.step_dist)
|
||||||
|
accel = min(self.max_xy_accel, s.max_step_accel * s.step_dist)
|
||||||
|
move = Move(self, newpos, abs(axes_d[3]), axes_d, speed, accel)
|
||||||
|
move.process(0., 0.)
|
||||||
def move(self, newpos, speed, sloppy=False):
|
def move(self, newpos, speed, sloppy=False):
|
||||||
# Round to closest step position
|
axes_d = [newpos[i] - self.commanded_pos[i] for i in StepList]
|
||||||
newpos = [int(newpos[i]*self.steppers[i].inv_step_dist + 0.5)
|
self.commanded_pos[:] = newpos
|
||||||
for i in StepList]
|
if axes_d[2]:
|
||||||
relsteps = [newpos[i] - self.pos[i] for i in StepList]
|
self._move_with_z(newpos, axes_d, speed)
|
||||||
self.pos = newpos
|
|
||||||
if relsteps == [0]*len(newpos):
|
|
||||||
# no move
|
|
||||||
return
|
return
|
||||||
#logging.debug("; dist %s @ %d\n" % (
|
move_d = math.sqrt(axes_d[0]**2 + axes_d[1]**2)
|
||||||
# [newpos[i]*self.steppers[i].step_dist for i in StepList], speed))
|
if not move_d:
|
||||||
# Create move and queue it
|
if axes_d[3]:
|
||||||
move = Move(self, relsteps, speed)
|
self._move_only_e(newpos, axes_d, speed)
|
||||||
|
return
|
||||||
|
# Common xy move - create move and queue it
|
||||||
|
speed = min(speed, self.max_xy_speed)
|
||||||
|
move = Move(self, newpos, move_d, axes_d, speed, self.max_xy_accel)
|
||||||
move.calc_junction(self.move_queue.prev_move())
|
move.calc_junction(self.move_queue.prev_move())
|
||||||
self.move_queue.add_move(move)
|
self.move_queue.add_move(move)
|
||||||
def home(self, axis):
|
def home(self, axis):
|
||||||
|
|
Loading…
Reference in New Issue