Skip to content
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

Add arc interpolation features (G2/G3) and M214 command for controlling settings #2657

Merged
merged 10 commits into from
May 2, 2022

Conversation

FormerLurker
Copy link
Contributor

I don't expect this pull request to be immediately accepted, and would greatly appreciate your thoughts and feelings about the changes I have made. This is my first attempt to modify any firmware, so I expect a lot of critical feedback.

I recently created a program to convert G0/G1 commands into G2/G3 commands where possible, and noticed that some arcs were coming out as flat edges. These edges were clearly rounded in the original gcode, and I tracked the issue down to the MM_PER_ARC_SEGMENT setting. I did not have much luck at all decreasing this setting. A value of 0.1 (similar to the minimum distance between segments in my original file) yielded terrible stuttering that degraded the print quality severely. A value of 0.75 yielded flat edges in some places. I couldn't seem to find a good value that produced rounded small curves that didn't result in stuttering that impacted quality, so I started exploring the Marlin 2.0 code, and came up with a few ideas of my own that I've added to this pull request.

I've added the following features from Marlin 2.0 to enhance arc interpolation:

MIN_ARC_SEGMENTS - Minimum number of segments in a full circle of any radius (0 = disabled)
ARC_SEGMENTS_PER_SEC - Number of segments per second to be generated based on current F (0 = disabled)

Note that when the two settings above conflict, the smallest arc length calculated from the above two settings is used. Also MM_PER_ARC_SEGMENT is the max segment length allowed.

I've added a third setting to prevent extremely small segments from being generated:

MIN_MM_PER_ARC_SEGMENT - The minimum length of any segment (0 = disabled)

I've also added a new command, M214 (is that and appropriate gcode?) with the following definition that can be used to control the above settings:

### M214 - Set Arc configuration values (Use M500 to store in eeprom)

#### Usage

    M214 [N] [S] [R] [F]

#### Parameters
- `N` - A float representing the max and default millimeters per arc segment.  Must be greater than 0.
- `S` - A float representing the minimum allowable millimeters per arc segment.  Set to 0 to disable
- `R` - An int representing the minimum number of segments per arcs of any radius,
        except when the results in segment lengths greater than or less than the minimum
        and maximum segment length.  Set to 0 to disable.
- 'F' - An int representing the number of segments per second, unless this results in segment lengths
        greater than or less than the minimum and maximum segment length.  Set to 0 to disable.

Please let me know if you feel the parameters I used (N, S, R and F) seem appropriate to you.

I also added M214 settings to the M503 configuration display command, which produces output similar to this:

Recv: echo:Arc Settings: N=Arc segment length max (mm) S=Arc segment length Min (mm), R=Min arc segments, F=Arc segments per second.
Recv: echo:  M214 N1.00 S0.50 R16 F0

I have tested M500 to ensure it works as expected for storing these settings to EEPROM. You will find these new settings within the ConfigurationStore.h file:

    // Arc Interpolation Settings, configurable via M214
    float mm_per_arc_segment;
    float min_mm_per_arc_segment;
    int min_arc_segments; // If less than or equal to zero, this is disabled
    int arc_segments_per_sec; // If less than or equal to zero, this is disabled

I've added default settings for the arc interpolation to each .h file in the /variants folder and have removed the settings from configuration_adv.h.

I have tested installing the latest firmware release (3.8.1) and then upgrading to my variant, and the default settings seemed to load correctly.

The thing I am NOT sure about is the initialization of these new settings. Though I'm sure you will review this completely, and some changes will be necessary, I am not at all sure that I did the initialization properly within ConfigurationStore.cpp. Any help or advice there would be greatly appreciated.

I also removed the N_ARC_CORRECTION setting and functionality completely. I was seeing bad interpolations using a wide variety of settings (endpoints quite far from the arc itself) due to the small angle approximation of COS and SIN. I removed the small angle approximation completely, so there is one COS and one SIN calculation that happens at the beginning of the interpolation loop as long as there is more than one segment. This change immediately resulted in arcs whose endpoints fit nearly exactly along the arc itself for all sizes I tested. The correction factor seemed completely unnecessary after using accurate SIN/COS values. I realize there is a trade-off here, since using those functions results in the first arc segment being inserted into the planner a bit later, but in my testing not only did I not notice any performance difference, but the resulting arcs fit better, especially in arcs of a small radius or where MM_PER_ARC_SEGMENT is large. I did experiment with using a cutoff angle for deciding when to use true SIN and COS, but I did not notice any performance gains at all, so I dropped that in favor of simpler code. Please let me know what you think of this. I wrote a small program that generates the gcode equivalent of the interpolated arcs the firmware is producing if you are wondering how I discovered this.

Additionally, I think the default settings may need to be tweaked somewhat, though they seem to work well for me. There is still some slowdown (but no stuttering) with the default settings when printing arcs with G2/G3 of a very small radius at high speeds (50+mm/sec), and I think that could be eliminated. Now that I've added M214, testing should be MUCH easier. I was hoping to find a way to print multiple copies of the same object and inject the M214 command between each object, but I have not yet found a way to do that automatically. I'm sure there is a way, I just haven't found it yet.

If you need some gcode that includes a lot of arcs to test these changes, please let me know. I have code for the MK2.5 w/mmu 2.0, and could generate gcode for any model you want. I created a 1 layer file containing 6 circles using archimedean chord fill where each circle is printed at a different speed (from 10-60MM/s) to test stuttering, which was quite useful and illuminating.

Thank you very much for your time and consideration!

Firmware/Marlin_main.cpp Outdated Show resolved Hide resolved
Firmware/Marlin_main.cpp Outdated Show resolved Hide resolved
Firmware/Marlin_main.cpp Outdated Show resolved Hide resolved
Firmware/motion_control.cpp Outdated Show resolved Hide resolved
Firmware/motion_control.cpp Outdated Show resolved Hide resolved
@leptun
Copy link
Collaborator

leptun commented May 7, 2020

I'll give this PR a try tomorrow if I have time. Can you please post the gcode you mentioned in the initial comment? I'd like one for MK3(S) without MMU if that is possible.

I like the idea of this PR quite a lot. It would be very interesting if arc commands were more streamlined into slicers... Oh well, guess we're stuck with the crappy STL format for now. It would be so nice if STEP files could be used in Slicers so that actual arcs in the model files could be used for generating the gcode...

@FormerLurker
Copy link
Contributor Author

Yes sir, and thank you for your review! I will have all the changes posted to the repository by then.

@wavexx
Copy link
Collaborator

wavexx commented May 7, 2020

Related #2606

@FormerLurker
Copy link
Contributor Author

Here is a zip file containing gcode you can use to test the PR sliced for the MK3S:

PRFiles.zip

Description of Contents:

  • 6_cylinder_project/6_cylinders_6_speeds.3mf - Contains a 3mf file for the 6 cylinder test, which prints six single layer cylinders with Archemedian infill, each with a speed ranging from 10mm/s to 60mm/s.
  • G2_G3_Test_Gcode folder - Contains three files that utilize G2/G3. One is the 6 cylinder test, the other two are Benchys sliced at 0.2mm layer height. One uses the quality profile, the other uses the speed profile.
  • Original files folder - Contains the original gcode used to produce the files in the G2_G3_Test_Gcode folder.
  • 6_cylinders_showing_print_speeds.jpg - An image captured from Simplify3D's gcode analysis feature showing the print speeds. The 10mm/s cylinder will be towards the back left and the 60mm/s cylinder is towards the front right.
  • ArcConversionInfo.jpg - Some interesting statistics regarding the conversion from the original files to those using G2/G3. I thought you might like to see this.

If you would like to try other models, or if you want me to convert other gocde files, please let me know. The full source for the Arc Welder conversion library, as well as a console application that can be used to produce the G2/G3 code from the command line are available in the ArcWelderLib repository that you can find in my github pages. See the ArcWelderConsole project for the command line version. There is also an InverseProcessor project in there that can be used to output gcode that shows the actual line segments that will be put into the planner (though it now needs some slight modification due to the recent modifications I made for this pull request). The project was created using Visual Studio, and some alterations will need to be made if you want to compile it from source. I could send windows binaries if you want.

You might also like to try the ArcWelderPlugin for OctoPrint. It is in pre-release status ATM, but you can install from the OctoPrint plugin manager by clicking Get More and pasting this url into the From url... box: https://github.com/FormerLurker/ArcWelderPlugin/archive/0.1.0rc1.dev2.zip

Please let me know if I can do anything else to help you test, and thank you for spending your valuable time on this PR!

@FormerLurker
Copy link
Contributor Author

First, congratulations on getting the new release out!

Second, I wanted to mention that I will be implementing some enhancements from this pull request that may bring the small angle approximation back into the picture. It turns out adding another term to the approximation makes a HUGE difference and has a relatively small cost.

@ochm
Copy link

ochm commented Oct 25, 2020

Hi all, do you plan add this feature to firmware, please?

@arne182
Copy link

arne182 commented Oct 26, 2020

I am also in support of this PR

@aguschanchu
Copy link

Please add this feature to the firmware!

@marchetb
Copy link

Add this please 🙏 👍

@XRyu
Copy link

XRyu commented Nov 3, 2020

Thumbs up for this! Please bring this to the firmware 👍

@FormerLurker
Copy link
Contributor Author

Working on some changes to bring this more in-line with Marlin 2. Re-adding N_ARC_CORRECTION and the small angle approximation with an additional term since it seems to be plenty accurate after that. Should speed up getting the first segment into the planner. Will also update M214 and M500 to reflect this. FYI, I'm also going to try to get M214 into marlin 2 where it could make it's way eventually (hopefully) into the Buddy firmware.

@frango1963
Copy link

Please add this feature to the firmware!

@FormerLurker
Copy link
Contributor Author

Fyi, this has been slow going, but it should be ready in the next month.

@Cjkeenan
Copy link

Cjkeenan commented Jan 4, 2021

I hope this goes through in the near future as I have been having issues with my Pi Zero W plugged into the back of my mk3s+ and small circular details in which the nozzle seems to just smudge the entire detail leaving a mess, ex. very small half spheres looking more like smudged cylinders.

It only happens when printing from OctoPrint even when the exact same GCode file is used from the SD card. One thing to note, the smudging seems to be a little less noticeable when using OctoPrint safe-mode but still not as good as the local SD card.

My best guess now is that there is micro-stuttering happening due to the large quantity of G-codes associated with curves and the less than optimal specs of the Zero W. I was hoping this PR along with your ArcWelder plugin may help to alleviate that additional overhead and this would be a good addition to go along with the official Prusa Zero W guides as a sort of optimization.

@FormerLurker
Copy link
Contributor Author

I've committed some recent and substantial changes, but have noticed one issue at the last minute. I will fix this, test, and amend this pull request ASAP, so no need to review this yet. Thank you.

@FormerLurker
Copy link
Contributor Author

FormerLurker commented Jan 12, 2021

So, I added one line variable to the ConfigurationStore, a uint8_t, and now the compilation is failing. I noticed in the build details it says it is using 100% of the available memory, but I'm getting 93% using the .h file indicated in the error log. Any ideas as to what is going on?

Edit: The .h file that failed was 1_75mm_MK3-EINSy10a-E3Dv6full.h.

@FormerLurker
Copy link
Contributor Author

Here is what sketch claims to use for me when building for the MK3.h file:

image

@leptun
Copy link
Collaborator

leptun commented Jan 13, 2021

You are building without multi language support. In reality, the code size is dangerously close to 100%

@3d-gussner
Copy link
Collaborator

@FormerLurker The firmware source code is for MK3, MK3S/+, MK2.5 and MK2.5S/+ (MK2, MK2S), so you have to pay attention to your code/PR that it will compile correctly for all variants.

The Travis CI test compiles all variants and checks if they succeed:

So rule of thumb is to compile the firmware for all variants upfront before pushing a PR.

The MK3 build contains the PAT9125 filament sensor which is more complex and so needs more resources compared to MK3S/+.
Usually the MK2.5S+, MK2.5, MK2 and MK2S are way smaller as these don't have the TMC2130 drivers and use less resources compared to the MK3 and MK3S+.

Variants by recourse usage (high to low):

  • MK3 TMC2130 + PAT9125
  • MK3S/+ TMC2130
  • MK2.5 PAT9125
  • MK2.5S

@FormerLurker
Copy link
Contributor Author

Thank you @leptun and @3d-gussner, I will work to reduce the LOC. It's right on the edge. I think the optimizer was omitting some code earlier due to an unreachable code block (code error due to a last minute change). I can def shave off a bit. Will report back.

@FormerLurker
Copy link
Contributor Author

Makes sense. Let me think about it a bit more. We do have the advantage of being able to do some more complicated math for the recovery than during a normal command.

@FormerLurker
Copy link
Contributor Author

Actually storing the radius and extrusion remaining may just do the trick. I don't think we can calculate the radius from the offset since we don't know the starting point.

Firmware/Configuration_adv.h Outdated Show resolved Hide resolved
// Clamp to the calculated position.
clamp_to_software_endstops(position);
// Insert the segment into the buffer
plan_buffer_line(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS], feed_rate, extruder, position);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on what we plan to do during recovery, the last argument to plan_buffer_line here the 4 floats are up for "abuse". In G1 this would be "target", since the final coordinate is the invariant during the move.

What we do for for arcs can be different.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be different, but we still need to store XYZE since all of these could be relative. I just find it annoying that I and J is always relative as well. The only purpose of gcode_target is to fix issues with relative gcode commands

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on how we see this, 4 extra floats will always be required somehow for either the (absolute) start or the end of the move because of this :/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain how it's done differently in marlin 2.0 that could be used here (in order to save on resources)?

@wavexx
Copy link
Collaborator

wavexx commented Apr 30, 2022

I approved this, I think we can work on it as we go :)

@wavexx
Copy link
Collaborator

wavexx commented Apr 30, 2022

The radius can be calculated only when the first (non interrupted) move is scheduled. We always know when we're working on a recovered move, so we can just re-use the old radius in that case.

@leptun
Copy link
Collaborator

leptun commented Apr 30, 2022

Well, considering we have 2 approvals, I'm going to merge this now and work from there.
As a side note, this PR also greatly improves the quality of prints which use G2/3 commands:
image
image
(your fw being the current state of the branch with aw gcode)

@leptun
Copy link
Collaborator

leptun commented Apr 30, 2022

@wavexx

The radius can be calculated only when the first (non interrupted) move is scheduled. We always know when we're working on a recovered move, so we can just re-use the old radius in that case.

We don't store the radius globally. Only the offsets from the start position. Also, these get overridden by the next G2/3 command, so I'd be careful with those. The XYZE cached values also get overridden by any G1 command since the global cache for XYZE is common between G0/1/2/3

@wavexx
Copy link
Collaborator

wavexx commented Apr 30, 2022

We don't store the radius globally. Only the offsets from the start position. Also, these get overridden by the next G2/3 command, so I'd be careful with those. The XYZE cached values also get overridden by any G1 command since the global cache for XYZE is common between G0/1/2/3

By this I mean store the radius just for the move. This would need to be stored for each block for this reason.
When stopping, extract the radius from the block, and supply that radius again as the first G2 during recovery.

Yes, it's ugly :]

@leptun
Copy link
Collaborator

leptun commented Apr 30, 2022

Using just the radius will create inconsistencies in the print. Consider the following scenario: If the move is stopped at the middle of a G2 segment, the radius should be actually smaller than if it was at a segment end (exactly the radius). This is probably good enough for high precision segments (max 1mm), but the lower resolution you go (>1mm), the worse the offset will get after resume. Also, the restarted move will not aligh with the segment split of the previous move. I think this will most likely result in some kind of visual artifact.

@FormerLurker
Copy link
Contributor Author

If the move is stopped at the middle of a G2 segment, the radius should be actually smaller than if it was at a segment end (exactly the radius)

I'm not sure this is true. Typically you would draw an arc on paper by calculating the midpoint of the circle (move from starting point by the relative offset), and drawing from the current position to the target position. However, If you flip your compass around (pointy end on the current position instead of the center of the circle), and draw a circle of a fixed radius, it will always intersect in the same two places as long as the pointy end is on the arc. The correct intersection depends on the direction. Here's a crude sketch I made with a screwdriver, a pen, and a piece of foam, so it's not great, but accurate enough to get the idea:

PXL_20220430_182122528

From that, you can reconstruct the offset by finding the intersections of two circles, selecting the correct intersection (not sure how to do that just yet), and just figuring the X and Y coordinates of the vector from the starting point to the intersection. This assumes we will know the current absolute position, and that it is on the arc somewhere.

I'll try to create a program that will, given a G2/G3 command and a point along the arc, will attempt to create a new G2/G3 command to complete the arc with only the radius, the extrusion remaining, and the target coordinates (do we know the final destination of the G2/G3, or just the final destination of the interrupted segment?)

@leptun
Copy link
Collaborator

leptun commented May 2, 2022

I'm not sure I understand the explanation. I'll try to explain my part a bit more:
image

Consider the following sequence:

G1 X0 Y0 ; start at the origin
G2 X30 Y30 I30 J0

In the picture above, the resolution of the arc was intentionally dropped to highlight the issue.
The problem I see is that the hard stop event can happen at any point during the arc. In the picture you have the perfect arc (dashed line) and the actual interpolated arc by G2 (normal line). Since the stop event can happen at any moment, the "current_position" at the hard stop position can be anywhere along the interpolated segments, not on the perfect arc. This can cause problems since the distance to the center of the arc isn't technically constant (it's the lowest at the midpoint of every interpolated segment). On resume, the arc is restarted, but the starting position will most likely be wrong because the arc would actually start a bit closer to the center in the midpoint case. If the pause instead happens exactly at the beginning/end of an interpolated segment, then the arc after resume will perfectly match the one before the resume. I'm not sure how this can be avoided besides not doing a hard stop in the middle of a segment.

If we use start position, end position and radius for the second arc, this is what it would look like (blue line):
image

@leptun
Copy link
Collaborator

leptun commented May 2, 2022

Also, something I didn't consider at all is the handling of broken arc commands. What I mean by this is when the center offsets create invalid geometry. These arcs can be identified by the fact they draw an arc, and then do a straight last move to the target position. I'm 100% sure we can't replicate their behaviour on resume (not that we would want to do that, but still something to consider).

Copy link
Collaborator

@DRracer DRracer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving as well - but the pause/resume feature must be resolved in the future to make this reliably for all scenarios.

@leptun leptun merged commit 37e1c11 into prusa3d:MK3 May 2, 2022
@FormerLurker
Copy link
Contributor Author

First, THANK YOU SO MUCH for merging this in! I'm so thankful for all of your time and effort!

Next, I'm not sure where to carry on the discussion about pause/resume. Should I create an issue where we can work on a solution?

@leptun
Copy link
Collaborator

leptun commented May 4, 2022

Yes, please create an issue about this where we can dump all the info we have so far (and ideas)

@leptun leptun added the Gcode arcs everything that is related to arcs that are generated by the printer using G2/G3 and M214 commands label Aug 21, 2022
@3d-gussner
Copy link
Collaborator

@FormerLurker Thanks again for this PR. Finally it got merged. 🥳
Are you familiar with https://reprap.org/wiki/G-code and editing it?
As you have introduced the M214 g-code we should also add this to the wiki.
Please let me know if you are doing this or if I should do it.

@FormerLurker
Copy link
Contributor Author

@3d-gussner, thank all of YOU for your time and support! I'm familiar with the wiki (I have used it EXTENSIVELY over the years), but I've never edited anything. I will take a look as soon as I have some time, and will let you know how it goes.

@arekm
Copy link
Contributor

arekm commented Oct 4, 2022

Usage question.

PrusaSlicer doesn't do G2/G3 for arcs, right?

Based on prusa3d/PrusaSlicer#4352 the only way to play with this is ArcWelder plugin, right? (and no solution to wire that into prusaslicer)

Edit: never mind, this explains everything
https://github.com/FormerLurker/ArcWelderLib#running-arcwelder-from-slic3r-slic3rpe-prusaslicer-and-superslicer

@wavexx
Copy link
Collaborator

wavexx commented Oct 4, 2022

AFAIK it hasn't been integrated in prusaslicer yet because post-processing the gcode as arc-welder does can result in non-coherent segments across layers. This can result in different printing behavior from multiple fronts that the slicer cannot control, so it makes reported slicing issues much harder to reason about.

It's ironic that it was merged into a 32bit FW, where I would consider it's primary feature (the resulting g-code space savings) not so important.

@FormerLurker
Copy link
Contributor Author

@wavexx,

AFAIK it hasn't been integrated in prusaslicer yet because post-processing the gcode as arc-welder does can result in non-coherent segments across layers.

FYI, I have heard this before from several people, but have never seen an example of this with any reasonable resolution setting. Also, the resolution could be set by the slicer to prevent this. Every time this 'issue' has been reported to me, it's been a visualization error within some slicer or gcode viewer. I can think of many reasons why this feature isn't added to PrusaSlicer, but I don't think the above is a good one.

It's ironic that it was merged into a 32bit FW

The MK3 firmware is 32 bit? What about the MK2? I ran this fork on my MK2 a bunch for testing.

the resulting g-code space savings

For me, the main feature is reduced serial transfer so I can stop hearing about 'zits' being created when using OctoPrint :) It was a rare issue, but annoying, and created a lot of github issues for Octolapse.

@leptun
Copy link
Collaborator

leptun commented Oct 5, 2022

@FormerLurker
Clarification from @wavexx (since I was also confused):

@leptun: What was merged into the 32 bit fw?
@wavexx: Not in the 32bit but for the 32bit. I was referring to the fact that the bambulab slicer (rebadged prusaslicer) includes arc-welder, but the printer doesn't really need it (I actually think it's counter-productive if you don't need the serial speed)

The MK3 is still the 8 bit firmware. Only Mini and newer are the 32 bit firmware (buddy firmware). The MK2 is a separate fork of the 8 bit firmware, but it is not being actively maintained.

@johnnynoone
Copy link

@3d-gussner
M214 is still not described on RepRap wiki. If you can, please add it. Thank you in advance. :)

@3d-gussner
Copy link
Collaborator

@johnnynoone Thanks for the reminder. Done https://reprap.org/wiki/G-code#M214:_Set_Arc_configuration_values @FormerLurker Is that okay so?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Gcode arcs everything that is related to arcs that are generated by the printer using G2/G3 and M214 commands
Projects
None yet
Development

Successfully merging this pull request may close these issues.