docs(flomni): FZP how-tos as Markdown, updated for the PREEMPT_RT loop, move-ahead renamed to move-beyond

Markdown versions of local_contact_only-howto_calibrate_the_fzp_scanner.pdf and
local_contact_only-howto_limit_the_fzp_scanner_range.pdf (original text kept, update sections for the present
flOMNI loop). New section "Move-beyond" (FAST_FZP_PID_LOOP(0) < 0 of the sample PIDs): mechanism, sizing (about
half the FZP stroke), Abbe correction on the FZP side, hold (5) now reads the FZP drive incl. feedforward
(flomni repo, branch fzp-move-beyond, 6a15f68). The PDFs are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aWcwG89wi7oVyrefukXX6
This commit is contained in:
Claude committed 2026-10-23 10:34:48 +00:00
1 parent 8796953913
commit ea9f8cdf01
2 files changed
+224

No files matched your search

@@ -0,0 +1,183 @@
# How to calibrate the FZP scanner of flOMNI
Source of `local_contact_only-howto_calibrate_the_fzp_scanner.pdf`. The original text is kept unchanged; the
section "Update 2026-10-20" at the end lists what the present flOMNI loop (PREEMPT_RT, 100 µs, branch `rt_preemt`)
needs in addition (corrected 2026-10-23: "move-ahead" → **move-beyond**, see the section at the end).
```text
This is a description of how to calibrate the FZP scanner of flOMNI
Rmemeber to do all modifications for both directions x and y!
1. Align flOMNI without the FZP scanner. This implies disabling the
FZP scanner completely. To do so, in OrchestraHMI edit the parameters
of the FZP scanner as follows.
(a) Module PID_i_FZP: Set Param FAST_FZP_PID_LOOP(0) to 2. Then the FZP
will just remain
statically centered to the capacitive sensor position in closed loop.
(b) Module PID_i: Set Param FAST_FZP_PID_LOOP(0) to 0. If this is
negative, the FZP scanner will move ahead in a point-by-point scan by
that amount.
(c) Module PID_i: Set Param FAST_FZP_PID_LOOP(5) to 0. If this is
larger than zero the sample stage motion is stopped if the position is
reached by the FZP scanner (which is the case when the scanner is not
active). Zero means thus full compensation by the sample stage.
In this state the setup can be aligned, and the affine matrix for
ptychography can be found. This refers then purely to the alignment of
the interferometer and X-ray detector.
2. In the next step we perform a scan (with a small beam and small
range) such that only the FZP scanner is used and the sample stage
will not move.
Before we start ensure that the Correction Matrix for the capacitive
sensors is cleared.
To do this change the parameters of the flOMNI control
loop as follows:
(a) Module AnalogInput_6259: Set parameter MatrixCorr to 1 0 0 1
(b) Module PID_j_fzp: Set Param FAST_FZP_PID_LOOP(0) to 1 to re-activate
the FZP scanner.
(c) Module PID_i: Set Param FAST_FZP_PID_LOOP(5) to 10, so up to 10
volts output of the fast PID is accepted before starting a movement of
the sample stage.
(d) Module PID_i: Param FAST_FZP_PID_LOOP(4) 0
(e) Module PID_i: Param FAST_FZP_PID_LOOP(6) 1
with d and e it will now stabilize the sample stage position at zero
(f) Module PID_i: Param FAST_FZP_PID_LOOP(0) remains 0 (no move ahead
in a scan)
With these settings perform a scan. Use a field of view matching the
range of the FZP scanner. Watch the sample stage position in Orchestra
to ensure it does not change!
A good range should be 2.8 microns in x and 1.8 microns in y.
Reconstruct this scan with Michal's position refinement code and ask
for an update of the FZP matrix. This can then be put into the
AnalogInput_6259 module.
I guess one has to have the matrix found for the interferometer in the
reconsturction code, such that the new matrix of the FZP scanner will
adjust the coordinate system of the FZP scanner to the system of the
interferometer. Later these positions are used together and should
match in direction.
3. Back to default operating values:
(a) Module PID_i: Set Param FAST_FZP_PID_LOOP(0) to -0.5, for up to
0.5 microns move ahead in a scan.
All other parameters are already as supposed to be.
Default values for the sample stage pid, fast FZP parameter are "-
0.5,0,0,0,0.1,0.5,0"
Default values for the fzp pid, fast FZP parameter are "1,0.0001,0.1,-
4,0,0,0"
```
Naming: "move ahead" in the text above (sample PIDs `FAST_FZP_PID_LOOP(0)` < 0) is called **move-beyond** from
2026-10-23 on: the sample stage moves *beyond* the scan point towards the next one, the FZP compensates and so sits
beyond its centre during the exposure, on the side that gives it more stroke for the next step. It is not a
"pre-move" of the FZP to the next point (that is `TargetSelect` `use_final` 1, see below).
## Update 2026-10-20 (flOMNI loop on PREEMPT_RT, mpc2844)
The loop has new modules since the text above was written (flOMNI `FeedbackLoop/tuning_guide.md`,
`feedback_report.md` 5.6, 6n, 6o, 6q). Parameters are changed in the web HMI (`http://mpc2844:8080`, tab
Parameters) instead of OrchestraHMI. `i` = x or y, as above. Module names now: `PID_x`, `PID_y` (sample stage),
`PID_x_fzp`, `PID_y_fzp` (FZP scanner), `AnalogInput_6289` (was `AnalogInput_6259`).
**FAST_FZP_PID_LOOP has 7 elements** (`PID.cpp`). Sample stage PIDs: (0) move-beyond (negative, µm, point-by-point
scans only; section "Move-beyond" below), (4) FZP error threshold (µm) and (5) FZP drive limit (V) for holding the sample stage while the FZP
takes the step (5 = 0: never held), (6) 1 = hold the sample stage at 0. FZP PIDs: (0) mode (1 operation, 2 FZP
centred on its cap sensor = disabled, 3 not commissioned, 4 debug), (1)–(3) min / max / gain of the old velocity
correction (−4 before 2026-10-19, now 0: replaced by the feedforward).
**In addition to step 1 (FZP disabled):**
- `Feedforward`: `enable` 0,0 — the static FZP feedforward adds its drive also in FZP mode 2.
- `AbbeCorrection`: `enable` 0 (default until the signs are known).
- `TargetSelect_x`, `TargetSelect_y`: `use_final` 0; `OnTargetTrigger`: `WaitForSlew` 1 (defaults).
- `FreqGen`: `enable` 0 (excitation for measurements; default).
**In addition to step 2 (FZP-only calibration scan):**
- FZP range: `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` are ±1.5 V in operation (the cap sensors are calibrated over
about ±2 V only). For the field of view above (2.8 × 1.8 µm) widen them, e.g. to ±10 V (FZP ≈ 0.13 µm/V in x,
≈ 0.05 µm/V in y). The feedforward stays off (`enable` 0,0) during this scan.
- The detector trigger (`OnTargetTrigger`) checks the relative error sample vs FZP (tolerance / hold time from the
ConstEmitter, BEC `interferometer_trigger_*`, default 10 nm / 3 ms) and waits `MinDelay_ms` (15 ms).
**Step 3, default operating values (replace the values in the text above):**
| Module | Parameter | Value |
|---|---|---|
| `PID_x`, `PID_y` | `FAST_FZP_PID_LOOP` | `0,0,0,0,0.1,0,0` (no move-beyond: −0.5 µm is larger than the ±1.5 V FZP stroke, x ≈ ±0.19 µm, y ≈ ±0.08 µm) |
| `PID_x_fzp` | `FAST_FZP_PID_LOOP`, `Ub` / `Lb` | `1,0.0001,0.1,0,0,0,0`, ±1.5 V |
| `PID_y_fzp` | `FAST_FZP_PID_LOOP`, `Ub` / `Lb` | `1,0.0005,0.02,0,0,0,0`, ±1.5 V |
| `Feedforward` | `enable`; `ff_limit`, `out_min` / `out_max` | 1,1; ±1.5 V (as the FZP `Ub` / `Lb`) |
| `OnTargetTrigger` | `WaitForSlew`, `MinDelay_ms` | 1, 15 |
| `TargetSelect_x`, `TargetSelect_y` | `use_final` | 0 |
| `AbbeCorrection` | `enable` | 0 (until the signs are known) |
| `AnalogInput_6289` | `MatrixCorr` | the new matrix from the calibration |
Check afterwards: web HMI tab **Loop check**, level 1 (it expects these values and lists live values that differ
from the `.xmp` files); save the new values as default (Parameters → Unsaved changes) or in the `.xmp`.
**After the calibration (per experiment): faster scans, exposure during the motion.** With the FZP calibrated
over a wider range the FZP can take the small steps alone (flOMNI `feedback_report.md` 6q, tested at ±7 V):
- `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` and the `Feedforward` limits to the calibrated range (e.g. ±7 V);
- `TargetSelect_x` / `_y` `use_final` 1 (the FZP aims at the new point at once, the sample stage follows its
S-curve) and `OnTargetTrigger` `WaitForSlew` 0 (trigger on the relative error only); optionally `MinDelay_ms` 0;
- optionally move-beyond, sized to the FZP stroke (≈ half of it, see below): at ±7 V (x ≈ ±0.9 µm, y ≈ ±0.36 µm)
`PID_x` (0) e.g. −0.5, `PID_y` (0) e.g. −0.2 (the values tested in `feedback_report.md` 6q).
Measured with 101 ms exposures: 0.2–1 µm steps trigger after 4.5–5.8 ms instead of 17–32 ms; the error during the
exposure grows with the step (1 µm: ≈ 6 nm max instead of 2.5 nm), so the setting depends on the experiment.
## Move-beyond (`PID_x` / `PID_y` `FAST_FZP_PID_LOOP(0)` < 0), 2026-10-23
**What it does** (`PID.cpp`, unchanged since the original loop). Only in point-by-point scans (scan state 2). The
TrajectoryGenerator gives the next scan point (outputs 5 / 6, PID input 9). The sample PID then aims not at the
target T but at T + m · sign(next − T), m = min(|FAST_FZP_PID_LOOP(0)|, |next − T|): the sample stage moves *beyond*
the point towards the next one, by at most the step. The FZP loop keeps its own target T (it does not see m), so the
FZP compensates the offset: during the exposure it holds the relative position at T while sitting at +m off its
centre. At the next point the FZP then has stroke R + m in the step direction instead of R (R = stroke from the centre),
so larger steps are taken by the FZP alone. Steps smaller than |FAST_FZP_PID_LOOP(0)|: the sample stage is one point
ahead, the FZP moves between 0 and +step only. The offset is not shaped (InputSlew acts on T only); the sample stage
moves to it with its own (slow) loop, i.e. partly during the exposure, and the FZP compensates that motion.
**Is it still the same in the new loop?** Yes. The relative position is computed as before:
FZP error = Cap − (Chan − target) (mode 1), `Position sample vs FZP` = Chan − Cap (PID output 3, RT_sampler). The
sample PIDs still get the shaped target (InputSlew) + move-beyond and the raw interferometer. The FZP side now has
two additions that act on the FZP side only: `TargetSelect` (`use_final` 0 = shaped target as before, 1 = final
target) and `AbbeCorrection` (Chan corrected for the stage tilt, see below). The `Feedforward` uses the same FZP
target and Chan, so it supplies the +m offset of the FZP directly (static term m / K).
**Sizing.** During the exposure the FZP has to hold m + Abbe correction + noise / settling inside its stroke:
|m| ≤ R − reserve, as a rule ≈ R / 2. R per axis = `Ub` × K (K ≈ 0.13 µm/V x, ≈ 0.05 µm/V y):
| FZP `Ub`/`Lb` | R x / y | move-beyond x / y (≈ R/2) |
|---|---|---|
| ±1.5 V (production) | ±0.19 / ±0.08 µm | ≈ −0.1 / ≈ −0.04 µm (production: 0) |
| ±7 V (after calibration) | ±0.9 / ±0.36 µm | −0.5 / −0.2 µm (tested, 6q) |
The original default −0.5 µm in both axes dates from the ±9 V limits of the old loop (x ≈ ±1.2 µm, about R/2; in y
±9 V ≈ ±0.45 µm, i.e. about the full stroke). When the FZP saturates while holding the offset, the relative error grows and the trigger
waits.
**Abbe correction.** `AbbeCorrection` adds a new term to the measured sample x / y on the FZP side only:
x = Chan2_x + θz · offset_y_for_x, y = Chan1_y + θz · offset_x_for_y (θz = Chan3 / 13 mm). So yes: it is an error of
the sample x / y position (stage tilt about z, not seen by the x / y interferometer beams) that is corrected by the
FZP x / y, not by the sample stage (`PID_x` / `PID_y` keep the raw Chan, the rotZ piezo is too slow). The positions
recorded for the reconstruction (`Position sample vs FZP`) contain it as well. It uses FZP stroke (limited to
`correction_limit_um` 0.1 µm per axis; y at rest ≈ 2 nm, ≈ 30–37 nm during 20 µm x steps) — keep this in the reserve
of the move-beyond, in y it is a large part of the ±0.08 µm stroke. Keep it off (`enable` 0) during the FZP
calibration scan (step 2).
**Notes.**
- Hold of the sample stage, (4) / (5): input 8 of `PID_x` / `PID_y` was the FZP PID output *before* the
`Feedforward` (with the feedforward on only the remainder, not the real FZP drive). Since 2026-10-23 it is the
`Feedforward` output (FZP drive incl. feedforward), so (5) again compares the real FZP drive. No change with the
feedforward off or with (5) = 0 (production).
- On the last point of a scan the next point output is 0, so the move-beyond points towards 0 (as in the old loop).
- `TargetSelect` `use_final` 1 is a different thing: the FZP aims at the new point at once while the sample stage
follows the S-curve. Both can be combined (6q, E0).
@@ -0,0 +1,41 @@
# How to limit the FZP scanner range of flOMNI
Source of `local_contact_only-howto_limit_the_fzp_scanner_range.pdf`. The original text is kept unchanged; the
section "Update 2026-10-20" lists what the present flOMNI loop (PREEMPT_RT, 100 µs, branch `rt_preemt`) needs in
addition. "ahead movement" (element 0) is called **move-beyond** from 2026-10-23 on: the sample stage moves beyond the
scan point towards the next one, the FZP compensates and sits off-centre, which gives it stroke for the next step.
```text
to limit the fzp scanner ranges
in PID_ FAST FZP PID LOOP
0: reduce to small ahead movement range, such as -0.05
4: reduce error threshold, eg 0.01
5: inform PID that FZP range is small, such as 0.1
in PID_ _FZP
limit the analog output LB and UB
```
## Update 2026-10-20 (flOMNI loop on PREEMPT_RT, mpc2844)
The limited range is the present operating state: `PID_x_fzp` / `PID_y_fzp` `Ub` / `Lb` = ±1.5 V because the cap
sensors are calibrated over about ±2 V only (flOMNI `feedback_report.md` 6n). FZP ≈ 0.13 µm/V in x, ≈ 0.05 µm/V in y,
so ±1.5 V ≈ ±0.19 µm (x) / ±0.08 µm (y). In the sample stage PIDs (`PID_x`, `PID_y`) `FAST_FZP_PID_LOOP` is
`0,0,0,0,0.1,0,0`: no move-beyond, (5) = 0 (the sample stage always compensates fully).
Changing the FZP range now involves, in addition to the text above:
- **`Feedforward`** (static FZP feedforward): `ff_limit`, `out_min` / `out_max` to the same values as the FZP `Ub` /
`Lb` (they limit drive + feedforward).
- **`FreqGen`** `out_min` / `out_max` (channels 0 / 1) limit the FZP drive only while an excitation is running
(measurements).
- **`OnTargetTrigger`** `WaitForSlew` and **`TargetSelect_x` / `_y`** `use_final`: keep the defaults (1 / 0) with a
small range. `use_final` 1 + `WaitForSlew` 0 ("exposure during the motion") only makes sense when the FZP range
covers the scan step; steps larger than the range then still wait for the sample stage.
- Move-beyond (0) must fit into the stroke: the FZP holds it during the exposure, together with the Abbe correction
(up to `correction_limit_um` 0.1 µm when enabled) and noise. Rule ≈ half the stroke: at ±1.5 V ≈ −0.1 µm (x),
≈ −0.04 µm (y); at ±7 V −0.5 / −0.2 µm (tested). Production: 0. Details: calibration how-to, section "Move-beyond".
- (5) compares the real FZP drive incl. the feedforward (input 8 of `PID_x` / `PID_y` = `Feedforward` output since
2026-10-23; before it was the FZP PID output without the feedforward).
- After a change: web HMI tab **Loop check** level 1 (expected settings) and, with the beamline ready, the scan test
(level 4: positioning time, stability during the exposure per step size).