klipper
klipper copied to clipboard
cnc : work offset commands
A coordinate system is an XYZE offset to the native machine space.
All coordinate systems default to 0,0,0,0 at start but persist across restarts.
- G53 (machine space)
- G54-9 (select coordinate system)
- G10L2 (set coordinate system offsets)
Plans for the remaining cnc command set on discourse. Feedback welcome!
Thanks. I think I need some additional information before doing a review.
Can you provide a brief description of what "work spaces" are and what real-world tasks one would accomplish with them (or point me to a link with a succinct description of it)?
Also, it would be great if you could provide some information on 3rd party tools that produce these commands. FWIW, I don't have any interest in other firmware that ingests them (and FWIW I wouldn't add a comparison to other firmware to the Klipper documentation).
I saw a few things in the code that I'm unsure of (eg, changes to gitignore, run-time files without configuring their location, very generic "cnc" and "position" module names, use of the insecure "pickle" module). However, I think I need to understand the basics before I can provide meaningful feedback.
Cheers, -Kevin
Instead of focusing on specific commands, my thought process has been around CNC concepts, of which, coordinate systems (aka workspaces) exist no matter what firmware is implemented.
The thing I always have to wrap my head around when I think about CNC vs 3D printing is where is the origin. For 3d printing, it's always relative to the bed. For CNC, however, it's always relative to where the raw material (or its fixture/vise) is located. All CAD/CAM packages generate gcode (see fusion 360 example below) relative to a specific location on the raw material (usually the XYZ of a corner). So, the machine needs to know where the bed is located so it doesn’t over-travel (machine space) and where the fixture (eg a vise) holding the raw material is located (work space offsets, sometimes called coordinate systems) relative to the bed. There are multiple of these work spaces (each with their own offset) because in cnc environments, you will do one run which will constitute machining several of the same part: you’ll have a fixture for each piece of raw material, establish offsets for each and then run the same gcode just by changing the work offset.
These offsets are persisted because once the fixture is located, the operator can do a run, remove the machined part, put in new raw materials and run the same gcode again (and again).
The other things that are required for the work offset concept to function is allowing the operator to manually set these work offsets (G10 L2) and use a probe to set the work offsets (G38.2).
I’ve tried to be very careful to only include concepts that are general across all cnc machining operations and are not firmware specific. The original “spec” of gcode has machine space (G53) and only 6 workspaces (G54-9); but other firmwares have extended it to 10, 15 and even 99 different workspaces and I have not included those extensions in the implementation. The other concepts will be probe target, spindle control, coolant control and tool selection.
I agree that ‘cnc’ is too generic and that each cnc concept should have its own configuration section. I have made that modification. ‘Position’ is a generic helper class that makes work offsets (and the other cnc concepts) much easier to implement by encapsulating coordinates in a mutable struct with the same memory efficiency as the immutable Coord
class by using __slots__
but allows better time and memory performance than having to construct a Coord
, deconstruct it manipulate a value and then reconstruct it with the new values.
I’m open to any file format; the work offset concept has that the values are set and saved without requiring a restart of the machine. But when the machine does get restarted, the values are reloaded. The machine space needs to be determined every time the machine starts, but all of the offsets are relative to the machine space and therefore can be persisted and don’t need to be re-established across restarts (aka the vise gets clamped down and doesn’t move relative to the bed). As these values change with each different part run, it didn’t feel like something that belonged in a configuration file. CNC machines only allow changes through G10 L2 and G38.2, so serializing (pickling) it seemed like a convenient way of discouraging changes. It doesn’t necessarily need to be secure, someone could set them directly if they choose but i’m not sure why anyone would. And adding this file .mem
to gitignore seemed to be the right way of preventing those files from being inadvertently included in someone’s commit after a run of the regression tests.
Could you clarify which files are “run-time without location”? Python locates all files based on the directory that they are in (as long as there is an __init__.py
file included in the directory) so there is a 1-to-1 correlation there for any imports of modules/packages. And the mem
file is just placed in the same directory as the configuration file. I can add a command line flag to specify that location if you prefer?
Here is a snippet of gcode directly from fusion 360 where the G54 is created.
(Machine)
( vendor: Sherline with rotary)
( model: 2000)
( description: Sherline Mini Mill)
(T2 D=12 CR=0 - ZMIN=-5 - flat end mill)
(T4 D=6 CR=0 - ZMIN=-24.5 - flat end mill)
G90 G94
G17
G21
G28 G91 Z0
G90
(Face2)
T2
S5000 M3
G54
G0 X57.8 Y-47.572
G0 Z15
G0 Z5
G1 Z-3.8 F333.3
G18 G3 X56.6 Z-5 I-1.2 K0 F1000
G1 X56.01
G1 X-56.01
G17 G2 X-56.01 Y-39.227 I0 J4.172
Thanks for the information. That is helpful.
How do these new offsets interact with the existing g-code offset system (G90
and SET_GCODE_OFFSET
)? Is it just a handy way of selecting from a set of offsets (with automated save/restore) or is there some inherent difference? Could these workspaces be implemented with macros today? (I'm not suggesting that it should be done with macros - I want to better understand if it is a fundamental capability or a bundling of existing capabilities.)
CNC machines only allow changes through G10 L2 and G38.2, so serializing (pickling) it seemed like a convenient way of discouraging changes.
When reading a pickled file, it's possible that arbitrary code could be executed. It's considered a security issue, and in general, the pickle module should not be used for anything. Use the python configfile or json modules for saving and restoring data between sessions.
Could you clarify which files are “run-time without location”?
The name and location of the "mem" file isn't configured - no other file in Klipper is like that. (All other files are passed via command-line or are set in the config file.) I need to better understand the high-level aspects of the module before I can provide meaningful comments though.
-Kevin
@KevinOConnor
I've added a command line flag for the work offsets storage file and removed the use of pickle in favor of the config parser.
These offsets use the same mechanism as SET_GCODE_OFFSET
(altering the base_position
and homing_position
in gcode_move
) but can load different, stored offsets (and clearing them). Since macros don't have read/write file access, storing these offsets across restarts wouldn't be possible.
I'm still not sure I fully understand this PR. Is it just bundling existing capabilities or does it provide some fundamental new functionality?
That is, could the functionality be obtained by macros that use SET_GCODE_OFFSET and the [save_variables]
module to save/restore state? To be clear, I'm not suggesting it is a good idea to implement this with macros. If it can't be implemented in macros, what are the fundamental new capabilities?
Thanks, -Kevin
Thank you for your contribution to Klipper. Unfortunately, a reviewer has not assigned themselves to this GitHub Pull Request. All Pull Requests are reviewed before merging, and a reviewer will need to volunteer. Further information is available at: https://www.klipper3d.org/CONTRIBUTING.html
There are some steps that you can take now:
- Perform a self-review of your Pull Request by following the steps at: https://www.klipper3d.org/CONTRIBUTING.html#what-to-expect-in-a-review If you have completed a self-review, be sure to state the results of that self-review explicitly in the Pull Request comments. A reviewer is more likely to participate if the bulk of a review has already been completed.
- Consider opening a topic on the Klipper Discourse server to discuss this work. The Discourse server is a good place to discuss development ideas and to engage users interested in testing. Reviewers are more likely to prioritize Pull Requests with an active community of users.
- Consider helping out reviewers by reviewing other Klipper Pull Requests. Taking the time to perform a careful and detailed review of others work is appreciated. Regular contributors are more likely to prioritize the contributions of other regular contributors.
Unfortunately, if a reviewer does not assign themselves to this GitHub Pull Request then it will be automatically closed. If this happens, then it is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.
Best regards, ~ Your friendly GitIssueBot
PS: I'm just an automated script, not a human being.
@KevinOConnor work offsets, i guess, could be implemented with SET_GCODE_OFFSET and save_variables module. but it seems like a messy / unstable way to implement. It all depends if klipper should natively support gcode functionality created by fusion 360 or if it will be left on a user-by-user basis. eg should some cnc commands be supported by macros (such as work offsets) and then some be implemented directly (such as probe commands which can't be implemented via macros). it creates a mixed implementation scenario which I don't think is a good user experience for users who are trying to use klipper as a cnc controller.
The way G53
has been implemented here does not match the typical expected CNC machine behavior as I understand it.
G54
through G59
are simple. They switch between six banks of offsets. Effectively it's just SET_GCODE_OFFSET
from previously saved values.
G53
, however, is expected to be "non-modal". It's not meant to change the currently loaded workspace.
It's intended to be used with other g-codes on the same line, e.g. G53 G0 X0 Y0 Z0
, meaning, rapid-move to actual machine 0,0,0, ignore and do not change the currently loaded work offset.
You'll find CNC machine oriented g-code to often take advantage of the "modal"-ness of g-codes and have commands seemingly span multiple lines.
G1
X0
F65
G0
G53 Y20
"3d printer g-code" has tended to not allow commands to span multiple lines or to have multiple commands on one line. The 3D printing community kind of pretends this sort of thing doesn't exist.
This "non-modal" G53
introduces some previously ignored behavior that is going to need more work to actually implement fully.
Also, I could be wrong, but I believe a machine is expected to boot into work offset 1. The "machine coordinates" aren't a state to ever be in.
Additionally, G10 L20
might as well be implemented in this PR. It's probably more often used than G10 L2
.
And finally I'll mention I've locally implemented G54
through G59
, G10 L2
, and G10 L20
with macros in terms of SET_GCODE_OFFSET
. Not saying that they should be (it does get a bit messy) but they can be and it interacts well with existing interfaces like mainsail. G53
is another story.
Unfortunately a reviewer has not assigned themselves to this GitHub Pull Request and it is therefore being closed. It is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.
Best regards, ~ Your friendly GitIssueBot
PS: I'm just an automated script, not a human being.
Unfortunately a reviewer has not assigned themselves to this GitHub Pull Request and it is therefore being closed. It is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.
Best regards, ~ Your friendly GitIssueBot
PS: I'm just an automated script, not a human being.
Unfortunately a reviewer has not assigned themselves to this GitHub Pull Request and it is therefore being closed. It is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.
Best regards, ~ Your friendly GitIssueBot
PS: I'm just an automated script, not a human being.
I'm a CNC machinist by trade.
G53
, however, is expected to be "non-modal". It's not meant to change the currently loaded workspace. It's intended to be used with other g-codes on the same line, e.g.G53 G0 X0 Y0 Z0
, meaning, rapid-move to actual machine 0,0,0, ignore and do not change the currently loaded work offset. You'll find CNC machine oriented g-code to often take advantage of the "modal"-ness of g-codes and have commands seemingly span multiple lines.
This is true. Modal commands are, once set, carried over until they are cancelled. Non-modal commands only apply to the block (line of code) in which they are used.
Consider the following bit of code:
N10 T1 M6;
N20 G00 G54 X-.5 Y-0.5;
N30 G43 H1 Z0.5;
N40 G01 Z-0.1 F60.;
N50 Y1.;
N60 X1.;
N70 Y-0.5;
N80 X-0.5;
N90 G00 G49 G53 Z0.;
G00, G01, G43, G49, and G54 are all modal commands, so they carry over from block to block until changed. The last block commands rapid linear interpolation, tool length offset cancellation, and retracts the Z axis to the machine zero position. I use this all the time in machining. Machine coordinates will only be active in this block. It would go back to G54 in subsequent blocks.
No one codes CNCs like your example. For one thing, the machine will feed to X0 at whever the last specified feedrate was. if you wanted this move to be at the 65 feedrate, the F65 would have to be in the same block as the X0
All that being said, having multiple work offsets is absolutely essential on a CNC machining center, I'm unconvinced as to their utility on a 3D printer.
No one codes CNCs like your example. For one thing, the machine will feed to X0 at whever the last specified feedrate was. if you wanted this move to be at the 65 feedrate, the F65 would have to be in the same block as the X0
My example was definitely contrived. The purpose was to demonstrate that on a "real" machine, X0
is valid as a line on its own, when following a prior G1
line. And as in your example, G0
and G53
can appear on the same line.
My point was that Klipper is completely unprepared for parsing this type of input. It currently assumes a line starts with one G
-code followed by some optional parameters (coordinates and a feedrate in the case of G0
) that MUST appear on the same line.
G53
cannot be cleanly implemented without notable changes to Klipper's gcode parsing.
No one codes CNCs like your example. For one thing, the machine will feed to X0 at whever the last specified feedrate was. if you wanted this move to be at the 65 feedrate, the F65 would have to be in the same block as the X0
My example was definitely contrived. The purpose was to demonstrate that on a "real" machine,
X0
is valid as a line on its own, when following a priorG1
line. And as in your example,G0
andG53
can appear on the same line.My point was that Klipper is completely unprepared for parsing this type of input. It currently assumes a line starts with one
G
-code followed by some optional parameters (coordinates and a feedrate in the case ofG0
) that MUST appear on the same line.M53
cannot be cleanly implemented without notable changes to Klipper's gcode parsing.
Oh, I understand that. My comment wasn't intended to criticize, and I totally agree with what you are saying. I'm just not seeing the utility of having multiple work offsets on a 3D printer.
I'm just not seeing the utility of having multiple work offsets on a 3D printer.
People are running CNC machines using Klipper.
Unfortunately a reviewer has not assigned themselves to this GitHub Pull Request and it is therefore being closed. It is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available.
Best regards, ~ Your friendly GitIssueBot
PS: I'm just an automated script, not a human being.
This PR was inadvertently closed due to a regression introduced by #6293.
-Kevin
While it would be a game changer to have a CNC Klipper implementation it's likely to require developers with CNC background to make it happen. A funny thing has happened over on the Laser Cutter side of the fence with LightBurn. Most of the LightBurn devs came to laser engraving/cutting from a background/work experience with CNC routing/engraving including the Lead dev OZ/Jason. They have now refactored their laser centric features of LightBurn and started a CNC version of LightBurn called Mill Mage.
Currently for CNC the only thing like Klipper is the Remora project for LinuxCNC( https://github.com/scottalford75/Remora ) and it's really feature complete. Because Remora works with LinuxCNC/EMC2 it gets decades of design work for kinematics and trajectory planning. But that's also its problem, it's not been modernized enough to break out of the hardcore CNC sector and into the consumer space. For example, you can't include the Spindle in the trajectory planning and if you do it'll kill the planning buffer. I've had to resort to tricking it with postprocessing of the GCode to get Remora/LinuxCNC working for laser engraving(high speed raster engraving).
Klipper capabilities on CNC machines like Shapeoko and others would likely dominate but it's a different mindset / experience set from the 3DP world.