fix: address v4 scan migration review findings for flomni/lamni/omny
CI for csaxs_bec / test (pull_request) Successful in 1m52s
Read the Docs Deploy Trigger / trigger-rtd-webhook (push) Successful in 2s
CI for csaxs_bec / test (push) Successful in 2m15s

- restore omny_fermat_scan scan_name (was broken by _v4 rename)
- restore missing settling delay after LamNI feedback_disable
- make LamNI drift-correction moves consistently concurrent
- use self.actions.set for flomni rtx/rtz moves
- cap flomni corridor_size (explicit and auto-estimated) at 3um

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit was merged in pull request #212.
This commit is contained in:
Mirko Holler
2026-07-29 17:12:01 +02:00
co-authored by Claude Sonnet 5
parent b813b05bb1
commit 53f246a8d3
3 changed files with 42 additions and 6 deletions
+35 -3
View File
@@ -20,6 +20,8 @@ import time
from typing import Annotated
import numpy as np
from scipy.spatial import cKDTree
from bec_lib import messages
from bec_lib.alarm_handler import Alarms
from bec_lib.endpoints import MessageEndpoints
@@ -62,6 +64,7 @@ class FlomniFermatScan(ScanBase):
}
MIN_POSITIONS = 20 # Minimum number of positions required for a valid scan
MAX_CORRIDOR_SIZE = 3 # Corridor size is capped at this value (in um) for stability
def __init__(
# fmt: off
@@ -122,6 +125,13 @@ class FlomniFermatScan(ScanBase):
logger.warning("The zshift is smaller than -100 um. It will be limited to -100 um.")
self.zshift = -100
if self.corridor_size is not None and self.corridor_size > self.MAX_CORRIDOR_SIZE:
logger.warning(
f"The corridor_size is larger than {self.MAX_CORRIDOR_SIZE} um. It will be"
f" limited to {self.MAX_CORRIDOR_SIZE} um."
)
self.corridor_size = self.MAX_CORRIDOR_SIZE
self.update_scan_info(
exp_time=exp_time,
frames_per_trigger=frames_per_trigger,
@@ -154,8 +164,12 @@ class FlomniFermatScan(ScanBase):
f"The number positions must exceed {self.MIN_POSITIONS}. Currently: {len(positions)}."
)
corridor_size = self.corridor_size
if corridor_size is None:
corridor_size = min(self._estimate_corridor_size(positions), self.MAX_CORRIDOR_SIZE)
self.positions = self.components.optimize_trajectory(
positions, optimization_type="corridor", corridor_size=self.corridor_size
positions, optimization_type="corridor", corridor_size=corridor_size
)
flip_axes = self.reverse_trajectory()
if flip_axes:
@@ -318,8 +332,8 @@ class FlomniFermatScan(ScanBase):
if self.flomni_rotation_status:
self.flomni_rotation_status.wait()
rtx_status = dev.rtx.set(self.cenx)
rtz_status = dev.rtz.set(self.positions[0][2])
rtx_status = self.actions.set(dev.rtx, self.cenx, wait=False)
rtz_status = self.actions.set(dev.rtz, self.positions[0][2], wait=False)
dev.rtx.controller.laser_tracker_on()
@@ -370,6 +384,24 @@ class FlomniFermatScan(ScanBase):
)
self.flomni_rotation_status = self.actions.set(self.dev.fsamroy, angle, wait=False)
@staticmethod
def _estimate_corridor_size(positions: np.ndarray, factor: float = 1.5) -> float:
"""
Estimate the corridor size based on the median nearest-neighbor distance
of the positions, matching the estimation used by the corridor path
optimizer if no corridor_size is provided.
Args:
positions (np.ndarray): Array of positions
factor (float): Scaling factor for the median distance
Returns:
float: Estimated corridor size
"""
tree = cKDTree(positions[:, :2])
dists, _ = tree.query(positions[:, :2], k=2) # k=1 is itself
return factor * np.median(dists[:, 1])
@staticmethod
def get_flomni_fermat_spiral_pos(
m1_start: float,
+6 -2
View File
@@ -69,6 +69,7 @@ class LamNIComponents(ScanComponents):
# disable the feedback
self._dev.rtx.controller.feedback_disable()
time.sleep(0.05)
rtx_current = self._dev.rtx.readback.get()
rty_current = self._dev.rty.readback.get()
@@ -109,8 +110,11 @@ class LamNIComponents(ScanComponents):
logger.info(
f"Compensating {[val/1000 for val in self.lamni_to_stage_coordinates(x_drift,y_drift)]}"
)
self._dev.lsamx.set(move_x).wait()
self._dev.lsamy.set(move_y).wait()
lsamx_set = self._dev.lsamx.set(move_x)
lsamy_set = self._dev.lsamy.set(move_y)
lsamx_set.wait()
lsamy_set.wait()
time.sleep(0.01)
rtx_current = self._dev.rtx.readback.get()
+1 -1
View File
@@ -42,7 +42,7 @@ class OmnyFermatScan(ScanBase):
# Scan name: This is the name of the scan, e.g. "line_scan". This is used for display purposes and to identify the scan type in user interfaces.
# Choose a descriptive name that does not conflict with existing scan names.
# It must be a valid Python identifier, that is, it can only contain letters, numbers, and underscores, and must not start with a number.
scan_name = "omny_fermat_scan_v4"
scan_name = "omny_fermat_scan"
gui_config = {
"Scan Parameters": [