Application note / 01
Calibration-free beam centring in SerialEM using the JEOL TemExt Plugin
A simple way of centring the electron beam with CLA1 without a stored beam-shift calibration: measure how the beam responds to two small deflector changes, calculate the local 2D response, and use it immediately.
The problem
SerialEM can measure the position of a sufficiently well-defined electron
beam with MeasureBeamPosition. This gives us the displacement
of the beam centre from the centre of the camera image in pixels.
That is only half of the problem. We still need to know how much beam shift to apply to remove that displacement.
One solution is to calibrate the relationship between CLA1 and camera coordinates beforehand. I wanted to avoid this. In particular, I did not want the script to assume that CLA1-X corresponds to camera X or that the two CLA1 axes have the same sensitivity.
Instead, the script measures the relationship itself each time it runs. It moves CLA1-X by a small known amount and observes what happens to the beam. It then does the same with CLA1-Y. Those two measurements are enough to calculate the local transformation between CLA1 coordinates and the camera image.
Once that transformation is known, the required CLA1 correction can be calculated directly.
Why two measurements are necessary
It would be tempting to measure an X error in the image and correct it with CLA1-X, then do the same for Y. On a real microscope this is not generally correct.
The CLA1 coordinate system can be rotated relative to the camera and the two axes can have different sensitivities. Changing one CLA1 component can therefore move the beam along both camera axes.
This can be seen directly in a measurement from a JEOL microscope used while testing the script. The locally measured response matrix was:
-1.806846 -0.682839
-0.566016 1.842703
The off-diagonal terms are not small. An X change of CLA1 produces a measurable Y displacement, while changing CLA1-Y also moves the beam in camera X. Treating the two axes independently would throw away useful information.
Measuring the response instead of storing it
Let the initial displacement of the beam from the centre of the image be
Close to the current operating point, a small change of a two-axis deflector can be treated as approximately linear:
where ΔD is the change of the two deflector values, Δr is the resulting movement of the beam in the image, and J is the local 2 × 2 response matrix.
The important point is that we do not need to know J beforehand.
Starting from the current CLA1 values, the script adds a known value δx to CLA1-X and measures the resulting beam movement. It then returns to the original state, adds δy to CLA1-Y, and measures the second movement.
If the measured changes are
and
then the local response is
We can now solve
for the CLA1 change required to bring the beam to the centre of the image.
In other words, the script performs a small local calibration, uses it immediately, and then discards it.
Choosing a useful calibration step
There is one practical problem with this approach. The CLA1 perturbation has to be large enough for its effect to be measured reliably.
If, for example, changing CLA1 by 50 DAC units moves the measured beam
position by only one or two pixels, the calibration will be very sensitive
to uncertainty in MeasureBeamPosition. Inverting an inaccurate
response matrix can then give a poor correction even though the mathematics
itself is perfectly valid.
I therefore made the calibration step adaptive.
The routine starts with a user-defined initialDelta. After
applying the test shift it calculates the magnitude of the measured
displacement:
If this is smaller than minResponse, the original deflector
state is restored, the delta is doubled, and the measurement is repeated.
deltaX = $initialDelta
Loop 10
testX = $initialCLA1X + $deltaX
Plugin JEOL SetDecCLA1 $testX $initialCLA1Y
T
MeasureBeamPosition
responseXX = $RepVal1 - $initialBeamX
responseYX = $RepVal2 - $initialBeamY
responseX = SQRT ( $responseXX * $responseXX + $responseYX * $responseYX )
Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
If $responseX >= $minResponse
validX = 1
Break
EndIf
deltaX = $deltaX * 2
If $deltaX > $maxDelta
Break
EndIf
EndLoop
X and Y are tested independently. They do not have to finish with the same delta because each measured response is divided by the perturbation that produced it when the response matrix is constructed.
This turned out to be useful for another reason: the same idea can be used with deflectors whose sensitivities are very different from CLA1.
Direct microscope access with TemExt
I originally implemented this approach using VirtualTemCon. SerialEM called VirtualTemCon functions and microscope values were returned through files. It worked, but it was an unnecessarily indirect route.
The JEOL TemExt Plugin for SerialEM makes the implementation much simpler. CLA1 can be read directly:
Plugin JEOL GetCLA1
initialCLA1X = $JEOLVal1
initialCLA1Y = $JEOLVal2
and changed directly:
Plugin JEOL SetDecCLA1 $targetCLA1X $targetCLA1Y
The whole operation therefore stays inside a SerialEM script. There is no intermediate application and no temporary file exchange.
A real example
The example below was obtained during testing of the TemExt version of the script.
The initial beam displacement reported by SerialEM was:
X = -492.88 px
Y = -380.89 px
corresponding to a radial displacement of approximately 623 pixels.
Both beam-position measurements used all four detected quadrants. The
initial fit error reported by MeasureBeamPosition was 0.340.
The two test perturbations produced the following local response matrix:
-1.806846 -0.682839
-0.566016 1.842703
Its determinant was -3.715979. Solving the resulting two-dimensional system gave the following CLA1 correction:
CLA1-X -314.40 DAC
CLA1-Y +110.13 DAC
After applying this correction, SerialEM measured:
X = -25.11 px
Y = +13.50 px
giving a residual radial displacement of 28.51 pixels.
A single locally calibrated correction therefore reduced the radial centring error from approximately 623 pixels to 28.5 pixels, or by 95.4%.
The residual is not particularly surprising. The response matrix describes the microscope locally, whereas in this example the calculated correction was relatively large. If greater precision is required, the procedure can simply be repeated around the new operating point. The second iteration then measures a new local response rather than assuming that the first matrix remains exact over a large deflector range.
This is not specific to CLA1
CLA1 is a convenient demonstration because its purpose is immediately familiar, but there is nothing CLA1-specific in the calculation.
The method requires only a two-axis control that can be read and changed, and an observable displacement of the beam that SerialEM can measure.
On a JEOL microscope the same approach can therefore be used, for example, with Gun Shift (GunAlign1) or Spot Alignment (SpotA). The TemExt getter and setter change, but the self-calibration and matrix solution remain the same.
This is also where adaptive selection of the test displacement becomes particularly useful. A delta appropriate for CLA1 is not necessarily appropriate for Gun Shift or Spot Alignment. Instead of storing a separate calibration step for every deflector, the routine can determine a useful measurement amplitude when it runs.
Checks worth adding before routine use
The example above is deliberately simple, but a production version should not blindly invert every response matrix it obtains.
First, both perturbations need to produce a meaningful beam displacement. This is already handled by the adaptive delta.
Second, the two measured response vectors must be sufficiently independent. If they are almost parallel, the matrix becomes poorly conditioned and measurement noise can be amplified into a very large calculated correction. A normalised measure based on the angle between the response vectors is preferable to testing the raw determinant against an arbitrary constant.
Third, every requested deflector value should be checked against its permitted range, and an additional maximum correction should be imposed. A mathematically valid but implausibly large correction is a reason to stop and investigate, not a reason to move the microscope.
Finally, the quality information returned by
MeasureBeamPosition can be used to reject unsuitable beam
images. In the example above all measurements used four quadrants and the
reported fit errors remained between approximately 0.30 and 0.36.
Where I want to take this next
There are several obvious extensions, but I would rather test them than assume that they improve the result.
One is to estimate the repeatability of MeasureBeamPosition
and choose the minimum calibration displacement relative to the measured
positional noise rather than using a fixed number of pixels.
Another is to measure both positive and negative perturbations of each deflector axis. A central-difference estimate would cost additional Trial images but would provide a useful test of local linearity.
More immediately, the same routine will be tested with Gun Shift and Spot Alignment. If the same calculation works reliably for all three controls, it becomes more useful to think of this as a general self-calibrating two-axis control method rather than a CLA1 centring script.
Conclusion
The useful part of this method is not that SerialEM can move CLA1. It is that the script does not need to know beforehand how CLA1 movement maps onto the camera.
It asks the microscope.
Two small experimental perturbations provide the local two-dimensional response. That response can be inverted immediately to calculate the correction required to centre the beam. If the perturbation is too small to measure reliably, the script increases it automatically.
In the first TemExt test shown here, one self-calibrated correction reduced an initial beam displacement of approximately 623 pixels to 28.5 pixels. No previously stored CLA1-to-camera calibration was used.
The same principle can be applied to other two-axis JEOL deflectors exposed through TemExt, making it a useful building block for more general microscope automation.
The complete script
The full function as used for the example above. It takes four numeric
arguments: initialDelta, the first CLA1 test step in DAC
units; minResponse, the smallest beam displacement in pixels
accepted as a measurable response; maxDelta, the upper limit
for the test step; and tolerance, the radial offset in pixels
below which no correction is applied. The first line shows the call used in
the test: a 200 DAC starting step, a 100 px minimum response, a 2000 DAC
ceiling and a 10 px tolerance.
CallFunction CenterBeamCLA1 200 100 2000 10
# ============================================================
# Adaptive self-calibrating beam centring with CLA1
# JEOL TemExt Plugin / SerialEM
# ============================================================
Function CenterBeamCLA1 4 0 initialDelta minResponse maxDelta tolerance
# --------------------------------------------------------
# Read original CLA1 state
# --------------------------------------------------------
Plugin JEOL GetCLA1
initialCLA1X = $JEOLVal1
initialCLA1Y = $JEOLVal2
# --------------------------------------------------------
# Measure initial beam position
# --------------------------------------------------------
T
MeasureBeamPosition
initialBeamX = $RepVal1
initialBeamY = $RepVal2
initialError = SQRT ( $initialBeamX * $initialBeamX + $initialBeamY * $initialBeamY )
Echo Initial beam offset: X = $initialBeamX px, Y = $initialBeamY px
If $initialError <= $tolerance
Echo Beam already centred within requested tolerance.
Return
EndIf
# ========================================================
# Determine measurable X perturbation
# ========================================================
deltaX = $initialDelta
validX = 0
Loop 10
testX = $initialCLA1X + $deltaX
Plugin JEOL SetDecCLA1 $testX $initialCLA1Y
T
MeasureBeamPosition
responseXX = $RepVal1 - $initialBeamX
responseYX = $RepVal2 - $initialBeamY
responseX = SQRT ( $responseXX * $responseXX + $responseYX * $responseYX )
Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
If $responseX >= $minResponse
validX = 1
Break
EndIf
deltaX = $deltaX * 2
If $deltaX > $maxDelta
Break
EndIf
EndLoop
If $validX == 0
Echo ERROR: Unable to obtain measurable CLA1-X response.
Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
Return
EndIf
# ========================================================
# Determine measurable Y perturbation
# ========================================================
deltaY = $initialDelta
validY = 0
Loop 10
testY = $initialCLA1Y + $deltaY
Plugin JEOL SetDecCLA1 $initialCLA1X $testY
T
MeasureBeamPosition
responseXY = $RepVal1 - $initialBeamX
responseYY = $RepVal2 - $initialBeamY
responseY = SQRT ( $responseXY * $responseXY + $responseYY * $responseYY )
Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
If $responseY >= $minResponse
validY = 1
Break
EndIf
deltaY = $deltaY * 2
If $deltaY > $maxDelta
Break
EndIf
EndLoop
If $validY == 0
Echo ERROR: Unable to obtain measurable CLA1-Y response.
Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
Return
EndIf
# ========================================================
# Construct and validate local response matrix
#
# J = | responseXX/deltaX responseXY/deltaY |
# | responseYX/deltaX responseYY/deltaY |
# ========================================================
J11 = $responseXX / $deltaX
J21 = $responseYX / $deltaX
J12 = $responseXY / $deltaY
J22 = $responseYY / $deltaY
determinant = $J11 * $J22 - $J12 * $J21
Echo Local response matrix:
Echo $J11 $J12
Echo $J21 $J22
Echo Determinant: $determinant
# A scale-normalised conditioning test should be used
# in the final implementation rather than an arbitrary
# absolute determinant threshold.
# ========================================================
# Solve J * correction = -initialErrorVector
# ========================================================
correctionX = (-$J22 * $initialBeamX + $J12 * $initialBeamY) / $determinant
correctionY = ( $J21 * $initialBeamX - $J11 * $initialBeamY) / $determinant
targetCLA1X = ROUND ( $initialCLA1X + $correctionX 0 )
targetCLA1Y = ROUND ( $initialCLA1Y + $correctionY 0 )
# ========================================================
# Apply calculated correction
# ========================================================
Echo Applying CLA1 correction:
Echo X: $correctionX DAC
Echo Y: $correctionY DAC
Plugin JEOL SetDecCLA1 $targetCLA1X $targetCLA1Y
# ========================================================
# Verify result
# ========================================================
T
MeasureBeamPosition
finalX = $RepVal1
finalY = $RepVal2
finalError = SQRT ( $finalX * $finalX + $finalY * $finalY )
Echo Final beam offset: X = $finalX px, Y = $finalY px
Echo Residual radial error: $finalError px
EndFunction
Copy the whole block into a SerialEM script window. The
CallFunction line can be moved to any other script that has
this one loaded.