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

bed_mesh: Implement adaptive bed mesh #6461

Merged
merged 1 commit into from
Jan 26, 2024

Conversation

voidtrance
Copy link
Contributor

This PR is for an implementation of adaptive bed meshes. Adaptive bed meshes use the exclude objects data to determine the area of the print bed being used and only create a mesh for that bed area. This has the benefit of greatly decreasing the amount of time needed to start a print without compromising print quality.

These changes have been tested on both Cartesian and Delta printers through the use of SimulAVR (with some custom Klipper modifications to enable it to report probe point z offset through Moonraker) and by many actual users in the Voron Discord.

Below is a ZIP archive containing two HTML files that show the behavior of the adaptive mesh. Plots where generated using test data from the SimulAVR simulation.

adaptive-mesh-plots.zip

The commands issues to define the print objects that produce these plots are below:

commands-cartesian.txt
commands-delta.txt

@kyleisah
Copy link

Amazing! Thank you for all your impressive work on this! 😄

Copy link
Contributor

@zellneralex zellneralex left a comment

Choose a reason for hiding this comment

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

small typo in md

docs/Bed_Mesh.md Outdated Show resolved Hide resolved
Copy link
Collaborator

@Arksine Arksine left a comment

Choose a reason for hiding this comment

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

Thanks. Overall the implementation looks good. I have some comments, however the are minor and mostly centered around code organization.

klippy/extras/bed_mesh.py Outdated Show resolved Hide resolved
klippy/extras/bed_mesh.py Outdated Show resolved Hide resolved
# If adaptive meshing is enabled and there are defined objects,
# generate adaptive mesh
if do_adaptive and exclude_object is not None and \
len(exclude_object.objects) != 0:
Copy link
Collaborator

Choose a reason for hiding this comment

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

It isn't valid to directly access the member attributes from a class in another module. Fortunately exclude_object exposes its objects via get_status, ie:

objects = exclude_object.get_status().get("objects", {})

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why isn't it valid? ExcludeObject._reset_state() is being called from the constructor so the member attribute should always be defined. I can switch the code as your suggestion gives me the same result but I just want to understand your comment.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's against our coding conventions as mentioned at: https://www.klipper3d.org/Code_Overview.html#adding-a-host-module

Accessing the internal member variables of other objects leads to a maintenance burden. Other developers working on ExcludeObject wont know that some other module has accessed its interanl state and those developers may make a local change that could subtly break the bed_mesh module. So, the convention is to avoid doing that.

-Kevin

klippy/extras/bed_mesh.py Outdated Show resolved Hide resolved
@Arksine
Copy link
Collaborator

Arksine commented Jan 18, 2024

The latest changes look good. One minor thing I missed earlier is that we should add the adaptive_margin option to the Config Reference as well. When that is done I'm ready to approve the PR.

@voidtrance voidtrance force-pushed the adaptive-bed-mesh branch 2 times, most recently from a494621 to d4037ba Compare January 18, 2024 22:22
@voidtrance
Copy link
Contributor Author

The latest changes look good. One minor thing I missed earlier is that we should add the adaptive_margin option to the Config Reference as well. When that is done I'm ready to approve the PR.

Done.

I also re-ran the simulated tests with the latest revision and compared it against the graphs that were generated with the original version submitted with the PR to make sure that thing did not break during the review edits.

@KevinOConnor
Copy link
Collaborator

Thanks. Happy to commit. I think we should give a few days to see if there are any other comments. Assuming nothing else comes up, I'll look to commit next week.

-Kevin

@kyleisah
Copy link

Thanks. Happy to commit. I think we should give a few days to see if there are any other comments. Assuming nothing else comes up, I'll look to commit next week.

Amazing, thank you so much!

docs/Bed_Mesh.md Outdated Show resolved Hide resolved
docs/Bed_Mesh.md Outdated Show resolved Hide resolved
docs/Bed_Mesh.md Outdated Show resolved Hide resolved
klippy/extras/bed_mesh.py Show resolved Hide resolved
@marbocub
Copy link
Contributor

Hello, I'm interested in this PR.
When the purge line in a start gcode is not labeled and is too far from printing objects, the nozzle may scratch the table at the line in the worst case because it is not probed around the line.

I think this has to be careful and think it would be helpful to include a note or a labeling method in the document.

@voidtrance
Copy link
Contributor Author

Hello, I'm interested in this PR.

When the purge line in a start gcode is not labeled and is too far from printing objects, the nozzle may scratch the table at the line in the worst case because it is not probed around the line.

I think this has to be careful and think it would be helpful to include a note or a labeling method in the document.

I am not sure that I understand. Are you asking for the purge line area to be probed or to document that the purge line area is not probed?

Whether the purge line will be probed depends entirely on the slicer-generated gcode. If the slicer generates a purge line that is labeled as an object, it will be probed. Otherwise, it won't.

I think that describing all of that and documenting slicer behavior is beyond the scope of Klipper documentation.

@marbocub
Copy link
Contributor

or to document that the purge line area is not probed?

Yes, this is my opinion.

Whether the purge line will be probed depends entirely on the slicer-generated gcode. If the slicer generates a purge line that is labeled as an object, it will be probed. Otherwise, it won't.

I think it would be kind to include this content or simply the first half to the document. This may reduce a user accidentally scratching the table.

@kyleisah
Copy link

I think it would be kind to include this content or simply the first half to the document. This may reduce a user accidentally scratching the table.

There is the adaptive_margin configuration to expand the bounds of the mesh, so purging may not be necessary as you can “purge” using something like a skirt.

Speaking from experience here, I’ve ran many, many adaptive meshes and have used many different purge configurations (adaptive and static) and have not damaged a buildplate or nozzle.

I believe it should be left up to the user what they feel is best to handle that, however I do not foresee it being a problem.

@marbocub
Copy link
Contributor

I understand. I appreciate your consideration for all.

@kyleisah
Copy link

I understand. I appreciate your consideration for all.

I appreciate you understanding. I do, however, agree that although I've had no problems with it in the past, if someone's machine is a unique setup or has mechanical issues causing them to rely on a full mesh, that should be laid out clearly. It would require a fairly unique complication with the machine, but I suppose it is possible afterall that issuing print moves outside of the adaptive meshing area can be problematic.

docs/Bed_Mesh.md Outdated Show resolved Hide resolved
Copy link

@kyleisah kyleisah left a comment

Choose a reason for hiding this comment

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

Added some verbiage for potential pitfalls when attempting print moves outside of an adaptive mesh's bounds on machines that have mechanical issues that a full mesh typically compensates for. I also added a note that adaptive bed meshes are not meant to be reused, and are best utilized by building a mesh prior to every print.

the area of the bed used by the objects being printed. When used, the method will
automatically adjust the mesh parameters based on the area occupied by the defined
print objects.

Copy link

@kyleisah kyleisah Jan 22, 2024

Choose a reason for hiding this comment

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

Suggested change
It is important to consider that adaptive bed meshing is best implemented on machines that can normally probe the entire bed and achieve a maximum variance less than or equal to 1 layer height. Machines that have mechanical issues that a full bed mesh normally compensates for may have undesirable results when attempting print moves **outside** of the detected print object area. If a full bed mesh on your machine typically has a variance greater than 1 layer height, caution must be taken when attempting print moves outside of the meshed area.
Due to the nature of using an adapted bed mesh, it is not recommended that a user load a saved adaptive mesh to re-use for printing. Adaptive bed meshing is best utilized by generating a new mesh prior to every print.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kyleisah, I have added this to the documentation in a reworded/reorg'ed form.

@Arksine, I would appreciate a review of the new documentation section from you, as well?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The updated documentation looks good to me. This also adds context to your previous PR regarding BED_MESH_CLEAR. I think we should disable autosave when adaptive is enabled. It will be relatively easy, just a few lines of code. I'll make the recommendations here, but I can submit it as a follow-up PR if you prefer not to merge them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The updated documentation looks good to me. This also adds context to your previous PR regarding BED_MESH_CLEAR. I think we should disable autosave when adaptive is enabled. It will be relatively easy, just a few lines of code. I'll make the recommendations here, but I can submit it as a follow-up PR if you prefer not to merge them.

Yes, please. The recommendation would be appreciated. I actually do prefer to merge them since doing them separately will introduce a window during which it would be possible to save an adapted mesh. Plus, I was already thinking along the lines of restricting it in the actual code. Just didn't know exactly how (due to the issues with my previous PR).

Copy link
Collaborator

Choose a reason for hiding this comment

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

See the review point here. Hopefully its clear enough, Github doesn't seem to allow suggestions outside of the modified code. Essentially we just set self._profile_name = None right before returning True from set_adaptive_mesh(). Then we modify the last line probe_finalize() to prevent from sending the None value save_profile().

@Sineos
Copy link
Collaborator

Sineos commented Jan 22, 2024

if someone's machine is a unique setup

@marbocub's concern is essentially valid. In the support activities, I have seen enough bed topologies that range from -0.5 to +0.5 or even worse. It seems that many users mistake bed-meshing as an alternative to leveled bed or generally have completely crooked beds.

Maybe a sound recommendation would be building a mesh of the entire bed and review it. If it deviates more than 1 layer-height, adaptive meshing should be used with caution.

@Sineos
Copy link
Collaborator

Sineos commented Jan 22, 2024

In addition: Not sure if the adaptive meshing changes anything here, but usually the "boundary" points of a mesh are carried over to the unmeshed region.
This might even create another challenge for crooked beds. Let's assume your last meshed point was -0.2 but for whatever reason your bed starts rising to +0.5 outside this point.
Now, carrying over -0.2 to the "physical +0.5 region" increases your problem to 0.7.

@kyleisah
Copy link

Maybe a sound recommendation would be building a mesh of the entire bed and review it. If it deviates more than 1 layer-height, adaptive meshing should be used with caution.

Yeah, I fully agree here. I will adjust the suggestion I've made with some more verbiage about this. I can fully relate with seeing some... "interesting" mesh results, so the concern is totally valid.

In addition: Not sure if the adaptive meshing changes anything here, but usually the "boundary" points of a mesh are carried over to the unmeshed region.

This is correct, I actually just double-checked this was the behavior today because I was not sure how that is handled. But yes. You can imagine it sort or like a bowl with a very wide flange. The edges of the mesh are scaled out in XY to infinity.

Some of these cases we are imagining are very extreme, almost as if generating a bed mesh on a waffle-iron, but they are absolutely within the realm of possibility.

@zellneralex
Copy link
Contributor

zellneralex commented Jan 28, 2024

As a short note, yes the empty string is the issue, @meteyou is already working on it.
‘He deceive to refactor the module so the fix should be part of the next Mainsail release

@pedrolamas: you need to check that for Fluidd also

@matmen
Copy link

matmen commented Jan 28, 2024

Fluidd correctly shows the adaptive mesh, it just doesn't show up in the profile list (as expected):
image

@zellneralex
Copy link
Contributor

zellneralex commented Jan 28, 2024

@matmen I do not doubt that, we saw the same as long a mesh like default was loaded before the execution with ADAPTIVE=1. Your picture shows also the name default.

So you should test if again if fluidd does not name a mesh with an empty name string default.

@voidtrance can you check that the name will also be an empty string if a mesh was already loaded, currently it looks like the name stays whatever it was before. Here is a picture of the data when you load a default mesh at Klipper init. E.g. using a delayed_gcode
image

and here the same print with no mesh loaded before
image

@matmen
Copy link

matmen commented Jan 28, 2024

@zellneralex The default bed mesh shows up because it's defined in my config - fluidd just shows the reported mesh list, which doesn't include the adaptive mesh (with an empty name). We only use the bed mesh name for showing which mesh profile is active, which is why the "default" profile in the screenshot is showing a load button.

If there's no bed mesh profiles reported, we just show a message, but the visualization still shows up:
image

@zellneralex
Copy link
Contributor

@zellneralex The default bed mesh shows up because it's defined in my config - fluidd just shows the reported mesh list, which doesn't include the adaptive mesh (with an empty name). We only use the bed mesh name for showing which mesh profile is active, which is why the "default" profile in the screenshot is showing a load button.

If there's no bed mesh profiles reported, we just show a message, but the visualization still shows up: image

Perfect, then fluidd should be save.

@Arksine
Copy link
Collaborator

Arksine commented Jan 28, 2024

FWIW, #6473 will add profile names to the adaptive mesh. It will be something like adaptive-<hexstring>, where the hexstring is the mesh's internal object id. That will make it relatively unique. It isn't intended that users save these meshes, although they could if they wanted to (perhaps for some experiment).

@kyleisah
Copy link

Really appreciate the rapid and collaborative work to patch this you guys. 😄 Thanks for doing that.

I've been monitoring some discord servers and forums and it seems like there are some areas the documentation is still lacking. I'll fork klipper and make some changes to the docs to improve any gaps in configuration/setup and make a PR. Really it just boils down to setting up exclude_object correctly and some other small things. Thank you again!

@voidtrance voidtrance deleted the adaptive-bed-mesh branch January 29, 2024 17:00
@AudreyAP
Copy link

@Arksine Is it maybe a good idea to add an option to make save_config ignore adaptive-xxxx meshes?

@voidtrance
Copy link
Contributor Author

@Arksine Is it maybe a good idea to add an option to make save_config ignore adaptive-xxxx meshes?

Arksine already mentioned in his fix up commit message that he is explicitly allowing the saving of adaptive meshes in case anyone wants to use them for some type of testing/research.

@Arksine
Copy link
Collaborator

Arksine commented Jan 29, 2024

You have to explicitly call BED_MESH_PROFILE SAVE=xxxx after probing an adaptive mesh to save it. You could call it adaptive-xxx or anything else. Without this step SAVE_CONFIG will ignore it.

@AudreyAP
Copy link

🤦‍♀️ whoops, I totally missed that.

Sounds great, thank you both.

@kot0005
Copy link

kot0005 commented Jan 31, 2024

Hi this feature is not working. I am on mainsail. It still probes the whole bed.

I was using a different adaptive bed mesh and it was working.

image

image

image

I dont save my meshes because i have a belted z and the bed needs to be calibrated every time as the bed drops a bit after steppers are disabled.

@AudreyAP
Copy link

@kot0005 remove the bed_mesh_profile load=default line, and send a screenshot of your start gcode settings in slicer.

@kot0005
Copy link

kot0005 commented Jan 31, 2024

@kot0005 remove the bed_mesh_profile load=default line, and send a screenshot of your start gcode settings in slicer.

hi, this is my start g code in slicer START_PRINT BED_TEMP=[first_layer_bed_temperature] EXTRUDER_TEMP=[first_layer_temperature]

if i remove the line you mentioned, will it still use the mesh it calibrated from the calibrate command ?

ty

@AudreyAP
Copy link

@kot0005 if you leave the line in there, it most definitely won't work.

Try adding M117 before you call start_print in slicer start gcode.

@kot0005
Copy link

kot0005 commented Jan 31, 2024

@kot0005 if you leave the line in there, it most definitely won't work.

Try adding M117 before you call start_print in slicer start gcode.

i removed the load profile code, no change. will try m117
image

@kot0005
Copy link

kot0005 commented Jan 31, 2024

@kot0005 if you leave the line in there, it most definitely won't work.

Try adding M117 before you call start_print in slicer start gcode.

mate the M117 in the slicer makes my printer start printing the file without heating anything..

ty for the help but i have gone back to my original gcode base adaptive mesh and working perfectly. This is def not ready for plug and play.

@AudreyAP
Copy link

@kot0005 if adding M117 before print start makes your print start not work, your config is severely broken. Swing by the discord server and we can get it working, don't want to continue spamming this pr. The feature works exactly as expected, this is a config issue.

@kot0005
Copy link

kot0005 commented Jan 31, 2024

@kot0005 if you leave the line in there, it most definitely won't work.
Try adding M117 before you call start_print in slicer start gcode.

mate the M117 in the slicer makes my printer start printing the file without heating anything..

Isnt the whole point of PRINT_START is to use the Gcode macros set up in the config file ? adding M117 Ignores all of this, not to mention it could damage the hotend extruder if its starts to print without heating up. Luckily mine was already heated a bit from previous test print.

I opened a topic on discord with my config file now. I dont think there is anything wrong with it.

@AudreyAP
Copy link

@kot0005 m117 simply clears the display. It absolutely does not stop print_start from working. I assume that you have misread and have replaced print_start with m117, which is not what I suggested.

@kot0005
Copy link

kot0005 commented Feb 1, 2024

@kot0005 m117 simply clears the display. It absolutely does not stop print_start from working. I assume that you have misread and have replaced print_start with m117, which is not what I suggested.

i did not replace print start.. i added M117 before print start like you suggested.

@zellneralex
Copy link
Contributor

What we really need is a sliced gcode file, we have seen that the exclude object definition was placed below the first user defined gcode. As said the feature works as intended, for setup help and debugging of your system use the Klipper discord.

@voidtrance
Copy link
Contributor Author

@kot0005 m117 simply clears the display. It absolutely does not stop print_start from working. I assume that you have misread and have replaced print_start with m117, which is not what I suggested.

i did not replace print start.. i added M117 before print start like you suggested.

@kot0005 Please, let's not use the PR thread for debugging issues. There are other forums better suited for this.

What we really need is a sliced gcode file, we have seen that the exclude object definition was placed below the first user defined gcode. As said the feature works as intended, for setup help and debugging of your system use the Klipper discord.

For what it's worth, a PR was merged a couple of days ago to the preprocessing script that fixes this and the M117 hack is no longer needed. I don't know how long it will take for it to make it's way into Moonraker's environment, however.

@Arksine
Copy link
Collaborator

Arksine commented Feb 1, 2024

Moonraker's dependency on preprocess-cancellation has been updated. The next time a user updates Moonraker it should be installed.

@voidtrance
Copy link
Contributor Author

Moonraker's dependency on preprocess-cancellation has been updated. The next time a user updates Moonraker it should be installed.

Do moonraker updates through the update manager also update the virtual environment? The script is installed in there, if I am not mistaken.

@Arksine
Copy link
Collaborator

Arksine commented Feb 1, 2024

Do moonraker updates through the update manager also update the virtual environment? The script is installed in there, if I am not mistaken.

Yes, it will update Moonraker's virtualenv.

sbtoonz pushed a commit to K1-Klipper/klipper that referenced this pull request Feb 16, 2024
Adaptive bed mesh allows the bed mesh algorithm
to probe only the area of the bed that is being
used by the current print.

It uses [exclude_objects] to get a list of the
printed objects and their area on the bed. It,
then, modifies the bed mesh parameters so only
the area used by the objects is measured.

Adaptive bed mesh works on both cartesian and
delta kinematics printers. On Delta printers,
the algorithm, adjusts the origin point and
radius in order to translate the area of the
bed being probe.

Signed-off-by: Mitko Haralanov <[email protected]>
Signed-off-by: Kyle Hansen <[email protected]>
Signed-off-by: Kevin O'Connor <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.