Close

Scan Error Continued: Zero-Camera Feed-Forward Correction

A project log for prism laser scanner

bringing additive manufacturing to the next level

hexastormHexastorm 08/02/2026 at 15:220 Comments

If you've been following along with the Prism Laser Scanner project, you know that moving from a wobbly galvanometer to a high-speed rotating glass prism introduces a nightmare of optical aberrations. In my previous logs, we could clearly see massive orthogonal errors (pyramidal tilt between facets) and scan line jitter (timing differences). Each facet pair isn't perfectly planar or at 90 degrees, and mechanical mounting adds subtle angle defects.

I am thrilled to report a major breakthrough. We can now correct for facet distortion along the scanline without the need for a camera. (For the orthogonal error, I will make a separate dedicated blog post).

Although you know from my previous blog post that you can connect a camera, it is not easy. It requires ND filters, global shutter cameras, and much more development.

1. Zero-Camera Feed-Forward: Predicting Scan Errors from Photodiode Timing

We can actually predict along-scan errors on every single motor spin-up using our onboard photodiode sensor. Every time a facet sweeps, the laser bundle hits the photodiode, allowing the FPGA / ESP32 to measure the exact rotation period of each individual physical facet (facet period in ms).

As the prism rotates, the laser beam undergoes lateral shift across the glass faces (acting as a rotating transparent parallel plate). The physical transversal displacement τ(I) on the substrate as a function of the angle of incidence I follows Snell's Law:

\tau(I) = T \cdot \sin(I) \left( 1 - \sqrt{\frac{1 - \sin^2(I)}{n^2 - \sin^2(I)}} \right)

Where T = 2 × inradius is the thickness of the glass prism (30.0 mm) and n = 1.49 is the refractive index. Taking the derivative near the center of the scanline (I ≈ 0°) gives the angular displacement sensitivity:

\frac{d\tau}{dI} = T \left( 1 - \frac{1}{n} \right) = 30.0\text{ mm} \times \left( 1 - \frac{1}{1.49} \right) = 9.866\text{ mm/rad} = 0.1722\text{ mm/deg}

Because of the square root and sine terms in τ(I), the effective scan speed is not strictly constant across the scanline. Counterintuitively, it is slowest at the center and increases symmetrically towards the edges! As the angle of incidence gets steeper, a small change in rotation causes a much more violent lateral shift inside the thick glass.

At 3000 RPM (angular velocity ω = 314.159 rad/s), this linear optical scan speed across the substrate at line center is:

v_{\text{scan}}(0) = \frac{d\tau}{dI} \cdot \omega = 9.866\text{ mm/rad} \times 314.159\text{ rad/s} = 3.10\text{ mm/ms} = 3.10\ \mu\text{m}/\mu\text{s}

However, we don't multiply v_scan(0) by the full timing error δt directly. The photodiode triggers at the start of the sweep (frac = 0.0), while our active laser exposure window is centered at center_frac = 0.525 (start_frac = 0.35 to end_frac = 0.70). The accumulated timing offset at the active scanline region scales with this fractional position. To put it otherwise: the full timing error only exists at the end of the sweep, but when you are halfway, you have only accumulated half the error.

k_{\text{scale}} = v_{\text{scan}}(0) \times \text{center\_frac} = 3.10\ \mu\text{m}/\mu\text{s} \times 0.525 = 1.628\ \mu\text{m}/\mu\text{s} \approx 1.6\ \mu\text{m}/\mu\text{s}

Because each facet has microscopic angular defects, the photodiode measures slight timing variations (δt = facet_period_ms - P_median) on the order of 10 to 20 microseconds per facet. Note, P_median = 4.99918 ms. By multiplying these timing offsets by our scale factor k_scale ≈ 1.6 μm/μs, the software computes an instant feed-forward correction:

f_{\text{scan\_estimated}} = k_{\text{scale}} \cdot (\text{facet\_period\_ms}[i] - P_{\text{median}})

Math Note: Exact Derivation vs. Real-World Physics
If we want to be mathematically rigorous, we shouldn't use the center speed approximation. We should calculate the exact mean speed by taking the integral of the velocity (which is simply the total physical displacement τ) from the photodiode trigger to our center fraction, and dividing by the elapsed time. Doing this exact derivation yields a mean speed of 3.87 μm/μs, which gives a theoretical k_scale = 2.03 μm/μs. The MAE worsens from 13.99 to 17.20 but is still better than original 22.87 (see end).

However, when we applied this "perfect" k=2.03 to our actual measurements, the residual errors got worse. Running a least-squares optimization on the physical prism data reveals the true optimal scale factor for this specific unit is actually k = 1.37. Why? We didn't give the model the angle at which the diode is hit and the position of the center. Furthermore, our calculation assumes we measures a shift at the center. We measure, however a shift over the entire line.

Why this is huge: This single feed-forward step reduces along-scan error by over 50% automatically on spin-up without needing any camera hardware or test prints!

2. Residual Error Analysis (Measured vs. Predicted)

To verify the accuracy of this feed-forward model, we evaluate the residual error (Residual = Measured Scan - Predicted Scan) across all 4 facets against optical baseline measurements:

Facet Photodiode Period Timing Offset (δt) Uncorrected Error Photodiode Prediction Residual Error Improvement
0 5.01929 ms +20.12 µs +9.80 µm +32.18 µm -22.38 µm Shifted direction, magnitude reduced
1 4.98513 ms -14.05 µs -9.80 µm -22.48 µm +12.68 µm Over-corrected slightly
2 5.01323 ms +14.05 µs +22.59 µm +22.48 µm +0.11 µm 99.5% error reduction!
3 4.98138 ms -17.80 µs -49.27 µm -28.47 µm -20.80 µm 57.8% error reduction!

Because the residual error magnitude across all facets is significantly lower than the uncorrected errors, applying this feed-forward prediction provides an immediate, zero-cost physical alignment upgrade on every motor start.

3. Performance Summary

Here is the overall system performance improvement achieved by adding photodiode timing feed-forward:

Overall, the MAE drops significantly, proving that this core feed-forward relationship is correct and highly effective. However, it is not a done deal that our specific scalar is universal. This data is based on a single measured prism (N=1). To prove definitively whether the exact derivation, the linear approximation, or a purely empirical fit is the ultimate solution, I need to measure a batch of, say, 20 different prisms. Once we have that statistical volume, we can lock in the universal behavior.

In the next log, I'll share our new direct printing test method to conquer the orthogonal (pyramidal) error!

Raw Math for Screen Readers & LLMs

1. tau(I) = T * sin(I) * (1 - sqrt((1 - sin^2(I)) / (n^2 - sin^2(I))))
2. dtau/dI = T * (1 - 1/n) = 30.0 mm * (1 - 1/1.49) = 9.866 mm/rad = 0.1722 mm/deg
3. v_scan(0) = dtau/dI * omega = 9.866 mm/rad * 314.159 rad/s = 3.10 mm/ms = 3.10 µm/µs
4. k_scale = v_scan(0) * center_frac = 3.10 µm/µs * 0.525 = 1.628 µm/µs ≈ 1.6 µm/µs
5. f_scan_estimated = k_scale * (facet_period_ms[i] - P_median)
  

Discussions