Closed Squid116 closed 6 years ago
Here is a quick video showing the pause: https://youtu.be/wWB5UVfcHV8
What is your Z Jerk set to? Try increasing it to see if it helps.
Z zerk @tinkyhead? Sure you don't mean E jerk?
No, I mean Z Jerk.
But it isn't moving in z at all? I have the same results with ubl disabled.
That answers my question, then. In some instances having too small a Z jerk interacts with bed leveling to create unwanted pauses. If I think of anything else for you to try, I'll let you know.
I'll try the jerk anyway and see, but unless G29 D isn't working, I doubt it.
Could it be related to my lowish acceleration values? It should be reaching speed in those distances...not that I'm sure that matters.
There should be no pauses in the middle of single linear moves. Would you mind attaching the G-code you're using so we can make sure it has no weird issues? (May need to ZIP it first.)
@tinkyhead, already Attached to first post. Kfactor (16).gcode.txt
I have a re-arm sitting on the shelf, waiting for the right time to pull it out, could this be that time?
Used the same file but removed the G29, and manually did a G29 D beforehand, same result.
@Sebastianv650 — How does E jerk affect the behavior of the K-factor test? Can it lead to pauses as the extrusion amount changes in that test?
I can try upping the E jerk in the morning, and see how it goes. What is a sensible value? Titan extruder (3:1), 16 microsteps, 837 steps/mm
If the exactly same thing happens with K=0 we can rule out E jerk. Also the low acceleration shouldn't hurt, if any it should help for the print. My first intention watching the video was also in the bed leveling direction. Can we say for sure that Marlin with UBL enabled but without G29 behaves like Marlin built without UBL? Never played with UBL, so it's maybe a dumb question.. If we can rule out UBL, I will load the latest bugfix to my printer and check if I can see something strange during the pattern.
How does E jerk affect the behavior of the K-factor test? Can it lead to pauses as the extrusion amount changes in that test?
E jerk can slow down the used acceleration for the print move, but it will never alter entry or exit speeds.
What is a sensible value? Titan extruder (3:1), 16 microsteps, 837 steps/mm
I don't know, but if you find one let us know as I will convert my printer to a Titan as soon as my 3mm filament is eaten away :)
This morning I tried: Changing to bilinear Changing to no levelling Doubling jerk
All with no noticeable difference to the result. I'm going to try the alternate pattern to see if the problem is only with the first segment.
Just tried the alternate pattern and the pauses still seem to be there on the acceleration phases, although the blobbing seems to be less.
Is there a way to check the entry and exit speeds?
Just tried with //#define LIN_ADVANCE commented out and same behavior. Also tried increasing the length of the slow segments, no dice.
Changed the slow print speed to 10 - Bingo! It worked, no pause, could see a nice starvation at the start of fast segment.
Same result at 15, but a pause for 20 and higher (also test 50).
Just a thought that it might be pausing at 10 & 15, but the deceleration phase is fast enough that I cant see it/it isn't notable, but it definitely produces the result id expect from a LA test (with LA disabled) which the faster speeds don't.
Good to know it's LA independent. I should be able to do some tests this evening. First thing to check is if the problem persists if there is no E movement..
Is there a way to check the entry and exit speeds?
Nothing you can enable by sending an M code or so, but you can insert some serial echo lines inside the planner and/or the trapezoid_generator_reset function which is called once on every new executed block. But it will slow down the execution speed so it's only useful to test specific short gcodes like this slow-fast-slow combination.
I can confirm this behavior as well, at first I thought it was because I enabled Bezier_Jerk_Control but upon disabling the issue remained. Also using UBL for whatever that's worth.
I can reproduce the issue even with X axis only moves. I guess it wasn't noticed until now because it's only noticeable with very low acceleration values. With a=100mm/s² it's obvious, with 1000 it's not visible.
Let's see what's the reason behind it!
The jerk limiting code inside the planner is limiting the junction speed between slow-fast moves to the axis jerk speed. During the fast-slow transition, no limit is applied so
vmax_junction = min(block->nominal_speed, previous_nominal_speed);
is used as junction speed.
I don't understand the meaning of the smaller_speed_factor
involved in v_exit
calculation, but I guess that might be the crucial point.
@thinkyhead do you understand this section of the jerk code? I compared it with Prusa FW and its behaviour is identical, so it's no fault which occurred after its porting to Marlin.
I'm not sure if the current way of calculating "jerk" is doing what we want at all:
All the madness is visible if you let Marlin travel along a regular poligon like an octagon at constant speed. Each cornering happens by the same angle, so I would expect the same junction speed due to jerk at each corner. I used a nonagon for this part, circling it twice, here is the result showing the junction speeds:
Gcode:
G91
G1 F4200
G1 X7.019 Y-19.284
G1 X17.772 Y-10.261
G1 X20.209 Y3.563
G1 X13.191 Y15.72
G1 Y20.521
G1 X-13.191 Y15.72
G1 X-20.209 Y3.563
G1 X-17.772 Y-10.261
G1 X-7.019 Y-19.284
; second loop
G1 X7.019 Y-19.284
G1 X17.772 Y-10.261
G1 X20.209 Y3.563
G1 X13.191 Y15.72
G1 Y20.521
G1 X-13.191 Y15.72
G1 X-20.209 Y3.563
G1 X-17.772 Y-10.261
G1 X-7.019 Y-19.284
Junction speeds:
4.00 (start = jerk speed)
7.64
8.00
6.75
6.22
6.22
6.75
8.00
7.64
11.69
7.64
8.00
6.75
6.22
6.22
6.75
8.00
7.64
If we want to stay with the per-axis code (think about deltas, Core and others) we have to understand how to fix the special case "same direction - increased speed". And make a decission which behaviour is the wanted one.
@Sebastianv650 Thanks for that investigation - I thought I was going mad, but good to see it isn't just me...
My initial reaction is, "Dammit, are we ever going to fix the bloody jerk code? We've been over this a million times, and there's no end to the problems."
I would recommend we take whatever Grbl is currently doing and completely replace our broken jerk implementation, assuming it's not just as bad.
In fact the grbl planner looks incredible lean, so it's always worth a look anyway. Only regarding the trapezoid realtime-calculation and step execution I'm not sure if this can be transfered to Marlin. And the planner is not written for deltas.
I can't invest the time for a planner port at the moment, but just mention my name if there are questions if someone starts it. I spent some time last winter for initial thoughts for planner changes, so maybe I can help in some cases. And of course it's always fun for me to test and analyze the behaviour of a planner like I did above :)
// Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
const float jerk = (v_exit > v_entry)
? // coasting axis reversal
( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : max(v_exit, -v_entry) )
: // v_exit <= v_entry coasting axis reversal
( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : max(-v_exit, v_entry) );
Following on from what @Sebastianv650 suggested, I think this code is the root of the problem - I think coasting vs reversal is the wrong concept to apply here.
At the end of the day we are either accelerating, decelerating or maintaining.
What do you mean with:
no changes to the junction speed should be made.
This code section defines the max. possible jerk at the junction (=not always the executed one). We need a max. value to operate with.
Your second point is also quite confusing to me. Why you want to add max jerk to the entry speed? If the entry speed is 70mm/s and maxjerk is 10 (both along one axis), I'm quite sure you don't want an executed jerk of 80mm/s. I don't know any printer which could handle this value ;)
Likewise v_factor
should be calculated as the ratio of v_entry
to the calculated VMAX for the axis.
I can't see the ratio of speed change vs max jerk it currently uses as having any meaningful value.
@sebastianv650
When accelerating (the 20->70 junction) there should be no jerk limiting, it should stay at the previous nominal until it hits the junction.
The second point only applies to deceleration - (i.e. the 70 -> 20 junction) then the junction speed should be v_entry + jerk (i.e. 20 plus the max jerk). That way it can instantaneously hit v_entry when it enters the segment.
The second point only applies to deceleration - (i.e. the 70 -> 20 junction) then the junction speed should be v_entry + jerk (i.e. 20 plus the max jerk). That way it can instantaneously hit v_entry when it enters the segment.
No, we always want smooth speeds. Jerk is not used to jump speeds between segments, it's used to limit the force due to a mass changing direction onto the printers structure. In this example with a fast 70 and a slow 20mm/s line, we want to cruise at 70mm/s until a distance before the segment end where we have to start decelerating at given print acceleration setting so we reach 20mm/s just at the segments end. The 20mm/s segment starts at 20mm/s, cruises at 20mm/s and starts decelerating to 0 at a point before its end so we reach 0mm/s (minimum_planner_speed in real live) at each end.
In this slow-fast-slow one-axis example, there is no jerk at all which could (or should) be handled by the jerk speed control. There is no jerk between two paths along the same direction. Jerk comes into play when we angle the second (for example slow) segment 45° around Z-axis so we make a X axis only move first, continued by a XY-axis move at the same or different speed. While the junction speed along the travel path might be still 20mm/s, now the X and Y axis will see a jerk as the X and Y speed component is now changing instantly at the junction. For example at the point right before the junction (first segment), X=20mm/s and Y=0mm/s. Right after the junction (second segment), the X speed is 14.14mm/s and Y= also 14.14mm/s. So the real jerk is (20-14.14)=5.86mm/s along X and 14.14mm/s for Y. So in this example, the junction speed for the angled move might have to be reduced in reality as 14.14mm/s for Y might exceed the printer allowed Y jerk limit.
grbls way of calculating jerk is slightly different but most likely more suitable. The only point I have to think about is how to implement a 4th axis (extruder) into the math. I don't think the circle they are calculating is meaningful or helpful in 4 dimensional space.
What we are trying to do just below this, is to limit the junction speed to a value that is safe within the jerk limits, if possible. On an acceleration that isn't possible without exceeding the slow speed, so we don't want to change the junction speed.
On your example X is decelerating, so the max safe junction speed (for X) should be within the target speed (14.14) +/- max jerk (say 10). Given we have been cruising at 20 then we are within the that bounds and the junction is safe. Y is accelerating, so we have to respect its exit speed (0) and we'll need to apply acceleration to Y.
Take the reverse of your scenario, going from a 45 degree line to a 0 degree line along X, assume both axes are at 14.14, then y is decelerating to 0, then we need to slow the max junction speed for Y to target (0) +/- max jerk(10) - our max junction speed is 10, we'll need to slow y from 14.14 to at least 10 to safely make this transition. X is accelerating, so we have to respect the exit speed (14.14) and accelerate to 20.
Obviously to slow any one axis to the safe junction speed, the overall speed will need to be modified so that that axis respects the max junction speed.
Here's the jerk code @Squid116 pointed out earlier, expanded for readability:
LOOP_XYZE(axis) {
// Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop.
float v_exit = previous_speed[axis] * smaller_speed_factor,
v_entry = current_speed[axis];
if (limited) {
v_exit *= v_factor;
v_entry *= v_factor;
}
// Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
float jerk;
if (v_exit > v_entry) { // accelerating?
if (v_entry > 0 || v_exit < 0) // coasting?
jerk = v_exit - v_entry; // the abs difference
else // reversing?
jerk = max(v_exit, -v_entry); // the faster one
}
else { // decelerating or maintaining speed?
if (v_entry < 0 || v_exit > 0) // coasting?
jerk = v_entry - v_exit; // the abs difference
else // reversing?
jerk = max(-v_exit, v_entry); // the faster one
}
if (jerk > max_jerk[axis]) {
v_factor *= max_jerk[axis] / jerk;
++limited;
}
}
Here's the old (1.0.x) code which we replaced with the Prusa MK2 code:
https://github.com/MarlinFirmware/Marlin/blob/1.0.x/Marlin/planner.cpp#L944-L970
// Start with a safe speed
float vmax_junction = max_xy_jerk / 2;
float vmax_junction_factor = 1.0;
if (fabs(current_speed[Z_AXIS]) > max_z_jerk / 2)
vmax_junction = min(vmax_junction, max_z_jerk / 2);
if (fabs(current_speed[E_AXIS]) > max_e_jerk / 2)
vmax_junction = min(vmax_junction, max_e_jerk / 2);
vmax_junction = min(vmax_junction, block->nominal_speed);
float safe_speed = vmax_junction;
if (moves_queued > 1 && previous_nominal_speed > 0.0001) {
float jerk = HYPOT(current_speed[X_AXIS] - previous_speed[X_AXIS], current_speed[Y_AXIS] - previous_speed[Y_AXIS]);
//if(FABS(previous_speed[X_AXIS]) > 0.0001 || FABS(previous_speed[Y_AXIS]) > 0.0001)
vmax_junction = block->nominal_speed;
if (jerk > max_xy_jerk) vmax_junction_factor = max_xy_jerk / jerk;
if (fabs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]) > max_z_jerk)
vmax_junction_factor = min(vmax_junction_factor, max_z_jerk / fabs(current_speed[Z_AXIS] - previous_speed[Z_AXIS]));
if (fabs(current_speed[E_AXIS] - previous_speed[E_AXIS]) > max_e_jerk)
vmax_junction_factor = min(vmax_junction_factor, max_e_jerk / fabs(current_speed[E_AXIS] - previous_speed[E_AXIS]));
vmax_junction = min(previous_nominal_speed, vmax_junction * vmax_junction_factor); // Limit speed to max previous speed
}
block->max_entry_speed = vmax_junction;
Note that when we adopted the Prusa MK2 jerk code, jerk values had to be cut in half to fit the new procedure. And we didn't check to make sure it was ok for Delta, but so far it seems to be.
For reference, here is the jerk code from GRBL: https://github.com/grbl/grbl/blob/master/grbl/planner.c#L326
I realy like their slim code, looks quite effective. I think I will build an Excel sheet to play with the jerk code a bit. If we want to give this code a try, I see following points to be considered:
Here is the GRBL xls sheet if you want to play with it. The code seems to work just fine also with 4 axis, if the results are also meaningful has to be checked in detail. For the problem this issue is handling, it results in perfect results: Movements into the same directions have an infinite possible jerk speed while reversing axis are slowed down to 0.
Note the two tabs, one for 3 axis (GRBL original) and 4 axis extended: GRBL jerk.xlsx
Should we apply a quick hack to simply skip over jerk-limiting when an axis is "coasting" in the same direction? It seems to me that jerk shouldn't even apply for the moves that are giving the issue here.
It seems to me that jerk shouldn't even apply for the moves that are giving the issue here.
That's true, but it would only fix a very special case. Also angled moves are affected, the effect seems to get lower with increased angle. And we are living now quite a long time with that imperfect jerk code and most people not even noticed it. So I think we should take the time to do only one fix - a proper one. Taking as much as possible from GRBL planner is maybe the best idea. They have acceleration limits per axis, only jerk limits per axis are missing. I guess they can be implemented in the same manner as the other limits, see https://github.com/grbl/grbl/blob/master/grbl/planner.c#L340
Edit: Here is a good explanation of the meaning of junction deviation [mm]: https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/
Ok, so I'm starting on adapting the junction deviation approach to see how far I can get with it. How important is it to include the E axis? In other words, does the E axis affect this part of the equation, or can it be jerk-limited using the standard approach while XYZ use the deviation method?
I have no real answer for that, every time I'm thinking about that I'm feeling my brain starts liquifying due to the 4 dimensional thing. Therefore I would recommend starting with the version whichs feels more realistic for you and then we check the output of some typical junction situations for plausibility.
FWIW I think that E axis should "follow" X, Y and Z to assure proper extrusion, nothing else... In case of issues caused by inertia on E axis, tuning X, Y and Z accelerations and reducing speed could solve them...
Have been away for work - just back now. The article @Sebastianv650 posted is a good read and makes sense to follow that idea if possible.
Regarding the e axis - my gut feel is that e won't (ever?) often be a limiting factor. It would be nice if there was a way to accurately gauge how often e becomes the limiting factor - but practically that would mean having it work for XYZE before you could make the decision - chicken and egg scenario.
You could implement for XYZ and dump all the relevant info from a few dry runs, then use a spreadsheet or something to see how often e might become a limiting factor?
I think that E axis should "follow" X, Y and Z to assure proper extrusion
That is managed in Stepper::isr
as a natural element of the Bresenham synchronization. Here we're just talking about whether to take E-Jerk or "E angular change" into account as part of the Junction Deviation code. I'm pretty sure it can be ignored unless E is reversing direction, in which case it makes sense to divide the move.
I would recommend starting with the version which feels more realistic
Realistically, it's a big challenge to port over the junction deviation code, simply due to its being applied in a smattering of different places. I'm going to go ahead and leave things as they are for Marlin 1.1.9, and then we can give this more time and proper scrutiny in preparation for the 2.0 release.
@thinkyhead do you know much about the junction deviation implementation here:
https://github.com/Squid116/Marlin/blob/bugfix-1.1.x/Marlin/planner.cpp#L1311-1360
It looks reasonably complete, at least for a cartesian machine, I might light it up and see how it goes?
@Squid116 — That snippet has been hanging around in Marlin for a long time. I'm sure it's very out of date and incomplete. I started on the project of bringing the current Grbl implementation in, and while it's very similar, it still needs more work. Parts of it are distributed in buffer_segment and also in the trapezoid generator methods.
Is it worth raising this over in the Prusa repo? they might have more resources to look into addressing the flaw with their current Jerk code? I assume they'd want this fixed too...
Sure! Even if we implemented it in the current Marlin they'd most likely make a whole separate effort anyway, as they are focused on their specific Cartesian design and don't really follow this project.
This is as far as I've gotten so far…
@thinkyhead I don't follow the logic of how this might impact buffer_segment or the trapezoids, if the code in your branch sets vmax_junction/max_junction_speed_sqr (and by doing this the block->entry_speed below) then they should continue to work?
I'm sure I'm missing something - it is quite a complicated code base, and I'm just dipping my toe in now.
Sorry if I'm annoying you on this, I'm just keen to work out what's going/seeing if I can help with a fix. Particularly with my large (slow) machine I see the effects and it bugs me, I also think solving this I could squeeze some more speed out, although that would be a secondary benefit.
Well, I’m looking at grbl and the planner code there makes some changes to the block_t structure. There are some new fields, and these are used in the forward and reverse pass code. It seems preferable from my point of view to take all of grbl‘s updates into account, but if you feel confident that we can just borrow this one slice, go ahead and give it a try.
When I am printing the k-factor test pattern, the hotend slows right down/comes to a complete stop after the initial slow segment - I would have expected the planner to have detected the change of speed is moving in the same direction and not slowed down/or stopped towards the end of the slow segment? Because of this I am seeing blobs at the start of the fast segment even with K=0.
I don't think I have configured anything that would have messed with the read-ahead like this, but I'm not 100% sure,and I don't know where to look.
Happens both from USB and SD card.
here is my config: https://github.com/Squid116/Marlin/blob/bugfix-1.1.x/Marlin/Configuration.h
and config_adv: https://github.com/Squid116/Marlin/blob/bugfix-1.1.x/Marlin/Configuration_adv.h
kfactor (16).gcode.txt
(Edit: updated links to correct branch in my github repo)