Auto-tune Linear Advance on the MK4S via the nozzle loadcell.
1. Printer
2. Filament & temps
3. Sweep parameters
Max Flow — parameters
Free-air stepped flow ramp: extrudes at increasing volumetric flow
rates and watches the loadcell back-pressure for the point where
the hotend can't melt fast enough. Temperature + filament come from
the shared cards above. This extrudes into the air, so expect a
string/bird's-nest at high flow.
Advanced (settle, warm-up & tare)
Live loadcell
idle ·
·
K=— ·
0%
Live Map — print file
Upload a sliced ASCII .gcode (in PrusaSlicer, untick
Export as binary G-code). We inject the metric preamble and
print it, mapping the live nozzle loadcell force onto the gcode as it
prints — the part geometry is untouched. The map is saved so you can
scrub the layers again later.
idle ·
·
0%
Live Map — 2D nozzle-load map
Top-down view of one layer. The path is coloured by nozzle force
(force mode) over a faint gcode backdrop; switch to feature mode to
see what each region is (perimeter / infill / …). Live, it follows the
layer being printed; grab the slider to study a finished layer, then
Jump to live to resume.
no file loaded
Live Map — review & saved runs
Layer detection
How samples were assigned to layers. The staircase is the median-filtered
streamed z; vertical lines are the detected layer boundaries
(green = z step snapped to a travel,
orange = z step only,
red = arc-length fallback).
Nozzle load by feature
Tared force per feature type, aggregated over the run.
Touch Probe — lateral sensitivity test
Step 1: characterise the sensor before building the probe.
This pushes the nozzle tip sideways (±X / ±Y) into a rigid
target and records the Z loadcell while it creeps in, to find out
whether lateral contact registers a clean, repeatable force at all.
The loadcell is an axial (Z) sensor; lateral force reaches it only as
a bending moment, so this is an open question we have to measure.
Safe by design: the creep is short (the
creep distance is a hard cap on how far we can push past
contact) and slow, and the nozzle is cold by default — so
even if nothing stops the move, the worst case is a gentle lean, not a
crash. Set up: clamp a rigid object so the nozzle
just touches it partway through the creep, then point the probe at it
with the standoff + axis below. Contact should land after the first
~20 % of the creep (that early part is used as the no-contact
baseline).
Touch Probe — results
Force vs. probe-axis position, every touch overlaid. A usable lateral
probe needs a clean knee: force flat (no contact), then a
sharp, monotonic rise — landing at the same position
on every touch. Vertical dashed lines mark the detected contact point
per touch; tight clustering = good repeatability.
Verdict
—
Touch Probe — per-touch viewer
Inspect one touch at a time (or use ← / → keys). The dashed line is
the detected contact position; the shaded band before it is the
no-contact baseline window used to tare + set the noise floor.
no run loaded
Max Flow — results
Steady-state nozzle force vs flow rate. The fitted curve is the
well-behaved power law F = a·Qᵇ + c; markers show where force
breaks upward off it (melting limit), where per-level noise rises
(instability onset), and where force collapses (extruder skipping).
Recommended max = earliest soft breakdown, derated ~15%. The amber
curve (right axis) is the per-step force σ (noise) — its axis is
scaled to the quiet low-flow steps, so the post-skip σ explosion
clips off the top rather than flattening the rest; hover for true
values.
Recommended max flow
—
mm³/s — earliest soft breakdown, derated
Max Flow — per-step viewer
Sanity-check that each flow level was sliced correctly. Pick a step
on the left (or use ← / → keys). The shaded band is the measured
window (the settle portion is discarded); the dashed line is the
steady-state force used for that point on the curve above.
no run loaded
Segment browser — step-response regions
ported: markniu/bd_pressure
Step through every low-high-low segment (low_n + high_n + low_{n+1})
and inspect what the analyser saw. The 8-region step-response
decomposition (R1 baseline … R8 recovery tail) is adapted from
markniu's
bd_pressure Klipper module, re-run offline on the Prusa loadcell
stream. Click a K on the left to focus it;
use prev/next buttons or ← / → keys to step through that K's segments.
Segments excluded by the auto-quality gate are flagged with the reason.
no run loaded
Loadcell baseline (pre-sweep)
Held-idle window captured after heat-up, before priming. Mean is
the static head load; std is the noise floor; drift is the
difference between the 2nd-half and 1st-half means (high drift =
thermal not yet settled). If mean ≈ 0, the firmware tared; if not,
analysis still works (per-window mean-subtraction handles it) but
an absolute scale becomes unreadable.
mean
—
std
—
drift
—
samples
—
Results — composite step-response cost
metrics: markniu/bd_pressureweighting: this project
Composite cost = weighted sum of normalised per-K step-response
metrics (median over included segments). The per-segment metrics
follow bd_pressure's transient decomposition; the live re-weightable
composite and the weight optimiser below are this project's
extension. Sliders adjust weights live — K_opt recomputes without
re-running the printer. The per-metric K_opts on the right show
whether the metrics agree.
Recommended K (composite argmin)
—
parabolic interp around the cost minimum
Ground truth from your test print — used by the weight optimiser.
per-metric K_opt
argmin of each metric in isolation
Weight optimiser
this project
Tune the composite-cost weights against ground-truth K values
you've measured from test prints. Annotate saved runs with your
measured best K (the field above the weight sliders), then run
this to fit weights that predict those K's under bootstrap
resampling of the segments. Loss = bias² + α · variance summed
across runs.
annotated runs available
—
Metrics included in the search (uncheck to exclude a metric — its weight is locked at 0):
RMS error
—
median |Δ|
—
mean bootstrap σ
—
runs used
—
duration
—
Per-run prediction
run
K_user
K_pred
± σ
Δ
n_segs
Recommended weights
metric
shipped default
optimised (max=1)
Results — zero-crossing fits (comparison)
Alternative K_opt estimators to cross-check the composite cost
above; each panel is tagged with its origin. The integral-area fits
port the Snapmaker U1 flow-calibrator math
(150 ms transition window ≈ one melt-pressure τ, per-cycle local
mean); phase-lag and signed fall-area are this project's own
estimators (phase-lag follows the Ellis 3DP under/over-PA framing).
All fits consume the SAME cycle list as the step-response analysis
above, so a cycle it excluded (dropouts in the critical zone,
sample rate < 40 Hz, signal-below-noise) is excluded here too.
Hover a point to see how many cycles survived for that K. Phase-lag
and legacy-integral are kept for visual comparison only. The
phase-lag fit is a quadratic (the lag response saturates at high K,
which drags a straight line's crossing high); when the lag never
crosses zero in-range it falls back to a global linear fit and the
model label below reads "linear fallback".
Phase-lag fit
this project
—
model=— · R²=—
Integral-area fit (centered)
ported: Snapmaker U1
—
slope=— · R²=—
Integral (legacy sign(dC/dt))
Snapmaker U1 · original form
—
slope=— · R²=—
Signed fall-area fit (overshoot↔lag)
this project
—
slope=— · R²=—
Per-K median of fall_signed_area = ∫(force − baseline)
over the fall+recovery window. +area = force
lingers above baseline (slow bleed / drool → under-PA);
−area = force dips below (undershoot → over-PA).
Crosses zero at K_opt — a sign-based estimator independent of the
composite-cost argmin.
Diagnostics
Live UDP stats (auto-refreshes every second). If a metric you M331'd is
missing or running at ~0 Hz, that name probably doesn't exist on this
firmware build — the M331 handler writes "Metric not found" to serial
and PrusaLink discards the reply, so the failure is invisible from
gcode. Throttle is compile-time (the 4th arg of
each METRIC_DEF); M331/M332 can only enable/disable, not
re-rate. Power-cycling the printer reverts every metric to its
compile-time enable/disable default.