I first tried to use LaserGRBL or lightburn but those two doesn't suit my CNC. It is a queenbee screw driven. Therefore I need spaces to be able to reach the maximum velocity. Auggie did that and I used a python script to generate a greyscale matrix with variable feedrates and power. Since the raster is bidirectionnal, I saw some blur caused by the small delay of M67 commands. With a bit of tweaking, I managed to wipe them. If someone is interested, here's the script :
# =========================================
# Laser Calibration - Feedrates
# =========================================
def linspace(start, stop, n):
if n <= 1: return [start]
return [start + i * (stop - start) / (n - 1) for i in range(n)]
# --- PARAMETERS ---
min_power = 8
max_power = 35
num_steps = 10
power_steps = linspace(min_power, max_power, num_steps)
feedrates = [1400, 1600, 2000, 2400, 2600, 2800, 3000]
# Base 16ms (Parfaite à F1500)
laser_latency_ms = 16.0
# blur tweak
boost_at_3000 = -0.25
reference_feed = 1500.0
# runway premove
pre_move = 15.0
band_width = 2.0
gap_width = 1.0
line_step = 0.14
output_file = "laser_calib_negative_boost.nc"
# --- CALCULATIONS ---
lines_per_band = int(band_width / line_step)
total_length = (num_steps * 2.0) + ((num_steps - 1) * 0.5)
gcode = []
gcode.append("G21 G90 G17 G94")
gcode.append("M3 M67 E0 Q0")
current_y = 0.0
for feed in feedrates:
# 1. Base 16ms
base_offset = (feed * laser_latency_ms) / 60000
# 2. Boost négatif progressif
extra_boost = 0.0
if feed > reference_feed:
# La valeur sera négative ici
extra_boost = boost_at_3000 * ((feed - reference_feed) / (3000.0 - reference_feed))
current_offset = base_offset + extra_boost
gcode.append(f"\n( BAND F{feed} | Offset: {current_offset:.3f}mm )")
for line in range(lines_per_band):
if line % 2 == 0:
x_dir, x_start = 1, 0.0
corr = current_offset
else:
x_dir, x_start = -1, total_length
corr = -current_offset
start_runway = x_start - (pre_move * x_dir)
gcode.append(f"G0 X{start_runway:.3f} Y{current_y:.3f}")
x_cursor = x_start
gcode.append(f"G1 X{x_cursor + corr:.3f} F{feed}")
p_seq = list(enumerate(power_steps)) if x_dir == 1 else list(enumerate(power_steps))[::-1]
for i, p in p_seq:
gcode.append(f"M67 E0 Q{p:.2f}")
x_cursor += x_dir * 2.0
gcode.append(f"G1 X{x_cursor + corr:.3f}")
if (x_dir == 1 and i < num_steps-1) or (x_dir == -1 and i > 0):
gcode.append("M67 E0 Q0")
x_cursor += x_dir * 0.5
gcode.append(f"G1 X{x_cursor + corr:.3f}")
gcode.append("M67 E0 Q0")
end_runway = x_cursor + (pre_move * x_dir)
gcode.append(f"G1 X{end_runway:.3f}")
current_y += line_step
current_y += gap_width
gcode.append("\nM5\nM30")
with open(output_file, "w") as f:
f.write("\n".join(gcode))
print(f"G-code généré : {output_file}")
print(f"F1500 : Offset = {(1500*laser_latency_ms)/60000:.3f} mm (Boost: 0.000)")
print(f"F3000 : Offset = {((3000*laser_latency_ms)/60000 + boost_at_3000):.3f} mm (Boost: {boost_at_3000})")
Next step : gamma settings and a converter Image ->Gcode.