Files
gh-k-dense-ai-claude-scient…/skills/pylabrobot/references/hardware-backends.md
2025-11-30 08:30:10 +08:00

13 KiB

Hardware Backends in PyLabRobot

Overview

PyLabRobot uses a backend abstraction system that allows the same protocol code to run on different liquid handling robots and platforms. Backends handle device-specific communication while the LiquidHandler frontend provides a unified interface.

Backend Architecture

How Backends Work

  1. Frontend: LiquidHandler class provides high-level API
  2. Backend: Device-specific class handles hardware communication
  3. Protocol: Same code works across different backends
# Same protocol code
await lh.pick_up_tips(tip_rack["A1"])
await lh.aspirate(plate["A1"], vols=100)
await lh.dispense(plate["A2"], vols=100)
await lh.drop_tips()

# Works with any backend (STAR, Opentrons, simulation, etc.)

Backend Interface

All backends inherit from LiquidHandlerBackend and implement:

  • setup(): Initialize connection to hardware
  • stop(): Close connection and cleanup
  • Device-specific command methods (aspirate, dispense, etc.)

Supported Backends

Hamilton STAR (Full Support)

The Hamilton STAR and STARlet liquid handling robots have full PyLabRobot support.

Setup:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import STAR
from pylabrobot.resources import STARLetDeck

# Create STAR backend
backend = STAR()

# Initialize liquid handler
lh = LiquidHandler(backend=backend, deck=STARLetDeck())
await lh.setup()

Platform Support:

  • Windows
  • macOS
  • Linux
  • Raspberry Pi

Communication:

  • USB connection to robot
  • Direct firmware commands
  • No Hamilton software required

Features:

  • Full liquid handling operations
  • CO-RE tip support
  • 96-channel head support (if equipped)
  • Temperature control
  • Carrier and rail-based positioning

Deck Types:

from pylabrobot.resources import STARLetDeck, STARDeck

# For STARlet (smaller deck)
deck = STARLetDeck()

# For STAR (full deck)
deck = STARDeck()

Example:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import STAR
from pylabrobot.resources import STARLetDeck, TIP_CAR_480_A00, Cos_96_DW_1mL

# Initialize
lh = LiquidHandler(backend=STAR(), deck=STARLetDeck())
await lh.setup()

# Define resources
tip_rack = TIP_CAR_480_A00(name="tips")
plate = Cos_96_DW_1mL(name="plate")

# Assign to rails
lh.deck.assign_child_resource(tip_rack, rails=1)
lh.deck.assign_child_resource(plate, rails=10)

# Execute protocol
await lh.pick_up_tips(tip_rack["A1"])
await lh.transfer(plate["A1"], plate["A2"], vols=100)
await lh.drop_tips()

await lh.stop()

Opentrons OT-2 (Supported)

The Opentrons OT-2 is supported through the Opentrons HTTP API.

Setup:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import OpentronsBackend
from pylabrobot.resources import OTDeck

# Create Opentrons backend (requires robot IP address)
backend = OpentronsBackend(host="192.168.1.100")  # Replace with your robot's IP

# Initialize liquid handler
lh = LiquidHandler(backend=backend, deck=OTDeck())
await lh.setup()

Platform Support:

  • Any platform with network access to OT-2

Communication:

  • HTTP API over network
  • Requires robot IP address
  • No Opentrons app required

Features:

  • 8-channel pipette support
  • Single-channel pipette support
  • Standard OT-2 deck layout
  • Coordinate-based positioning

Limitations:

  • Uses older Opentrons HTTP API
  • Some features may be limited compared to STAR

Example:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import OpentronsBackend
from pylabrobot.resources import OTDeck

# Initialize with robot IP
lh = LiquidHandler(
    backend=OpentronsBackend(host="192.168.1.100"),
    deck=OTDeck()
)
await lh.setup()

# Load deck layout
lh.deck = Deck.load_from_json_file("opentrons_layout.json")

# Execute protocol
await lh.pick_up_tips(tip_rack["A1"])
await lh.transfer(plate["A1"], plate["A2"], vols=100)
await lh.drop_tips()

await lh.stop()

Tecan EVO (Work in Progress)

Support for Tecan EVO liquid handling robots is under development.

Current Status:

  • Work-in-progress
  • Basic commands may be available
  • Check documentation for current feature support

Setup (when available):

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import TecanBackend
from pylabrobot.resources import TecanDeck

backend = TecanBackend()
lh = LiquidHandler(backend=backend, deck=TecanDeck())

Hamilton Vantage (Mostly Supported)

Hamilton Vantage has "mostly" complete support.

Setup:

from pylabrobot.liquid_handling.backends import Vantage
from pylabrobot.resources import VantageDeck

lh = LiquidHandler(backend=Vantage(), deck=VantageDeck())

Features:

  • Similar to STAR support
  • Some advanced features may be limited

Simulation Backend

ChatterboxBackend (Simulation)

Test protocols without physical hardware using the simulation backend.

Setup:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends.simulation import ChatterboxBackend
from pylabrobot.resources import STARLetDeck

# Create simulation backend
backend = ChatterboxBackend(num_channels=8)

# Initialize liquid handler
lh = LiquidHandler(backend=backend, deck=STARLetDeck())
await lh.setup()

Features:

  • No hardware required
  • Simulates all liquid handling operations
  • Works with visualizer for real-time feedback
  • Validates protocol logic
  • Tracks tips and volumes

Use Cases:

  • Protocol development and testing
  • Training and education
  • CI/CD pipeline testing
  • Debugging without hardware access

Example:

from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends.simulation import ChatterboxBackend
from pylabrobot.resources import STARLetDeck, TIP_CAR_480_A00, Cos_96_DW_1mL
from pylabrobot.resources import set_tip_tracking, set_volume_tracking

# Enable tracking for simulation
set_tip_tracking(True)
set_volume_tracking(True)

# Initialize with simulation backend
lh = LiquidHandler(
    backend=ChatterboxBackend(num_channels=8),
    deck=STARLetDeck()
)
await lh.setup()

# Define resources
tip_rack = TIP_CAR_480_A00(name="tips")
plate = Cos_96_DW_1mL(name="plate")

lh.deck.assign_child_resource(tip_rack, rails=1)
lh.deck.assign_child_resource(plate, rails=10)

# Set initial volumes
for well in plate.children:
    well.tracker.set_liquids([(None, 200)])

# Run simulated protocol
await lh.pick_up_tips(tip_rack["A1:H1"])
await lh.transfer(plate["A1:H1"], plate["A2:H2"], vols=100)
await lh.drop_tips()

# Check results
print(f"A1 volume: {plate['A1'].tracker.get_volume()} µL")  # 100 µL
print(f"A2 volume: {plate['A2'].tracker.get_volume()} µL")  # 100 µL

await lh.stop()

Switching Backends

Backend-Agnostic Protocols

Write protocols that work with any backend:

def get_backend(robot_type: str):
    """Factory function to create appropriate backend"""
    if robot_type == "star":
        from pylabrobot.liquid_handling.backends import STAR
        return STAR()
    elif robot_type == "opentrons":
        from pylabrobot.liquid_handling.backends import OpentronsBackend
        return OpentronsBackend(host="192.168.1.100")
    elif robot_type == "simulation":
        from pylabrobot.liquid_handling.backends.simulation import ChatterboxBackend
        return ChatterboxBackend()
    else:
        raise ValueError(f"Unknown robot type: {robot_type}")

def get_deck(robot_type: str):
    """Factory function to create appropriate deck"""
    if robot_type == "star":
        from pylabrobot.resources import STARLetDeck
        return STARLetDeck()
    elif robot_type == "opentrons":
        from pylabrobot.resources import OTDeck
        return OTDeck()
    elif robot_type == "simulation":
        from pylabrobot.resources import STARLetDeck
        return STARLetDeck()
    else:
        raise ValueError(f"Unknown robot type: {robot_type}")

# Use in protocol
robot_type = "simulation"  # Change to "star" or "opentrons" as needed
backend = get_backend(robot_type)
deck = get_deck(robot_type)

lh = LiquidHandler(backend=backend, deck=deck)
await lh.setup()

# Protocol code works with any backend
await lh.pick_up_tips(tip_rack["A1"])
await lh.transfer(plate["A1"], plate["A2"], vols=100)
await lh.drop_tips()

Development Workflow

  1. Develop: Write protocol using ChatterboxBackend
  2. Test: Run with visualizer to validate logic
  3. Verify: Test on simulation with real deck layout
  4. Deploy: Switch to hardware backend (STAR, Opentrons)
# Development
lh = LiquidHandler(backend=ChatterboxBackend(), deck=STARLetDeck())

# ... develop protocol ...

# Production (just change backend)
lh = LiquidHandler(backend=STAR(), deck=STARLetDeck())

Backend Configuration

Custom Backend Parameters

Some backends accept configuration parameters:

# Opentrons with custom parameters
backend = OpentronsBackend(
    host="192.168.1.100",
    port=31950  # Default Opentrons API port
)

# ChatterboxBackend with custom channels
backend = ChatterboxBackend(
    num_channels=8  # 8-channel simulation
)

Connection Troubleshooting

Hamilton STAR:

  • Ensure USB cable is connected
  • Check that no other software is using the robot
  • Verify firmware is up to date
  • On macOS/Linux, may need USB permissions

Opentrons OT-2:

  • Verify robot IP address is correct
  • Check network connectivity (ping robot)
  • Ensure robot is powered on
  • Confirm Opentrons app is not blocking API access

General:

  • Use await lh.setup() to test connection
  • Check error messages for specific issues
  • Ensure proper permissions for device access

Backend-Specific Features

Hamilton STAR Specific

# Access backend directly for hardware-specific features
star_backend = lh.backend

# Hamilton-specific commands (if needed)
# Most operations should go through LiquidHandler interface

Opentrons Specific

# Opentrons-specific configuration
ot_backend = lh.backend

# Access OT-2 API directly if needed (advanced)
# Most operations should go through LiquidHandler interface

Best Practices

  1. Abstract Hardware: Write backend-agnostic protocols when possible
  2. Test in Simulation: Always test with ChatterboxBackend first
  3. Factory Pattern: Use factory functions to create backends
  4. Error Handling: Handle connection errors gracefully
  5. Documentation: Document which backends your protocol supports
  6. Configuration: Use config files for backend parameters
  7. Version Control: Track backend versions and compatibility
  8. Cleanup: Always call await lh.stop() to release hardware
  9. Single Connection: Only one program should connect to hardware at a time
  10. Platform Testing: Test on target platform before deployment

Common Patterns

Multi-Backend Support

import asyncio
from typing import Literal

async def run_protocol(
    robot_type: Literal["star", "opentrons", "simulation"],
    visualize: bool = False
):
    """Run protocol on specified backend"""

    # Create backend
    if robot_type == "star":
        from pylabrobot.liquid_handling.backends import STAR
        backend = STAR()
        deck = STARLetDeck()
    elif robot_type == "opentrons":
        from pylabrobot.liquid_handling.backends import OpentronsBackend
        backend = OpentronsBackend(host="192.168.1.100")
        deck = OTDeck()
    elif robot_type == "simulation":
        from pylabrobot.liquid_handling.backends.simulation import ChatterboxBackend
        backend = ChatterboxBackend()
        deck = STARLetDeck()

    # Initialize
    lh = LiquidHandler(backend=backend, deck=deck)
    await lh.setup()

    try:
        # Load deck layout (backend-agnostic)
        # lh.deck = Deck.load_from_json_file(f"{robot_type}_layout.json")

        # Execute protocol (backend-agnostic)
        await lh.pick_up_tips(tip_rack["A1"])
        await lh.transfer(plate["A1"], plate["A2"], vols=100)
        await lh.drop_tips()

        print("Protocol completed successfully!")

    finally:
        await lh.stop()

# Run on different backends
await run_protocol("simulation")      # Test in simulation
await run_protocol("star")            # Run on Hamilton STAR
await run_protocol("opentrons")       # Run on Opentrons OT-2

Additional Resources