Fast and accurate calibration of galvo lens

I am using my laser etcher to make precision cuts in printed circuit boards, and need to calibrate my galvo lens accurately. I describe here a fast and easy procedure that gives about ±0.05mm uncertainty. The key features are:

  1. a custom burn file for acrylic that accurately etches 1.4mm diameter holes at calibration points,

  2. measuring the distances with a digital caliper, sending the numbers via a bluetooth keypad to a

  3. python program that automatically updates the parameters in Lightburn, so no typing mistakes.

I tried using the Lightburn calibration routine in an iterative manner to improve the accuracy of the calibration. However, this did not work and I was even unable to remove a visible bulge. I then wrote a simple Python algorithm that updates the calibration by iteration. I found it converges rapidly and gives very low errors. I explain how it works later in the document.

This photo of the digital-caliper measurement shows how the caliper tips self-center into the holes. The bluetooth link button is under my right index finger. To self-center reproducibly, push down on the caliper with the left hand between the tips, and wiggle the wheel back and forth with the right thumb.

Purchase the following 3 parts, for about $100 total:

  1. 6”x6”x1/8” black acrylic sheets, by Adrattnay; Amazon , $9 for 5 sheets.

  2. 8” digital calipers with a bluetooth link, by iGaging; Amazon, $76. Don’t buy expensive calipers that need a special interface; any added accuracy won’t matter.

  3. Centerline gauge-set that screws onto the calipers to measure hole distances, HHIP 4100-0040; Amazon, $21.

Setup and calibrate the above parts, and ready Python:

  1. With the burn file linked below “GalvoBurnCal”, adjust the burn setting to give a cylindrical hole that is deep enough so that the tip of the centerline gauge does not bottom out of the hole. My settings are for a 6W UV laser, the ComMarker Omni X with the 150x150mm lens.

  2. Install the centerline gauge on the calipers, making sure that the tips of the two cones are level.

  3. Measure the offset of the centerline gauge. This can be done with another set of calipers, but the easiest way is to measure with a pair of holes drilled by a CNC with a known separation. My measurement is longer than the design value, so I use an additive offset of -0.33mm.

  4. Setup the bluetooth connection via instructions with the caliper, which installs as a bluetooth keyboard on your computer. Test the bluetooth keypad interface by sending measurements to a text file on your computer: open any text file, and when pushing the bluetooth button on the caliper you should see the measured value with a return are “typed” into the text file.

    1. For MAC OS, I had to grant terminal accessibility by going to “Systems setting / Privacy & Security / Accessibility”, and then turned on accessibility.

    2. For windows, it connects with “add device” in bluetooth.

  5. Set the Python file path to “prefs” for updating the LightBurn parameters. In LightBurn, click on “File / Preferences / Open Prefs Folder” to open a folder where “prefs” is located. Right click on the file path at the top, and choose “copy address”. Then paste this address in the Python program “GalvoCalibrate” for the FilePath, as text. My Python program sets this as shown here, where I have added the r and two quotes for formating:

FilePath = r“C:\Users\jmart\AppData\Local\LightBurn”

The initial calibration starts by selecting in Lightburn the “Laser Tools / Device Setting”, and then selecting the “Galvo and Basic Settings” tab. Set all the lens parameters on the bottom left to 1, and then adjust the “Scale” settings so that the burn pattern “GalvoBurnCal” with 120mm spacing fits on your acrylic sheet. For my galvo, it is 80%.

Iterative calibration uses the following procedure:

  1. In LightBurn, burn “GalvoBurnCal” as a calibration test pattern into the acrylic sheet with your current LightBurn parameter settings.

  2. In Python, open “HoleCal.txt”, a text file to be used for data entry. It is in the same folder as “GalvoCalibrate”. Click on this file, and enter the offset (for example -0.33mm) as the first-line entry. The cursor should be on the 2nd line.

  3. Measure the 14 parameters in the sequence H1-H6, V1-V6, and D1-D2, clicking to transmit the bluetooth entry and checking that they are being entered correctly in the “HoleCal.txt” file. Unlike a ruler measurement, this step is easy and takes about 1-2 minutes..

  4. Close Lightburn. This allows the “prefs” file to be updated properly in the next step.

  5. In Python, run “GalvoCalibrate”. The Python program will analyze the “HoleCal.txt” data to measure the scale, bulge, skew and trapezoid. Enter “y” to read the current “prefs” file to update and save these parameters for the next time you run LightBurn.

2 or 3 iterations are typically needed to converge to ±0.03mm errors, as displayed by the “Galvo Calibrate” program.

Results. The “GalvoCalibration” program displays the parameters from the measurements, and computes the change in the parameters to iterate. It outputs the following, where the first measurements are errors in millimeters, and the second is the change in parameters going to Lightburn.

  1. The initial calibration as described above. Here, you see scale errors from 60mm, and significant bulge in Y of 1.95mm

Measurements (mm)

Avg. X,Y = 59.7367 61.5150

Bulge X,Y = -0.2075 1.9500

Skew = 0.0250

Trap X,Y = -0.0325 -0.0500

Change in LightBurn parameters (dim’less)

Delta S_X,Y = 0.0035 -0.0202

Delta B_X,Y = 0.0083 -0.0707

Delta Skew = 0.0003

Delta T_X,Y = 0.0008 -0.0012

  1. After the 3rd iteration, an additional measurement shows a very accurate calibration, to the precision of the caliper measurements.

Measurements (mm)

Avg. X,Y = 60.0300 60.0333

Bulge X,Y = -0.0300 0.0425

Skew = -0.0150

Trap X,Y = 0.0400 -0.0175

  1. Here are the settings of the LightBurn parameters after the 3rd calibration

The algorithm. The “GalvoCalibration” program computes the following measurements from the 14 inputs H1-H6, V1-V6 and D1-D2, as defined in the Lightburn lens calibration.

Sx = (H1+H2+H3+H4+H5+H6)/6 # average x, for scale

Sy = (V1+V2+V3+V4+V5+V6)/6 # average y

Bx = ((H1-H3)+(H5-H3)+(H2-H4)+(H6-H4))/4 # bulge x

By = ((V1-V2)+(V3-V2)+(V4-V5)+(V6-V5))/4 # bulge y

K = (D1-D2)/4 # skew x and y

Tx = ( (H1-H5)/2 + (H2-H6)/2 )/2 # trapezoid x

Ty = ( (V1-V3)/2 + (V4-V6)/2 )/2 # trapezoid y

It’s a problem that we do not know how LightBurn defines its parameters. To relate the two, I run calibration measurements where I change the LightBurn parameters by +0.1, burn the file, and then determine the difference between it and the unchanged values of my parameter measurements as defined above. For a change in LightBurn of Bulge X by 0.1, I find my Bx changed by 2.5075mm:

Scale X,Y = 1.6367 -0.0767

Bulge X,Y = 2.5075 0.3875

Skew = -0.1450

Trap X,Y = 0.0025 -0.0925

I found that ignoring the cross-coupling terms, for example Scale X or Bulge Y in this example, still gives good results. These cross-corrections are small, and do not matter because we are iterating the calibrations. Here, I do not try to get all the calibrations correct in one step. This is a general principle why feedback works.

A change in the LightBurn Bulge X parameter changes my Bx measurement by 2.5075mm. From this number, I can then compute the needed change in the LightBurn parameter DeltaBx when iterating the calibrations, as shown below. Here the scaling factors are all obtained by doing the above calibrations for each parameter. The scale corrections DelSx and DelSy are a simple expression, but I found that adjusting it by 0.7 gives faster convergence. This is probably due to the non-linear angular dependence of distance, as discussed later.

DelBx = -0.1*(Bx/2.51)

DelBy = -0.1*(By/2.76)

DelKx = +0.1*(K /9.19)

DelKy = +0.1*(K /9.19)

DelTx = -0.1*(Tx/3.93)

DelTy = +0.1*(Ty/4.14)

DelSx = (1-Sx/60)*0.7

DelSy = (1-Sy/60)*0.7

The problem: angle non-linearity. When I burn a test calibration of an array of holes with 10mm spacing, I see non-uniform spacing. This can even be seen by eye in the photograph below. This comes from the geometry of the galvo lens writing to a horizontal surface, since the Lightburn software controls only the angle of the galvo lens.


For this geometry, the angle theta is converted to a horizontal distance d’ by the formula

d’ = f tan(theta)

where f is the focal length of the lens, specified as 231mm for my system. LightBurn is calibrated using the approximation that the angle theta is small and given by theta = d/f, giving d’ = d, where d is the designed distance. Expanding the tangent formula in a Taylor series to the next correction, one finds

d’ = d[ 1 + (⅓)(d/f)^2 ]

This idea can be tested by measuring the distance between the center mark and holes at varying distances. From the above formula, the ratio of the measured d’/d versus d should have a parabolic shape, with a curvature that corresponds to approximately the focal length of the lens. This is plotted below, and the best fit of the focal length 240mm is close to that specified for my lens. The calibration was done at 60mm, where the ratio is 1 as expected. There is a significant change in the spacing near the origin, about 2%.


It would be useful for LightBurn to include this angle correction, as then the lens could be further calibrated to have very low distortion for precision etching.

Software

  1. Lightburn calibration file “GalvoBurnCal.lbrn2 GalvoBurnCal.lbrn2 (102.6 KB)

  2. Python program “GalvoCalibrate.py” GalvoCalibrate.txt (3.6 KB)

  3. Example python data “HoleCal.txt”
    -0.33
    60.31
    60.47
    60.34
    60.42
    60.28
    60.34
    60.42
    60.42
    60.42
    60.30
    60.25
    60.37
    170.02
    170.08

@JohnPhysics this is really interesting. I responded to the email you sent to support.

This is very interesting, and I think it also shows why a general-purpose nonlinear correction map could be useful.

What you describe with d’ = f tan(theta) is a predictable component of the distortion, so LightBurn could theoretically compensate for it mathematically.

However, there can also be additional residual/nonlinear errors from the actual lens + galvo system that are not necessarily described perfectly by a single focal-length/tangent model.

EZCAD2 has an interesting solution for this: after the normal COR/9-point lens calibration, there is an additional, somewhat hidden Excel correction function (F3 → Ctrl+E). It allows a dense grid of points to have independent ΔX and ΔY offsets.

For example, if a point is supposed to be at X=25.00 mm but the actual mark is at X=25.50 mm, the correction for that point is simply:

ΔX = -0.50 mm

The software then interpolates the correction between the grid points.

So the processing chain is essentially:

User coordinate → local nonlinear XY correction map → existing lens/COR calibration → galvo command

This doesn’t require a complicated optical model. The user can simply measure the residual error and provide the correction values.

I think this could actually complement the angle correction you describe: LightBurn could first compensate the predictable tan(theta) geometry, and then an optional dense correction map could compensate the remaining lens/galvo-specific distortion.

This would be particularly useful for precision applications such as rulers/scales, templates, PCB work, arrays of small parts, etc., where a 1–2% local scale error is significant even though the overall 9-point calibration looks correct.

Thank you for this information. It is good to know there is a solution with EZCAD2.

Lightburn support has shown interest in adding this feature, so I am optimistic this can be fixed fairly quickly. Looking at my data, I think a simple angle correction will be good enough to 0.03mm or so, at which the accuracy of the caliper measurements would limit the calibrations. And it is only two parameters, angle for X and Y, which will probably be the same number. So this probably is good enough, but we will do measurements.

From lens physics, you can generally write the distortions as moments, so calibrating for the first few moments tends to be good enough. So we will see if that is the case here.