Skip to content

Make STARBackend.probe_liquid_heights() Smart#876

Open
BioCam wants to merge 12 commits intoPyLabRobot:mainfrom
BioCam:make-probe_liquid_heights-smart
Open

Make STARBackend.probe_liquid_heights() Smart#876
BioCam wants to merge 12 commits intoPyLabRobot:mainfrom
BioCam:make-probe_liquid_heights-smart

Conversation

@BioCam
Copy link
Collaborator

@BioCam BioCam commented Feb 4, 2026

The Problem

probe_liquid_heights had several limitations that prevented it from handling real-world channel/container configurations:

  • Single X position only — probing containers at different X positions raised NotImplementedError, even though the method could handle them sequentially.
  • Channel-index-unaware offsets — when multiple channels targeted the same container (e.g. a trough), Y offsets were computed for "N channels" without knowing which channels. Non-consecutive
    channels like [0,1,2,5,6,7] were spaced as if they were [0,1,2,3,4,5], ignoring the physical gap required by phantom intermediate channels (3 and 4). If the container was too small to fit all
    channels, this raised an unhandled ValueError instead of falling back gracefully.
  • No Y-aware batching — channels that couldn't physically coexist (due to 9mm minimum spacing, descending-Y-order requirements, or unpositioned intermediate channels between non-consecutive batch
    members) would raise errors mid-command instead of being automatically partitioned into compatible batches.
  • No Z control — every lateral movement went to full Z safety with no option for faster traversal at a known safe height, and the final Z raise could not be customized or skipped.
  • No duplicate channel validation — passing the same channel twice could cause undefined behavior.

This PR

Makes probe_liquid_heights "smart" 🤓, i.e. automatically handle any channel/container configuration:

  1. Grouping by X — containers at different X positions are grouped (with 0.1mm float tolerance) and probed sequentially (single X carriage constraint).
  2. Y sub-batching with intermediate channel positioning — within each X group, channels are sorted by index and greedily partitioned into compatible sub-batches respecting the 9mm minimum spacing ×
    channel index difference. Phantom channels between non-consecutive batch members are explicitly positioned at minimum spacing to prevent firmware errors. Each sub-batch is probed in parallel.
  3. Channel-aware offsets — when all channels target the same container, Y offsets are computed for the full channel span (min to max, including phantoms) using
    get_wide_single_resource_liquid_op_offsets, then only the actual channel offsets are kept. Channels like [0,1,2,5,6,7] now correctly fit in one batch. If the container is too small, offsets fall
    back to center and Y sub-batching automatically serializes the channels that can't coexist.
  4. Z control — three new traverse height parameters allow customizing Z positioning at each stage, all defaulting to full Z safety. A move_to_z_safety_after flag allows skipping the final Z raise
    entirely. On any error, all channels return to full Z safety regardless of these settings.
Parameter Default Effect
min_traverse_height_at_beginning_of_command None → full Z safety Z height for involved channels before first detection
min_traverse_height_during_command None → full Z safety Z height between X groups and Y sub-batches
z_position_at_end_of_command None → full Z safety Z height for involved channels after all detections
move_to_z_safety_after True Skip final Z raise entirely when False
  1. allow_duplicate_channels — defaults to False with a clear error message; aspirate/dispense pass True since they may legitimately probe the same container multiple times.

No breaking changes — single-X, consecutive-channel usage produces identical behavior.

Pseudocode: full method flow

[!NOTE]

1. Defaults & Offsets

  • Default use_channels to [0, 1, ..., N-1] if not provided
  • If no resource_offsets given and all containers are the same instance:
    • Calculate the full channel span (including any phantom channels between non-consecutive ones)
    • If the container is wide enough to fit that span:
      • Spread all channels (real + phantom) wide across the container
      • Keep only the offsets for the actual channels being used
      • If odd span, shift everything +4.5mm to avoid the container center
    • Otherwise, fall back to center offsets — Y sub-batching will serialize

2. Validation

  • LLD mode must be GAMMA or PRESSURE
  • Containers, channels, and offsets must be equal length
  • No duplicate channels (unless explicitly allowed)
  • All channels must have tips attached
  • Query tip lengths sequentially (shared C0 module)

3. Initial Z Raise

  • Move all channels to full Z safety
  • Then optionally lower only the involved channels to min_traverse_height_at_beginning_of_command

4. Compute Target Positions

  • For each container + offset pair, calculate the absolute X and Y target

5. Group by X

  • Group targets by X position (with 0.1mm float tolerance)
  • One X carriage → groups are processed sequentially

6. Select Detection Function

  • GAMMA → capacitive liquid level detection (cLLD)
  • PRESSURE → pressure-based liquid level detection (pLLD)

7. Detection Loop

For each X group:

  • Raise previous group's channels (Z safety or custom traverse height)
  • Move X carriage to group's X position

Partition the group into Y sub-batches:

  • Sort by channel index ascending
  • Greedily assign each channel to the first batch where it fits
    (i.e. enough Y gap for the channel index difference × 9mm minimum spacing)

For each Y sub-batch:

  • Raise previous sub-batch's channels (Z safety or custom traverse height)
  • Position batch channels + any intermediate phantoms in Y
  • Compute Z search bounds (container top → cavity bottom, adjusted for tip length)
  • Run detection n_replicates times:
    • Probe all batch channels in parallel
    • Read detected heights
    • Record each result (height or None if no liquid found)

On any error → move all channels to full Z safety and re-raise

8. Aggregate Results

  • For each channel, average its valid measurements
  • Convert absolute heights to relative (above cavity bottom)
  • If a channel found liquid on some replicates but not others → raise inconsistency error

9. Final Z Raise

  • If move_to_z_safety_after:
    • Move involved channels to z_position_at_end_of_command, or full Z safety if not set

Return relative heights.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the probe_liquid_heights() method to automatically handle complex channel and container configurations that previously required manual batching or raised errors. The changes enable the method to intelligently group containers by X position, partition channels into compatible Y sub-batches, compute channel-aware offsets for non-consecutive channel configurations, and provide fine-grained Z-axis control during probing operations.

Changes:

  • Added automatic X-position grouping and Y sub-batching to handle any channel/container configuration
  • Implemented channel-index-aware offset calculation for non-consecutive channels (e.g., [0,1,2,5,6,7])
  • Added three new traverse height parameters and a flag to control Z positioning at different stages
  • Added duplicate channel validation with an override flag for legitimate multi-probe scenarios
  • Exposed all cLLD and pLLD detection parameters as method arguments

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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