-
Notifications
You must be signed in to change notification settings - Fork 296
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
Spindle Mode setting ignored #422
Comments
Interesting problem. Hopefully we can get this sorted. @giseburt You're probably the best person to respond here? Also @MitchBradley... any ideas? 😄 |
Sorry, I don't understand the code well enough to say anything helpful. I haven't looked at the interactions between spindle speed and planning. I never do on-the-fly spindle speed changes for my milling programs. |
Heh Heh Heh. With Marlin, are you using the 2.x (dev) stuff, with 6th order jerk control? Interested in hearing from people if it works well in practise. 😄 |
I have Marlin 2.x running on a couple of spare microcontrollers that are not hooked up to motors. I use it for testing CNCjs's Marlin interface. I have no idea how well it actually controls motors. One impressive thing is the huge number of different chips they support. I got it running on a Teensy and an ESP32 with relative ease. That said, some features don't work on all platforms. Notably, immediate commands that bypass the queue are only supported on a few micros. I think that is because the bypass feature is implemented deep inside the serial port interrupt driver. |
What I got so far is, that spindle speed changes (no matter if inline or standalone) are enqueued as BLOCK_TYPE_COMMAND. BLOCK_TYPE_SPINDLE_SPEED exists but it looks unused to me. So what I really need is a way to run the speed change in sync with the movement commands without it affecting planning at all. patyork hardcoded those changes for the tinyg in his fork https://github.com/patyork/TinyG by setting entry and exit velocities according to the previous and next movement command but I am not quite sure how to change the planner accordingly. I started work on supporting the spindle speed block type based on patyorks work but since my understanding on how the planner works is limited this might take a lot of trial and error. |
Can't get my head around how commands are handled in the planner.. I just went in, added a new function to queue the spindle speed change, using BLOCK_TYPE_SPINDLE_SPEED, setting vmax values (cruise, exit, ...) to very high numbers and the length to 0. Then inside the linear planner I used the previous blocks exit velocity to overwrite cruise speed and exit velocity of the spindle speed block. Also setting hint to PERFECT_CRUISE (also tried the generic COMMAND one - can't remember the full name atm) Build and flashed it - no change in the way G1 with spindle speed changes is handled 🤨. |
At least skipping the planner and executing the changes immediately does work (in terms of smooth movement). Did this just for sanity checking because I find it kind of funny, if all changes I make presumably do nothing to the way these commands are handled... |
hmm i don't think fixing commands is the way to go for me ... there are just too many dependencies between previous and next blocks in the planner for me to figure out... maybe i will just find a way to modify the movements following an S command to include the change upon entry ... this would reduce the the required modifications a lot |
Hi there.
This is a problem we’ve started work world (being able to have commands that the planner plans “past” instead of a plan-to-stop [PTS]). Some commands need to have a PTS, and other (depending on configuration) can be made to not stop.
So, a quick reminder: The planner works in two part: Backward (as moves are added in from gcode) and forward (just-in-time, one or two moves ahead of what’s currently executing). Each block (roughly a gcode line) contains a move (“aline”), a command, or a few other things that basically mean command that we can safely ignore.
Backward planning’s job is to set all the *_max velocities and related variables so that at no point does the machine go faster (accelerate harder or cruise longer) than it should so it can reach all set velocities and jerks as maximum values. The forward planning takes the current velocity obtained during runtime (or zero as an initial condition) and determines what it can get up to given the constraints set by back planning.
I’ve seen that forward planning carries the running block’s exit_velocity forward over commands, so that part is taken care of. (We’ve been prepping for this for a while, but haven’t had testing opportunities.) Now we need to make sure that back-planning hops over commands correctly.
All the work needs to happen (AFAICT, testing needed) inside _plan_block in plan_line.cpp lines 346 - 364, where you see we set optimal (to lock plan-ability of all previous moves, since they cannot be optimized further), lock exit_velocity at 0, braking_velocity at 0, and unset plannable for this block. This is (roughly) how that code would read if it planned over all command blocks (which is not advisable):
if (bf->block_type == BLOCK_TYPE_COMMAND) {
// IF we're going to plan past these, we need to setup this command to match the previous block
bf->exit_vmax = bf->pv->exit_vmax;
bf->cruise_velocity = max(braking_velocity, bf->pv->cruise_velocity);
bf->exit_velocity = bf->pv->exit_velocity;
// may need to grab a few other vmax values from bf->pv as well...
braking_velocity = bf->pv->exit_velocity;
bf->plannable = !optimal && bf->pv->plannable;
bf->hint = COMMAND_BLOCK;
}
Now, I said that’s not advisable. This is because some commands take time. Any command that takes any significant time will cause the motion to stop since the runtime will wait for it to finish to make moves, and so the plan must reflect that.
OK, NOW WITH ALL THAT SAID: I think you’re right that commands are not the way to handle S-commands, except when the S-command has a what condition (such as waiting for a pin or a timer so spindle can speed up). Spindle speed should (in the general case) be set by the runtime along with the moves executing.
(For further discussion of where we ant to go with this see https://github.com/synthetos/g2/wiki/Raster-Streaming-Protocol and links from that page.)
Spindle speed should be housed in the GCodeState_t struct just like feedrate. Set, just like feedrate in gcode_parser.cpp, and then later the PWM value precomputed from the S value and the segment velocity can be pushed into the stPrepSingleton struct (which is used as st_pre) so that _load_move() can set the PWM output.
…-Rob
On Aug 16, 2019, 12:21 PM -0500, Konni Hartmann ***@***.***>, wrote:
Can't get my head around how commands are handled in the planner.. I just went in, added a new function to queue the spindle speed change, using BLOCK_TYPE_SPINDLE_SPEED, setting vmax values (cruise, exit, ...) to very high numbers and the length to 0. Then inside the linear planner I used the previous blocks exit velocity to overwrite cruise speed and exit velocity of the spindle speed block. Also setting hint to PERFECT_CRUISE (also tried the generic COMMAND one - can't remember the full name atm)
Build and flashed it - no change in the way G1 with spindle speed changes is handled 🤨.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Thanks for the in depth explanation giseburt. The things I tried yesterday involved exactly those lines. I differentiated between generic commands and spindle speed changes, but I was unable to get a smooth transition (maybe I didn't get all the places). For my tests I explicitly used different speeds without disabling the laser (S0) - since this introduces dwelling. What I now have kind of working is the second method I described earlier (combining the speed change with the aline move). This works really well, but I need to work around some other expected problems I have introduced (for example atm I cannot set spindle speed without a move and only G1 is supported). Also I am testing for speed changes in every aline segment. Before: https://youtu.be/SnWvNd15bGk |
The improvement is noticeable. So the way I felt it should work, from a high level, is still move based. Much like with a PDF or on-screen graphics, you can change the drawing color/style all you want, but nothing happens until you tell it to draw a line. So, a line with just a Later, during the prep of the sections (head, cruise, tail) the speed setting is applied using previously configured settings (velocity-to-pulserate curves, etc) to compute how many pulses-per-step and pulse duration of the laser. Then when the move is passed to the step generator, the laser is pulsed on the step clock as well, using instead of using a loosely-related PWM an actual pulses-per-step is used, making it so that no location on the material is pulsed longer than configured. Likely the easiest way to do this is to add a “virtual stepper” (like the hobby servo) with one huge exception: instead of it being driven by the cartesian planning, it’s instead driven as linear “movements” computed in the prep stage. I’d suggest putting it at the top of The final component would be to make the laser motor stretch pulses. This is not that hard. The motors For bonus points you could use the spindle start/stop to tell the laser “stepper" to start/stop (instead of relying on a secondary external signal to enable/disable the laser electronically). |
I agree with what Rob said in the laser case where it is dangerous to turn on the laser when not moving, but in the milling case, I want/need to turn on the spindle independently of moving. GRBL handles that dichotomy with a "laser mode" configuration setting. In laser mode, M3 (normally spindle on CW) and M4 (spindle on CCW) have different meanings. M3 means "Sn is not coupled to motion - just set the laser power immediately when the S word is processed", while M4 does the smart stuff - the laser is off when not moving, and the power is dialed down when slowing for a corner. I would suggest using the GRBL approach if possible. Why create yet another dialect difference in the already-crazy GCode world? |
I don’t have an issue with a GRBL-compatible laser mode. There are several dialects, and several things that none of them currently support that I feel we should. What I don’t want (beyond initial coding and testing) is a whole-system switch to/from laser mode or any given dialect. We have been aiming at using the toolhead concept to allow tools to be configured by the toolhead type that they belong to. In this case, the tool is a “laser” toolhead, and that toolhead would be configured to use GRBL-compatible spindle controls. Another option would be the raster mode linked to above. In all cases we would want velocity-based pulses, either PPI (pulses-per-inch, an industry term) for raster work or cutting or power-level ramping for cutting. Note on power level ramping: this means driving a secondary pin with a PWM waveform that controls the laser’s power level, not pulsing the laser on and off but instead communicating to the power supply of the laser much like a brushless ESC is controlled. Many lasers (like LED-based lasers) do not have this capability due to the nature of how they operate, but some lasers like CO2 lasers do. When you can control the power level, then you can cut without pulsing the laser by holding it on and ramping the power level relative to velocity. (Otherwise you’ll get burns at sharp corners.) In this mode the same gcode tells it desired power levels (presumably by overriding the spindle co tells for that tool), but instead of driving PPI it instead drives power level based on the velocity in each segment. Code wise this is not very complicated, and will likely be easier than PPI mode, but isn’t available on every laser type, so both must eventually be implemented. |
Interesting. Is that to support multi-function machines? eg things that could (in one session) run a laser pass to (say) draw text on a part, then run a CNC pass to cut it out? |
Yes. Each toolhead could be a different type. You could combine additive (3D printing) and subtractive (CNC) in the same machine. Or laser and pen. Or any number of combinations. The goal being that the gcode supports it (with a little help from JSON when needed). |
Thanks for the in depth comments I will try and see what I can come up during the next days. (Currently mostly consumed by work on my 3d printer but I should have time during the weekend). I like the idea of using the toolhead approach - maybe I will hack something together there. What I found out for my setup is that using a resolution of 10 "pixels" per mm (possibly switching the laser every 0.1mm) I get slowdown for feedrates approaching 1000mm/min. |
I have been reading this thread with great interest, as I am in the process of using G2Core to replace the OEM control in a 40W Full Spectrum Laser. nnexai, did you ever solve the issue with control of laser power during axis movement? |
Here is a video of g2core running on my CNC with a new 5.5 watt laser I just added. I've modified the code so that tool 32 selects laser mode (would like to add an attribute to the actual tool so that this isn't bound to tool 32 but this just some quick code and it is easy to add this later). Additionally M4 selects dynamic power mode which adjusts the laser power based on velocity. It actually works much better than I expected. I used to get dots and burnt corners and now it is as clean as a whistle. If people are interested I can post the code. I didn't fork the code just cloned edge. I haven't contributed code yet to this project so I'm not sure if I can just create a PR or do I need to fork. Here is a screenshot from the video: Here is something else I engraved with dynamic power: |
I like it! The result compares favorably to GRBL's dynamic laser power mode. I would like to see your patch. |
Not sure if I used the correct process for getting the changes up, please suggest best practices. Changes are in #431 |
I don't have a PR ready yet but I have proof-of-concept code which uses the 'W' axis to control laser power. Values between 0 and 1.000 mm translate to 0 - 100% power. It works well but when the gcode stream gets up to a couple hundred lines per second it starts to pause a bit but the dynamic laser control does a nice job of keeping it from burning. Overall the proof-of-concept is very workable. There is still much to do like have a way to "home" the laser. That is, there needs to be a way to zero the "stepper" value which is the laser power. For now I simply select tool 1 to take it out of laser mode and then issue a G0W0 which will sync the canonical and "laser motor". Here is a video of it running. It's running at 1800mm/min, 50% maximum laser power, 0.25mm dot size, USB serial to a Raspberry pi. In order to keep velocity issues out of the way I cranked the 'W' axis jerk rates up to 100,000 and the max feed rate to 100,000. This keeps the other axis as the dominant drivers of velocity... at least it appears that way. Here is a close up of the finished work and a copy of the image used. Note I found a bug in the image to gcode translator which caused the obvious line on the arm along with other minor artifacts. |
That looks really nifty. 😄 |
Why W instead of S? GRBL uses S for laser power, and it seems better to adopt previous usage instead of further divergence in GCode dialects. |
This is just a PoC and so I wanted to test the ability of using a "laser motor" while minimizing code change. I see a couple of options, one is to translate 'S' into 'W' internally when in laser mode - which I consider a bit of a hack, the other is to add a new axis, which is more work but provides more upside. I was also considering extending this implementation with a simple and possibly general protocol change (I don't like going outside the standard but just a thought). Basically it is to allow for a compressed set of values to be passed in for a parameter. The compressed set would always have to be last on a line and is not allowed to span lines. So going with the 'S' for laser power it would look like this: G1X1.234Y5.678S<~9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF~> The Z85 decoded values would be stretched across the distance traveled. So if for example the distance traveled is 10mm and there are 200 'S' values then each mm would contain 20 power levels. The code would interpolate (if needed) between these points as it traveled over the vector. This does limit the maximum amount to what can be contained in a single line but are still see huge benefits (assuming an average of 14 bytes per point "G1X123.456S100" vs 1.325 bytes per point "G1X123.456S<~...~>" == 265/200). 10x protocol savings. This doesn't take into consideration defining the bit-depth of the compressed data or negotiating the maximum characters per line. An advantage is that the height, width, ppmm, direction, transformation, overscan, ... need not be handled by the firmware. This can be handled more easily by the image to gcode translator. Looking ta the code the pre-parser and gcode parsing could be changed relatively easily to accommodate this (at first glance. :) )... but again just getting familiar with all the code. Just some thoughts. Also let me know if this discussion should be taken elsewhere. |
The part I don't get is what is the use case for making the laser power a linear function of distance traveled. The synthetic case of an image with a linear gradient in grayscale intensity doesn't seem common enough to warrant such a feature. Perhaps the primary objective is to put the laser power word on the same line with the G1X.... motion? I do see the value of the "compressed data" idea because that is basically a raster scan primitive. That said, shoehorning it into an existing GCode seems dubious. Perhaps it would be better to have a JSON command that does exactly what you want. That would give you the flexibility of adding as many control parameters as necessary, without messing with the GCode parser, risking the introduction of bugs there, making people say WTF? when they see a G1 command that doesn't match any existing standard or documentation for G1, etc. If the JSON route seems too g2core-specific - even though it seems somewhat unlikely that this would be picked up elsewhere - you might consider the Marlin approach of structuring it as an M-code. Marlin has a gazillion wacky M-codes for doing 3d-printing specific stuff like tuning PID loops for extrusion temperature. You could get some small protocol savings without departing from the standard by using G91 incremental mode, omitting the G1's (perfectly legal), and adding a new "incremental spindle" mode (G91.2). Instead of writing M5 you would write M5 Taking this a step forward, you could create an autostep mode invoked by, say, M222. In that mode, the above example would be written as: M5 |
@MitchBradley - These are interesting ideas. I hadn't thought of an incremental mode via other M or G codes but this is basically where I was going. I completely understand not breaking the existing gcode parser. I didn't want to get too proprietary and was trying to keep change to a minimum. There are a few things going on that I probably didn't explain well.
So using a combination of the second and third bullets above I think significant reduction can be achieved. Getting back to your last example, how about: Thanks for listening and the advice. |
Hi guys, I’m hoping to drop a new release onto Edge (after moving edge into master) that has new spindle controls and tool work. This will provide three things that are relevant to this conversation (but nothing different as far as gcode protocol goes): 1- Spindle controls broken into a different sub-module, so it can be redefined what 2- The spindle commands will be stored in the gcode model and pushed to the planner. The spindle modules can optionally require that those are PTS (plan-to-stop) commands, of that they are merely quick spindle calls at movement load-time. 3- Each “tool” can use a different spindle type. So you could say that in this configuration tools 1-10 are a VFD-like spindle and tool 11 is a laser. Maybe tool 12 is a laser with different settings, like etching instead of cutting. I will shortly after that work on a laser spindle, but will likely not be able to test it, so I’ll look to you guys (if you’re willing) to give it a go. I plan on making it work as a combination motor (as in a subclass of Stepper), and a spindle (subclass of the unreleased Toolhead). That way it can take Anyway, I’ll drop a note here once the edge push is done, and we can discuss gcode protocol further as well once the primitives are in place. |
@giseburt Sounds like some very useful and substantial changes are on the way. I would be happy to help test or whatever else is needed. Coordinating like the W moves above and getting the planner out of the way (like I've only partially worked around with ridiculously aggressive jerk and speeds for the W axis spindle control) would be great. It should simplify the code path for the spindle axis since nowhere near as much planning is needed. I've been pleasantly surprised at just how well g2core performs. Even when using very short line lengths (.25mm ish) and speeds of 1800mm/min the system has worked well. It was only when pushed to 500,000 jerk and max velocity on all axis and driven at 2400mm/min that I started to have issues with something hanging in the planner (json commands still responded). Note that we are talking about short segments (about 6ms) and modified code so its not surprising something locked up. |
mhlong10, Thank you for excellent work on G2 laser power control; very impressive. I am in the process of rebuilding/heavily modifying a laser cutter and have some questions about your set up, programming changes, and the laser module you are using. Can you reach out to me privately via email to discuss? Sorry to all for posting in this way - I was unable to find contact info elsewhere. |
@jpbarad7 If you are trying to modify an OEM laser then you are getting into more than I feel comfortable giving advice for. There are a lot of variables in the system and lasers are unforgiving and dangerous. The laser module I'm using is. https://www.ebay.com/itm/450nm-7W-Blue-Laser-Module-With-Heatsink-For-Laser-Cutter-Engraver-CNC-DIY-Laser/392206265262 As far as hardware goes I have homemade CNC with VFD spindle I've been using for several months - more info is on my youtube channel. A couple weeks ago I added the above laser, basically mounted in onto the spindle housing and attached the PWM input to the spindle PWM output of the g2core shield. One thing to be careful with some of these modules is that they are "on" by default. I added a 680 ohm pulldown on the PWM on the laser and have an open-collector to +5v. This causes it to fail safely to "off". The software changes and other information can be found in the the comments above and the PR. I didn't submit a PR for the raster changes ('W' axis as a laser) because it is not ready.... and given that @giseburt has a drop coming soon which significantly affects spindle/laser control, I'm going to wait until his new drop before I do much more work in this area. One thing you will find if you decide to do raster laser printing before smooth laser motion is ready is that even with jerky movement, the dynamic laser control keeps the image from getting all screwed up. |
@mhlong10 The "M222 X.15 S<...>" formulation doesn't fit into the core GCode syntax, where a GCode "word" is a letter followed by a number. I'm not keen on going outside core syntax. That would be hard for ancillary programs that parse GCode to cope with - programs like CNCjs, GCode previewers, and any number of others. Remember, it's not just g2core that looks at the GCode stream, it's the entire ecosystem. |
I know it was mentioned above, but for those that skim, @aldenhart and myself have worked up a proposal for a laser mode here: https://github.com/synthetos/g2/wiki/Raster-Streaming-Protocol (It’s just that, a proposal, and open for discussion.) One of the core concepts we try to maintain in g2core is compatibility with gcode parsers, even when we’re doing something that they clearly won’t interpret correctly, we can at least make it parse correctly. This is part of why we have active comments. We also pay close attention to how gcodes are used in other projects. I’ve personally found that it’s helpful to think of gcode as a data format and not as a “language.” It’s closer to JSON and XML than to JavaScript or Python. You parse gcode and act upon the data found there, as opposed to interpreting it into byte code and eventually machine language. It’s a subtle distinction, but it helps me stay sane. 😁 |
@mhlong10 Thank you for your reply and for your cautions about laser
safety.
The modifications I am making are to a 40W Full Spectrum Laser. Thus far,
I have replaced the OEM control with a G2 core, upgraded the steppers, and
added limit switches to the ends of travel, as well as external interfaces
to control air flow, vacuum, and water circulation. The most usable
improvement is an increase in the cutting capacity from 12" x 14" to 15" by
24". I also plan to add a stepper to the honey-combed bed for Z-axis
control. I do not intend to make any changes to the laser tube or power
supply at this time, however, I might consider exchanging the water-cooled
CO2 laser tube with something solid state - The size limitation of the FSL
metal cabinet do not allow for a larger, more powerful CO2 tube.
For safety, I also added external circuitry to allow PWM output to the
laser only when the X or Y axis steppers are in motion - this may be an
option/variable setting in G2, but I couldn't locate it.
In my limited reading, it seems the available laser modules don't compare
favorably with the power output of a CO2 - that's one of the questions I
had for you; have you found this to be the case? Do you plan to use your
laser for 'cutting' or only engraving?
I'm hoping to have my laser operational this weekend and would like to
experiment with your program modifications until the new EDGE becomes
available. Do you have a .bin file you can share, or is it best to make
the changes listed in the previous emails on this topic?
Except for an occasional Arduino code download and this recent G2 project,
I don't have much experience with Github. If communications of this type
are not appropriate in this area, please let me know how to reach you
privately.
If others are interested, please chime in.
Thank you all again for sharing your time and the fruits of your labor.
|
The following photos demonstrate the difference between standard laser
power cutting/engraving settings (first star) and MHLong's dynamic laser
power settings (second star).
Both stars were engraved on card stock at 25% laser power using a 40W CO2
laser (Full Spectrum) upgraded to g2core. Similar /identical results are
obtained with higher laser powers and other materials.
The corner and entry point burns which occurred with the standard laser
power settings are virtually eliminated with MHLong's dynamic laser power
setting modifications.
I use Fusion 360 for CAD and to generate G code. I edited one of the
available post processors to add "T 32" to the beginning of each post and
changed the standard M3 (CW spindle on) to M4 (CCW spindle on).
Because this controller will be only used on my laser, no further
modifications to the g2core program will ever be necessary.
Thanks again to MHLong for his great work on this project.
|
@jpbarad7 I'm glad to see you have everything going and that the dynamic laser mode worked out for you. The results posted are great. I'm curious what feed rate you used for the stars. With the 5.5w laser I'm able to get similar results (on slightly darker card stock) with 30% power @ 2000mm/min and cut thru card stock at 100% power @ 1200mm/min. I'm also able to cut 3mm birch ply with 3 passes of 100% @ 500mm/min (proper focus is important). I've been very busy with my paying job and not had time to do much else. I hope to get back to this soon and add an acceptable raster laser mode as well. |
Whoo! That'd be awesome. 😁 |
@mhlong10 The feed rate was set at Fusion 360's default rate of 1000
mm/min, with a laser power of 25%. I have experimented with feed rates up
to 5000 mm/min with good success. 85% power cuts full-thickness at that
speed. I haven't experimented with anything other than card stock at this
point - I'm still working on the interface to control water/air/vacuum
etc.. I have currently have my rapid feed rate set at 15,000 mm/min but
think I can go much higher. I replaced the OEM steppers with high
efficiency Moons steppers (MS17HD2P4150) with a significant improvement in
performance.
Do you work in the programming industry? You seem to have a great grasp of
concepts that many struggle with (including me).
|
@jpbarad7 You guessed it. :) I'm an engineer/programmer by trade and I love to dabble in it on the side as time permits. |
I am currently trying to use a gShield on an Arduino Due running g2 for laser engraving. Cutting works quite well by using PWM and Spindle Speed Control for enabling/disabling the laser.
For engraving I just need a simple way to keep g2 from de-accelerating on every spindle speed change.
To my understanding this should be possible by setting SPINDLE_MODE "continuous" ( $spmo = 2 ) and SPINDLE_SPINUP_DELAY to 0
I then went ahead and generated gcode using a number of G1 movements. If no spindle speed changes are paired with the G1 commands everything runs smooth. By adding S0 / S1 to the G1 commands, the movement is stuttering (constantly accelerating and breaking for each change)
My understanding of the source code is code limited, but as far as I can tell the spindle mode is ignored for movement planning. In addition I am unable to pinpoint the lines responsible for those movements (most likely in one of the planner-files?).
I tried commenting out those mp_request_out_of_band_dwell(spindle.spinup_delay); lines, but since spindle.spinup_delay is already zero the command is already effectively a NOP.
Since I do not intent to switch tools and modes of my machine I would appreciate some help in fixing this problem even if it is just a crude workaround.
Currently running on the latest commit of the EDGE branch.
The text was updated successfully, but these errors were encountered: