From 96b17341bc3ca77c1cd0c2d9b4302ef22c926783 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Sat, 24 Jan 2026 23:39:50 +0000 Subject: [PATCH 1/7] open draft for discussion --- .../star_automated_plate_definition.ipynb | 985 ++++++++++++++++++ 1 file changed, 985 insertions(+) create mode 100644 docs/cookbook/star_automated_plate_definition.ipynb diff --git a/docs/cookbook/star_automated_plate_definition.ipynb b/docs/cookbook/star_automated_plate_definition.ipynb new file mode 100644 index 00000000000..d52cb61791f --- /dev/null +++ b/docs/cookbook/star_automated_plate_definition.ipynb @@ -0,0 +1,985 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Semi-Automated Plate Definition Generation\n", + "\n", + "- tags: #platedefinition #resourcemovement #plateadapter #hamiltonstar\n", + "- Last updated: 2026-01-26" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "- Machines used:\n", + " - Hamilton STAR\n", + "- Non-PLR dependencies: None \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preview of Machine Behvaiour" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Protocol Mode" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "protocol_mode = \"simulation\" # \"execution\" or \"simulation\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Import Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Non-PLR Dependencies\n", + "\n", + "None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Machine & Visualizer" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + " \n", + "import random \n", + "import time\n", + "\n", + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.resources.hamilton import STARLetDeck\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "\n", + "if protocol_mode == \"execution\":\n", + "\n", + " from pylabrobot.liquid_handling.backends import STARBackend\n", + "\n", + " star = STARBackend()\n", + "\n", + "elif protocol_mode == \"simulation\":\n", + "\n", + " from pylabrobot.liquid_handling.backends.hamilton.STAR_chatterbox import STARChatterboxBackend\n", + " \n", + " star = STARChatterboxBackend()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Required Resources" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " hamilton_mfx_carrier_L5_base,\n", + " hamilton_mfx_plateholder_DWP_metal_tapped,\n", + " hamilton_mfx_plateholder_DWP_flat,\n", + " Coordinate\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create Initial Plate Definition" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.plate import Plate\n", + "from pylabrobot.resources.utils import create_ordered_items_2d\n", + "from pylabrobot.resources.well import (\n", + " CrossSectionType,\n", + " Well,\n", + " WellBottomType,\n", + ")\n", + "\n", + "def biorad_96_wellplate_200uL_Vb(name: str, with_lid: bool = False) -> Plate:\n", + " \"\"\"Bio-rad cat. no.: HSP9601 (50/package), HSP9601B (8*50/package).\n", + "\n", + " Bio-rad plate with 96-wells (V-bottoms).\n", + " \"The patented rigid 2-component design is specifically engineered to\n", + " withstand the stresses of thermal cycling.\" -> excellent for automation!\n", + "\n", + " - Colour: white shell/clear well\n", + " - alternative cat. no.:\n", + " - red shell (HSP-9611)\n", + " - yellow shell (HSP-9621)\n", + " - blue shell (HSP-9631)\n", + " - green shell (HSP-9641)\n", + " - black shell (HSP-9661)\n", + " - Material: Polypropylene\n", + " - Cleanliness: \"Certified to be free of DNase, RNase, and human genomic DNA\"\n", + " - Total volume/well = 200 uL\n", + " - URL: https://www.bio-rad.com/en-uk/sku/HSP9601-hard-shell-96-well-pcr-plates-\n", + " low-profile-thin-wall-skirted-white-clear?ID=HSP9601\n", + " - technical drawing: ./techical_drawings/biorad_96_wellplate_200uL_Vb.png\n", + " \"\"\"\n", + " well_diameter = 5.46 # mm\n", + " return Plate(\n", + " name=name,\n", + " size_x=127.76,\n", + " size_y=85.48,\n", + " size_z=16.06,\n", + " lid=None,\n", + " model=biorad_96_wellplate_200uL_Vb.__name__,\n", + " ordered_items=create_ordered_items_2d(\n", + " Well,\n", + " num_items_x=12,\n", + " num_items_y=8,\n", + " dx=11.65,\n", + " dy=8.51,\n", + " dz=1.1,\n", + " item_dx=9.0,\n", + " item_dy=9.0,\n", + " size_x=well_diameter,\n", + " size_y=well_diameter,\n", + " size_z=14.4,\n", + " bottom_type=WellBottomType.V,\n", + " material_z_thickness=0.65,\n", + " cross_section_type=CrossSectionType.CIRCLE,\n", + " compute_volume_from_height=None,\n", + " compute_height_from_volume=None,\n", + " ),\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instantiate Frontend & Connect to Machine" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2123\n", + "File server started at http://127.0.0.1:1339 . Open this URL in your browser.\n", + "C0CDid0001\n" + ] + } + ], + "source": [ + "deck = STARLetDeck()\n", + "lh = LiquidHandler(backend=star, deck=deck)\n", + "\n", + "await lh.setup()\n", + "\n", + "vis = Visualizer(resource=lh)\n", + "await vis.setup()\n", + "\n", + "await star.disable_cover_control() # 😈" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configure Deck Layout" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup MFX Carrier with DWP PlateHolder that has a pedestal\n", + "\n", + "plateholder_pedestal_0 = hamilton_mfx_plateholder_DWP_metal_tapped(\n", + " name=f\"plateholder_pedestal_0\"\n", + ")\n", + "\n", + "mfx_carrier_0 = hamilton_mfx_carrier_L5_base(\n", + " name=\"mfx_carrier_0\",\n", + " modules={0: plateholder_pedestal_0}\n", + ")\n", + "\n", + "deck.assign_child_resource(mfx_carrier_0, rails=1)\n", + "\n", + "# Setup MFX Carrier with DWP PlateHolder that has a flat bottom\n", + "\n", + "plateholder_flat_0 = hamilton_mfx_plateholder_DWP_flat(name=f\"plateholder_flat_0\")\n", + "\n", + "mfx_carrier_1 = hamilton_mfx_carrier_L5_base(\n", + " name=\"mfx_carrier_1\",\n", + " modules={0: plateholder_flat_0}\n", + ")\n", + "\n", + "pcr_plate_0 = biorad_96_wellplate_200uL_Vb(name=\"pcr_plate_0\")\n", + "\n", + "mfx_carrier_1[0] = pcr_plate_0 \n", + "\n", + "deck.assign_child_resource(mfx_carrier_1, rails=8)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Execution" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Store Coordinates to Probe on Flat" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_channel_wells(plate, channels_used):\n", + " \"\"\"\n", + " Generate a list of wells for multi-channel pipetting with evenly spaced rows.\n", + " \n", + " Returns:\n", + " List of well names (e.g., [\"A1\", \"C1\", \"E1\", \"H1\", \"A2\", \"C2\", ...])\n", + " \"\"\"\n", + " num_rows, num_cols = plate.num_items_y, plate.num_items_x\n", + " \n", + " # Generate evenly spaced row indices\n", + " row_indices = [round(i * (num_rows - 1) / (channels_used - 1)) for i in range(channels_used)]\n", + " \n", + " # Generate well names: all columns, with selected rows per column\n", + " return [f\"{chr(ord('A') + row)}{col}\" \n", + " for col in range(1, num_cols + 1) \n", + " for row in row_indices]" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "n_channels = 3\n", + "wells_to_probe = generate_channel_wells(pcr_plate_0, channels_used=n_channels)\n", + "\n", + "positions_to_probe_flat = [\n", + " well.get_absolute_location(\"c\", \"c\", \"top\")\n", + " for well in pcr_plate_0.children\n", + " if well.get_identifier() in wells_to_probe\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Move Plate to PlateHolder with Pedestal" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0TTid0002tt01tf1tl0519tv03600tg2tu0\n", + "C0TPid0003xp07854 07854 07854 00000&yp5286 5196 5106 0000&tm1 1 1 0&tt01tp1830tz1750th2450td0\n", + "C0ZTid0004xs07975xd0ya1250yb1070pa07pb08tp2350tz2250th2800tt14\n" + ] + } + ], + "source": [ + "teaching_tip_rack = lh.deck.get_resource(\"teaching_tip_rack\")\n", + "await lh.pick_up_tips(teaching_tip_rack[\"A1:C1\"], use_channels=[0,1,2,3,4,5][:n_channels])\n", + "\n", + "await star.pick_up_core_gripper_tools(front_channel=7)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0ZPid0005xs03254xd0yj1142yv0050zj1881zy0500yo0885yg0825yw40th2800te2800\n", + "C0ZRid0006xs01679xd0yj1147zj1929zi000zy0500yo0885th2800te2800\n", + "False\n" + ] + } + ], + "source": [ + "move_target = mfx_carrier_0[0]\n", + "\n", + "if protocol_mode == \"simulation\":\n", + " time.sleep(2)\n", + " \n", + "await lh.move_plate(\n", + " plate=pcr_plate_0,\n", + " to=move_target,\n", + " use_arm=\"core\",\n", + " pickup_distance_from_top=6,\n", + " core_grip_strength=40,\n", + " return_core_gripper=False,\n", + ")\n", + "\n", + "if protocol_mode == \"execution\":\n", + " # \"smart\" command, will ask operator for input if it cannot find plate in\n", + " # move_target location place into condition for simulation mode\n", + "\n", + " # (1) check transfer success, (2) push plate flush\n", + " await star.core_check_resource_exists_at_location_center(\n", + " location=pcr_plate_0.get_absolute_location(),\n", + " resource=pcr_plate_0,\n", + " gripper_y_margin=9,\n", + " enable_recovery=True,\n", + " audio_feedback=False,\n", + " )\n", + "\n", + "print(star.core_parked)\n", + "\n", + "if protocol_mode == \"simulation\":\n", + " time.sleep(2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Store Coordinates to Probe on Pedestal" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "positions_to_probe_pedestal = [\n", + " well.get_absolute_location(\"c\", \"c\", \"top\")\n", + " for well in pcr_plate_0.children\n", + " if well.get_identifier() in wells_to_probe\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Probe True PlateHolder Origin" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "plate_holder_flat_child_coord = mfx_carrier_1[0].get_absolute_location()+mfx_carrier_1[0].child_location\n", + "\n", + "test_location_w_offset = plate_holder_flat_child_coord + Coordinate(x=3, y=3, z=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0ZAid0023\n", + "C0JPid0024pn01\n", + "C0JXid0025xs02645\n", + "moving channel 0 to y: 74.5\n", + "C0KZid0026pn01zj1795\n", + "C0ZAid0027\n" + ] + } + ], + "source": [ + "channel_idx = 0\n", + "\n", + "# Prepare Z position of X-Y probing\n", + "await star.move_all_channels_in_z_safety()\n", + "await star.prepare_for_manual_channel_operation(channel=channel_idx)\n", + "\n", + "await star.move_channel_x(x=test_location_w_offset.x, channel=channel_idx)\n", + "await star.move_channel_y(y= test_location_w_offset.y, channel=channel_idx)\n", + "\n", + "probed_z_position = await star.clld_probe_z_height_using_channel(\n", + " channel_idx=channel_idx,\n", + " channel_speed=6.0, start_pos_search=test_location_w_offset.z+20,\n", + " detection_edge=5, post_detection_dist=5.0,\n", + " move_channels_to_safe_pos_after=False\n", + " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.z\n", + "await star.move_channel_z(z=probed_z_position+1.5, channel=channel_idx)\n", + "\n", + "# X-Y Probing\n", + "offset_n_replicates = 3\n", + "\n", + "front_pos_list = []\n", + "for x in range(offset_n_replicates):\n", + " probed_front_position = await star.clld_probe_y_position_using_channel(\n", + " channel_idx=channel_idx,\n", + " probing_direction=\"forward\",\n", + " channel_speed=3,\n", + " post_detection_dist=3.0,\n", + " end_pos_search=test_location_w_offset.y-5\n", + " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.y\n", + " front_pos_list.append(probed_front_position)\n", + "\n", + "left_pos_list =[]\n", + "for x in range(offset_n_replicates):\n", + " probed_left_position = await star.clld_probe_x_position_using_channel(\n", + " channel_idx=channel_idx,\n", + " probing_direction=\"left\",\n", + " post_detection_dist=1.0,\n", + " end_pos_search=test_location_w_offset.x-5\n", + " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.x\n", + " left_pos_list.append(probed_left_position)\n", + "\n", + "await star.move_all_channels_in_z_safety()\n", + "\n", + "true_plate_holder_flat_child_coord = Coordinate(\n", + " x=round(sum(left_pos_list)/len(left_pos_list), 1),\n", + " y=round(sum(front_pos_list)/len(front_pos_list), 1),\n", + " z=probed_z_position,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Move Plate back onto tapped PlateHolder" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0ZPid0028xs01679xd0yj1147yv0050zj1929zy0500yo0885yg0825yw40th2800te2800\n", + "C0ZRid0029xs03254xd0yj1142zj1881zi000zy0500yo0885th2800te2800\n" + ] + } + ], + "source": [ + "move_target = mfx_carrier_1[0]\n", + "\n", + "await lh.move_plate(\n", + " plate=pcr_plate_0,\n", + " to=move_target,\n", + " use_arm=\"core\",\n", + " pickup_distance_from_top=6,\n", + " core_grip_strength=40,\n", + " return_core_gripper=False,\n", + ")\n", + "\n", + "if protocol_mode == \"execution\":\n", + "\n", + " await star.core_check_resource_exists_at_location_center(\n", + " location=pcr_plate_0.get_absolute_location(),\n", + " resource=pcr_plate_0,\n", + " gripper_y_margin=9,\n", + " enable_recovery=True,\n", + " audio_feedback=False,\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Interactive X-Y Verification of Bottom-Left Well" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0ZAid0030\n", + "C0JPid0031pn01\n", + "C0JXid0032xs02759\n", + "moving channel 0 to y: 82.74\n", + "C0KZid0033pn01zj1985\n" + ] + } + ], + "source": [ + "test_location = pcr_plate_0.column(0)[-1].get_absolute_location(\n", + " x=\"center\",y=\"center\",z=\"top\"\n", + ")\n", + "\n", + "await lh.backend.move_all_channels_in_z_safety()\n", + "\n", + "await lh.backend.prepare_for_manual_channel_operation(channel=channel_idx)\n", + "\n", + "# Position channel with teaching needle above bottom-left well\n", + "await lh.backend.move_channel_x(x=test_location.x, channel=channel_idx)\n", + "await lh.backend.move_channel_y(y=test_location.y, channel=channel_idx)\n", + "await lh.backend.move_channel_z(z=test_location.z+5, channel=channel_idx)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0RXid0074\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0RXid0075\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0JXid0076xs02616\n", + "C0RXid0077\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0RXid0078\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0JXid0079xs02617\n", + "C0RXid0080\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0RXid0081\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0JXid0082xs02618\n", + "C0RXid0083\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0RXid0084\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0JXid0085xs02619\n", + "C0RXid0086\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0RXid0087\n", + "Error getting positions: 'NoneType' object is not subscriptable\n", + "C0KZid0088pn01zj1790\n", + "C0RXid0089\n", + "Error getting positions: 'NoneType' object is not subscriptable\n" + ] + } + ], + "source": [ + "import tkinter as tk\n", + "from tkinter import ttk\n", + "import asyncio\n", + "import threading\n", + "\n", + "def create_gui(lh, channel_idx, plateholder_child_location):\n", + " \"\"\"\n", + " Create a GUI to control liquid handler position\n", + " \n", + " Args:\n", + " lh: Liquid handler instance with backend\n", + " channel_idx: Channel index to control\n", + " plateholder_child_location: Reference location to calculate relative position from\n", + " \"\"\"\n", + " \n", + " # Create main window\n", + " root = tk.Tk()\n", + " root.title(\"Simple Counter GUI\")\n", + " root.geometry(\"1050x350\")\n", + " \n", + " # Store reference location\n", + " ref_pos = {\n", + " 'X': plateholder_child_location.x,\n", + " 'Y': plateholder_child_location.y,\n", + " 'Z': plateholder_child_location.z\n", + " }\n", + " \n", + " # Track positions locally (fallback for simulation)\n", + " current_positions = {\n", + " 'X': plateholder_child_location.x,\n", + " 'Y': plateholder_child_location.y,\n", + " 'Z': plateholder_child_location.z\n", + " }\n", + " \n", + " # Labels to update\n", + " float_labels1 = {} # Relative to PlateHolder Child Location\n", + " float_labels2 = {} # Relative to Deck (absolute)\n", + " resolution = tk.DoubleVar(value=0.1) # Default to 0.1 mm\n", + " \n", + " # Define colors for each section\n", + " colors = {'X': '#658F11', 'Y': '#B63E4B', 'Z': '#3E7DD7'}\n", + " \n", + " def run_async(coro):\n", + " \"\"\"Helper to run async coroutine and schedule GUI updates\"\"\"\n", + " def run_in_thread():\n", + " try:\n", + " # Get or create event loop for this thread\n", + " try:\n", + " loop = asyncio.get_event_loop()\n", + " except RuntimeError:\n", + " loop = asyncio.new_event_loop()\n", + " asyncio.set_event_loop(loop)\n", + " \n", + " # Run the coroutine\n", + " loop.run_until_complete(coro)\n", + " except Exception as e:\n", + " print(f\"Error in async operation: {e}\")\n", + " \n", + " # Run in background thread to not block tkinter\n", + " thread = threading.Thread(target=run_in_thread, daemon=True)\n", + " thread.start()\n", + " \n", + " async def get_current_positions():\n", + " \"\"\"Get current channel positions from backend\"\"\"\n", + " try:\n", + " x_pos = await lh.backend.request_x_pos_channel_n(pipetting_channel_index=channel_idx)\n", + " y_pos = await lh.backend.request_y_pos_channel_n(pipetting_channel_index=channel_idx)\n", + " z_pos = await lh.backend.request_z_pos_channel_n(pipetting_channel_index=channel_idx)\n", + " \n", + " # If any position is None, use locally tracked positions (simulation fallback)\n", + " if x_pos is None or y_pos is None or z_pos is None:\n", + " return current_positions.copy()\n", + " \n", + " # Update locally tracked positions\n", + " current_positions['X'] = x_pos\n", + " current_positions['Y'] = y_pos\n", + " current_positions['Z'] = z_pos\n", + " \n", + " return {'X': x_pos, 'Y': y_pos, 'Z': z_pos}\n", + " except Exception as e:\n", + " print(f\"Error getting positions: {e}\")\n", + " # Return locally tracked positions as fallback\n", + " return current_positions.copy()\n", + " \n", + " def update_displays():\n", + " \"\"\"Update all position displays from current backend positions\"\"\"\n", + " async def async_update():\n", + " try:\n", + " pos = await get_current_positions()\n", + " \n", + " # Schedule GUI update on main thread\n", + " def gui_update():\n", + " for axis in ['X', 'Y', 'Z']:\n", + " absolute_pos = pos[axis]\n", + " relative_pos = absolute_pos - ref_pos[axis]\n", + " \n", + " # Update relative position (to plateholder child location)\n", + " float_labels1[axis].config(text=f\"{relative_pos:.1f}\")\n", + " \n", + " # Update absolute position (to deck)\n", + " float_labels2[axis].config(text=f\"{absolute_pos:.1f}\")\n", + " \n", + " root.after(0, gui_update)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error updating displays: {e}\")\n", + " \n", + " run_async(async_update())\n", + " \n", + " def update_counter(section, change):\n", + " \"\"\"Update the counter for a given section based on resolution\"\"\"\n", + " step = resolution.get()\n", + " \n", + " async def async_move():\n", + " try:\n", + " # Get current position\n", + " pos = await get_current_positions()\n", + " \n", + " # Calculate new position\n", + " new_pos = pos[section] + (change * step)\n", + " \n", + " # Update locally tracked position\n", + " current_positions[section] = new_pos\n", + " \n", + " # Send movement command - use 'channel' parameter\n", + " if section == 'X':\n", + " await lh.backend.move_channel_x(x=new_pos, channel=channel_idx)\n", + " elif section == 'Y':\n", + " await lh.backend.move_channel_y(y=new_pos, channel=channel_idx)\n", + " elif section == 'Z':\n", + " await lh.backend.move_channel_z(z=new_pos, channel=channel_idx)\n", + " \n", + " # Update displays after movement\n", + " await asyncio.sleep(0.1) # Small delay for position to settle\n", + " pos_updated = await get_current_positions()\n", + " \n", + " # Schedule GUI update on main thread\n", + " def gui_update():\n", + " absolute_pos = pos_updated[section]\n", + " relative_pos = absolute_pos - ref_pos[section]\n", + " float_labels1[section].config(text=f\"{relative_pos:.1f}\")\n", + " float_labels2[section].config(text=f\"{absolute_pos:.1f}\")\n", + " \n", + " root.after(0, gui_update)\n", + " \n", + " except Exception as e:\n", + " print(f\"Error moving {section} axis: {e}\")\n", + " \n", + " run_async(async_move())\n", + " \n", + " # Create a frame for the description labels\n", + " desc_frame = tk.Frame(root)\n", + " desc_frame.grid(row=0, column=0, padx=(10, 5), pady=20, sticky=\"e\")\n", + " \n", + " # Add description labels\n", + " desc_label1 = tk.Label(desc_frame, text=\"Relative to PlateHolder\\nChild Location:\", \n", + " font=('Arial', 11, 'bold'), justify='right')\n", + " desc_label1.pack(pady=(40, 25))\n", + " \n", + " desc_label2 = tk.Label(desc_frame, text=\"Relative to Deck:\", \n", + " font=('Arial', 11, 'bold'), justify='right')\n", + " desc_label2.pack()\n", + " \n", + " # Create sections for X, Y, and Z\n", + " for i, section in enumerate(['X', 'Y', 'Z']):\n", + " # Outer container frame\n", + " container = tk.Frame(root)\n", + " container.grid(row=0, column=i+1, padx=10, pady=20, sticky=\"nsew\")\n", + " \n", + " # Colored header label\n", + " header = tk.Label(container, text=f\"{section}\", \n", + " font=('Arial', 12, 'bold'), bg=colors[section],\n", + " fg='white', pady=5)\n", + " header.pack(fill='x')\n", + " \n", + " # Content frame with white/default background\n", + " frame = tk.Frame(container, borderwidth=2, relief='solid', padx=10, pady=10)\n", + " frame.pack(fill='both', expand=True)\n", + " \n", + " # Minus button\n", + " minus_btn = ttk.Button(frame, text=\"-\", width=8,\n", + " command=lambda s=section: update_counter(s, -1))\n", + " minus_btn.grid(row=0, column=0, padx=5, pady=5)\n", + " \n", + " # Plus button\n", + " plus_btn = ttk.Button(frame, text=\"+\", width=8,\n", + " command=lambda s=section: update_counter(s, 1))\n", + " plus_btn.grid(row=0, column=1, padx=5, pady=5)\n", + " \n", + " # First float label - Relative to PlateHolder Child Location\n", + " float_label1 = tk.Label(frame, text=\"0.0\", font=('Arial', 12, 'bold'))\n", + " float_label1.grid(row=1, column=0, columnspan=2, pady=(15, 5))\n", + " float_labels1[section] = float_label1\n", + " \n", + " # Second float label - Relative to Deck (absolute position)\n", + " float_label2 = tk.Label(frame, text=f\"{ref_pos[section]:.1f}\", \n", + " font=('Arial', 12, 'bold'))\n", + " float_label2.grid(row=2, column=0, columnspan=2, pady=5)\n", + " float_labels2[section] = float_label2\n", + " \n", + " # Resolution section\n", + " resolution_frame = ttk.LabelFrame(root, text=\"Resolution\", padding=15, labelanchor='n')\n", + " resolution_frame.grid(row=1, column=1, columnspan=3, padx=10, pady=(10, 20), sticky=\"ew\")\n", + " \n", + " # Configure resolution label font\n", + " style = ttk.Style()\n", + " style.configure('Bold.TLabelframe.Label', font=('Arial', 12, 'bold'))\n", + " resolution_frame.configure(style='Bold.TLabelframe')\n", + " \n", + " # Create inner frame to center the radio buttons\n", + " radio_container = tk.Frame(resolution_frame)\n", + " radio_container.pack(expand=True)\n", + " \n", + " # Create radio buttons for resolution\n", + " resolutions = [\n", + " (\"0.1 mm\", 0.1),\n", + " (\"1 mm\", 1.0),\n", + " (\"10 mm\", 10.0)\n", + " ]\n", + " \n", + " for i, (text, value) in enumerate(resolutions):\n", + " rb = tk.Radiobutton(radio_container, text=text, variable=resolution, \n", + " value=value, font=('Arial', 11, 'bold'))\n", + " rb.pack(side=tk.LEFT, padx=20)\n", + " \n", + " # Configure grid weights for responsive layout\n", + " for i in range(1, 4):\n", + " root.columnconfigure(i, weight=1)\n", + " \n", + " # Initialize displays with current positions (delayed to let GUI render)\n", + " root.after(100, update_displays)\n", + " \n", + " # Start the GUI event loop\n", + " root.mainloop()\n", + "\n", + "create_gui(lh, channel_idx, true_plate_holder_flat_child_coord)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Z Probing of Wells (on Flat PlateHolder)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Move Plate to PlateHolder with Pedestal" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Z Probing of Flat PlateHolder" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Z Probing of Wells (on Pedestal)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Update Plate Definition" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 1f12d7e48adf0bcd364245409c4c83fcf1eb3cd3 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Sun, 25 Jan 2026 01:42:38 +0000 Subject: [PATCH 2/7] Add Z-probing of Wells (on Flat PlateHolder) --- .../star_automated_plate_definition.ipynb | 428 ++++++------------ 1 file changed, 150 insertions(+), 278 deletions(-) diff --git a/docs/cookbook/star_automated_plate_definition.ipynb b/docs/cookbook/star_automated_plate_definition.ipynb index d52cb61791f..ba9ef3da374 100644 --- a/docs/cookbook/star_automated_plate_definition.ipynb +++ b/docs/cookbook/star_automated_plate_definition.ipynb @@ -93,6 +93,7 @@ "from pylabrobot.liquid_handling import LiquidHandler\n", "from pylabrobot.resources.hamilton import STARLetDeck\n", "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.utils import chunk_list\n", "\n", "if protocol_mode == \"execution\":\n", "\n", @@ -196,7 +197,9 @@ " compute_volume_from_height=None,\n", " compute_height_from_volume=None,\n", " ),\n", - " )" + " )\n", + "\n", + "plate_definition_function = biorad_96_wellplate_200uL_Vb" ] }, { @@ -268,7 +271,7 @@ " modules={0: plateholder_flat_0}\n", ")\n", "\n", - "pcr_plate_0 = biorad_96_wellplate_200uL_Vb(name=\"pcr_plate_0\")\n", + "pcr_plate_0 = plate_definition_function(name=\"pcr_plate_0\")\n", "\n", "mfx_carrier_1[0] = pcr_plate_0 \n", "\n", @@ -568,18 +571,15 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "C0ZAid0030\n", - "C0JPid0031pn01\n", - "C0JXid0032xs02759\n", - "moving channel 0 to y: 82.74\n", - "C0KZid0033pn01zj1985\n" + "C0ZAid0130\n", + "C0JPid0131pn01\n" ] } ], @@ -590,298 +590,103 @@ "\n", "await lh.backend.move_all_channels_in_z_safety()\n", "\n", - "await lh.backend.prepare_for_manual_channel_operation(channel=channel_idx)\n", - "\n", + "await lh.backend.prepare_for_manual_channel_operation(channel=channel_idx)" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0JXid0141xs02759\n", + "275.88, current_dx=11.6\n" + ] + } + ], + "source": [ "# Position channel with teaching needle above bottom-left well\n", - "await lh.backend.move_channel_x(x=test_location.x, channel=channel_idx)\n", - "await lh.backend.move_channel_y(y=test_location.y, channel=channel_idx)\n", - "await lh.backend.move_channel_z(z=test_location.z+5, channel=channel_idx)\n" + "test_x = test_location.x + 0.0\n", + "\n", + "await lh.backend.move_channel_x(x=test_x, channel=channel_idx)\n", + "\n", + "current_dx = round(test_x - true_plate_holder_flat_child_coord.x - pcr_plate_0.children[0].get_size_x()/2, 1)\n", + "print(f\"{test_x}, {current_dx=}\")" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 61, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "C0RXid0074\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0RXid0075\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0JXid0076xs02616\n", - "C0RXid0077\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0RXid0078\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0JXid0079xs02617\n", - "C0RXid0080\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0RXid0081\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0JXid0082xs02618\n", - "C0RXid0083\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0RXid0084\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0JXid0085xs02619\n", - "C0RXid0086\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0RXid0087\n", - "Error getting positions: 'NoneType' object is not subscriptable\n", - "C0KZid0088pn01zj1790\n", - "C0RXid0089\n", - "Error getting positions: 'NoneType' object is not subscriptable\n" + "moving channel 0 to y: 82.74\n", + "82.74, current_dy=8.5\n" ] } ], "source": [ - "import tkinter as tk\n", - "from tkinter import ttk\n", - "import asyncio\n", - "import threading\n", + "test_y = test_location.y + 0.0\n", "\n", - "def create_gui(lh, channel_idx, plateholder_child_location):\n", - " \"\"\"\n", - " Create a GUI to control liquid handler position\n", - " \n", - " Args:\n", - " lh: Liquid handler instance with backend\n", - " channel_idx: Channel index to control\n", - " plateholder_child_location: Reference location to calculate relative position from\n", - " \"\"\"\n", - " \n", - " # Create main window\n", - " root = tk.Tk()\n", - " root.title(\"Simple Counter GUI\")\n", - " root.geometry(\"1050x350\")\n", - " \n", - " # Store reference location\n", - " ref_pos = {\n", - " 'X': plateholder_child_location.x,\n", - " 'Y': plateholder_child_location.y,\n", - " 'Z': plateholder_child_location.z\n", - " }\n", - " \n", - " # Track positions locally (fallback for simulation)\n", - " current_positions = {\n", - " 'X': plateholder_child_location.x,\n", - " 'Y': plateholder_child_location.y,\n", - " 'Z': plateholder_child_location.z\n", - " }\n", - " \n", - " # Labels to update\n", - " float_labels1 = {} # Relative to PlateHolder Child Location\n", - " float_labels2 = {} # Relative to Deck (absolute)\n", - " resolution = tk.DoubleVar(value=0.1) # Default to 0.1 mm\n", - " \n", - " # Define colors for each section\n", - " colors = {'X': '#658F11', 'Y': '#B63E4B', 'Z': '#3E7DD7'}\n", - " \n", - " def run_async(coro):\n", - " \"\"\"Helper to run async coroutine and schedule GUI updates\"\"\"\n", - " def run_in_thread():\n", - " try:\n", - " # Get or create event loop for this thread\n", - " try:\n", - " loop = asyncio.get_event_loop()\n", - " except RuntimeError:\n", - " loop = asyncio.new_event_loop()\n", - " asyncio.set_event_loop(loop)\n", - " \n", - " # Run the coroutine\n", - " loop.run_until_complete(coro)\n", - " except Exception as e:\n", - " print(f\"Error in async operation: {e}\")\n", - " \n", - " # Run in background thread to not block tkinter\n", - " thread = threading.Thread(target=run_in_thread, daemon=True)\n", - " thread.start()\n", - " \n", - " async def get_current_positions():\n", - " \"\"\"Get current channel positions from backend\"\"\"\n", - " try:\n", - " x_pos = await lh.backend.request_x_pos_channel_n(pipetting_channel_index=channel_idx)\n", - " y_pos = await lh.backend.request_y_pos_channel_n(pipetting_channel_index=channel_idx)\n", - " z_pos = await lh.backend.request_z_pos_channel_n(pipetting_channel_index=channel_idx)\n", - " \n", - " # If any position is None, use locally tracked positions (simulation fallback)\n", - " if x_pos is None or y_pos is None or z_pos is None:\n", - " return current_positions.copy()\n", - " \n", - " # Update locally tracked positions\n", - " current_positions['X'] = x_pos\n", - " current_positions['Y'] = y_pos\n", - " current_positions['Z'] = z_pos\n", - " \n", - " return {'X': x_pos, 'Y': y_pos, 'Z': z_pos}\n", - " except Exception as e:\n", - " print(f\"Error getting positions: {e}\")\n", - " # Return locally tracked positions as fallback\n", - " return current_positions.copy()\n", - " \n", - " def update_displays():\n", - " \"\"\"Update all position displays from current backend positions\"\"\"\n", - " async def async_update():\n", - " try:\n", - " pos = await get_current_positions()\n", - " \n", - " # Schedule GUI update on main thread\n", - " def gui_update():\n", - " for axis in ['X', 'Y', 'Z']:\n", - " absolute_pos = pos[axis]\n", - " relative_pos = absolute_pos - ref_pos[axis]\n", - " \n", - " # Update relative position (to plateholder child location)\n", - " float_labels1[axis].config(text=f\"{relative_pos:.1f}\")\n", - " \n", - " # Update absolute position (to deck)\n", - " float_labels2[axis].config(text=f\"{absolute_pos:.1f}\")\n", - " \n", - " root.after(0, gui_update)\n", - " \n", - " except Exception as e:\n", - " print(f\"Error updating displays: {e}\")\n", - " \n", - " run_async(async_update())\n", - " \n", - " def update_counter(section, change):\n", - " \"\"\"Update the counter for a given section based on resolution\"\"\"\n", - " step = resolution.get()\n", - " \n", - " async def async_move():\n", - " try:\n", - " # Get current position\n", - " pos = await get_current_positions()\n", - " \n", - " # Calculate new position\n", - " new_pos = pos[section] + (change * step)\n", - " \n", - " # Update locally tracked position\n", - " current_positions[section] = new_pos\n", - " \n", - " # Send movement command - use 'channel' parameter\n", - " if section == 'X':\n", - " await lh.backend.move_channel_x(x=new_pos, channel=channel_idx)\n", - " elif section == 'Y':\n", - " await lh.backend.move_channel_y(y=new_pos, channel=channel_idx)\n", - " elif section == 'Z':\n", - " await lh.backend.move_channel_z(z=new_pos, channel=channel_idx)\n", - " \n", - " # Update displays after movement\n", - " await asyncio.sleep(0.1) # Small delay for position to settle\n", - " pos_updated = await get_current_positions()\n", - " \n", - " # Schedule GUI update on main thread\n", - " def gui_update():\n", - " absolute_pos = pos_updated[section]\n", - " relative_pos = absolute_pos - ref_pos[section]\n", - " float_labels1[section].config(text=f\"{relative_pos:.1f}\")\n", - " float_labels2[section].config(text=f\"{absolute_pos:.1f}\")\n", - " \n", - " root.after(0, gui_update)\n", - " \n", - " except Exception as e:\n", - " print(f\"Error moving {section} axis: {e}\")\n", - " \n", - " run_async(async_move())\n", - " \n", - " # Create a frame for the description labels\n", - " desc_frame = tk.Frame(root)\n", - " desc_frame.grid(row=0, column=0, padx=(10, 5), pady=20, sticky=\"e\")\n", - " \n", - " # Add description labels\n", - " desc_label1 = tk.Label(desc_frame, text=\"Relative to PlateHolder\\nChild Location:\", \n", - " font=('Arial', 11, 'bold'), justify='right')\n", - " desc_label1.pack(pady=(40, 25))\n", - " \n", - " desc_label2 = tk.Label(desc_frame, text=\"Relative to Deck:\", \n", - " font=('Arial', 11, 'bold'), justify='right')\n", - " desc_label2.pack()\n", - " \n", - " # Create sections for X, Y, and Z\n", - " for i, section in enumerate(['X', 'Y', 'Z']):\n", - " # Outer container frame\n", - " container = tk.Frame(root)\n", - " container.grid(row=0, column=i+1, padx=10, pady=20, sticky=\"nsew\")\n", - " \n", - " # Colored header label\n", - " header = tk.Label(container, text=f\"{section}\", \n", - " font=('Arial', 12, 'bold'), bg=colors[section],\n", - " fg='white', pady=5)\n", - " header.pack(fill='x')\n", - " \n", - " # Content frame with white/default background\n", - " frame = tk.Frame(container, borderwidth=2, relief='solid', padx=10, pady=10)\n", - " frame.pack(fill='both', expand=True)\n", - " \n", - " # Minus button\n", - " minus_btn = ttk.Button(frame, text=\"-\", width=8,\n", - " command=lambda s=section: update_counter(s, -1))\n", - " minus_btn.grid(row=0, column=0, padx=5, pady=5)\n", - " \n", - " # Plus button\n", - " plus_btn = ttk.Button(frame, text=\"+\", width=8,\n", - " command=lambda s=section: update_counter(s, 1))\n", - " plus_btn.grid(row=0, column=1, padx=5, pady=5)\n", - " \n", - " # First float label - Relative to PlateHolder Child Location\n", - " float_label1 = tk.Label(frame, text=\"0.0\", font=('Arial', 12, 'bold'))\n", - " float_label1.grid(row=1, column=0, columnspan=2, pady=(15, 5))\n", - " float_labels1[section] = float_label1\n", - " \n", - " # Second float label - Relative to Deck (absolute position)\n", - " float_label2 = tk.Label(frame, text=f\"{ref_pos[section]:.1f}\", \n", - " font=('Arial', 12, 'bold'))\n", - " float_label2.grid(row=2, column=0, columnspan=2, pady=5)\n", - " float_labels2[section] = float_label2\n", - " \n", - " # Resolution section\n", - " resolution_frame = ttk.LabelFrame(root, text=\"Resolution\", padding=15, labelanchor='n')\n", - " resolution_frame.grid(row=1, column=1, columnspan=3, padx=10, pady=(10, 20), sticky=\"ew\")\n", - " \n", - " # Configure resolution label font\n", - " style = ttk.Style()\n", - " style.configure('Bold.TLabelframe.Label', font=('Arial', 12, 'bold'))\n", - " resolution_frame.configure(style='Bold.TLabelframe')\n", - " \n", - " # Create inner frame to center the radio buttons\n", - " radio_container = tk.Frame(resolution_frame)\n", - " radio_container.pack(expand=True)\n", - " \n", - " # Create radio buttons for resolution\n", - " resolutions = [\n", - " (\"0.1 mm\", 0.1),\n", - " (\"1 mm\", 1.0),\n", - " (\"10 mm\", 10.0)\n", - " ]\n", - " \n", - " for i, (text, value) in enumerate(resolutions):\n", - " rb = tk.Radiobutton(radio_container, text=text, variable=resolution, \n", - " value=value, font=('Arial', 11, 'bold'))\n", - " rb.pack(side=tk.LEFT, padx=20)\n", - " \n", - " # Configure grid weights for responsive layout\n", - " for i in range(1, 4):\n", - " root.columnconfigure(i, weight=1)\n", - " \n", - " # Initialize displays with current positions (delayed to let GUI render)\n", - " root.after(100, update_displays)\n", - " \n", - " # Start the GUI event loop\n", - " root.mainloop()\n", + "await lh.backend.move_channel_y(y=test_y, channel=channel_idx)\n", "\n", - "create_gui(lh, channel_idx, true_plate_holder_flat_child_coord)" + "current_dy = round(test_y - true_plate_holder_flat_child_coord.y - pcr_plate_0.children[0].get_size_y()/2, 1)\n", + "print(f\"{test_y}, {current_dy=}\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C0KZid0136pn01zj1935\n" + ] + }, + { + "data": { + "text/plain": [ + "193.5" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Only to move the teaching needle into the well for X/Y correction\n", + "test_z = test_location.z + 0.0\n", + "await lh.backend.move_channel_z(z=test_z, channel=channel_idx)\n", + "test_z" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdin", + "output_type": "stream", + "text": [ + "Have you updated the dx and dy? \n" + ] + } + ], + "source": [ + "final_query = input(f\"Have you updated the dx and dy?\")\n", + "await lh.backend.move_all_channels_in_z_safety()\n" + ] }, { "cell_type": "code", @@ -897,6 +702,73 @@ "### Z Probing of Wells (on Flat PlateHolder)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if protocol_mode == \"execution\":\n", + " \n", + " await lh.backend.move_all_channels_in_z_safety()\n", + " \n", + " for positions in chunk_list(positions_to_probe_flat, chunk_size=n_channels):\n", + " \n", + " x_pos = [coord.x for coord in positions]\n", + " y_pos = [coord.y for coord in positions]\n", + " z_start_pos = positions[0].z+20 \n", + " \n", + " await lh.backend.move_channel_x(0, x_pos[0])\n", + "\n", + " await lh.backend.position_channels_in_y_direction(\n", + " {channel: y for channel, y in zip(use_channels[:n_channels], y_pos)}\n", + " )\n", + " await lh.backend.position_channels_in_z_direction(\n", + " {channel: z_start_pos for channel in use_channels}\n", + " )\n", + "\n", + " # Detect well cavity bottoms\n", + " start_pos_search_list = [z_start_pos for x in range(n_channels)]\n", + " measured_well_bottoms_replicate_summary = []\n", + " tech_n_replicates = 3 \n", + "\n", + " for n in range(tech_n_replicates):\n", + " measured_well_bottoms = await asyncio.gather(\n", + " *[\n", + " lh.backend.ztouch_probe_z_height_using_channel(\n", + " channel_idx=channel_idx,\n", + " tip_len=lh.head[channel_idx].get_tip().total_tip_length, # must be declared to avoid STAR C0 module\n", + " channel_speed=18.0, start_pos_search=start_pos_z,\n", + " lowest_immers_pos=probed_z_position-2,\n", + " detection_limiter_in_PWM=0, push_down_force_in_PWM=0,\n", + " post_detection_dist=0.0, # must be 0 to avoid STAR C0 module\n", + " move_channels_to_safe_pos_after=False # must be False to avoid STAR C0 module\n", + " )\n", + " for channel_idx, start_pos_z in zip(\n", + " use_channels, start_pos_search_list\n", + " )\n", + " ]\n", + " )\n", + " measured_well_bottoms_replicate_summary.append(measured_well_bottoms)\n", + "\n", + " start_pos_search_list = [x+3 for x in measured_well_bottoms] # Accelerate\n", + "\n", + " mean_measured_well_bottoms = [\n", + " sum(replicates)/tech_n_replicates\n", + " for replicates in list(zip(*measured_well_bottoms_replicate_summary))\n", + " ]\n", + " # SAFETY: move UP before starting next loop!\n", + " await lh.backend.position_channels_in_z_direction(\n", + " {channel: z_start_pos for channel in use_channels}\n", + " )\n", + " \n", + " \n", + " time.time() - offset_measurement_start_time\n", + " \n", + " \n", + " await lh.return_tips()" + ] + }, { "cell_type": "code", "execution_count": null, From 89c54b2bbd9e342241b6b6ed15d1f71a99524104 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Sun, 25 Jan 2026 02:06:17 +0000 Subject: [PATCH 3/7] update ztouch probing --- .../star_automated_plate_definition.ipynb | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/cookbook/star_automated_plate_definition.ipynb b/docs/cookbook/star_automated_plate_definition.ipynb index ba9ef3da374..81a34e0a28f 100644 --- a/docs/cookbook/star_automated_plate_definition.ipynb +++ b/docs/cookbook/star_automated_plate_definition.ipynb @@ -688,13 +688,6 @@ "await lh.backend.move_all_channels_in_z_safety()\n" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -711,19 +704,23 @@ "if protocol_mode == \"execution\":\n", " \n", " await lh.backend.move_all_channels_in_z_safety()\n", + " probing_start_time = time.time()\n", + "\n", + " plate_on_flat_results = []\n", " \n", " for positions in chunk_list(positions_to_probe_flat, chunk_size=n_channels):\n", " \n", " x_pos = [coord.x for coord in positions]\n", " y_pos = [coord.y for coord in positions]\n", " z_start_pos = positions[0].z+20 \n", + " use_channels = list(range(n_channels))\n", " \n", - " await lh.backend.move_channel_x(0, x_pos[0])\n", + " await star.move_channel_x(0, x_pos[0])\n", "\n", - " await lh.backend.position_channels_in_y_direction(\n", - " {channel: y for channel, y in zip(use_channels[:n_channels], y_pos)}\n", + " await star.position_channels_in_y_direction(\n", + " {channel: y for channel, y in zip(use_channels, y_pos)}\n", " )\n", - " await lh.backend.position_channels_in_z_direction(\n", + " await star.position_channels_in_z_direction(\n", " {channel: z_start_pos for channel in use_channels}\n", " )\n", "\n", @@ -733,16 +730,21 @@ " tech_n_replicates = 3 \n", "\n", " for n in range(tech_n_replicates):\n", + "\n", + " # Parallelise ztouch probing\n", + " # taking care to avoid parameters which require the unparallelizable C0 command module\n", " measured_well_bottoms = await asyncio.gather(\n", " *[\n", - " lh.backend.ztouch_probe_z_height_using_channel(\n", - " channel_idx=channel_idx,\n", - " tip_len=lh.head[channel_idx].get_tip().total_tip_length, # must be declared to avoid STAR C0 module\n", - " channel_speed=18.0, start_pos_search=start_pos_z,\n", - " lowest_immers_pos=probed_z_position-2,\n", - " detection_limiter_in_PWM=0, push_down_force_in_PWM=0,\n", - " post_detection_dist=0.0, # must be 0 to avoid STAR C0 module\n", - " move_channels_to_safe_pos_after=False # must be False to avoid STAR C0 module\n", + " star.ztouch_probe_z_height_using_channel(\n", + " channel_idx=channel_idx,\n", + " tip_len=lh.head[channel_idx].get_tip().total_tip_length, # must be declared to avoid STAR C0 module\n", + " channel_speed=10.0,\n", + " start_pos_search=start_pos_z,\n", + " lowest_immers_pos=probed_z_position-2,\n", + " detection_limiter_in_PWM=0,\n", + " push_down_force_in_PWM=0,\n", + " post_detection_dist=0.0, # must be 0 to avoid STAR C0 module\n", + " move_channels_to_safe_pos_after=False # must be False to avoid STAR C0 module\n", " )\n", " for channel_idx, start_pos_z in zip(\n", " use_channels, start_pos_search_list\n", @@ -757,16 +759,16 @@ " sum(replicates)/tech_n_replicates\n", " for replicates in list(zip(*measured_well_bottoms_replicate_summary))\n", " ]\n", + " plate_on_flat_results.append(mean_measured_well_bottoms)\n", + " \n", " # SAFETY: move UP before starting next loop!\n", - " await lh.backend.position_channels_in_z_direction(\n", + " await star.position_channels_in_z_direction(\n", " {channel: z_start_pos for channel in use_channels}\n", " )\n", " \n", " \n", - " time.time() - offset_measurement_start_time\n", - " \n", - " \n", - " await lh.return_tips()" + " print(f\"Well probing took {time.time() - probing_start_time} sec\")\n", + " " ] }, { From 0cfbfb1d757f646af4b1ae3331ad76220dbf59dc Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Sun, 25 Jan 2026 02:21:33 +0000 Subject: [PATCH 4/7] TODO for plate size_z measurement & smart traverse height update --- .../star_automated_plate_definition.ipynb | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/cookbook/star_automated_plate_definition.ipynb b/docs/cookbook/star_automated_plate_definition.ipynb index 81a34e0a28f..01b660e0ca7 100644 --- a/docs/cookbook/star_automated_plate_definition.ipynb +++ b/docs/cookbook/star_automated_plate_definition.ipynb @@ -697,23 +697,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ "if protocol_mode == \"execution\":\n", - " \n", - " await lh.backend.move_all_channels_in_z_safety()\n", + "\n", " probing_start_time = time.time()\n", "\n", + " await lh.backend.move_all_channels_in_z_safety()\n", + " use_channels = list(range(n_channels))\n", + " await lh.backend.prepare_for_manual_channel_operation(channel=use_channels[-1])\n", + " \n", " plate_on_flat_results = []\n", " \n", " for positions in chunk_list(positions_to_probe_flat, chunk_size=n_channels):\n", " \n", " x_pos = [coord.x for coord in positions]\n", " y_pos = [coord.y for coord in positions]\n", - " z_start_pos = positions[0].z+20 \n", - " use_channels = list(range(n_channels))\n", + " z_start_pos = positions[0].z+20 # TODO: add size_z measurement\n", + " \n", " \n", " await star.move_channel_x(0, x_pos[0])\n", "\n", @@ -766,11 +769,17 @@ " {channel: z_start_pos for channel in use_channels}\n", " )\n", " \n", - " \n", " print(f\"Well probing took {time.time() - probing_start_time} sec\")\n", " " ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Z Probing of PlateHolder w/ Pedestal" + ] + }, { "cell_type": "code", "execution_count": null, From 935c6f3c7e00cb3e424cc266eb3e03097b6a0b22 Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Wed, 28 Jan 2026 19:02:01 +0000 Subject: [PATCH 5/7] completed v1, complete run < 20 min --- .../biorad_96_wellplate_200uL_Vb.png | Bin 0 -> 141108 bytes .../star_automated_plate_definition.ipynb | 868 ------- .../star_semiautomated_plate_definition.ipynb | 1985 +++++++++++++++++ 3 files changed, 1985 insertions(+), 868 deletions(-) create mode 100644 docs/cookbook/assets/star_semiautomated_plate_definition/biorad_96_wellplate_200uL_Vb.png delete mode 100644 docs/cookbook/star_automated_plate_definition.ipynb create mode 100644 docs/cookbook/star_semiautomated_plate_definition.ipynb diff --git a/docs/cookbook/assets/star_semiautomated_plate_definition/biorad_96_wellplate_200uL_Vb.png b/docs/cookbook/assets/star_semiautomated_plate_definition/biorad_96_wellplate_200uL_Vb.png new file mode 100644 index 0000000000000000000000000000000000000000..4de1fb7f449423e508b279f2a53177dd0c8b7a51 GIT binary patch literal 141108 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�-W4-YRw6>^z= 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 b?;gm|%9A9s`hEp\n", - " \n", - " Your browser does not support the video tag.\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Protocol Mode" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "protocol_mode = \"simulation\" # \"execution\" or \"simulation\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "## Import Statements" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Non-PLR Dependencies\n", - "\n", - "None" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Machine & Visualizer" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2\n", - " \n", - "import random \n", - "import time\n", - "\n", - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.resources.hamilton import STARLetDeck\n", - "from pylabrobot.visualizer.visualizer import Visualizer\n", - "from pylabrobot.utils import chunk_list\n", - "\n", - "if protocol_mode == \"execution\":\n", - "\n", - " from pylabrobot.liquid_handling.backends import STARBackend\n", - "\n", - " star = STARBackend()\n", - "\n", - "elif protocol_mode == \"simulation\":\n", - "\n", - " from pylabrobot.liquid_handling.backends.hamilton.STAR_chatterbox import STARChatterboxBackend\n", - " \n", - " star = STARChatterboxBackend()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Required Resources" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " hamilton_mfx_carrier_L5_base,\n", - " hamilton_mfx_plateholder_DWP_metal_tapped,\n", - " hamilton_mfx_plateholder_DWP_flat,\n", - " Coordinate\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Create Initial Plate Definition" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources.plate import Plate\n", - "from pylabrobot.resources.utils import create_ordered_items_2d\n", - "from pylabrobot.resources.well import (\n", - " CrossSectionType,\n", - " Well,\n", - " WellBottomType,\n", - ")\n", - "\n", - "def biorad_96_wellplate_200uL_Vb(name: str, with_lid: bool = False) -> Plate:\n", - " \"\"\"Bio-rad cat. no.: HSP9601 (50/package), HSP9601B (8*50/package).\n", - "\n", - " Bio-rad plate with 96-wells (V-bottoms).\n", - " \"The patented rigid 2-component design is specifically engineered to\n", - " withstand the stresses of thermal cycling.\" -> excellent for automation!\n", - "\n", - " - Colour: white shell/clear well\n", - " - alternative cat. no.:\n", - " - red shell (HSP-9611)\n", - " - yellow shell (HSP-9621)\n", - " - blue shell (HSP-9631)\n", - " - green shell (HSP-9641)\n", - " - black shell (HSP-9661)\n", - " - Material: Polypropylene\n", - " - Cleanliness: \"Certified to be free of DNase, RNase, and human genomic DNA\"\n", - " - Total volume/well = 200 uL\n", - " - URL: https://www.bio-rad.com/en-uk/sku/HSP9601-hard-shell-96-well-pcr-plates-\n", - " low-profile-thin-wall-skirted-white-clear?ID=HSP9601\n", - " - technical drawing: ./techical_drawings/biorad_96_wellplate_200uL_Vb.png\n", - " \"\"\"\n", - " well_diameter = 5.46 # mm\n", - " return Plate(\n", - " name=name,\n", - " size_x=127.76,\n", - " size_y=85.48,\n", - " size_z=16.06,\n", - " lid=None,\n", - " model=biorad_96_wellplate_200uL_Vb.__name__,\n", - " ordered_items=create_ordered_items_2d(\n", - " Well,\n", - " num_items_x=12,\n", - " num_items_y=8,\n", - " dx=11.65,\n", - " dy=8.51,\n", - " dz=1.1,\n", - " item_dx=9.0,\n", - " item_dy=9.0,\n", - " size_x=well_diameter,\n", - " size_y=well_diameter,\n", - " size_z=14.4,\n", - " bottom_type=WellBottomType.V,\n", - " material_z_thickness=0.65,\n", - " cross_section_type=CrossSectionType.CIRCLE,\n", - " compute_volume_from_height=None,\n", - " compute_height_from_volume=None,\n", - " ),\n", - " )\n", - "\n", - "plate_definition_function = biorad_96_wellplate_200uL_Vb" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Instantiate Frontend & Connect to Machine" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Websocket server started at http://127.0.0.1:2123\n", - "File server started at http://127.0.0.1:1339 . Open this URL in your browser.\n", - "C0CDid0001\n" - ] - } - ], - "source": [ - "deck = STARLetDeck()\n", - "lh = LiquidHandler(backend=star, deck=deck)\n", - "\n", - "await lh.setup()\n", - "\n", - "vis = Visualizer(resource=lh)\n", - "await vis.setup()\n", - "\n", - "await star.disable_cover_control() # 😈" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Configure Deck Layout" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "# Setup MFX Carrier with DWP PlateHolder that has a pedestal\n", - "\n", - "plateholder_pedestal_0 = hamilton_mfx_plateholder_DWP_metal_tapped(\n", - " name=f\"plateholder_pedestal_0\"\n", - ")\n", - "\n", - "mfx_carrier_0 = hamilton_mfx_carrier_L5_base(\n", - " name=\"mfx_carrier_0\",\n", - " modules={0: plateholder_pedestal_0}\n", - ")\n", - "\n", - "deck.assign_child_resource(mfx_carrier_0, rails=1)\n", - "\n", - "# Setup MFX Carrier with DWP PlateHolder that has a flat bottom\n", - "\n", - "plateholder_flat_0 = hamilton_mfx_plateholder_DWP_flat(name=f\"plateholder_flat_0\")\n", - "\n", - "mfx_carrier_1 = hamilton_mfx_carrier_L5_base(\n", - " name=\"mfx_carrier_1\",\n", - " modules={0: plateholder_flat_0}\n", - ")\n", - "\n", - "pcr_plate_0 = plate_definition_function(name=\"pcr_plate_0\")\n", - "\n", - "mfx_carrier_1[0] = pcr_plate_0 \n", - "\n", - "deck.assign_child_resource(mfx_carrier_1, rails=8)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---\n", - "## Execution" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Store Coordinates to Probe on Flat" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "def generate_channel_wells(plate, channels_used):\n", - " \"\"\"\n", - " Generate a list of wells for multi-channel pipetting with evenly spaced rows.\n", - " \n", - " Returns:\n", - " List of well names (e.g., [\"A1\", \"C1\", \"E1\", \"H1\", \"A2\", \"C2\", ...])\n", - " \"\"\"\n", - " num_rows, num_cols = plate.num_items_y, plate.num_items_x\n", - " \n", - " # Generate evenly spaced row indices\n", - " row_indices = [round(i * (num_rows - 1) / (channels_used - 1)) for i in range(channels_used)]\n", - " \n", - " # Generate well names: all columns, with selected rows per column\n", - " return [f\"{chr(ord('A') + row)}{col}\" \n", - " for col in range(1, num_cols + 1) \n", - " for row in row_indices]" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "n_channels = 3\n", - "wells_to_probe = generate_channel_wells(pcr_plate_0, channels_used=n_channels)\n", - "\n", - "positions_to_probe_flat = [\n", - " well.get_absolute_location(\"c\", \"c\", \"top\")\n", - " for well in pcr_plate_0.children\n", - " if well.get_identifier() in wells_to_probe\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Move Plate to PlateHolder with Pedestal" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0TTid0002tt01tf1tl0519tv03600tg2tu0\n", - "C0TPid0003xp07854 07854 07854 00000&yp5286 5196 5106 0000&tm1 1 1 0&tt01tp1830tz1750th2450td0\n", - "C0ZTid0004xs07975xd0ya1250yb1070pa07pb08tp2350tz2250th2800tt14\n" - ] - } - ], - "source": [ - "teaching_tip_rack = lh.deck.get_resource(\"teaching_tip_rack\")\n", - "await lh.pick_up_tips(teaching_tip_rack[\"A1:C1\"], use_channels=[0,1,2,3,4,5][:n_channels])\n", - "\n", - "await star.pick_up_core_gripper_tools(front_channel=7)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0ZPid0005xs03254xd0yj1142yv0050zj1881zy0500yo0885yg0825yw40th2800te2800\n", - "C0ZRid0006xs01679xd0yj1147zj1929zi000zy0500yo0885th2800te2800\n", - "False\n" - ] - } - ], - "source": [ - "move_target = mfx_carrier_0[0]\n", - "\n", - "if protocol_mode == \"simulation\":\n", - " time.sleep(2)\n", - " \n", - "await lh.move_plate(\n", - " plate=pcr_plate_0,\n", - " to=move_target,\n", - " use_arm=\"core\",\n", - " pickup_distance_from_top=6,\n", - " core_grip_strength=40,\n", - " return_core_gripper=False,\n", - ")\n", - "\n", - "if protocol_mode == \"execution\":\n", - " # \"smart\" command, will ask operator for input if it cannot find plate in\n", - " # move_target location place into condition for simulation mode\n", - "\n", - " # (1) check transfer success, (2) push plate flush\n", - " await star.core_check_resource_exists_at_location_center(\n", - " location=pcr_plate_0.get_absolute_location(),\n", - " resource=pcr_plate_0,\n", - " gripper_y_margin=9,\n", - " enable_recovery=True,\n", - " audio_feedback=False,\n", - " )\n", - "\n", - "print(star.core_parked)\n", - "\n", - "if protocol_mode == \"simulation\":\n", - " time.sleep(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Store Coordinates to Probe on Pedestal" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "positions_to_probe_pedestal = [\n", - " well.get_absolute_location(\"c\", \"c\", \"top\")\n", - " for well in pcr_plate_0.children\n", - " if well.get_identifier() in wells_to_probe\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Probe True PlateHolder Origin" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "plate_holder_flat_child_coord = mfx_carrier_1[0].get_absolute_location()+mfx_carrier_1[0].child_location\n", - "\n", - "test_location_w_offset = plate_holder_flat_child_coord + Coordinate(x=3, y=3, z=0)" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0ZAid0023\n", - "C0JPid0024pn01\n", - "C0JXid0025xs02645\n", - "moving channel 0 to y: 74.5\n", - "C0KZid0026pn01zj1795\n", - "C0ZAid0027\n" - ] - } - ], - "source": [ - "channel_idx = 0\n", - "\n", - "# Prepare Z position of X-Y probing\n", - "await star.move_all_channels_in_z_safety()\n", - "await star.prepare_for_manual_channel_operation(channel=channel_idx)\n", - "\n", - "await star.move_channel_x(x=test_location_w_offset.x, channel=channel_idx)\n", - "await star.move_channel_y(y= test_location_w_offset.y, channel=channel_idx)\n", - "\n", - "probed_z_position = await star.clld_probe_z_height_using_channel(\n", - " channel_idx=channel_idx,\n", - " channel_speed=6.0, start_pos_search=test_location_w_offset.z+20,\n", - " detection_edge=5, post_detection_dist=5.0,\n", - " move_channels_to_safe_pos_after=False\n", - " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.z\n", - "await star.move_channel_z(z=probed_z_position+1.5, channel=channel_idx)\n", - "\n", - "# X-Y Probing\n", - "offset_n_replicates = 3\n", - "\n", - "front_pos_list = []\n", - "for x in range(offset_n_replicates):\n", - " probed_front_position = await star.clld_probe_y_position_using_channel(\n", - " channel_idx=channel_idx,\n", - " probing_direction=\"forward\",\n", - " channel_speed=3,\n", - " post_detection_dist=3.0,\n", - " end_pos_search=test_location_w_offset.y-5\n", - " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.y\n", - " front_pos_list.append(probed_front_position)\n", - "\n", - "left_pos_list =[]\n", - "for x in range(offset_n_replicates):\n", - " probed_left_position = await star.clld_probe_x_position_using_channel(\n", - " channel_idx=channel_idx,\n", - " probing_direction=\"left\",\n", - " post_detection_dist=1.0,\n", - " end_pos_search=test_location_w_offset.x-5\n", - " ) if protocol_mode == \"execution\" else plate_holder_flat_child_coord.x\n", - " left_pos_list.append(probed_left_position)\n", - "\n", - "await star.move_all_channels_in_z_safety()\n", - "\n", - "true_plate_holder_flat_child_coord = Coordinate(\n", - " x=round(sum(left_pos_list)/len(left_pos_list), 1),\n", - " y=round(sum(front_pos_list)/len(front_pos_list), 1),\n", - " z=probed_z_position,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Move Plate back onto tapped PlateHolder" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0ZPid0028xs01679xd0yj1147yv0050zj1929zy0500yo0885yg0825yw40th2800te2800\n", - "C0ZRid0029xs03254xd0yj1142zj1881zi000zy0500yo0885th2800te2800\n" - ] - } - ], - "source": [ - "move_target = mfx_carrier_1[0]\n", - "\n", - "await lh.move_plate(\n", - " plate=pcr_plate_0,\n", - " to=move_target,\n", - " use_arm=\"core\",\n", - " pickup_distance_from_top=6,\n", - " core_grip_strength=40,\n", - " return_core_gripper=False,\n", - ")\n", - "\n", - "if protocol_mode == \"execution\":\n", - "\n", - " await star.core_check_resource_exists_at_location_center(\n", - " location=pcr_plate_0.get_absolute_location(),\n", - " resource=pcr_plate_0,\n", - " gripper_y_margin=9,\n", - " enable_recovery=True,\n", - " audio_feedback=False,\n", - " )" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Interactive X-Y Verification of Bottom-Left Well" - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0ZAid0130\n", - "C0JPid0131pn01\n" - ] - } - ], - "source": [ - "test_location = pcr_plate_0.column(0)[-1].get_absolute_location(\n", - " x=\"center\",y=\"center\",z=\"top\"\n", - ")\n", - "\n", - "await lh.backend.move_all_channels_in_z_safety()\n", - "\n", - "await lh.backend.prepare_for_manual_channel_operation(channel=channel_idx)" - ] - }, - { - "cell_type": "code", - "execution_count": 60, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0JXid0141xs02759\n", - "275.88, current_dx=11.6\n" - ] - } - ], - "source": [ - "# Position channel with teaching needle above bottom-left well\n", - "test_x = test_location.x + 0.0\n", - "\n", - "await lh.backend.move_channel_x(x=test_x, channel=channel_idx)\n", - "\n", - "current_dx = round(test_x - true_plate_holder_flat_child_coord.x - pcr_plate_0.children[0].get_size_x()/2, 1)\n", - "print(f\"{test_x}, {current_dx=}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "moving channel 0 to y: 82.74\n", - "82.74, current_dy=8.5\n" - ] - } - ], - "source": [ - "test_y = test_location.y + 0.0\n", - "\n", - "await lh.backend.move_channel_y(y=test_y, channel=channel_idx)\n", - "\n", - "current_dy = round(test_y - true_plate_holder_flat_child_coord.y - pcr_plate_0.children[0].get_size_y()/2, 1)\n", - "print(f\"{test_y}, {current_dy=}\")" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C0KZid0136pn01zj1935\n" - ] - }, - { - "data": { - "text/plain": [ - "193.5" - ] - }, - "execution_count": 51, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Only to move the teaching needle into the well for X/Y correction\n", - "test_z = test_location.z + 0.0\n", - "await lh.backend.move_channel_z(z=test_z, channel=channel_idx)\n", - "test_z" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [ - { - "name": "stdin", - "output_type": "stream", - "text": [ - "Have you updated the dx and dy? \n" - ] - } - ], - "source": [ - "final_query = input(f\"Have you updated the dx and dy?\")\n", - "await lh.backend.move_all_channels_in_z_safety()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Z Probing of Wells (on Flat PlateHolder)" - ] - }, - { - "cell_type": "code", - "execution_count": 68, - "metadata": {}, - "outputs": [], - "source": [ - "if protocol_mode == \"execution\":\n", - "\n", - " probing_start_time = time.time()\n", - "\n", - " await lh.backend.move_all_channels_in_z_safety()\n", - " use_channels = list(range(n_channels))\n", - " await lh.backend.prepare_for_manual_channel_operation(channel=use_channels[-1])\n", - " \n", - " plate_on_flat_results = []\n", - " \n", - " for positions in chunk_list(positions_to_probe_flat, chunk_size=n_channels):\n", - " \n", - " x_pos = [coord.x for coord in positions]\n", - " y_pos = [coord.y for coord in positions]\n", - " z_start_pos = positions[0].z+20 # TODO: add size_z measurement\n", - " \n", - " \n", - " await star.move_channel_x(0, x_pos[0])\n", - "\n", - " await star.position_channels_in_y_direction(\n", - " {channel: y for channel, y in zip(use_channels, y_pos)}\n", - " )\n", - " await star.position_channels_in_z_direction(\n", - " {channel: z_start_pos for channel in use_channels}\n", - " )\n", - "\n", - " # Detect well cavity bottoms\n", - " start_pos_search_list = [z_start_pos for x in range(n_channels)]\n", - " measured_well_bottoms_replicate_summary = []\n", - " tech_n_replicates = 3 \n", - "\n", - " for n in range(tech_n_replicates):\n", - "\n", - " # Parallelise ztouch probing\n", - " # taking care to avoid parameters which require the unparallelizable C0 command module\n", - " measured_well_bottoms = await asyncio.gather(\n", - " *[\n", - " star.ztouch_probe_z_height_using_channel(\n", - " channel_idx=channel_idx,\n", - " tip_len=lh.head[channel_idx].get_tip().total_tip_length, # must be declared to avoid STAR C0 module\n", - " channel_speed=10.0,\n", - " start_pos_search=start_pos_z,\n", - " lowest_immers_pos=probed_z_position-2,\n", - " detection_limiter_in_PWM=0,\n", - " push_down_force_in_PWM=0,\n", - " post_detection_dist=0.0, # must be 0 to avoid STAR C0 module\n", - " move_channels_to_safe_pos_after=False # must be False to avoid STAR C0 module\n", - " )\n", - " for channel_idx, start_pos_z in zip(\n", - " use_channels, start_pos_search_list\n", - " )\n", - " ]\n", - " )\n", - " measured_well_bottoms_replicate_summary.append(measured_well_bottoms)\n", - "\n", - " start_pos_search_list = [x+3 for x in measured_well_bottoms] # Accelerate\n", - "\n", - " mean_measured_well_bottoms = [\n", - " sum(replicates)/tech_n_replicates\n", - " for replicates in list(zip(*measured_well_bottoms_replicate_summary))\n", - " ]\n", - " plate_on_flat_results.append(mean_measured_well_bottoms)\n", - " \n", - " # SAFETY: move UP before starting next loop!\n", - " await star.position_channels_in_z_direction(\n", - " {channel: z_start_pos for channel in use_channels}\n", - " )\n", - " \n", - " print(f\"Well probing took {time.time() - probing_start_time} sec\")\n", - " " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Z Probing of PlateHolder w/ Pedestal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Move Plate to PlateHolder with Pedestal" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Z Probing of Flat PlateHolder" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Z Probing of Wells (on Pedestal)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Update Plate Definition" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/cookbook/star_semiautomated_plate_definition.ipynb b/docs/cookbook/star_semiautomated_plate_definition.ipynb new file mode 100644 index 00000000000..0d52434e844 --- /dev/null +++ b/docs/cookbook/star_semiautomated_plate_definition.ipynb @@ -0,0 +1,1985 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Semi-Automated Plate Definition Generation\n", + "\n", + "- tags: #platedefinition #resourcemovement #plateadapter #hamiltonstar\n", + "- Last updated: 2026-01-26" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Prerequisites\n", + "\n", + "- Machines used:\n", + " - Hamilton STAR\n", + "- Non-PLR dependencies: None \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preview of Machine Behvaiour" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Master Information\n", + "\n", + "i.e. declare parameters that might change from run to run" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "protocol_mode = \"execution\" # \"execution\" or \"simulation\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Choose how many channels you want to be involed in probing actions (1-6 is acceptable)\n", + "n_channels = 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Import Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Non-PLR Dependencies\n", + "\n", + "None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Machine & Visualizer" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + " \n", + "import random \n", + "import time\n", + "import asyncio\n", + " \n", + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.resources.hamilton import STARLetDeck, STARDeck\n", + "from pylabrobot.visualizer.visualizer import Visualizer\n", + "from pylabrobot.utils import chunk_list\n", + "\n", + "if protocol_mode == \"execution\":\n", + "\n", + " from pylabrobot.liquid_handling.backends import STARBackend\n", + "\n", + " star = STARBackend()\n", + "\n", + "elif protocol_mode == \"simulation\":\n", + "\n", + " from pylabrobot.liquid_handling.backends.hamilton.STAR_chatterbox import STARChatterboxBackend\n", + " \n", + " star = STARChatterboxBackend()\n", + "\n", + "shutdown_executed = False" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Required Resources" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " hamilton_mfx_carrier_L5_base,\n", + " hamilton_mfx_plateholder_DWP_metal_tapped,\n", + " hamilton_mfx_plateholder_DWP_flat,\n", + " Coordinate\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Create Initial Plate Definition\n", + "\n", + "Ideally, the manufactuer publishes technical drawings which act as a starting material that requires verification in physical reality + measurements of missing parameters:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![sd](./assets/star_semiautomated_plate_definition/biorad_96_wellplate_200uL_Vb.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{note}\n", + "PyLabRobot always uses \"front-left-bottom\" references!\n", + "This means that every technical drawing measurement has to be converted accordingly:\n", + "e.g. `dx = 14.38 (left to center_x) - 5.46 / 2 (radius of well)`.\n", + "\n", + "Furthermore, this is a special plate: its well spacing is symmetric in both the x and y dimensions!\n", + "This is not common, and you should always perform a multiple sanity checks to ensure you are not taking measurements from the incorrect direction: e.g. `127.76 - (14.38)*2 = 99.0` mm -> 1st column center_x and 12th column center_x are exactly 99.0 mm apart as expected based on the ANSI/SLAS 4-2004 standard and an 11 column distance -> the plate is symmetric across its welll spacing in the `x` dimension\n", + "```\n", + "\n", + "```{warning}\n", + "It is now clear whether `16.06-14.81 = 1.25` refers to the `dz` with or without the `material_z_thickness`.\n", + "This means without measurement we risk crashing pipette heads into the well cavity bottom - if a plate holder with a pedestal (i.e. central elevation) is used.\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.plate import Plate\n", + "from pylabrobot.resources.utils import create_ordered_items_2d\n", + "from pylabrobot.resources.well import (\n", + " CrossSectionType,\n", + " Well,\n", + " WellBottomType,\n", + ")\n", + "\n", + "def biorad_96_wellplate_200uL_Vb(name: str, with_lid: bool = False) -> Plate:\n", + " \"\"\"Bio-rad cat. no.: HSP9601 (50/package), HSP9601B (8*50/package).\n", + "\n", + " Bio-rad plate with 96-wells (V-bottoms).\n", + " \"The patented rigid 2-component design is specifically engineered to\n", + " withstand the stresses of thermal cycling.\" -> excellent for automation!\n", + "\n", + " - Colour: white shell/clear well\n", + " - alternative cat. no.:\n", + " - red shell (HSP-9611)\n", + " - yellow shell (HSP-9621)\n", + " - blue shell (HSP-9631)\n", + " - green shell (HSP-9641)\n", + " - black shell (HSP-9661)\n", + " - Material: Polypropylene\n", + " - Cleanliness: \"Certified to be free of DNase, RNase, and human genomic DNA\"\n", + " - Total volume/well = 200 uL\n", + " - URL: https://www.bio-rad.com/en-uk/sku/HSP9601-hard-shell-96-well-pcr-plates-\n", + " low-profile-thin-wall-skirted-white-clear?ID=HSP9601\n", + " - technical drawing: ./assets/star_semiautomated_plate_definition/biorad_96_wellplate_200uL_Vb.png\n", + " \"\"\"\n", + " well_diameter = 5.46 # mm\n", + " return Plate(\n", + " name=name,\n", + " size_x=127.76,\n", + " size_y=85.48,\n", + " size_z=16.06,\n", + " lid=None,\n", + " model=biorad_96_wellplate_200uL_Vb.__name__,\n", + " ordered_items=create_ordered_items_2d(\n", + " Well,\n", + " num_items_x=12,\n", + " num_items_y=8,\n", + " dx=round(14.38-well_diameter/2, 2), # symmetric well spacing across X centerline\n", + " dy=round(11.24-well_diameter/2, 2), # symmetric well spacing across Y centerline\n", + " dz=1.1, # initial guess\n", + " item_dx=9.0,\n", + " item_dy=9.0,\n", + " size_x=well_diameter,\n", + " size_y=well_diameter,\n", + " size_z=14.4,\n", + " bottom_type=WellBottomType.V,\n", + " material_z_thickness=0.65, # initial guess\n", + " cross_section_type=CrossSectionType.CIRCLE,\n", + " compute_volume_from_height=None,\n", + " compute_height_from_volume=None,\n", + " ),\n", + " )\n", + "\n", + "# Modify the above if you want to change/create your own plate definition\n", + "# then simply change this variable which is used for the rest of this recipe\n", + "plate_definition_function = biorad_96_wellplate_200uL_Vb" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "11.65" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "14.38-5.46/2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Utility Functions\n", + "\n", + "Inevitably we have to create specialised solutions for specialised problems.\n", + "Python makes this easy by quickly generating utility functions.\n", + "These can be stored with you 'automated Protocol' script or, if used repeadetly be added to your internal repository, or even added to the `pylabrobot.utils` package." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_channel_wells(plate: Plate, channels_used: int):\n", + " \"\"\"\n", + " Generate a list of wells for multi-channel pipetting with evenly spaced rows.\n", + " \n", + " Returns:\n", + " List of well names:\n", + " e.g., generate_channel_wells(test_plate_0, 4) -> [\"A1\", \"C1\", \"E1\", \"H1\", \"A2\", \"C2\", ...])\n", + " \"\"\"\n", + " num_rows, num_cols = plate.num_items_y, plate.num_items_x\n", + " \n", + " # Generate evenly spaced row indices\n", + " row_indices = [round(i * (num_rows - 1) / (channels_used - 1)) for i in range(channels_used)]\n", + " \n", + " # Generate well names: all columns, with selected rows per column\n", + " return [f\"{chr(ord('A') + row)}{col}\" \n", + " for col in range(1, num_cols + 1) \n", + " for row in row_indices]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instantiate Frontend & Connect to Machine" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-01-28 18:23:49,585 - pylabrobot.io.usb - INFO - Finding USB device...\n", + "2026-01-28 18:23:49,618 - pylabrobot.io.usb - INFO - Found USB device.\n", + "2026-01-28 18:23:49,621 - pylabrobot.io.usb - INFO - Found endpoints. \n", + "Write:\n", + " ENDPOINT 0x2: Bulk OUT ===============================\n", + " bLength : 0x7 (7 bytes)\n", + " bDescriptorType : 0x5 Endpoint\n", + " bEndpointAddress : 0x2 OUT\n", + " bmAttributes : 0x2 Bulk\n", + " wMaxPacketSize : 0x40 (64 bytes)\n", + " bInterval : 0x0 \n", + "Read:\n", + " ENDPOINT 0x81: Bulk IN ===============================\n", + " bLength : 0x7 (7 bytes)\n", + " bDescriptorType : 0x5 Endpoint\n", + " bEndpointAddress : 0x81 IN\n", + " bmAttributes : 0x2 Bulk\n", + " wMaxPacketSize : 0x40 (64 bytes)\n", + " bInterval : 0x0\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2121\n", + "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" + ] + }, + { + "data": { + "text/plain": [ + "'C0CDid0016er00/00'" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "deck = STARDeck()\n", + "lh = LiquidHandler(backend=star, deck=deck)\n", + "\n", + "await lh.setup()\n", + "\n", + "vis = Visualizer(resource=lh)\n", + "await vis.setup()\n", + "\n", + "await star.disable_cover_control() # 😈" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configure Deck Layout" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup MFX Carrier with DWP PlateHolder that has a pedestal\n", + "\n", + "plateholder_pedestal_0 = hamilton_mfx_plateholder_DWP_metal_tapped(\n", + " name=f\"plateholder_pedestal_0\"\n", + ")\n", + "\n", + "mfx_carrier_0 = hamilton_mfx_carrier_L5_base(\n", + " name=\"mfx_carrier_0\",\n", + " modules={0: plateholder_pedestal_0}\n", + ")\n", + "\n", + "deck.assign_child_resource(mfx_carrier_0, rails=10)\n", + "\n", + "# Setup MFX Carrier with DWP PlateHolder that has a flat bottom\n", + "\n", + "plateholder_flat_0 = hamilton_mfx_plateholder_DWP_flat(name=f\"plateholder_flat_0\")\n", + "\n", + "mfx_carrier_1 = hamilton_mfx_carrier_L5_base(\n", + " name=\"mfx_carrier_1\",\n", + " modules={0: plateholder_flat_0}\n", + ")\n", + "\n", + "deck.assign_child_resource(mfx_carrier_1, rails=17)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening in existing browser session.\n" + ] + } + ], + "source": [ + "# Add plate onto PlateHolder with the pedestal\n", + "\n", + "test_plate_0 = plate_definition_function(name=\"test_plate_0\")\n", + "\n", + "mfx_carrier_0[0] = test_plate_0 " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Step Calculations" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['A1', 'E1', 'H1', 'A2', 'E2', 'H2', 'A3', 'E3', 'H3', 'A4']\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[289359:289359:0100/000000.565807:ERROR:content/zygote/zygote_linux.cc:673] write: Broken pipe (32)\n" + ] + } + ], + "source": [ + "wells_to_probe = generate_channel_wells(test_plate_0, channels_used=n_channels)\n", + "\n", + "if len(wells_to_probe) > 10: # You might want to use this recipe to create a 6-wellplate definition\n", + " print(wells_to_probe[:10])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---\n", + "## Execution" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "recipe_execution_start_time = time.time()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pickup Teaching Needles & CORE Grippers" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'C0ZTid0019er00/00sx000 000 000 000 000 000 000 000sg000 000 000 000 000 000 486 509'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "teaching_tip_rack = lh.deck.get_resource(\"teaching_tip_rack\")\n", + "await lh.pick_up_tips(teaching_tip_rack[\"A1:C1\"], use_channels=[0,1,2,3,4,5][:n_channels])\n", + "\n", + "await star.pick_up_core_gripper_tools(front_channel=7)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1) Focus: Flat PlateHolder -> `dz` + `material_z_thickness`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 1.a.) Probe True PlateHolder Origin" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "plate_holder_pedestal_child_coord = mfx_carrier_0[0].get_absolute_location()+mfx_carrier_0[0].child_location\n", + "\n", + "plate_holder_flat_child_coord = mfx_carrier_1[0].get_absolute_location()+mfx_carrier_1[0].child_location\n", + "\n", + "test_location_w_offset = plate_holder_flat_child_coord + Coordinate(x=3, y=3, z=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "from tqdm.auto import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "2a5e625b11ed471399ef73d2d6aedecb", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/3 [00:00 `material_z_thickness`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.a) Store Coordinates to Probe on Pedestal" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "positions_to_probe_pedestal = [\n", + " well.get_absolute_location(\"c\", \"c\", \"top\") + Coordinate(x_offset, y_offset, 0)\n", + " for well in test_plate_0.children\n", + " if well.get_identifier() in wells_to_probe\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.b) Z Probing of Wells (on Pedestal)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "d33a4d7487194f06a258e474b5f9b894", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + " 0%| | 0/12 [00:00 Date: Wed, 28 Jan 2026 23:50:46 +0000 Subject: [PATCH 6/7] tweaks --- .../star_semiautomated_plate_definition.ipynb | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/docs/cookbook/star_semiautomated_plate_definition.ipynb b/docs/cookbook/star_semiautomated_plate_definition.ipynb index 0d52434e844..2efeaea88f2 100644 --- a/docs/cookbook/star_semiautomated_plate_definition.ipynb +++ b/docs/cookbook/star_semiautomated_plate_definition.ipynb @@ -7,7 +7,7 @@ "# Semi-Automated Plate Definition Generation\n", "\n", "- tags: #platedefinition #resourcemovement #plateadapter #hamiltonstar\n", - "- Last updated: 2026-01-26" + "- Last updated: 2026-01-29" ] }, { @@ -715,7 +715,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -760,7 +760,7 @@ " )\n", " probed_plate_top_results.append(replicate_probed_plate_top)\n", "\n", - "await lh.backend.move_all_channels_in_z_safety()\n", + "await star.move_all_channels_in_z_safety()\n", "\n", "\n", "mean_probed_plate_top = sum(probed_plate_top_results)/len(probed_plate_top_results)\n", @@ -832,7 +832,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -840,9 +840,9 @@ " x=\"center\",y=\"center\",z=\"top\"\n", ") + offset\n", "\n", - "await lh.backend.move_all_channels_in_z_safety()\n", + "await star.move_all_channels_in_z_safety()\n", "\n", - "await lh.backend.prepare_for_manual_channel_operation(channel=channel_idx)" + "await star.prepare_for_manual_channel_operation(channel=channel_idx)" ] }, { @@ -854,7 +854,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -870,7 +870,7 @@ "x_offset = 0.0\n", "test_x = test_location.x + x_offset\n", "\n", - "await lh.backend.move_channel_x(x=test_x, channel=channel_idx)\n", + "await star.move_channel_x(x=test_x, channel=channel_idx)\n", "\n", "current_dx = round(\n", " test_x - true_plate_holder_flat_child_coord.x - test_plate_0.children[0].get_size_x()/2,\n", @@ -887,7 +887,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -903,7 +903,7 @@ "y_offset = 0.0\n", "test_y = test_location.y + y_offset\n", "\n", - "await lh.backend.move_channel_y(y=test_y, channel=channel_idx)\n", + "await star.move_channel_y(y=test_y, channel=channel_idx)\n", "\n", "current_dy = round(\n", " test_y - true_plate_holder_flat_child_coord.y - test_plate_0.children[0].get_size_y()/2,\n", @@ -921,7 +921,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -936,8 +936,8 @@ } ], "source": [ - "# Only to move the teaching needle into the well for X/Y correction\n", - "test_z = test_location.z + 2.0\n", + "# Only to move the teaching needle into the well for visual X/Y correction\n", + "test_z = mean_plate_holder_flat_top_surface + measured_plate_size_z + 5.0\n", "await lh.backend.move_channel_z(z=test_z, channel=channel_idx)\n", "test_z" ] @@ -958,7 +958,7 @@ "metadata": {}, "outputs": [ { - "name": "stdin", + "name": "stdout", "output_type": "stream", "text": [ "Have you updated the dx and dy? \n" @@ -1753,7 +1753,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -1765,7 +1765,7 @@ } ], "source": [ - "print(f\"Required pdates to dx = {x_offset}, and dy = {y_offset}\")" + "print(f\"Required updates to dx = {x_offset}, and dy = {y_offset}\")" ] }, { @@ -1802,7 +1802,12 @@ "If there is a deviation in the plate height, check that whether the wells protrude above the plate.\n", "This is a common plate feature that facilitates heat sealing.\n", "\n", - "Technical drawings often only report the combined plate and well size_z." + "Technical drawings often only report the combined plate and well size_z.\n", + "\n", + "```{note}\n", + "For PyLabRobot, you have to define `plate.size_z` as *just* the pure plate height, i.e. without the protruding wells.\n", + "This ensures `pickup_distance_from_top=6` will correctly pick up a plate 6 mm from its top!\n", + "```" ] }, { From d9b94a1a1d6e4f1afc71563b61363631b8e4603d Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Thu, 29 Jan 2026 10:30:16 +0000 Subject: [PATCH 7/7] move shutdown flag instantiation to setup() --- docs/cookbook/star_semiautomated_plate_definition.ipynb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/cookbook/star_semiautomated_plate_definition.ipynb b/docs/cookbook/star_semiautomated_plate_definition.ipynb index 2efeaea88f2..62c0fc2debe 100644 --- a/docs/cookbook/star_semiautomated_plate_definition.ipynb +++ b/docs/cookbook/star_semiautomated_plate_definition.ipynb @@ -118,9 +118,7 @@ "\n", " from pylabrobot.liquid_handling.backends.hamilton.STAR_chatterbox import STARChatterboxBackend\n", " \n", - " star = STARChatterboxBackend()\n", - "\n", - "shutdown_executed = False" + " star = STARChatterboxBackend()" ] }, { @@ -368,7 +366,8 @@ "vis = Visualizer(resource=lh)\n", "await vis.setup()\n", "\n", - "await star.disable_cover_control() # 😈" + "await star.disable_cover_control() # 😈\n", + "shutdown_executed = False" ] }, {