extruder: Initial support for "smoothed pressure advance"

Support averaging the extruder position over a time range to "smooth
out" the velocity changes that occur during pressure advance.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2019-11-05 16:21:06 -05:00
parent da06e185fb
commit 6d0c55b6c1
7 changed files with 108 additions and 175 deletions

View File

@ -130,13 +130,12 @@ filament_diameter: 3.500
# during deceleration. It is measured in millimeters per
# millimeter/second. The default is 0, which disables pressure
# advance.
#pressure_advance_lookahead_time: 0.010
# A time (in seconds) to "look ahead" at future extrusion moves when
# calculating pressure advance. This is used to reduce the
# application of pressure advance during cornering moves that would
# otherwise cause retraction followed immediately by pressure
# buildup. This setting only applies if pressure_advance is
# non-zero. The default is 0.010 (10 milliseconds).
#pressure_advance_smooth_time: 0.040
# A time range (in seconds) to use when calculating the average
# extruder velocity for pressure advance. A larger value results in
# smoother extruder movements. This parameter may not exceed 200ms.
# This setting only applies if pressure_advance is non-zero. The
# default is 0.040 (40 milliseconds).
#
# The remaining variables describe the extruder heater.
heater_pin: ar10

View File

@ -10,6 +10,9 @@ All dates in this document are approximate.
chip id by default. Update the "serial" setting in the "mcu" config
section accordingly.
20191121: The pressure_advance_lookahead_time parameter has been
removed. See example.cfg for alternate configuration settings.
20191112: The tmc stepper driver virtual enable capability is now
automatically enabled if the stepper does not have a dedicated stepper
enable pin. Remove references to tmcXXXX:virtual_enable from the

View File

@ -156,9 +156,9 @@ The following standard commands are supported:
Sets the target temperature for a heater. If a target temperature is
not supplied, the target is 0.
- `SET_PRESSURE_ADVANCE [EXTRUDER=<config_name>] [ADVANCE=<pressure_advance>]
[ADVANCE_LOOKAHEAD_TIME=<pressure_advance_lookahead_time>]`:
Set pressure advance parameters. If EXTRUDER is not specified, it
defaults to the active extruder.
[SMOOTH_TIME=<pressure_advance_smooth_time>]`: Set pressure advance
parameters. If EXTRUDER is not specified, it defaults to the active
extruder.
- `STEPPER_BUZZ STEPPER=<config_name>`: Move the given stepper forward
one mm and then backward one mm, repeated 10 times. This is a
diagnostic tool to help verify stepper connectivity.

View File

@ -22,12 +22,11 @@ Use a slicer to generate g-code for the large hollow square found in
speed (eg, 100mm/s), zero infill, and a coarse layer height (the layer
height should be around 75% of the nozzle diameter).
Prepare for the test by issuing the following G-Code commands:
Prepare for the test by issuing the following G-Code command:
```
SET_VELOCITY_LIMIT SQUARE_CORNER_VELOCITY=1 ACCEL=500
SET_PRESSURE_ADVANCE ADVANCE_LOOKAHEAD_TIME=0
```
These commands make the nozzle travel slower through corners and they
This command makes the nozzle travel slower through corners to
emphasize the effects of extruder pressure. Then for printers with a
direct drive extruder run the command:
```
@ -118,17 +117,6 @@ Important Notes
enough torque to push the required filament. If this occurs, either
use a lower acceleration value or disable pressure advance.
* The pressure_advance_lookahead_time parameter controls how far in
advance to check if a head slow-down is immediately followed by a
speed-up - it reduces pointless pressure changes in the head. It is
recommended to follow the steps above so that it is set to zero
during tuning and to use the default (0.010) during normal prints.
It is possible to tune this setting - higher values will reduce the
amount of pressure change in the nozzle during cornering, but
setting it too high can cause blobbing during cornering. (Tuning
this value is unlikely to impact ooze.) The default of 10ms should
work well on most printers.
* Once pressure advance is tuned in Klipper, it may still be useful to
configure a small retract value in the slicer (eg, 0.75mm) and to
utilize the slicer's "wipe on retract option" if available. These

View File

@ -91,10 +91,8 @@ defs_kin_winch = """
defs_kin_extruder = """
struct stepper_kinematics *extruder_stepper_alloc(void);
void extruder_add_move(struct trapq *tq, double print_time
, double accel_t, double cruise_t, double decel_t, double start_e_pos
, double start_v, double cruise_v, double accel
, double extra_accel_v, double extra_decel_v);
void extruder_set_pressure(struct stepper_kinematics *sk
, double pressure_advance, double half_smooth_time);
"""
defs_serialqueue = """

View File

@ -4,6 +4,7 @@
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <stddef.h> // offsetof
#include <stdlib.h> // malloc
#include <string.h> // memset
#include "compiler.h" // __visible
@ -11,70 +12,58 @@
#include "pyhelper.h" // errorf
#include "trapq.h" // move_get_distance
struct extruder_stepper {
struct stepper_kinematics sk;
double pressure_advance_factor, half_smooth_time, inv_smooth_time;
};
static double
extruder_calc_position(struct stepper_kinematics *sk, struct move *m
, double move_time)
{
return m->start_pos.x + move_get_distance(m, move_time);
struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk);
double hst = es->half_smooth_time;
if (!hst)
// Pressure advance not enabled
return m->start_pos.x + move_get_distance(m, move_time);
// Calculate average position over smooth_time
double area = trapq_integrate(m, 'x', move_time - hst, move_time + hst);
double base_pos = area * es->inv_smooth_time;
// Calculate position 'half_smooth_time' in the past
double start_time = move_time - hst;
struct move *sm = trapq_find_move(m, &start_time);
double start_dist = move_get_distance(sm, start_time);
double pa_start_pos = sm->start_pos.y + (sm->axes_r.y ? start_dist : 0.);
// Calculate position 'half_smooth_time' in the future
double end_time = move_time + hst;
struct move *em = trapq_find_move(m, &end_time);
double end_dist = move_get_distance(em, end_time);
double pa_end_pos = em->start_pos.y + (em->axes_r.y ? end_dist : 0.);
// Calculate position with pressure advance
return base_pos + (pa_end_pos - pa_start_pos) * es->pressure_advance_factor;
}
void __visible
extruder_set_pressure(struct stepper_kinematics *sk
, double pressure_advance, double half_smooth_time)
{
struct extruder_stepper *es = container_of(sk, struct extruder_stepper, sk);
if (! half_smooth_time) {
es->pressure_advance_factor = es->half_smooth_time = 0.;
return;
}
es->sk.scan_past = es->sk.scan_future = half_smooth_time;
es->half_smooth_time = half_smooth_time;
es->inv_smooth_time = .5 / half_smooth_time;
es->pressure_advance_factor = pressure_advance * es->inv_smooth_time;
}
struct stepper_kinematics * __visible
extruder_stepper_alloc(void)
{
struct stepper_kinematics *sk = malloc(sizeof(*sk));
memset(sk, 0, sizeof(*sk));
sk->calc_position_cb = extruder_calc_position;
sk->active_flags = AF_X;
return sk;
}
// Populate a 'struct move' with an extruder velocity trapezoid
void __visible
extruder_add_move(struct trapq *tq, double print_time
, double accel_t, double cruise_t, double decel_t
, double start_e_pos
, double start_v, double cruise_v, double accel
, double extra_accel_v, double extra_decel_v)
{
struct coord start_pos, axes_r;
start_pos.x = start_e_pos;
axes_r.x = 1.;
start_pos.y = start_pos.z = axes_r.y = axes_r.z = 0.;
if (accel_t) {
struct move *m = move_alloc();
m->print_time = print_time;
m->move_t = accel_t;
m->start_v = start_v + extra_accel_v;
m->half_accel = .5 * accel;
m->start_pos = start_pos;
m->axes_r = axes_r;
trapq_add_move(tq, m);
print_time += accel_t;
start_pos.x += move_get_distance(m, accel_t);
}
if (cruise_t) {
struct move *m = move_alloc();
m->print_time = print_time;
m->move_t = cruise_t;
m->start_v = cruise_v;
m->half_accel = 0.;
m->start_pos = start_pos;
m->axes_r = axes_r;
trapq_add_move(tq, m);
print_time += cruise_t;
start_pos.x += move_get_distance(m, cruise_t);
}
if (decel_t) {
struct move *m = move_alloc();
m->print_time = print_time;
m->move_t = decel_t;
m->start_v = cruise_v + extra_decel_v;
m->half_accel = -.5 * accel;
m->start_pos = start_pos;
m->axes_r = axes_r;
trapq_add_move(tq, m);
}
struct extruder_stepper *es = malloc(sizeof(*es));
memset(es, 0, sizeof(*es));
es->sk.calc_position_cb = extruder_calc_position;
es->sk.active_flags = AF_X;
return &es->sk;
}

View File

@ -46,19 +46,23 @@ class PrinterExtruder:
config, 'activate_gcode', '')
self.deactivate_gcode = gcode_macro.load_template(
config, 'deactivate_gcode', '')
self.pressure_advance = config.getfloat(
'pressure_advance', 0., minval=0.)
self.pressure_advance_lookahead_time = config.getfloat(
'pressure_advance_lookahead_time', 0.010, minval=0.)
self.extrude_pos = 0.
self.pressure_advance = self.pressure_advance_smooth_time = 0.
pressure_advance = config.getfloat('pressure_advance', 0., minval=0.)
smooth_time = config.getfloat('pressure_advance_smooth_time',
0.040, above=0., maxval=.200)
self.extrude_pos = self.extrude_pa_pos = 0.
# Setup iterative solver
ffi_main, ffi_lib = chelper.get_ffi()
self.extruder_add_move = ffi_lib.extruder_add_move
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
self.trapq_append = ffi_lib.trapq_append
self.trapq_free_moves = ffi_lib.trapq_free_moves
self.stepper.setup_itersolve('extruder_stepper_alloc')
self.sk_extruder = ffi_main.gc(ffi_lib.extruder_stepper_alloc(),
ffi_lib.free)
self.stepper.set_stepper_kinematics(self.sk_extruder)
self.stepper.set_trapq(self.trapq)
toolhead.register_step_generator(self.stepper.generate_steps)
self.extruder_set_pressure = ffi_lib.extruder_set_pressure
self._set_pressure_advance(pressure_advance, smooth_time)
# Register commands
gcode = self.printer.lookup_object('gcode')
if self.name == 'extruder':
@ -71,12 +75,24 @@ class PrinterExtruder:
desc=self.cmd_SET_PRESSURE_ADVANCE_help)
def update_move_time(self, flush_time):
self.trapq_free_moves(self.trapq, flush_time)
def _set_pressure_advance(self, pressure_advance, smooth_time):
old_smooth_time = self.pressure_advance_smooth_time * .5
if not self.pressure_advance:
old_smooth_time = 0.
new_smooth_time = smooth_time * .5
if not pressure_advance:
new_smooth_time = 0.
toolhead = self.printer.lookup_object("toolhead")
toolhead.note_step_generation_scan_time(new_smooth_time,
old_delay=old_smooth_time)
self.extruder_set_pressure(self.sk_extruder,
pressure_advance, new_smooth_time)
self.pressure_advance = pressure_advance
self.pressure_advance_smooth_time = smooth_time
def get_status(self, eventtime):
return dict(
self.get_heater().get_status(eventtime),
pressure_advance=self.pressure_advance,
lookahead_time=self.pressure_advance_lookahead_time
)
return dict(self.get_heater().get_status(eventtime),
pressure_advance=self.pressure_advance,
smooth_time=self.pressure_advance_smooth_time)
def get_heater(self):
return self.heater
def set_active(self, print_time, is_active):
@ -89,7 +105,6 @@ class PrinterExtruder:
return self.heater.stats(eventtime)
def check_move(self, move):
move.extrude_r = move.axes_r[3]
move.extrude_max_corner_v = 0.
if not self.heater.can_extrude:
raise homing.EndstopError(
"Extrude below minimum temp\n"
@ -107,7 +122,6 @@ class PrinterExtruder:
elif move.extrude_r > self.max_extrude_ratio:
if move.axes_d[3] <= self.nozzle_diameter * self.max_extrude_ratio:
# Permit extrusion if amount extruded is tiny
move.extrude_r = self.max_extrude_ratio
return
area = move.axes_r[3] * self.filament_area
logging.debug("Overextrude: %s vs %s (area=%.3f dist=%.3f)",
@ -129,99 +143,41 @@ class PrinterExtruder:
and abs(move.move_d * prev_move.extrude_r - extrude) >= .001):
# Extrude ratio between moves is too different
return 0.
move.extrude_r = prev_move.extrude_r
return move.max_cruise_v2
def lookahead(self, moves, flush_count, lazy):
lookahead_t = self.pressure_advance_lookahead_time
if not self.pressure_advance or not lookahead_t:
return flush_count
# Calculate max_corner_v - the speed the head will accelerate
# to after cornering.
for i in range(flush_count):
move = moves[i]
if not move.decel_t:
continue
cruise_v = move.cruise_v
max_corner_v = 0.
sum_t = lookahead_t
for j in range(i+1, flush_count):
fmove = moves[j]
if not fmove.max_start_v2:
break
if fmove.cruise_v > max_corner_v:
if (not max_corner_v
and not fmove.accel_t and not fmove.cruise_t):
# Start timing after any full decel moves
continue
if sum_t >= fmove.accel_t:
max_corner_v = fmove.cruise_v
else:
max_corner_v = max(
max_corner_v, fmove.start_v + fmove.accel * sum_t)
if max_corner_v >= cruise_v:
break
sum_t -= fmove.accel_t + fmove.cruise_t + fmove.decel_t
if sum_t <= 0.:
break
else:
if lazy:
return i
move.extrude_max_corner_v = max_corner_v
return flush_count
def move(self, print_time, move):
axis_d = move.axes_d[3]
axis_r = move.axes_r[3]
accel = move.accel * axis_r
start_v = move.start_v * axis_r
cruise_v = move.cruise_v * axis_r
accel_t, cruise_t, decel_t = move.accel_t, move.cruise_t, move.decel_t
# Update for pressure advance
extra_accel_v = extra_decel_v = 0.
start_pos = self.extrude_pos
if (axis_d >= 0. and (move.axes_d[0] or move.axes_d[1])
and self.pressure_advance):
# Calculate extra_accel_v
pressure_advance = self.pressure_advance * move.extrude_r
prev_pressure_d = start_pos - move.start_pos[3]
if accel_t:
npd = move.cruise_v * pressure_advance
extra_accel_d = npd - prev_pressure_d
if extra_accel_d > 0.:
extra_accel_v = extra_accel_d / accel_t
axis_d += extra_accel_d
prev_pressure_d += extra_accel_d
# Calculate extra_decel_v
emcv = move.extrude_max_corner_v
if decel_t and emcv < move.cruise_v:
npd = max(emcv, move.end_v) * pressure_advance
extra_decel_d = npd - prev_pressure_d
if extra_decel_d < 0.:
axis_d += extra_decel_d
extra_decel_v = extra_decel_d / decel_t
# Generate steps
self.extruder_add_move(
self.trapq, print_time, accel_t, cruise_t, decel_t, start_pos,
start_v, cruise_v, accel, extra_accel_v, extra_decel_v)
self.extrude_pos = start_pos + axis_d
is_pa = 0.
if axis_r > 0. and (move.axes_d[0] or move.axes_d[1]):
is_pa = 1.
# Queue movement (x is extruder movement, y is movement with pa)
self.trapq_append(self.trapq, print_time,
move.accel_t, move.cruise_t, move.decel_t,
move.start_pos[3], self.extrude_pa_pos, 0.,
1., is_pa, 0.,
start_v, cruise_v, accel)
self.extrude_pos = move.end_pos[3]
if is_pa:
self.extrude_pa_pos += move.axes_d[3]
cmd_SET_PRESSURE_ADVANCE_help = "Set pressure advance parameters"
def cmd_default_SET_PRESSURE_ADVANCE(self, params):
extruder = self.printer.lookup_object('toolhead').get_extruder()
extruder.cmd_SET_PRESSURE_ADVANCE(params)
def cmd_SET_PRESSURE_ADVANCE(self, params):
self.printer.lookup_object('toolhead').get_last_move_time()
gcode = self.printer.lookup_object('gcode')
pressure_advance = gcode.get_float(
'ADVANCE', params, self.pressure_advance, minval=0.)
pressure_advance_lookahead_time = gcode.get_float(
'ADVANCE_LOOKAHEAD_TIME', params,
self.pressure_advance_lookahead_time, minval=0.)
self.pressure_advance = pressure_advance
self.pressure_advance_lookahead_time = pressure_advance_lookahead_time
smooth_time = gcode.get_float(
'SMOOTH_TIME', params,
self.pressure_advance_smooth_time, minval=0., maxval=.200)
self._set_pressure_advance(pressure_advance, smooth_time)
msg = ("pressure_advance: %.6f\n"
"pressure_advance_lookahead_time: %.6f" % (
pressure_advance, pressure_advance_lookahead_time))
"pressure_advance_smooth_time: %.6f" % (
pressure_advance, smooth_time))
self.printer.set_rollover_info(self.name, "%s: %s" % (self.name, msg))
gcode.respond_info(msg, log=False)