From dabffcc22c84c92878bbd680a57e23874ded757d Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 14 Dec 2019 10:30:25 -0500 Subject: [PATCH] kin_extruder: Convert pressure advance to use "weighted average" Signed-off-by: Kevin O'Connor --- docs/Kinematics.md | 9 ++-- docs/img/pressure-velocity.png | Bin 24301 -> 24394 bytes klippy/chelper/kin_extruder.c | 73 ++++++++++++++++++++++++--------- scripts/graph_extruder.py | 11 +++-- 4 files changed, 66 insertions(+), 27 deletions(-) diff --git a/docs/Kinematics.md b/docs/Kinematics.md index f4b84550..ee501dff 100644 --- a/docs/Kinematics.md +++ b/docs/Kinematics.md @@ -284,8 +284,8 @@ rate, the more filament must be pushed in during acceleration to account for pressure. During head deceleration the extra filament is retracted (the extruder will have a negative velocity). -The "smoothing" is implemented by averaging the extruder position over -a small time period (as specified by the +The "smoothing" is implemented using a weighted average of the +extruder position over a small time period (as specified by the `pressure_advance_smooth_time` config parameter). This averaging can span multiple g-code moves. Note how the extruder motor will start moving prior to the nominal start of the first extrusion move and will @@ -294,6 +294,7 @@ continue to move after the nominal end of the last extrusion move. Key formula for "smoothed pressure advance": ``` smooth_pa_position(t) = - ( definitive_integral(pa_position, from=t-smooth_time/2, to=t+smooth_time/2) - / smooth_time ) + ( definitive_integral(pa_position(x) * (smooth_time/2 - abs(t - x)) * dx, + from=t-smooth_time/2, to=t+smooth_time/2) + / (smooth_time/2)^2 ) ``` diff --git a/docs/img/pressure-velocity.png b/docs/img/pressure-velocity.png index 89e91ab44d9e5c551077c1ba0b5720b8ab6ff03a..5268a94866a3ef4f94147c22cc117e9a89414b2b 100644 GIT binary patch literal 24394 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
k2e3k&&;F;Ix+*sUwgjwb7Q)J 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 literal 24301 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�yHve&97^Rcb(>cPz!iYWsmZ78~HA=Zu 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 diff --git a/klippy/chelper/kin_extruder.c b/klippy/chelper/kin_extruder.c index d630f7dd..2fce006b 100644 --- a/klippy/chelper/kin_extruder.c +++ b/klippy/chelper/kin_extruder.c @@ -12,55 +12,90 @@ #include "pyhelper.h" // errorf #include "trapq.h" // move_get_distance -// Calculate the definitive integral of the move distance +// Without pressure advance, the extruder stepper position is: +// extruder_position(t) = nominal_position(t) +// When pressure advance is enabled, additional filament is pushed +// into the extruder during acceleration (and retracted during +// deceleration). The formula is: +// pa_position(t) = (nominal_position(t) +// + pressure_advance * nominal_velocity(t)) +// Which is then "smoothed" using a weighted average: +// smooth_position(t) = ( +// definitive_integral(pa_position(x) * (smooth_time/2 - abs(t-x)) * dx, +// from=t-smooth_time/2, to=t+smooth_time/2) +// / ((smooth_time/2)**2)) + +// Calculate the definitive integral of the motion formula: +// position(t) = base + t * (start_v + t * half_accel) static double -move_integrate_distance(struct move *m, double start, double end) +extruder_integrate(double base, double start_v, double half_accel + , double start, double end) { - double half_v = .5 * m->start_v, sixth_a = (1. / 3.) * m->half_accel; - double si = start * start * (half_v + sixth_a * start); - double ei = end * end * (half_v + sixth_a * end); + double half_v = .5 * start_v, sixth_a = (1. / 3.) * half_accel; + double si = start * (base + start * (half_v + start * sixth_a)); + double ei = end * (base + end * (half_v + end * sixth_a)); return ei - si; } -// Calculate the definitive integral of extruder with pressure advance +// Calculate the definitive integral of time weighted position: +// weighted_position(t) = t * (base + t * (start_v + t * half_accel)) static double -pa_move_integrate(struct move *m, double start, double end) +extruder_integrate_time(double base, double start_v, double half_accel + , double start, double end) +{ + double half_b = .5 * base, third_v = (1. / 3.) * start_v; + double eighth_a = .25 * half_accel; + double si = start * start * (half_b + start * (third_v + start * eighth_a)); + double ei = end * end * (half_b + end * (third_v + end * eighth_a)); + return ei - si; +} + +// Calculate the definitive integral of extruder for a given move +static double +pa_move_integrate(struct move *m, double start, double end, double time_offset) { if (start < 0.) start = 0.; if (end > m->move_t) end = m->move_t; + // Calculate base position and velocity with pressure advance double pressure_advance = m->axes_r.y; - double avg_v = m->start_v + (start + end) * m->half_accel; - double pa_add = pressure_advance * avg_v; - double base = (m->start_pos.x + pa_add) * (end - start); - return base + move_integrate_distance(m, start, end); + double base = m->start_pos.x + pressure_advance * m->start_v; + double start_v = m->start_v + pressure_advance * 2. * m->half_accel; + // Calculate definitive integral + double ha = m->half_accel; + double iext = extruder_integrate(base, start_v, ha, start, end); + double wgt_ext = extruder_integrate_time(base, start_v, ha, start, end); + return wgt_ext - time_offset * iext; } // Calculate the definitive integral of the extruder over a range of moves static double -pa_range_integrate(struct move *m, double start, double end) +pa_range_integrate(struct move *m, double move_time, double hst) { - double res = pa_move_integrate(m, start, end); + // Calculate integral for the current move + double res = 0., start = move_time - hst, end = move_time + hst; + res += pa_move_integrate(m, start, move_time, start); + res -= pa_move_integrate(m, move_time, end, end); // Integrate over previous moves struct move *prev = m; while (unlikely(start < 0.)) { prev = list_prev_entry(prev, node); start += prev->move_t; - res += pa_move_integrate(prev, start, prev->move_t); + res += pa_move_integrate(prev, start, prev->move_t, start); } // Integrate over future moves while (unlikely(end > m->move_t)) { end -= m->move_t; m = list_next_entry(m, node); - res += pa_move_integrate(m, 0., end); + res -= pa_move_integrate(m, 0., end, end); } return res; } struct extruder_stepper { struct stepper_kinematics sk; - double half_smooth_time, inv_smooth_time; + double half_smooth_time, inv_half_smooth_time2; }; static double @@ -73,8 +108,8 @@ extruder_calc_position(struct stepper_kinematics *sk, struct move *m // Pressure advance not enabled return m->start_pos.x + move_get_distance(m, move_time); // Apply pressure advance and average over smooth_time - double area = pa_range_integrate(m, move_time - hst, move_time + hst); - return area * es->inv_smooth_time; + double area = pa_range_integrate(m, move_time, hst); + return area * es->inv_half_smooth_time2; } void __visible @@ -86,7 +121,7 @@ extruder_set_smooth_time(struct stepper_kinematics *sk, double smooth_time) es->sk.gen_steps_pre_active = es->sk.gen_steps_post_active = hst; if (! hst) return; - es->inv_smooth_time = 1. / smooth_time; + es->inv_half_smooth_time2 = 1. / (hst * hst); } struct stepper_kinematics * __visible diff --git a/scripts/graph_extruder.py b/scripts/graph_extruder.py index 6223bb11..5c0fb5cf 100755 --- a/scripts/graph_extruder.py +++ b/scripts/graph_extruder.py @@ -67,13 +67,16 @@ def calc_pa_raw(t, positions): i = time_to_index(t) return positions[i] + pa * (positions[i+1] - positions[i]) -def calc_pa_smooth(t, positions): +def calc_pa_weighted(t, positions): + base_index = time_to_index(t) start_index = time_to_index(t - PA_HALF_SMOOTH_T) + 1 end_index = time_to_index(t + PA_HALF_SMOOTH_T) + diff = .5 * (end_index - start_index) pa = PRESSURE_ADVANCE * INV_SEG_TIME - pa_data = [positions[i] + pa * (positions[i+1] - positions[i]) + pa_data = [(positions[i] + pa * (positions[i+1] - positions[i])) + * (diff - abs(i-base_index)) for i in range(start_index, end_index)] - return sum(pa_data) / (end_index - start_index) + return sum(pa_data) / diff**2 ###################################################################### @@ -92,7 +95,7 @@ def plot_motion(): pa_positions = [calc_pa_raw(t, positions) for t in times] pa_velocities = gen_deriv(pa_positions) # Smoothed motion - sm_positions = [calc_pa_smooth(t, positions) for t in times] + sm_positions = [calc_pa_weighted(t, positions) for t in times] sm_velocities = gen_deriv(sm_positions) # Build plot shift_times = [t - MARGIN_TIME for t in times]