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
- Frontend:
LiquidHandlerclass provides high-level API - Backend: Device-specific class handles hardware communication
- 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 hardwarestop(): 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
- Develop: Write protocol using ChatterboxBackend
- Test: Run with visualizer to validate logic
- Verify: Test on simulation with real deck layout
- 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
- Abstract Hardware: Write backend-agnostic protocols when possible
- Test in Simulation: Always test with ChatterboxBackend first
- Factory Pattern: Use factory functions to create backends
- Error Handling: Handle connection errors gracefully
- Documentation: Document which backends your protocol supports
- Configuration: Use config files for backend parameters
- Version Control: Track backend versions and compatibility
- Cleanup: Always call
await lh.stop()to release hardware - Single Connection: Only one program should connect to hardware at a time
- 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
- Backend Documentation: https://docs.pylabrobot.org/user_guide/backends.html
- Supported Machines: https://docs.pylabrobot.org/user_guide/machines.html
- API Reference: https://docs.pylabrobot.org/api/pylabrobot.liquid_handling.backends.html
- GitHub Examples: https://github.com/PyLabRobot/pylabrobot/tree/main/examples