Skip to content

v1 CLARIOstar: fluorescence grids, and the centres of any wells from them - #1531

Merged
BioCam merged 1 commit into
PyLabRobot:mainfrom
BioCam:bmg-clariostar-v1-signal-raster
Oct 22, 2026
Merged

BioCam merged 1 commit into
PyLabRobot:mainfrom
BioCam:bmg-clariostar-v1-signal-raster

Conversation

@BioCam

@BioCam BioCam commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

The Problem

The v1 CLARIOstar driver can read a well only at one point, its nominal centre from the plate definition. It cannot tell where a well's signal actually is: whether the plate sits where the model says, whether a plate definition's well positions are right, or where the centre of a well of non-standard labware lies.

The reader itself can find this out. It has a scan (07) that reads the fluorescence signal over a 31 x 31 grid of points around a point on the plate holder and reports the centre of the signal it found. The scan takes one or two points; no driver of ours sends it.

PR Content

Two methods on clariostar.fluorescence:

  • measure_grid: the grid of counts around one or two given points on the plate holder (x and y in mm from its left-front corner), at one focal height and gain, with the centre the reader found for each. The points need not be wells.
  • measure_well_centers: the same for any wells of the plate on the plate holder, scanned two at a time. Without a focal height, a z-scan of the first well (measure_z_profile, which reads the signal at a range of focal heights) gives the focal height, and the gain too unless one is given.
from pylabrobot.lib.fluorescence import FluorescenceChannel
from pylabrobot.resources import Coordinate

channel = FluorescenceChannel(
  excitation_wavelength=488,
  excitation_bandwidth=14,
  emission_wavelength=535,
  emission_bandwidth=30,
  dichroic_split_wavelength=507.5,
)

# the plate's first and last well (wells=None); any wells with wells=[...]
grids = await clariostar.fluorescence.measure_well_centers(channel, focal_height=15.0, gain=2239)
for grid in grids:
  print(grid.well, grid.center, grid.spacing, len(grid.counts), "x", len(grid.counts[0]))
# A1 Coordinate(014.450, 074.130, 000.000) 0.33 31 x 31
# H12 Coordinate(113.330, 011.480, 000.000) 0.33 31 x 31

# any one or two points on the plate holder
grids = await clariostar.fluorescence.measure_grid(
  channel, positions=[Coordinate(14.3, 74.3, 0)], gain=2239, focal_height=15.0
)

A real scan of a plate's A1 and H12, replayed through measure_well_centers (the call above): each grid's counts, with the well's rim as the bright ring, and the centre the reader found against the plate definition's, 0.13-0.28 mm off. The panels are drawn in the order the reader sends the points; which way its rows and columns run on the plate is not verified yet.

measure_well_centers on A1 and H12

Each result holds the grid's position, its well (None for a given point), the centre the reader found (None if it found none), the spacing between points in mm, the counts row by row, and the focal height and gain it was read at. A grid takes about 50-80 s a point; cancelling the call stops the scan on the reader.

The scan sends three commands that are new to the driver:

  • 07, the scan itself;
  • 05 0d, the grid's layout (side and point spacing), right after it;
  • 05 04, the centres the reader found, once it is idle.

Each grid comes back through the existing data request (05 02) whenever status reports data ready. The scan reuses the plate block and the optics encodings of a fluorescence read.

The decoding is tested against a recorded scan of a plate's first and last well, the only pair verified on the reader. Any other point or well is accepted as the scan allows, but it is untested on hardware and warns so.

🤖 Generated with Claude Code

…m them

- `Fluorescence.measure_grid` reads the signal at every point of a 31 x 31 grid around one or two
  given points on the plate holder, at one focal height and gain, with the centre the reader
  finds in each; untested on hardware for points other than a plate's first and last well.
- `Fluorescence.measure_well_centers` does so for any wells of the plate, two to a scan; without
  a focal height a z-scan of the first well gives it, and the gain too unless one is given.
- The scan (`07`), its layout (`05 0d`) and the centres (`05 04`) are new; the plate block and
  the optics are the existing encodings, and the counts are u32 from byte 34.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BioCam
BioCam marked this pull request as ready for review October 22, 2026 21:29
@BioCam
BioCam merged commit f6154de into PyLabRobot:main Oct 22, 2026
44 of 46 checks passed
@BioCam
BioCam deleted the bmg-clariostar-v1-signal-raster branch October 22, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant