MarlinFirmware / Marlin

Marlin is an optimized firmware for RepRap 3D printers based on the Arduino platform. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.
https://marlinfw.org
GNU General Public License v3.0
16.14k stars 19.21k forks source link

Collecting ideas and advises for LIN_ADVANCE v2 #9048

Closed Sebastianv650 closed 5 years ago

Sebastianv650 commented 6 years ago

This will be a quite long one but if you like to solve problems and you want to help me improving LIN_ADVANCE I would be very pleased if you continue reading and dive with me into the details of planner code, stepper code and nozzle pressure correction. I will explain as much of my findings and knowledge I got since I started the LIN_ADVANCE implementation as possible so it should be a quite good start point for everyone who want's to touch LIN_ADVANCE related code or help improving it.

Why we need a LIN_ADVANCE "version 2"

While LIN_ADVANCE works great for me and most (I have no real numbers) others, issue reports here are showing it's not working for everyone. Especialy bowden printers struggle with high needed K values and it doesn't work at all for kinematic printers due to the way the extruder speed is calculated.

It's possible to track this down to two main problems:

The extruder step resolution, or the way how the planner treats the extruder

You might know how the trapezoids looks like which are calculated by the planner code. Here is an example I found, just ignore the numbers:

Image of trapezoids

Here we see two joined motion segments. First we have a linear acceleration ramp followed by a cruising distance. Then, the planner decelerates for cornering or something similar to respect jerk limits. Next is accelerating to cruising speed again and decelerating to 0 as this is the last move. Note that the planner ties final speed of segment 1 and the start speed of segment 2 together. In reality, there can be a small difference due to numerical rounding and the step resolution, but it's nearly perfect.

But the problem is, that's only valid for the 3-dimensional print head move (X, Y, Z). It's physicaly not possible to link the E axis in the same nice way without coming to a full stop, except we have only one extrusion width within the whole print. While this was maybe true during the beginning of 3D printing, nowadays every slicer can use different extrusion widths, for example thin lines for perimeters and thick solid ones for infill. Even variable gap fill, like when you want to fill the tip of a triangle, is done. As a result, the extruder speed sometimes jumps between motion segments. Imagine the example of the triangle tip again. If we approach the tip from the triangle center, the print head moves along a straight line with a constant speed. But at points defined by the resolution of the slicer, the extrusion width gets reduced further and further. While we have a constant X, Y motion, the E axis reduces it's speed at the segment junction without deceleration. As extrusion speeds are low during print moves, this speed changes are well below the extruder jerk limits in all cases I have seen so far, and they will never cause the printer to loose steps - well, as long as we don't use a nozzle pressure correction like LIN_ADVANCE..

With LIN_ADVANCE, needed advance extra steps are calculated with "extra_steps = extruder_velocity * some_factor", therefore if we have a sudden speed change like due to the example above, this will result in an amplified sudden extruder move as LIN_ADVANCE wants to adjust the preload step amount. Up to now, it's my "dogma" that every printer who runs LIN_ADVANCE has to have the capability to do that.

And it becomes even more "funny" due to the resolution of the extruder stepper. The following example will clarify what I mean. The right section of the sketch below shows the line path that gets printed in our example. It's a straight line, followed by a 90° arc and again a straight line. In the graph on the left, we are only watching the marked section of the line path, so we exclude the acceleration to cruising speed and the deceleration to full stop. The XY jerk setting is high enough, so the planner will not slow down between the arc segments: Jitter due to e_step resolution When we have a look at the graph from left to right, we have a nice cruising (extruder) speed line first. As soon as the arc starts, we see some jitter: Some segments are printed at a slightly higher speed than the others. No one is an accelerated move. A stable extruder speed is not reached until the line starts again. What's going on? Let's have a look into one of the arc gcode segments. While it has a length of 0.583mm, it only needs 0.00772mm of extrusion (0.2mm layer height, 0.48mm line width and 3mm filament). My printer uses 100.5steps/mm in X and Y and 801steps/mm for the extruder, so in step unit this means: print move length = 58.592 steps extruded length = 6.184 steps As the printer can't do fractions of a step, the planner will cut the decimal places. But the fraction isn't lost. If the decimal places accumulates to a full step, it will be executed in one of the next segments. This "rounding" leads to the jitter around the nominal cruising speed. In real numbers, during this arc the extruder speed in terms of steps per second has a variation between the fastest speed and the lowest of impressive 20%! While this is not even noticeable in normal printings, it jerks around the extruder with LIN_ADVANCE just as the triangle gap fill example above did. Note that my demonstration arc with 0.583mm segment length is not even a very high resolution one. With increasing resolution, this problem gets worse as we might reach a point where some segments have no extruder moves at all and the extruder does one step in segment 1, no step in segment 2, one in segment 3 and so on.

The way how LIN_ADVANCE calculates the extruder speed as a work around

In fact LIN_ADVANCE as it's implemented today isn't using the real, physical extruder steps/s speed to calculate the needed advance steps because the speed jumps explained above would lead to an extruder jumping back and forth like crazy, maybe even losing steps due to LIN_ADVANCE wants to adopt the nozzle pressure in every single stepper ISR loop. Instead, it calculates the ratio between length of extruded filament and distance traveled based on the numbers given from the gcode (see planner.cpp, near the end of Planner::_buffer_line).

block->abs_adv_steps_multiplier8 = lround(
        extruder_advance_k
        * (UNEAR_ZERO(advance_ed_ratio) ? de_float / mm_D_float : advance_ed_ratio) // Use the fixed ratio, if set
        * (block->nominal_speed / (float)block->nominal_rate)
        * axis_steps_per_mm[E_AXIS_N] * 256.0
      );

Inside the step execution code, it then uses this ratio together with the ratio from nominal speed and nominal rate to recalculate the extruder speed from the current step rate (see stepper.cpp, inside the main stepper ISR). This way, LIN_ADVANCE has more something like the intended stepper speed in mind and not the jumping real extruder speed.

current_estep_rate[TOOL_E_INDEX] = ((uint32_t)acc_step_rate * current_block->abs_adv_steps_multiplier8) >> 17;

While this solves the jitter problem in arcs, it's not a solution for variable gap fill which will still lead to speed jumps!

Goals for a LIN_ADVANCE v2

My goals for a version two are as follows:

Current status of my v2 ideas and implementation

As @thinkyhead pointed out, the actual extrusion speed can always be calculated by step_rate * block->steps[E_AXIS] / block->step_event_count. Beside the before described fact that the calculated speed will be off in short print segments, this works fine and requires even much less cycle-consuming math operations. The second point, finding a smooth way from one extruder speed to the next, is the real challenge. Any help is welcome. Up to now I have created a working draft version, but it's not handling all real world cases and I even met my good old friend again, called lack of calculation power on the ATMega. I will not write down every idea I had in the last days which wasn't working in the end, instead here are two possibly working ones for you to give a start point for thinking:

Smoothing the extruder speed, as implemented in my local branch

To avoid junction extruder speed jumps, only use the final extruder speed of the last segment as a reference. My first idea was to even say the extruder speed can only change if segment has an acceleration or deceleration part, therefore this statement changes to: Keep track of the last extruder speed that we had at the end of the last acceleration or deceleration. This way, we can avoid two problems:

To avoid writing acceleration or deceleration 100 times more, in the next part I will only use acceleration. Deceleration works in the same way: For each acceleration ramp we can now calculate an extruder acceleration so we reach final extruder speed at the end of acceleration starting from extruder speed at end of last deceleration within the time we have for that acceleration section given by the planner. In a graph, this would mean we take the last extruder speed from segment before and draw a straight line to the extruder speed at the end of the acceleration part, ignoring a possible different extruder speed at the actual segment start.

Within the stepper ISR, we can now use that extruder acceleration to calculate the smoothened extruder speed for every loop we have and from that calculate the needed advance steps.

Easy at first glance, but problematic if you think about all the possible details:

To handle variable extrusion widths, it would be possible to roll back watching extrusion speeds at end of accelerations / decelerations only, resulting in watching extrusion speed at end of each segment again. This will lead to some unnecessary extruder moves during arcs, but they should be smooth enough (hopefuly). Furthermore if there is no acceleration part at the beginning of a segment, we would have to adjust the advance step pretension during the cruising part. Should be possible, but something to keep in mind.

Another idea using variable block->steps[E_AXIS]

As I'm already seeing some slow down under certain circumstances, I'm thinking about a way to get rid of all or most of the extra calculations due to LIN_ADVANCE. One might be to alter the executed stepper speed during acceleration and deceleration by overwriting block->steps[E_AXIS] value. But up to now, I'm not sure if this would result in the wanted effect, at least it will create a bunch of new challanges. Some of my points where this idea seems to stall:

If you got up to this point, thanks a lot for your patience! If you even understood everything that's amazing, if not and you are still intrested just ask questions. Finaly, every proposal is welcome!

mylife4aiurr commented 6 years ago

Just wanna express support. And hope to see farther improvements in lin_advance. Its really has helped over all print quality. I think its a great improvement. (Had a much higher k factor for my delta in 1.1.7 but it did work, once calibrated. Hope to see it back for delta's in the future)

Sineos commented 6 years ago

Ok, I will by no means pretend that I have understood everything but anyway, thanks a lot for this extensive write up. For me the topic sounds a bit like a oil or gas filled damper. They simplified have following characteristics:

In such a system the damper force goes towards 0 for low velocity and for high velocities may even kind of block the system. Solving this is some differential equation tho.

But as an analogy to the topic here, it may be a way to split the k-factor in two components. Maybe a constant one similar to the spring force (i.e. number of steps) and some portion depending on the acceleration.

Sebastianv650 commented 6 years ago

I don't think we need another factor, I don't see a damper as you described it in our extruder system. The force needed to push the filament through the nozzle is linear proportional to the extrusion speed, therefore we need twice the force when we double the extrusion speed. While I wasn't sure if this statement is true for a long time in the past, I'm now "99.9%" sure it's true due to the following points:

Therefore, the spring model to calculate the needed filament compression which will result in the needed extrusion force seems to be valid.

psavva commented 6 years ago

Any chance to have Cura's variable extrusion widths mixed in there?

iosonopersia commented 6 years ago

Hi everyone. I think that the spring model is simple and effective enough. I believe introducing a more complex model won't give any appreciable improvement, while it would slow down computation.

I didn't understand why actually LINEAR_ADVANCE is not supported by non-cartesian printers. As far as I know, kinematics machines split every movement in many little movements which are approximately linear. We could calculate the real length of those tiny movements by applying Pythagorean theorem to their start and end positions. Am I wrong?

I really would like to help, but I feel I didn't understand 100% the problem.

Bye

Sineos commented 6 years ago

Guess you know this already: https://airtripper.com/1338/airtripper-extruder-filament-force-sensor-introduction/

Sebastianv650 commented 6 years ago

Any chance to have Cura's variable extrusion widths mixed in there?

@psavva well, variable extrusion widths are basicaly supported by LIN_ADVANCE so there is no need to think about Cura in special. What was a problem with Cura in the past wasn't variable extrusion widths but wrong ones, spikes at incredible magnitude that occur randomly. So that's something up to Cura developers, I guess it's already fixed as I had a Cura gcode some times ago and it was fine. Beside that, v2 will work nicer with variable widths as it tries to smooth out speed jumps - if I get it to work.

I didn't understand why actually LINEAR_ADVANCE is not supported by non-cartesian printers.

@iosonopersia in fact I wasn't thinking about that in detail. @thinkyhead made that statement a short while ago during a change we discussed for 1.1.7. As he is way better in coding and knowing the Marlin details, I just accepted it. And it makes sense due to the way I calculate the extrusion speed and how I'm checking for print moves. I also wrote about that in the long text. But feel free to try it, just remove the sanity check which prevents you from building Marlin in this case. @mylife4aiurr seems to had it working in his delta also.

@Sineos yes, that's the one I was reffering to when I wrote about the extrusion force measurement :)

mylife4aiurr commented 6 years ago

But feel free to try it, just remove the sanity check which prevents you from building Marlin in this case. @mylife4aiurr seems to had it working in his delta also.

Have 4 printers (mk3 on the way when they get powdered metal pei sheets in). But the cheap Chinese Delta was my first. So between delta auto calibration, unified bed leveling, linear advance and skew correction it has never printed this well. Print quality is truly amazing. (I'm afraid to install the tmc2130 drivers I bought for it because its super loud but I dont wanna mess up a good thing)

Linear Advance made corners sharper and top layers better looking, less retraction necessary too. Plus i could bump up speeds. I'll have to try and figure how to disable the sanity check for delta's concerning linear advance.

iosonopersia commented 6 years ago

@mylife4aiurr , you simply need to find in sanitycheck.h these lines and delete them:

// Linear Advance
#if ENABLED(LIN_ADVANCE) && !IS_CARTESIAN
  #error "DEPENDENCY ERROR: Sorry! LIN_ADVANCE is only compatible with Cartesian."
#endif

If LIN_ADVANCE V1 works well with non cartesian machines, that would be one less problem to solve... We need someone to test it on a Core machine too.

CCS86 commented 6 years ago

Awesome @Sebastianv650, I'm glad to hear that you are still fired up to develop advance!

I won't likely be much use in the actual coding changes, but have a solid grasp on the physics involved and can help work through conceptual stuff. In addition to testing on my well-tuned bowden printer.

It sure seems like an overhaul of the actual motion planner, which did away with jerk (at least in its current form), instead using a "maximum path deviation" parameter to allow a smoother path, which did not "hit" every coordinate exactly, better leveraging linear acceleration; would solve many of the advance challenges (among others). Easier said than done! ;)

Is the current version of LIN ADVANCE bounded by the E max acceleration setting, or is it operating outside of its control?

Regarding the mention of dampers: I agree that there isn't a useful place for damping in the physical model of the advance engine. The linear spring model will work very well to link flow rate with head speed. But, perhaps adding a 'damping' to the addition/subtraction of advance E steps would help smooth out the advance moves. This kind of ties back in to bounding the moves with max E acceleration.

It might be even better to look ahead and filter / average the inputs to the advance engine. This could help on the arc jitter situation you described. I think this could also help in the issues I see with the current LIN ADVANCE. Even with modest K values, well below what I need to truly equalize flow, the extruder is pretty spastic and starts to skip steps. For example, with simple "line" infill from Cura:

image

I find that this gives a better printed result, compared to "zig zag" which connects passes with a print move (and inadvertently over-extrudes these direction changes). "Line" infill uses small rapid moves to move between passes. Even though each of these moves is executed in a small fraction of a second (0.002 s for one that I calculated), advance seems to look at them as extrusion rate = 0; making corrections for the start and stop of every pass.

As I look closer, there is an additional move that Cura makes, which could be contributing to (some of) my issues with advance, called "infill wipe". It is just a small G1 move, without extrusion, in the same direction as the infill pass. It is meant to give better adhesion between infill and walls, which it does well. It's possible that this is what is tricking advance into becoming active where it is not needed, in this case:

image

The same principle applies though. There is a realistic time required to change pressure at the nozzle through advance moves. Reacting to pressure changes in the code which take less than this amount of time to execute, is not productive, and contributes to issues. I understand that the implementation may not be simple. Some moves are physically very long, while others are incredibly short. So you can't just look-ahead some number of moves, but must instead look at distances and speeds to make time based decisions.

Sineos commented 6 years ago

Regarding the mention of dampers: I agree that there isn't a useful place for damping in the physical model of the advance engine. The linear spring model will work very well to link flow rate with head speed. But, perhaps adding a 'damping' to the addition/subtraction of advance E steps would help smooth out the advance moves. This kind of ties back in to bounding the moves with max E acceleration.

Maybe the analogy was badly chosen and I couldn't bring over my point. As far as I understood:

The first one is already being represented by the k-factor while the second one actually might change E steps without any acceleration or deceleration being involved. So the algorithm would have to look ahead and realize "hey, something is changing steps without any head speed changes so I need to adapt". This is what I meant with the dampening factor because it would kind of work against the previously calculated acceleration correction.

yes, that's the one I was reffering to when I wrote about the extrusion force measurement

I wonder how these measurements would look like with LIN_ADVANCE

thinkyhead commented 6 years ago

@Sebastianv650 — I just reading the OP so here are my first random thoughts…

Generally, when the slicer outputs G-code, I assume that it basically just outputs some linear motion and then a proportional amount of extrusion to fill up the line. So for each individual segment you can always calculate the extrusion-to-length ratio, and when wall widths are all the same this value shouldn't change. But if the slicer is using variable wall thickness, then this should manifest as an obvious change in the E-to-length ratio.

Since this can be figured ahead of segments, perhaps this changing ratio can be utilized as part of the trapezoid generation and block-joining procedure to ensure that the linear advance flow is slightly increased or reduced ahead of the block where it changes.

Sebastianv650 commented 6 years ago

Is the current version of LIN ADVANCE bounded by the E max acceleration setting, or is it operating outside of its control?

At the moment it's ignoring every limit. That's due to the fact I had no idea how to limit the esteps easily (=with acceptable calculation power) on the one side and on the other side it should never hit a limit as long as we have a smooth extrusion speed path.

As I look closer, there is an additional move that Cura makes, which could be contributing to (some of) my issues with advance, called "infill wipe".

Non-extrusion moves are not a problem as they don't alter the extruder pressure.

@thinkyhead this might be possible and something to keep in mind. At the moment I'm stuck some steps before that.

~~So what's my status: I think I have a quite fast and working way of calculating the needed advance steps now, including some smoothing by ignoring junction speed jumps due to extrusion width changes. But up to now I'm not testing with variable extrusion widths at all.~~

~~All my different approaches share one behaviour which realy puzzled me: As soon as the code works great for print moves with some significant length, meaning each move will take at least some fractions of a second, I start a simple test circle. Nothing with incredible high resolution, but short segments. And in all cases, it starts rattling like crazy. Therefore I stepped back once more, disabling LIN_ADVANCE, and created a test code only to dump some (extruder) speed values.~~

~~As long as the block buffer isn't drained the speeds are straight as you would expect them. As soon as I give it some more load so the buffer drains from time to time, the speeds go absolutely crazy. Have a look at the speed graph, we see here extruder speed along step loops:~~

tmp

I circled some examples. Two kinds of strange things happens here, maybe due to the same underlying thing, or maybe not:

I excluded two possible reasons that came into my mind:

I hope @thinkyhead might bring some light into this, as this part of the motion planner with forward / reverse pass is still a "greay box" for me. I understand what they do in theory, but not exactly what the code is doing.

Edit: Please ignore the stroke through part for the moment, investigation is ongoing and the error might be on my side!

Sebastianv650 commented 6 years ago

Sorted out some bugs, increased resolution of speed calculations etc. The spike after each buffer drain is the remaining "problem". I know it's by intention that a move starts at jerk speed, but I'm no fan of it. I think I will try to change that.. I also think that these start speeds are way above jerk setting, but I'm not sure how the planner handles jerk speed when the move happens in X and Y direction. If it's only one axis involed it's easy: Start speed = jerk speed.

Here is how the extruder speed graph over step loop looks like now. No wired jumps without accel. any more, except the ones when starting from an empty buffer: tmp

Beherith commented 6 years ago

Does lin advance thus benefit from a higher step per mm on the extruder, which would artificially limit speed on it as well? E.g. im running a direct extruder at 95 steps per mm, with 1/16th microstepping, but since i have tmc2130s with up to 1/256th microstepping, could i theoretically just increase the microstepping on E to get rid of the extruder jitter caused by lin advance? Also, is the K value dependant on steps per mm?

CCS86 commented 6 years ago

The K value should not have any dependence on steps/mm. It represents a physical spring rate of the combined compression of filament and elongation of the bowden tube.

@Sebastianv650 would have to confirm whether lin advance would benefit in any way. My gut says no.

mylife4aiurr commented 6 years ago

LIN_ADVANCE does not work with DELTA #7521

Why? Who tested and found No GO? Cant use the K factor gcode generator?

Just use a thingiverse object designed to calibrate lin_advance https://www.thingiverse.com/thing:2693748 https://www.thingiverse.com/thing:2497674

Disabled sanity check for Delta's working fine (higher K factor) but it works....

aaulick commented 6 years ago

Pressure changes don't just come from rounding jitter and variable width, also there is the filament width sensor to play nice with. How does filament width correction work, just adjusting extruder step count in the segment (appears identical to slight width variation per segment), or some other way?

Sebastianv650 commented 6 years ago

@Beherith K isn't dependant to steps/mm setting, at least it shouldn't. The step amount / mm on e axis is quite unimportant for LIN_ADVANCE as it's not using block step amount for speed calculation. By the way a higher espep/mm value shouldn't slow down your maximum print speed. I'm using 801/mm and my max espeed is 40mm/s - way more than any print move will ever use.

@mylife4aiurr the person in your linked issue did ;) thinkyhead also recognised it (independetly from this) in the last days. It might work to some degree, but the math behind is definitly not right for delta printers. This will be fixed in v2.

@aaulick the filament sensor works by adjusting the extruder length per block. Inside the planner it is identicaly to increasing flow rate per hand, it's combined in one factor. In fact that's a complicated one. Technicaly, the filament width sensor will never change extruder pressure. It compensates for diameter changes, therefore it changes the extruder speed but the extruded filament speed is kept constant. So based on the filament width sensor, LIN_ADVANCE would have to ignore adaptions. But what if somebody changes the flow rate? Usualy you would do that to compensate for a faulty esteps/mm setting, so changing the rate means you realy have a higher or lower espeed. So this one would have to be covered by LIN_ADVANCE. For real world application, I guess it's not worth investing too much time with that. If you use normal filament, your width shouldn't change more than maybe +-2%. That's nothing you will recognise in LIN_ADVANCE world, it's like adjusting the K factor +-2%. You will not see the diference.

aaulick commented 6 years ago

@Sebastianv650 A useful invariant for debugging is that total steps extruded between retraction moves should never be different for lin_advance enabled/disabled cases. Individual segments will have wildly different step counts in order to prepare the right pressure for starting the next segment, but the total filament in one extrusion stream starting from 0 pressure before last advance and ending at 0 pressure after last retract has to be the same number of steps, extra advance cancelled by extra retract. Do you have some debugging sanity code to verify this and print errors when total filament pushed out is wrong? This might have caught the original bug #7521 sooner.

Sebastianv650 commented 6 years ago

That's how I tested LIN_ADVANCE and also the reason why I designed the code in stepper.cpp how it is. It makes debugging easy. But there is no "live check", you would need to add some SERIAL_ECHOs.

aaulick commented 6 years ago

@Sebastianv650, I did some math related to modeling the linear advance. This doesn't help at all with the problems of discontinuous extrusion width (still need some smoothing feature there to prevent discontinuities) but it lets us put a unit on the current K constant and suggests a couple ways to calibrate K for a given printer config. Please check my math, let me know if this is correctly modeling the current code that's there in 1.1.18, and point out any mistakes or bad assumptions.

I have some more work on solving the differential equation relating extrude rate decay to feed rate when the two are out-of-sync. That might be useful for smoothing the extruder motions, not sure yet. Also possibly more coming on how to transfer K calibration to different filament or different nozzle temp.

Assumptions and definitions:

Given these assumptions, we can know:

Calibrate K with a filament force sensor using equation (5):

1) Pick a feed rate R. Park nozzle in empty space, heat to temp, and set up a long, steady extrusion at R extruder steps/s. Wait for pressure to stabilize, then take the extruder force reading during steady extrusion. 2) Stop extrusion, wait for oozing to stop or nearly stop (extra_steps ≈ 0). May help to cut the extrudate with scissors when it gets thin to prevent gravity pulling out extra ooze (would be extra_steps < 0). 3) Cool hotend. I am assuming that this freezes the filament in place at the hotend, staying at extra_steps ≈ 0. This should be true even with filaments that expand/contract with temperature change (ABS) if the change in size happens by oozing in or out the nearby nozzle. This should be true even with gravity oozing out the nozzle if there is no siphoning that creates negative pressure in the hotend. If anybody can validate or dispute these assumptions please do so. 4) Advance extruder carefully 1 step at a time (Nozzle is frozen, so this directly sets extra_steps), counting extra_steps until filament pressure sensor matches steady-state extrusion pressure at this rate. 5) Solve equation (5) for K, K = R / extra_steps 6) Repeat for a couple different extrusion rates and take an average. Sanity check: Experimental K values should not vary too much from each other.

Estimate K by weighing ooze using equation (5):

This way of estimating is only good if gravity does not cause your filament to ooze out of your hotend even at 0 pressure. Maybe OK for small nozzles, but if you have a volcano nozzle that oozes until the melt chamber is empty, you will only be measuring your melt chamber volume. 1) Pick a feed rate R (steps/s). Precision will be best if R is high to maximize ooze, but not so high there is any worry of janky extruder tricks like slipping or stalling. Calculate the mass feed rate Mr (mass/s) = filament density (mass/volume) step volume (volume/step) R (steps/s) 2) Set up a vertical divider on the print bed creating two areas A and B. You could do this by printing a razor blade holder with side supports for steadiness, and taping it to a known location on the print bed (or just leave the holder stuck to the bed after printing and insert the razor blade in-place) 3) Park nozzle in empty space off in a corner of the print bed, just a bit higher than the divider. Heat to printing temp, feed out enough filament to get steady flow, then stop extruding with no retraction at all. Wait for ooze to stop or nearly stop (extra_steps ≈ 0). May help to cut the extrudate with scissors when it gets thin to prevent gravity pulling out extra ooze (would be extra_steps < 0). User pushes button when ooze is negligible to continue. (This warm-up filament is not used, discard it). 4) Record ooze time T from stop feeding to user button-push. 5) Move nozzle to empty space over area A, near the divider, and extrude for at least time T at constant R steps/s waiting for pressure to equalize. (Probably using time much shorter than T will give nearly identical results, I am doing more math to put numbers to this.) 6) Stop extruding, move nozzle quickly over the divider to area B, and let it ooze again for time T. Sanity check: should stop oozing by T, same as in step 2 when user was timing the ooze. Divider should cut the extrudate into two piles, but if it doesn't, cut it yourself where it crosses the divider. 7) You now have two piles of filament, A and B. Pile A weighs R T - extra_steps. Pile C weighs extra_steps. 8) Using formula (5) calculate K K = Mr pile B weight Sanity check: Mr T = pile A weight + pile B weight 9) If your scale is not sensitive enough to measure pile C weight accurately, you can repeat steps 5-6 in a loop to get: K = Mr pile B weight / loop count Sanity check: Mr * T = (pile A weight + pile B weight) / loop count

Sebastianv650 commented 6 years ago

You wanted to write something even longer than my first text, isn't it? :-D

But I also found a not jet tested way to determine K. I will write it down as it fits to the topic: By chance I found out that if you extrude some filament using the Marlin LCD screen, as soon as the extrusion is done Marlin disables the extruder stepper. This means it's jumping back some degrees or mm filament as pressure is released. It should be possible to get a (rough) K value by using this.

BillyQuiet commented 6 years ago

Hi,

This speaks of preload in kisslicer slicer but may possibly be giving ideas : http://www.kisslicertalk.com/viewtopic.php?f=5&t=2025

Yoann

Sebastianv650 commented 6 years ago

@BillyQuiet maybe you can give me a short hint what they are talking about. While I understand it's about preloading, which is nothing else than LIN_ADVANCE does, I have no clue how Kisslicer wants to do this in gcode. The gcode as defined in Marlin only allowes synchronised moves, meaning a linear movement between all 4 axis. With this basic moves, there is no way of incorporating a smooth preload. Is this related to a special firmware / printer?

BillyQuiet commented 6 years ago

@Sebastianv650 Sorry but I can not help more than that, and I do not speak English well ^^ I'm just trying to help a little bit by sharing the information. I think it is a function close to linear advance but on the side of the slicer, with the same difficulty to achieve perfect result for everyone. This is not related to a special firmware or printer.

aaulick commented 6 years ago

@Sebastianv650 @BillyQuiet Looks like the Kisslicer is implementing acceleration ramps in the gcode output by splitting linear moves into lots of smaller pieces with speed ramping up/down?

@Beefeater on the kisslicer forum thread is characterizing the actual flow rates at constant extruder velocity as an exponential system with time constant tau.

Looks like Beefeater has actually taken some relevant math courses (unlike me) as their terminology is matching the wikipedia page... Their tau is the same info as our K (tau = 1/K) and it looks like they have a wizard somewhere similar to our calibration generator to estimate the value. They are assuming the whole extruder system is rigid and calculating Tau from viscoelasticity of the filament only, which their wizard is nominally measuring. But I think in the end it comes to the same thing -- both our wizards are doing an experiment to measure total stretch in the system by comparing ooze/blob sizes at different extrusion rates.

Lots of detailed analysis in that page which are relevant to us, some main bits which are relevant to Marlin:

aaulick commented 6 years ago

By my calculation, time to ooze down from one real extrude rate to another with stopped extruder is: (6) log(initial_extrusion_rate/end_extrusion_rate)/K Generally to go from one extrude rate to another with any constant extruder velocity feed_rate is (7) log(initial_extrusion_rate/(end_extrusion_rate - feed_rate) - feed_rate/(end_extrusion_rate - feed_rate))/K

Using equation (5) we can track extrusion_rate as extra_steps (current linear advance) instead, which may fit better in the code.

Using either one of these facts will break the invariant that we always extrude exactly the correct total stepcount of filament -- we can still count the change as extra_steps and track exactly how much filament left the nozzle, but it will be subject to floating point error now.

All these log functions are very expensive, but we can cache or precompute them as we will be seeing the same extrusion rates over and over again in the gcode.

Sebastianv650 commented 6 years ago

OK so I would say Kisslicer is more or less guessing. While the math can be fine, the slicer just hasn't the needed informations. All we have in gcode is a maximum execution speed. No real achieved speed, no junction speeds, no information about planner buffer state. Don't get me wrong, a good guess can work pretty fine. But I saw Slic3r failing with some tries to get a slicer side pressure correction to work. It's just not the right place to do it in my opinion.

aaulick commented 6 years ago

Agreed, firmware is better to put something so tied-in to the physics. Lots of complaints in the slicer thread that they never know just how the firmware does acceleration. I think we can benefit from Beefeater's analysis of the math though. Picking an acceleration profile to minimize error in K is good and not something I thought to try.

aaulick commented 6 years ago

@Sebastianv650 , any more on this?

You have said already that you have a solution in mind for resolving jitter. The change in width is amenable to a solution inserting a minimum width ramp at the discontinuity where you: 1) solve (7) for segment time, using some feed rate value, max feed, max retract, or zero: (7) equalization_time = log(initial_extrusion_rate/(end_extrusion_rate - feed_rate_e) - feed_rate_e/(end_extrusion_rate - feed_rate))/K 2) compute the distance traveled in that equalization time as a weighted average of old segment velocity so that the halfway point in equalization time occurs at the segment boundary 3) shave the segments appropriately to add two new equalization segments covering the equalization distance with same old xyz velocity and new e velocity.

Is this a workable plan for the discontinuous width problem?

Sebastianv650 commented 6 years ago

I paused this due to research and work on the planner and stepper, as a perfectly working planner and a deep understanding of both planner and stepper is needed for a LIN_ADVANCE integration. I'm now at a stage where I would state I'm understanding planner and stepper procedure and math almost to 100%. This knowledge led to some disillusion as there is not a lot of room to play:

One positive thing about all my research is that I also recognised something basicaly obvious, that as long as the extruder acceleration is constant, we just need to overlay the extruder speed with a fixed speed offset for advance during acceleration and deceleration. That might be helpful for a faster implementation, but I'm not realy sure because for the stepper we need time intervalls, so finding an implementation that makes use of adding a speed offset is not available now. With this, it would be also easy to prevent the printer from loosing steps: All we have to make sure is that the speed offset is not bigger then extruder jerk speed..

So I think I have to adjust my goal "a bit":

CCS86 commented 6 years ago

Any thoughts on a way to filter LIN_ADVANCE response in the time domain? If there was a lightweight way to identify the duration of a new extruder speed, you could avoid unnecessary corrections.

aaulick commented 6 years ago

@CCS86 I don't know if it's even true that we can safely ignore short-duration changes in speed. I've been looking at the output of Slic3r (1.38.5 Prusa) on one model, and it's putting little squirts of fill in a variety of different tiny thicknesses to fill small holes in my thinnish-wall model.

@Sebastianv650 the existing linear math works fine for constant extrusions, right? All the funny different width stuff seems to come in the fill, where it's much harder to do right with odd-size fill, but also we care less about small blobs.

Can we make an M-code for the slicer to tell us when we're doing fill, and with help from slicers that uptake this idea alternate between LIN_ADVANCE for perimeters and extra retract for fill?

Sebastianv650 commented 6 years ago

Any thoughts on a way to filter LIN_ADVANCE response in the time domain? If there was a lightweight way to identify the duration of a new extruder speed, you could avoid unnecessary corrections.

I'm also thinking that's not a good way and it solves the problem only in a small area of all possible combinations. If the extruder is basicaly able to go from pressure release speed to pressure adding speed without skipping, it can do that also during short blocks. Might sound bad on geared extruders, but it will to it's job.

@Sebastianv650 the existing linear math works fine for constant extrusions, right? All the funny different width stuff seems to come in the fill, where it's much harder to do right with odd-size fill, but also we care less about small blobs.

That's true. My hope is we don't need another M code by comparing to jerk speed. If we check that at the first step of a new block and it's OK, it's very likely OK for the complete block. So we can avoid rechecking again each loop.

Time to get another branch and start once more.

Sineos commented 6 years ago

Does it make sense to look at https://github.com/MarlinFirmware/Marlin/issues/8348, which might provide a smoother acceleration transition?

The code seems to be already there: https://github.com/MarlinFirmware/Marlin/blob/bugfix-1.1.x/Marlin/planner.cpp#L1212-L1259 though with my limited knowledge I could not get something useful from it.

CCS86 commented 6 years ago

On the time filtering:

Take Cura's normal method of infill, which functionally works quite well:

  1. Straight line infill pass
  2. Overshoot move to connect infill well to perimeter (G1 move without extrusion)
  3. Very short rapid move to position for the next infill pass

1: G1 F3000 X151.007 Y95.815 E660.40405 2: G1 X151.082 Y95.89 3: G0 F11400 X151.007 Y95.206

LIN_ADVANCE seems to see the flow rate as zero between these passes, but that does not accurately reflect reality. The overshoot move takes around 0.002 seconds to execute. I'm not sure how long the rapid move takes with acceleration, but it is also extremely short.

I think it is counter productive to use advance here. I don't think it will provide a functional benefit to the print, but will beat up the filament at the drive gear (like excessive retracts can), add heat to the extruder motor, noise, etc. Long and narrow areas of this infill can contain a huge number of these successive reversals.

If advance even used an oversimplified calculation to look ahead (even a few moves), see that the "zero" extrusion rate will be over in milliseconds, then back to higher flow rate, it could defer any correction.

Sebastianv650 commented 6 years ago

@Sineos it's not important how jerk is calculated for LIN_ADVANCE. Only thing that would be helpful is if we would know the max. and final speeds from a block. Junctions share the identical end/start speed and while the planner does this, the way how the stepper loop is calculating it's way along the trapezoid leads to never-reached final or max. speeds especialy at lower step rates. Simple example: K at a fixed acceleration means a fixed extruder speed delta. So if this speed delta is is 10 and the actual final extruder speed is 2, the absolute final speed will be -8. The next block starts at final speed of last block, 2. But for acceleration, the speed delta has to be added this time: 2+10=12. So the extruder has to jerk from -8 to 12, which requires it to have a working jerk value of 10. That's the hard thing a printer with advance has to do at every deceleration - acceleration junction. That's why there is some rattling on arcs with segments where jerk speed forces the planner to accelerate and decelerate for each single one.

@CCS86 So your proposed solution would be to ignore the slowdown at the end of the infill line and keep the filament pressure, so we can also ignore the next acceleration part from next infill line. Personaly, I wouldn't like that because it would mean I need more top solid infill layers and even another perimeter loop to prevent the blob from beeing seen in the final print. And it's a very Cura-specific filter. Slic3r for example keeps its infill lines connected so from a planners point of view infill is not different from perimeters. If some filter is needed (which I'm not sure at all), I would prefer general ones.

Sineos commented 6 years ago

@Sebastianv650 Thanks for the explanations and insights. Appreciated.

Itox001 commented 6 years ago

Sometimes the K needed for bowden systems is above the physical limit of the extruder assembly, and in this cases perhaps a more rudimentary form of pressure control could be used, which should be much more simple to program and while not giving the full advantages of a proper physically correct pressure control, could give quite decent results in systems where lin_advance can't be implemented.

My idea is a form of adaptive "coast at end" (a feature that stops extruding some distance before a line ends to relieve the pressure in the extruder before starting the next move, for those who don't know), like the one in simplify 3d, but smarter. The difference would be that instead of having a fixed value for the coast, it could be adapted depending on the delta speed. For example, if you were printing an inner perimeter at 60 mm/s and then got to the outside perimeter at 30 mm/s, the delta speed would be 30 mm/s, which could mean (for example) a coast of 0.3 mm just before the speed change. If you were printing at 60 mm/s and then came to a stop (0 mm/s) the delta speed would be 60 mm/s, which could be a 0.6 mm coast. The coast amount would be controlled by a single constant (which would be 0.01 in this example).

This approach I believe could give good results, because if I understood correctly from the discussion above, "The force needed to push the filament through the nozzle is linear proportional to the extrusion speed", which suits well the proportional coast to delta speed I described above.

Hope the coding/printing gurus can get something useful from this idea. Cheers!

Sebastianv650 commented 6 years ago

@Itox001 this approach would not always work as expected. When I understand it correctly, "at end" means a move followed by a non-extrusion move (travel, z jump, etc). The problem is that in some situations Marlin will not see the end until it's too late:

With a hughe buffer, which is not possible on the ATMega, it should be possible to implement such a smart coast at end. But it wouldn't be a pressure advance feature anyway, as it's only bleeding pressure away, never building it up.

Itox001 commented 6 years ago

Ah, I see. The difference with the simplify 3d implementation would be that the coast would not only be at the end of lines, but rather on every deceleration, so marlin wouldn't need to know if a line is ending, only that the print head is slowing down in order to start coasting. If I understand correctly, on the advanced configuration file on marlin there's a constant called MIN_STEPS_PER_SEGMENT which is 6 by default. This gives us a lower limit on how far ahead we can peek. With 1/16 microstepping and the most common pulley-motor config which results of 80 steps per mm, 14 6-step-blocks would be a length of 1.05 mm, which would effectively be the higher limit of how we can look ahead and how long we can coast. Reductions in the drivetrain or 400 steps per revolution motors would effectively cut this distance, but if I understand correctly, the coast that simplify 3d folks use is in the order of 0.2-0.3 mm and seems to give good results, so there would be a bit of play here.

You're right that this isn't a proper pressure control, but it seems to give very good results in simplify 3d to make z scar less visible and other parts where pressure buildup results in undesired blobbing. It doesn't build up pressure (could a reverse mechanism be applied to help with that?), but compromises have to be made in order to get a decent result in otherwise not working configs. Perhaps this feature would be better implemented in the slicer side of things?

Anyway I hope that if this isn't a viable idea, it can at least inspire someone to come up with something that can help us bowden folks with our extruder pressure problems.

Sebastianv650 commented 6 years ago

If someone wants to do some testing, there is a new version in development here: #9379

github-actions[bot] commented 4 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.