-
-
Notifications
You must be signed in to change notification settings - Fork 19.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[2.0.x] Apply feedrate to nozzle movement for kinematic machines #8778
[2.0.x] Apply feedrate to nozzle movement for kinematic machines #8778
Conversation
I've been down this rabbit-hole, including adding the mm length as another parameter to Consider the issue for SCARA also. The feedrate must be set dynamically to convert mm/s into degrees/s for two rotary joints. A script from Evezor uses the square root of the sum of the squares of the angular changes. I've also tried using the larger of the two angles as the basis for feedrate. Results are currently inconclusive as to which is better. In the case of a Delta, we see no problems with the current feedrate being based on the tower carriages. But you may find with slower moves that this gives inconsistent lines that speed up and slow down as they progress. Or, this problem may be obviated in Delta. We know it's an issue for SCARA, and especially troublesome when doing laser engraving. |
The commit on this PR seems to work well for my Delta machine. Considering that I don't see why this change wouldn't work for SCARA since the rate of angular change is a function of move time, which is computed using the provided distance. This change also works with the segmented leveled moves option that was just added since the distance is known. |
As it happens I added |
Marlin/src/module/planner.cpp
Outdated
@@ -714,11 +714,11 @@ void Planner::calculate_volumetric_multipliers() { | |||
* fr_mm_s - (target) speed of the move | |||
* extruder - target extruder | |||
*/ | |||
void Planner::_buffer_steps(const int32_t (&target)[XYZE], float fr_mm_s, const uint8_t extruder) { | |||
void Planner::_buffer_steps(const int32_t (&target)[ABCE], float fr_mm_s, const uint8_t extruder, const float millimeters /*= 0.0*/) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using const float &millimeters
(const reference) makes argument passing quicker — only 2 bytes instead of 4.
I'm testing this on a delta printer and seems to work |
5f5ed36
to
db031d5
Compare
cac9cfb
to
ed580b7
Compare
I just updated this PR to change the axis names in the LCD to ABC instead of XYZ for kinematic machines. I've noticed that the benefit of this change are very apparent on my delta machine when printing circles. With the current method, it's pretty obvious that the nozzle is changing speed as it goes around the circle. With this change in place, the nozzle appears to maintain a more constant velocity. |
To make merging 1.1.x fixes with 2.0.x easier let's hold this until 1.1.9 is released and all new development is closed on the 1.1.x branch. This can be merged shortly after that release as it will only be included in 2.0.x going forward. |
Sounds reasonable. For machines, what would you think about combining the velocity, acceleration, and jerk settings for all three axes? I can't think of a reason why someone would want different settings for the three towers, and it's a little obnoxious having to adjust all three in the LCD. |
Can we really wait fixing this? Basically this means delta support is broken in all marlin versions. Except we think it's fine to fill speed and acceleration values with "random" data. . |
As we have now got a consensus about the bug in Marlin today and which speed etc is related to what, would you create a PR for 1.1 bugfix also? I think we should get this sorted out as fast as possible as this is a long standing bug since DELTAs were introduced to Marlin. I think with the knowledge available now, we can convince @thinkyhead to merge them if necessary. |
Done - #9448 |
I'm eminently convince-able. Let's get delta unbroken. |
1d90422
to
7f57cd3
Compare
Marlin/src/module/planner.h
Outdated
@@ -376,7 +376,7 @@ class Planner { | |||
|
|||
FORCE_INLINE static void unskew(float &cx, float &cy, const float &cz) { | |||
if (WITHIN(cx, X_MIN_POS, X_MAX_POS) && WITHIN(cy, Y_MIN_POS, Y_MAX_POS)) { | |||
const float sx = cx + cy * xy_skew_factor + cz * xz_skew_factor, | |||
const float sx = cx + cy * xy_skew_factor + cz * (xz_skew_factor - (xy_skew_factor * yz_skew_factor)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope! See #8623 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so weird. I didn't make this change in either PR. Something must have happened with the auto merge to somehow regress the change.
Updated the translations, reversed the incorrect |
This way of e_accel calculation is the most precise one. Finaly this is now also usame on kinematic printers (DELTA, SCARA) but ONLY with PR MarlinFirmware#8778! Without that, Marlins acceleration and speed values are some fantasy numbers.
@tcm0116 @Sebastianv650 Error: The last segment in segmented moves is not known until after all other segments are done. And usually it will be very small. So the last segment should not be passing the same length as the rest. Solution: Do an additional |
The length of each segment is computed as:
Since it's a function of the total cartesian length of the movement, once all of the segments have been buffered, the sum of the lengths of each segment will be equal to the total length of the original movement. The last segment will likely be smaller than the rest due to rounding errors, but the total length provided to the planner across all of the segments will be correct.
Do you have data to back that up? I would think the last segment will be about the same length as the other segments, just not exactly the same. |
The last segment is not just another segment of the same length. It also acts as a correction to the imprecision of previous segments, which are merely adding a pre-calculated float on each pass. |
Hence, I stated:
Is the length of the last segment so much different that it warrants another call to |
The last segment is a direct move to the originally-given destination. Again, as a corrective to the approximation of the segment loop. |
If the number of segments comes out to 1, it might be good to just do a direct move and exit. Skip all the other logic. |
// Since segment_distance is only approximate,
// the final move must be to the exact destination.
const float last_segment_mm2 = (
sq(destination[X_AXIS] - raw[X_AXIS])
+ sq(destination[Y_AXIS] - raw[Y_AXIS])
+ sq(destination[Z_AXIS] - raw[Z_AXIS])
);
if (!UNEAR_ZERO(last_segment_mm2))
planner.buffer_line_kinematic(destination, fr_mm_s, active_extruder, SQRT(last_segment_mm2)); |
Why is absolute precision required for the length of the last segment and
not the others? That length value is only used for computing the time that
it should take to execute the segment at the desired feedrate. It does not
affect the final position of the nozzle.
|
Float rounding leads to accumulation of error in this loop: while (--segments) {
. . .
LOOP_XYZE(i) raw[i] += segment_distance[i];
. . .
} The old code used to loop all segments. I did an optimization to have it do the segment loop for one less than the total number, so yes (ideally) the last segment should be close to the calculated or configured segment length. |
^ Hmm, it's possible the figured cartesian length would be close enough in most cases. If it doesn't matter that it's exact, then perhaps it's ok as it is. |
One optimization that can be made: In Also, this PR leaves out |
With your approach, the raw array will end up having error because the sum
of all of the segment_lengths won't equal the total length of the original
movement. Each segment_cartesian_mm is an approximation that sum up to the
total length of the original movement. If you re-measure the length of the
last segment, then whatever the delta is between that value and
segment_cartesian_mm
will become error in raw.
|
Exactly.
|
It's significantly better than it was when the RSS was taken of the
distance moved by the towers and not the nozzle.
|
I'm not surprised. See also what's required to work out SCARA angular feedrate. |
I'd say to either compute the length of each segment or none of them, but
not a combination.
|
Not a combination of "each" and "none"? |
I think SCARA already handles the angular feedrate. It just needs to know
how long it should take to perform the movement, which this PR should
provide.
|
Yes, that's the idea. So do you think this PR supplants the current… #if ENABLED(SCARA_FEEDRATE_SCALING)
// For SCARA scale the feed rate from mm/s to degrees/s
// i.e., Complete the angular vector in the given time.
inverse_kinematics(raw);
ADJUST_DELTA(raw);
planner.buffer_segment(delta[A_AXIS], delta[B_AXIS], raw[Z_AXIS], raw[E_AXIS], HYPOT(delta[A_AXIS] - oldA, delta[B_AXIS] - oldB) * inverse_secs, active_extruder, linear_per_segment);
oldA = delta[A_AXIS]; oldB = delta[B_AXIS];
#else
planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder, linear_per_segment);
#endif |
In other words, don't just compute the length of the last segment, but I
think the current approach is sufficient.
|
Yes, after a reëxamination of the code it looks like the current approach should be fine. |
So, is the effect of this to always make the passed |
Sounds like two things may come from this:
|
This way of e_accel calculation is the most precise one. Finaly this is now also usame on kinematic printers (DELTA, SCARA) but ONLY with PR MarlinFirmware#8778! Without that, Marlins acceleration and speed values are some fantasy numbers.
Currently, the actual feedrate achieved by a kinematic machine is not the desired feedrate provided in G0/G1 commands. This is due to the way the
Planner
class computes the distance of the movement for kinematic machines.The distance of a movement for a kinematic machine is currently calculated in
Planner::_buffer_steps
as the square root of the sum of the squares of the distance moved by each axis. For cartesian machines, this makes sense. However, for kinematic machines, the distance moved by each axis is only indirectly related to the actual distance moved by the nozzle. Because of this, the distance computed is not valid for kinematic machines, and because the distance is used to compute the length of time of the movement and subsequently the required velocities of the axes, the final feedrate of the nozzle ends up being not what was desired.This PR aims to resolve this issue by passing the cartesian length of a movement to
Planner::_buffer_steps
in order to allow for proper computation of the necessary velocities for the axes. This is achievable on kinematic machines because the movements are already segmented, and the length of each segment is easily computed. This ends up replacing three multiplications and a square root call with a single multiplication.My original intent with this PR was to limit the maximum size of a segmented movement for a kinematic machine. This is necessary because
prepare_kinematic_move_to
does not consider any maximum feedrates when computing the number of segments for a given feedrate. For example, runningG1 X100 Y100 F99999999
will result on only one segment being generated because the large feedrate is not limited by anything.I still haven't solved this problem because the maximum feedrates defined in the Planner are for the individual axes and not the overall feedrate of the nozzle. One idea I have to solve this issue is to add a
DEFAULT_MAX_XY_FEEDRATE
which would apply to the cartesian movement of the nozzle, in a similar manner toDEFAULT_ACCELERATION
, and would essentially replace the currentPLANNER_XY_FEEDRATE()
macro.In order to alleviate any confusion, the documentation for
DEFAULT_MAX_FEEDRATE
should be updated to state that the values apply to the individual steppers (i.e. ABCE) and not the cartesian movement of the nozzle. For kinematic machines, I would also like to update the labels in the Control/Motion LCD menus to use ABC instead of XYZ, since "Vmax X" really means "the maximum velocity of the A stepper motor" on a kinematic machine, and "Vmax A" is more correct.Please let me know if you have any comments or suggestions as I don't want to go too far down this path to only find out that there's a fundamental issue with this approach.