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

LASER improvements #15335

Merged
merged 60 commits into from
Apr 3, 2020
Merged

Conversation

jediminer543
Copy link
Contributor

@jediminer543 jediminer543 commented Sep 22, 2019

Requirements

This pull request aims to improve marlin's handling of laser devices, allowing the buffering of power changes into the movement buffer. This allows for high speed rasterising with M3-5 without the need to pause for every power change as the planner syncs.

A prior version of this has been tested on a 6040 CO2 laser cutter with a SKR V1.1, but this is a rewrite of that patch, as the prior one was a hack, and badly done.

Description

Adds a LASER_POWER_INLINE define that allows power commands to be specified as inline if requested, in the format M3 S(power) I. This ensures that non-moving commands can be properly applied; i.e. ending the file with an M5 will still guarentee the laser will turn off. It would be easy to add an inversion define to this, meaning that the arg specifies they are not inline instead. This behaviour can be inverted with the LASER_POWER_INLINE_INVERT option, which means that M3 S(Pow) will be buffered. However, this means that just M5 will no longer immediately stop the laser.

This adds two bytes to the planner blocks and to the planner settings; one is the OCR power (or just 0/255 when running pwm-less) and a status bitmask, specifying whether the planner is under control of the laser, wether the laser is enable, and potentially allowing for the buffering of spindle direction as well.

This will allow the interpolation of the laser power at the start of the block and as a nominal value such that you can do a power per feedrate driven system. (This is still being worked on)

The above has now been acomplished with LASER_POWER_INLINE_TRAPEZOID which power for a given step from the motion trapezoid in the planner and then updats the power during the step ISR. A review of this code for optimisation purposes would be helpful (I'm not used to building micro-optimised realtime code so it could probably be optimised).

Adds LASER_MOVE_POWER which allows the smoothieware style inline power changing of G(0/1) X Y Z F S(power) which are automatically pushed onto the planner blocks. This also allows a LASER_MOVE_G0_OFF which interprets all G0 moves without a power command to disable the laser.

Additional changes

Added a SPEED_POWER_STARTUP define which allows the user to specify which power M3 with no arguments will bring the spindle/laser to when running, instead of just automatically jumping it up to full power.

Added SPEED_POWER_FLOAT which allows the user to enable floating point powers; so one could specify that the laser power is between 0.0 and 1.0, and that would still work.

Added SPINDLE_LASER_FREQUENCY which will attempt to update the PWM Frequency of the laser/spindle on HALs that support it (currently only the AVR HAL and the LPC HAL define set_pwm_frequency

Benefits

This fixes rasterisation with M3-5; and makes using a laser with marlin much easier.

Related Issues

(possibly others)

@jediminer543 jediminer543 marked this pull request as ready for review September 22, 2019 18:11
@jediminer543 jediminer543 changed the title Rework laser control to buffer to planner blocks Rework laser control to buffer to planner blocks and support feed based power Sep 23, 2019
@shitcreek
Copy link
Contributor

shitcreek commented Sep 24, 2019

Here's my report after testing out this PR:

  • With either M3/ inline G1, the laser (fan) is powered on immediately when the job starts (I'm certain there is nothing that would trigger the laser in my start gcode).
  • Canceling a job does not turn the laser off - it stays on
  • The laser menu option shows that the laser is at 0 (zero) by default with the 'Laser Off' option - this is before, during and after a job
  • When you click the laser power option, it then shows that it is at 10 (ten) - this is due to 'SPEED_POWER_MIN`
  • When you click back, the laser then shuts off and 'Laser On' option is shown - this is after the job has been cancelled
  • 'Laser On/Off' both do nothing
  • With G1, the movement is smooth and the LCD menu can be navigated without issue
  • With M3, the movement is segmented and causes the LCD menu to be severely less responsive and almost unusable
  • Neither option produced any noticeable gradient, both appear to be on at full blast

@jediminer543
Copy link
Contributor Author

@shitcreek Aye; That was me derping up and flipping the value of the enable() function;

Try again now?

@shitcreek
Copy link
Contributor

The LCD laser control is working properly now, but the rest of the issues remain.
I've even tested with SPEED_POWER_MAX set to 10 and SPEED_POWER_MIN set to 0 and it still burns at full blast (or so it appears to my eyes - I need to get an analog meter).

@jediminer543
Copy link
Contributor Author

Obligatory test it again

I figured out how to get the Linux HAL working had have been debuging the code directly using that, and it now all seems to be working correctly. My physical test laser is at a hackspace (so no full hardware integration tests yet)

@thinkyhead
Copy link
Member

'Laser On/Off' both do nothing

With the fans, if there's a print job the fan speed is put into the following planner blocks. But if there's no print job the fan speed is set right away. A similar approach is needed for the laser. It sounds like it's only putting the laser power in blocks, and not setting the power right away when there's no job running.

@jediminer543
Copy link
Contributor Author

jediminer543 commented Sep 26, 2019

It sounds like it's only putting the laser power in blocks, and not setting the power right away when there's no job running.

Yep; that's why there is currenly an inline vs non-inline flag; since not being able to turn the laser off without moving is a bad idea.

For anyone that doesn't want to figure out the internals of Marlin; this basicly means M3 S75 I will set a status somewhere, such that the next move basicly becomes G1 X(blah) Y(blah) S75 thus the move sets the power. But also no power change wll occur untill a move is made. (A block is a buffered straight line move, so an arc becomes multiple blocks)

Would it be a good idea to make it propogate regardless? I've currently rigged this up by adding a case to the else case to the if ((current_block = planner.get_current_block())) { in stepper.cpp in testing. This does work, but would also possibly cause problems.

The notable problem with that is if the stepper isr runs out of blocks mid print (cut?), it could burn through the material and/or start a fire, depending on when the machine runs out of blocks.

I.e a block with an exit factor of 0 will always have an exit laser power of zero; so a stall at full power would gracefully ramp the laser power down. Continuous apply would then immediately set it to the requested power by the last M3, thus starting the laser again.

But would mean that M5 == M5 I in the universal case. Maybe only apply it for cases where the laser is requested to be off by the planner?

@shitcreek
Copy link
Contributor

shitcreek commented Sep 27, 2019

We're getting really close, but it's still not right.

M3: the jerkiness from all the segmented moves cause prolonged burns.

As you can see below, the mid section is burned the deepest when it should be mid way.
The right side should be white or no burn but it doesn't taper off as it should.

test picture
M3 & G1
G1
M3

I think the laser power should be synced to the speed of the stepper instead of having any sort of acceleration (if that's the case here).

@jediminer543
Copy link
Contributor Author

Theoretically I've just added a continuously variable power setting; that will calculate the (tgt_power*cur_steprate)/nominal_steprate. This will hopefully fix issues like the one @shitcreek has been running into; which is likely caused by the lower resolution of the slope approximation. Enabled by LASER_POWER_INLINE_TRAPEZOID_CONT

I've added a warning note for 8 bit systems (I hope this is ok), since adding a 32bit divide (700 avr cycles) to the stepper ISR seems like a bad idea generally; and I've added a delay function, so hopefully it should be usable. On Cortex and equivelent; divides apparently only take 2-12 cycles, which at >50Mhz seems fine to me (Note that I'm not an embedded systems engineer, I have no idea how much delay is acceptable).

@jediminer543
Copy link
Contributor Author

jediminer543 commented Oct 2, 2019

I have done testing with LASER_POWER_INLINE_TRAPEZOID_CONT and it looks good; getting ~8300mm/s out of an LPC1768 from the SD card and doing full rasters well without stuttering (Doing them via UART seems to have issues, but that's probably windows being a sub par source)

I've attached one of the test pieces we did at the hackspace; I've redacted the face though because it seems appropriate (I was being given various photos to test with). Also sorry for blury photo.

image

The striations in the cut are actually the wood grain; the raster was done diagonally (after one of the tests aligned the raster dir with the wood grain and it looked horrible

@jediminer543
Copy link
Contributor Author

jediminer543 commented Oct 3, 2019

Hokay, so, all the code works; the issue now is that the relation between power and feed is non linear

Because when debugging it it will clearly provide a linear power ramp to the target power, but the end effect isn't linear. I'm going to add a configuration MCode; possibly enabling on the fly changing of the power config as well. (Is there a standard which code to pick?)

So, is there a recommended way of doing a lookup table in marlin? Since I think calculating whatever non-linearity it is on the fly is a bad idea.

@shitcreek
Copy link
Contributor

shitcreek commented Oct 4, 2019

I'm getting good results with #define SPINDLE_LASER_FREQUENCY 20000 - this however increases the laser intensity dramatically. To make up for this, I laser at 35mm/s and with 0-2% power. Since I have a cheap K40, going any faster results in loss of quality.
I am using a Creality board v2.1 (AVR) and at 50+mms the steppers begin to stutter. I'm not using LASER_POWER_INLINE_TRAPEZOID_CONTsince I have an AVR board.

Engraved with G1 : left - MDF, 30mms 0-3% power | right - wood, 35mms 0-2% power
IMG_20191003_211327384

With M3, the segmentation makes it unusable and the menu, although more usable than before, is still not very responsive.

We still need to add a condition to prevent the laser (fan) from coming on when canceling a job.

Strange behaviour: at the end of each job, after finishing my end gcode behaviour, the laser head moves inward for about 100mm and does a short cut.

@shitcreek
Copy link
Contributor

shitcreek commented Oct 4, 2019

I got the laser to shut off when stopping a job by adding:

#if HAS_CUTTER
    #if ENABLED(LASER_POWER_INLINE)
      cutter.inline_disable();
    #endif 
    cutter.disable(); // Reiterate spindle/laser shutdown
  #endif

to the stop() function in Marlin.cpp

@shitcreek
Copy link
Contributor

shitcreek commented Oct 4, 2019

I forgot to mention I got these warnings:

Marlin/src/module/planner.h:216:3: warning: type 'struct planner_settings_t' violates one definition rule [-Wodr]
 } planner_settings_t;
  ...
Marlin/src/module/planner.h:213:22: note: the first difference of corresponding definitions is field 'laser'
     settings_laser_t laser;

@thinkyhead thinkyhead force-pushed the bugfix-2.0.x branch 2 times, most recently from 03be096 to fb0e093 Compare October 5, 2019 03:12
@shitcreek
Copy link
Contributor

laser-test-burn
testing with different SPINDLE_LASER_FREQUENCY
As you can see, 2.5khz yields the best result. For certain frequencies, such as 600hz and 6khz, the laser doesn't fire at all or barely comes on.

@thinkyhead thinkyhead force-pushed the bugfix-2.0.x branch 2 times, most recently from 7fbaed6 to 5a9e575 Compare October 5, 2019 03:35
@thinkyhead thinkyhead merged commit df8b7df into MarlinFirmware:bugfix-2.0.x Apr 3, 2020
DerAndere1 pushed a commit to DerAndere1/Marlin that referenced this pull request Apr 9, 2020
ghost pushed a commit to bfhobbes/Marlin that referenced this pull request Apr 15, 2020
mathom pushed a commit to mathom/Marlin that referenced this pull request Apr 17, 2020
@jonathanmlang
Copy link

Just installed this on my k40 laser cuttet on a skr1.1 board. Works well though it seems to leave the laser on during accereration on G0 moves resulting in short lines engraved away from holes.

@thinkyhead
Copy link
Member

Interesting. We'll have to check with @shitcreek in his latest testing on what has been merged and whether any updates in the last few weeks have affected anything. Re-checking the behavior of G0 will be tops on the list since that should "just work" without any extra settings or G-code parameters.

@shitcreek
Copy link
Contributor

@jonathanmlang try this PR: #17661
I think I am responsible for that bug after some digging. Relocated translate_power back over to spindle_laser.cpp from of its .h counterpart. However it had worked the other way before - there's some new codes in the planner files which might explain it.

@lakeroe
Copy link

lakeroe commented Apr 30, 2020

Sorry for my interference here but I'm also very interested in the laser functionality and therefore tried following handwritten gcode (with and without PR: #17661 made no difference):

G1 Z45            ; lift Z axis
G1 X50 Y50 F10000 ; move to start position
G1 F1000          ; set cutting speed
M3 S40
G1 X51 S40
G0 X52 S0
G1 X53 S40
G0 X54 S0
G1 X55 S40
G0 X56 S0
G1 X57 S40
G0 X58 S0
G1 X59 S40
G0 X60 S0
G1 X61 S40
G0 X62 S0
G1 X63 S40
G0 X64 S0
G1 X65 S40
G0 X66 S0
G1 X67 S40
G4 P0      ; laser off
M3 S1
M3 S0
M5
G1 X70 Y70 F10000 ; move away

In my opinion this should produce a regular dashed line (_ _ _ _ _ _ _ _ ).
Without the very first M3 S40 I get one solid line (_________________).
With the very first M3 S40 I get an erratic dashed line (
_ _ _ ___ _ _ _).
Any ideas ? I can do further testing if somebody has any suggestions ...

@shitcreek
Copy link
Contributor

I ran your gcode and my results are similar except the laser does appear to turn off but not quick enough so it leaves a shallow burn mark between the dashes. Turning down the cut speed fixes the issue (F250-500) except for the case without the first M3 - it still leaves a shallow burn mark except before the very last dash where it clearly turned off completely before the last burn. I believe it's got to do with the initiation of the inline codes. I'll dig further. Thanks for testing and reporting.

@naka123
Copy link

naka123 commented May 2, 2020

I have similar errors in my engravings - M05 (with LASER_POWER_INLINE_INVERT enabled) sometimes not applied, or applied later than it should.

image

image

I found, that this error is depended on engraving speed, BLOCK_BUFFER_SIZE setting and DELTA_SEGMENTS_PER_SECOND or M665 S setting (I have a laser, attached to delta 3d printer). With some settings combination errors are disappearing, with some other its are appearing in different places.

There is my temporary fix, that working for my machine - https://gist.github.com/naka123/0d1dca5178a75b846276db756b9bfa4b

(It looks like setting power to zero with cutter.set_power(0); is not working)

@shitcreek
Copy link
Contributor

@naka123 great job! I've applied your patch minus this bit:

#if DISABLED(LASER_POWER_INLINE)
TERN_(HAS_CUTTER, cutter.apply_power(current_block->cutter_power));
#endif

I also cranked my PWM frequency back up to 20,000Hz and it now produces the correct cuts at up to 100mm/s. Going faster than that produces different results for with and without a starting M3. Frequency higher than 20K shows no difference.
wOut M3 - 2.5khz F250 & F1000 and 20khz F10000
with and without M3 - 50khz - F10000

I've also tested with 'LASER_POWER_INLINE_TRAPEZOID_CONT', LASER_POWER_INLINE_CONTINUOUS (btw, I'm running on a Creality v2.0 board (8 bit AVR) cartesian style) and adjusting BLOCK_BUFFER_SIZE. None of which made any difference.

@naka123
Copy link

naka123 commented May 3, 2020

I'm running on SKRv1.3 32bit LPC1768 board, which allow to have delta kinematics (deltas are special, you know :) ) and LASER_POWER_INLINE_TRAPEZOID_CONT (with _CONT_PER 0) running without performance impacts - at least up to 80mm/s (and maybe faster, not tried).

My laser is chinesse 500mW 405nm LED, driven with 2.5kHz (default) frequency PWM.

I use Lightburn software to generate and run gcode, so Marlin setting is adjusted to it's "M03 Sxxx / G1 for ON, and M05 / G0 for OFF" generation pattern. (i.e. LASER_POWER_INLINE_INVERT and LASER_POWER_INLINE_CONTINUOUS turned on)

With my fix, including
#if DISABLED(LASER_POWER_INLINE) TERN_(HAS_CUTTER, cutter.apply_power(current_block->cutter_power)); #endif
I now have no errors in both vector and raster modes, at speeds from 5mm/s to 80mm/s
image

@shitcreek
Copy link
Contributor

Deltas are processor hogs. I'll add that bit of your patch to my PR too. Thanks.
I'm using a cheap chinese K40.

@lakeroe
Copy link

lakeroe commented May 3, 2020

My SKR 1.3 board just died and I had to order a new one.
I'll report my testings as soon as possible ...

@lakeroe
Copy link

lakeroe commented May 8, 2020

After repairing my SKR 1.3 board and testing the latest fix, I can confirm it also works for me now ;-)
Thanks a lot for that !

@thinkyhead
Copy link
Member

#17661 is now posted for testing. Very experimental. Has not been tested yet.

@kaixxx
Copy link

kaixxx commented May 11, 2020

Hi
Thank you all for the great work on these laser improvements! I was eager to try it out but ran into an issue: Enabling LASER_POWER_INLINE corrupts the EEPROM and crashes Marlin. Do you have an idea what the problem might be? See my bug report here: #17947

jmp0x0000 pushed a commit to jmp0x0000/Marlin that referenced this pull request Aug 7, 2020
njibhu pushed a commit to njibhu/Marlin that referenced this pull request Aug 24, 2020
@mirozmrzli
Copy link

Hi all,

I'm interested in laser PWM. Am I to understand that laser PWM should scale in realtime with the current velocity (up to max set with either M3 or inline S)? If so, this is not my experience. I see no sign of scaling. If this is not implemented, any pointers/advice on how to get about doing this? I don't mind taking a stab at it. This FW feels like it's so close doing exactly what I want...

-M

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.