OT-2 Simulator with Visualizer#
This notebook shows how to use the OpentronsOT2Simulator backend to test OT-2 protocols offline, with the visualizer for real-time feedback.
See the OT-2 hello world notebook for using the real hardware.
from pylabrobot.liquid_handling import LiquidHandler
from pylabrobot.liquid_handling.backends import OpentronsOT2Simulator
from pylabrobot.resources.opentrons import (
OTDeck,
opentrons_96_filtertiprack_20ul,
opentrons_96_tiprack_300ul,
)
from pylabrobot.resources.celltreat import CellTreat_96_wellplate_350ul_Fb
from pylabrobot.resources import set_tip_tracking, set_volume_tracking
from pylabrobot.visualizer import Visualizer
set_tip_tracking(True)
set_volume_tracking(True)
Set up the simulator and visualizer#
The simulator takes optional pipette names. Defaults are p300_single_gen2 (left) and p20_single_gen2 (right).
backend = OpentronsOT2Simulator()
deck = OTDeck()
lh = LiquidHandler(backend=backend, deck=deck)
await lh.setup()
2026-03-12 18:32:17,220 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - OpentronsOT2Simulator setup: left=p300_single_gen2, right=p20_single_gen2
2026-03-12 18:32:17,220 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Homing (simulated).
vis = Visualizer(resource=lh)
await vis.setup()
Websocket server started at http://127.0.0.1:2121
File server started at http://127.0.0.1:1337 . Open this URL in your browser.
Add labware to the deck#
Assign tip racks and a plate. These will appear in the visualizer automatically.
tip_rack_20 = opentrons_96_filtertiprack_20ul(name="tip_rack_20")
deck.assign_child_at_slot(tip_rack_20, slot=1)
tip_rack_300 = opentrons_96_tiprack_300ul(name="tip_rack_300")
deck.assign_child_at_slot(tip_rack_300, slot=2)
plate = CellTreat_96_wellplate_350ul_Fb(name="plate")
deck.assign_child_at_slot(plate, slot=6)
Run a simple protocol#
Pick up a 20 uL tip (right pipette = channel 1), aspirate from well A1, dispense into well B1, and return the tip.
await lh.pick_up_tips(tip_rack_20["A1"], use_channels=[1])
2026-03-12 18:32:17,371 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Picked up tip from tip_rack_20_A1 with pipette sim-right
plate.get_well("A1").tracker.set_volume(200)
await lh.aspirate(plate["A1"], vols=[15], use_channels=[1])
2026-03-12 18:32:17,377 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Aspirated 15.00 µL from plate_well_A1
await lh.dispense(plate["B1"], vols=[15], use_channels=[1])
2026-03-12 18:32:17,384 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Dispensed 15.00 µL to plate_well_B1
await lh.return_tips()
2026-03-12 18:32:17,390 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Dropped tip to tip_rack_20_A1 with pipette sim-right
Now try the left pipette (p300) with the 300 uL tip rack.
await lh.pick_up_tips(tip_rack_300["A1"])
2026-03-12 18:32:17,395 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Picked up tip from tip_rack_300_A1 with pipette sim-left
await lh.aspirate(plate["A1"], vols=[100])
await lh.dispense(plate["C1"], vols=[100])
2026-03-12 18:32:17,402 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Aspirated 100.00 µL from plate_well_A1
2026-03-12 18:32:17,404 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Dispensed 100.00 µL to plate_well_C1
await lh.return_tips()
2026-03-12 18:32:17,417 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - Dropped tip to tip_rack_300_A1 with pipette sim-left
Clean up#
await vis.stop()
await lh.stop()
2026-03-12 18:32:17,442 - pylabrobot.liquid_handling.backends.opentrons_simulator - INFO - OpentronsOT2Simulator stopped.