关于 opentrons-protocol-api
opentrons-protocol-api 是用于为 OT-2 和 Flex 机器人编写 Python 液体处理流程的技能。它提供移液器、实验室器皿和硬件模块操作,并支持在机器人运行前进行本地模拟。
主要功能
- 01以 Python 文件编写流程
- 02控制移液转移和吸头使用
- 03加载实验室器皿并访问孔位
- 04操作温控和磁力模块
- 05在本地模拟流程
适用场景
- 01设置 PCR 反应并运行热循环
- 02在微孔板上制作系列稀释
- 03自动执行磁珠纯化
- 04协调液体处理与模块控制
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill opentrons-protocol-api安装后在 Claude Code 中输入/opentrons-protocol-api确认已加载。
详细介绍
Opentrons Python Protocol API
概述
Opentrons Protocol API v2 可让你将液体处理流程编写为纯 Python 文件,并在 OT-2 或 Flex 机器人上运行。每个流程都定义一个 metadata 字典、一个可选的 requirements 字典,以及一个 run(protocol) 函数。传递给 run() 的 ProtocolContext 对象提供所有甲板设置、移液器操作、模块控制和实用方法。上传到机器人之前,可以在任何计算机上使用 opentrons_simulate 模拟流程,也可以通过 Opentrons App 或 HTTP API 上传。
适用场景
- 设置 PCR 反应:将主混合液从试管架分配到热循环仪板中,从单个试管中加入模板 DNA,然后自动执行 PCR 程序。
- 执行系列稀释:以编程方式让多通道移液器在 96 孔板上逐步操作,使用指定的稀释液体积创建 2 倍或自定义稀释曲线。
- 进行 ELISA 板布局:向指定孔中加入封闭缓冲液、一抗、二抗和底物,并在每种试剂之间更换吸头。
- 自动执行磁珠纯化:启用/停用磁力模块,吸取上清液,用乙醇清洗并洗脱,全程通过自动化循环完成。
- 板型转换和转印:使用一条命令将整个 96 孔板转移到目标板;也可以将试管中的样品重新排布到板中。
- 集成硬件模块:在单个流程中协调温控、振荡和液体处理步骤,并精确计时。
- 如果要编写必须在 Hamilton STAR、Tecan Freedom EVO 或其他厂商的设备上运行的流程,请改用
PyLabRobot;对于仅使用 Opentrons 的工作流,原生 Protocol API 的集成更紧密,模块支持也更完善。 - 若要在自动化前检索和解析已发布的流程,请同时使用
protocolsio-integration搜索 protocols.io。
前置条件
- Python 包:
opentrons - 机器人类型:OT-2(插槽 1-11,Gen2 移液器)或 Flex(插槽 A1-D3,Flex 移液器)
- 环境:Python 3.10+;使用 Opentrons App 上传到实体机器人
- CLI 工具:软件包自带
opentrons_simulate,用于本地测试
pip install opentrons
# Verify installation and simulate a protocol locally
opentrons_simulate my_protocol.py
快速入门
展示所有必需元素的最简流程——元数据、实验室耗材、仪器和转移操作:
from opentrons import protocol_api
metadata = {
"protocolName": "Simple Reagent Distribution",
"author": "Lab Automation Team",
"apiLevel": "2.19",
}
def run(protocol: protocol_api.ProtocolContext):
# Load labware onto deck slots
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
source = protocol.load_labware("nest_12_reservoir_15ml", "2")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
# Load pipette and attach tip rack
pipette = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
# Distribute 50 µL from reservoir A1 to first 12 wells using one tip
pipette.distribute(50, source["A1"], plate.wells()[:12], new_tip="once")
protocol.comment("Distribution complete")
# Simulate locally — no robot needed
opentrons_simulate simple_reagent_distribution.py
核心 API
模块 1:流程元数据和甲板设置
每个流程都必须包含一个 metadata 字典,至少指定 apiLevel。可选的 requirements 字典用于设置目标机器人类型。所有实验室耗材和仪器都通过 ProtocolContext 加载。
from opentrons import protocol_api
# Minimum required metadata
metadata = {
"protocolName": "My Assay Protocol",
"author": "Jane Smith <[email protected]>",
"description": "96-well assay setup with temperature control",
"apiLevel": "2.19",
}
# Optional: target a specific robot type (Flex or OT-2)
requirements = {"robotType": "OT-2", "apiLevel": "2.19"}
def run(protocol: protocol_api.ProtocolContext):
# OT-2: slots numbered 1-11 in a 3×4 grid
tips_300 = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
tips_20 = protocol.load_labware("opentrons_96_tiprack_20ul", "4")
source = protocol.load_labware("nest_12_reservoir_15ml", "2", label="Buffer Reservoir")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
tube_rack = protocol.load_labware("opentrons_24_tuberack_nest_1.5ml_snapcap", "5")
# Load both pipettes (optional: one or two mounts)
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips_300])
p20 = protocol.load_instrument("p20_single_gen2", "right", tip_racks=[tips_20])
print(f"Deck has {len(protocol.deck)} slots; pipettes: {[p300.name, p20.name]}")
OT-2 甲板布局(3 列 × 4 行,按从左到右、从下到上的顺序编号):
Slot map (OT-2): Slot map (Flex, A-D rows, 1-3 cols):
10 | 11 | Trash D1 | D2 | D3
7 | 8 | 9 C1 | C2 | C3
4 | 5 | 6 B1 | B2 | B3
1 | 2 | 3 A1 | A2 | A3
常见 OT-2 移液器名称:p20_single_gen2、p300_single_gen2、p1000_single_gen2、p20_multi_gen2、p300_multi_gen2。
常见 Flex 移液器名称:p50_single_flex、p1000_single_flex、p50_multi_flex、p1000_multi_flex、flex_96channel_1000。
模块 2:移液器操作
用于精确逐步控制的底层吸液/分液/吹出操作。
def run(protocol: protocol_api.ProtocolContext):
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
source = protocol.load_labware("nest_12_reservoir_15ml", "2")
dest = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
p300.pick_up_tip()
# Aspirate and dispense — basic liquid movement
p300.aspirate(100, source["A1"]) # draw 100 µL from reservoir
p300.dispense(100, dest["A1"]) # expel into plate well
# Air gap to prevent dripping during transport
p300.aspirate(80, source["A2"])
p300.air_gap(20) # draw 20 µL air to cap the tip
p300.dispense(100, dest["A2"]) # dispenses liquid + air
# Mix in place (repetitions, volume)
p300.mix(3, 60, dest["A1"]) # mix 60 µL × 3 times
# Remove exterior droplets / expel residual
p300.touch_tip(dest["A1"]) # wipe tip on well rim
p300.blow_out(dest["A1"].top()) # expel last drop at top
p300.drop_tip()
protocol.comment("Low-level operations complete")
def run(protocol: protocol_api.ProtocolContext):
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
# Adjust flow rates (µL/s) for viscous or sensitive samples
p300.flow_rate.aspirate = 50 # slow down for viscous liquids (default ~150)
p300.flow_rate.dispense = 150 # default dispense speed
p300.flow_rate.blow_out = 300 # fast blow-out for complete expulsion
print(f"Aspirate rate: {p300.flow_rate.aspirate} µL/s")
模块 3:transfer() 快捷方法
transfer()、distribute() 和 consolidate() 会自动管理吸头,并支持混匀、吹出和空气间隙选项。
def run(protocol: protocol_api.ProtocolContext):
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
source = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")
dest = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
# transfer(): one source → one destination, with optional per-well tip changes
p300.transfer(
100,
source["A1"],
dest["A1"],
new_tip="always", # options: "always", "once", "never"
mix_after=(3, 50), # mix 50 µL × 3 reps after each dispense
blow_out=True,
touch_tip=True,
)
# transfer() with lists: pairwise source-destination mapping
sources = source.wells()[:8]
dests = dest.wells()[:8]
p300.transfer(75, sources, dests, new_tip="always")
# distribute(): one source → many destinations (single tip, multi-dispense)
p300.distribute(
50,
source["A1"],
dest.wells()[:12],
new_tip="once", # use one tip for all destinations
disposal_volume=10, # extra volume drawn to ensure accuracy
)
# consolidate(): many sources → one destination (collect, then dispense)
p300.consolidate(
50,
source.wells()[:8],
dest["A1"],
mix_after=(3, 100),
)
print("Compound transfer operations complete")
模块 4:实验室耗材、液体和孔位访问
从库中加载实验室耗材,按名称/行/列定位孔位,并定义液体,以便在 Opentrons App 中进行可视化追踪。
def run(protocol: protocol_api.ProtocolContext):
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "1")
p300 = protocol.load_instrument("p300_single_gen2", "left",
tip_racks=[protocol.load_labware("opentrons_96_tiprack_300ul", "2")])
# Access wells by alphanumeric name
well_a1 = plate["A1"]
# Access all wells (column-major order: A1, B1, C1, ..., H1, A2, ...)
all_wells = plate.wells()
print(f"Total wells: {len(all_wells)}") # 96
# Access by row (8 rows, A-H; each row has 12 wells)
row_a = plate.rows()[0] # [A1, A2, ..., A12]
row_b = plate.rows()[1] # [B1, B2, ..., B12]
# Access by column (12 columns, 1-12; each column has 8 wells)
col_1 = plate.columns()[0] # [A1, B1, C1, D1, E1, F1, G1, H1]
# Vertical position control within a well
p300.pick_up_tip()
p300.aspirate(80, well_a1.bottom(z=1)) # 1 mm above well bottom
p300.dispense(80, well_a1.top(z=-2)) # 2 mm below well top
p300.aspirate(80, well_a1.center()) # geometric center
p300.drop_tip()
def run(protocol: protocol_api.ProtocolContext):
reservoir = protocol.load_labware("nest_12_reservoir_15ml", "1")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")
# Define liquids for visual tracking in Opentrons App
pbs = protocol.define_liquid(name="1× PBS", description="Phosphate buffered saline", display_color="#0077BB")
sample = protocol.define_liquid(name="Sample", description="Cell lysate, 1 mg/mL protein", display_color="#EE7733")
# Assign liquids to wells with known starting volumes (µL)
reservoir["A1"].load_liquid(liquid=pbs, volume=10000)
reservoir["A2"].load_liquid(liquid=sample, volume=5000)
# Mark destination wells as empty
for well in plate.wells():
well.load_empty()
print("Liquids defined and assigned")
模块 5:硬件模块
控制温控、磁力、热循环仪和加热振荡模块。每个模块都通过其型号名称字符串加载,并占用特定的甲板插槽。
def run(protocol: protocol_api.ProtocolContext):
# --- Temperature Module (Gen2) ---
temp_mod = protocol.load_module("temperature module gen2", "3")
temp_plate = temp_mod.load_labware("corning_96_wellplate_360ul_flat")
temp_mod.set_temperature(celsius=4) # blocks until target reached
print(f"Temp module: {temp_mod.temperature}°C")
# temp_mod.deactivate() # turn off at end
# --- Magnetic Module (Gen2) ---
mag_mod = protocol.load_module("magnetic module gen2", "6")
mag_plate = mag_mod.load_labware("nest_96_wellplate_100ul_pcr_full_skirt")
mag_mod.engage(height_from_base=10) # raise magnets 10 mm from plate base
protocol.delay(seconds=300) # hold beads for 5 min
mag_mod.disengage()
# --- Heater-Shaker Module ---
hs_mod = protocol.load_module("heaterShakerModuleV1", "1")
hs_plate = hs_mod.load_labware("corning_96_wellplate_360ul_flat")
hs_mod.close_labware_latch()
hs_mod.set_target_temperature(celsius=37)
hs_mod.wait_for_temperature()
hs_mod.set_and_wait_for_shake_speed(rpm=500)
protocol.delay(minutes=30)
hs_mod.deactivate_shaker()
hs_mod.deactivate_heater()
hs_mod.open_labware_latch()
print("Heater-shaker cycle complete")
def run(protocol: protocol_api.ProtocolContext):
# --- Thermocycler Module (Gen2) ---
# Auto-occupies slots 7-11 on OT-2; no slot argument needed
tc_mod = protocol.load_module("thermocyclerModuleV2")
tc_plate = tc_mod.load_labware("nest_96_wellplate_100ul_pcr_full_skirt")
tc_mod.open_lid()
tc_mod.set_lid_temperature(celsius=105) # pre-heat lid to prevent condensation
# Initial denaturation
tc_mod.set_block_temperature(95, hold_time_seconds=180)
# PCR cycling profile
profile = [
{"temperature": 95, "hold_time_seconds": 15}, # denaturation
{"temperature": 60, "hold_time_seconds": 30}, # annealing
{"temperature": 72, "hold_time_seconds": 30}, # extension
]
tc_mod.execute_profile(steps=profile, repetitions=35, block_max_volume=25)
# Final extension and hold
tc_mod.set_block_temperature(72, hold_time_minutes=5)
tc_mod.set_block_temperature(4) # hold at 4°C indefinitely
tc_mod.deactivate_lid()
tc_mod.open_lid()
print("PCR complete; plate held at 4°C")
模块 6:高级流程功能
暂停以等待用户交互、记录可在应用中查看的注释、控制导轨灯,并检测模拟模式。
def run(protocol: protocol_api.ProtocolContext):
# Pause and prompt the user (robot stops, app shows message)
protocol.pause(msg="Add 10 µL of enzyme to tube A1, then resume")
# Timed delay (robot waits without user action)
protocol.delay(seconds=30, msg="Waiting 30s for reaction incubation")
protocol.delay(minutes=5)
# Log a comment visible in Opentrons App run log
protocol.comment("Starting serial dilution — columns 1 to 11")
# Rail lights for visual status indication
protocol.set_rail_lights(True) # lights on
protocol.set_rail_lights(False) # lights off
# Home all axes (useful after an error or before finishing)
protocol.home()
# Detect simulation vs. physical run — skip slow waits in simulation
if protocol.is_simulating():
protocol.comment("Running in simulation mode — skipping 10-min incubation")
else:
protocol.delay(minutes=10)
# Load waste bin (Flex only — OT-2 uses fixed trash)
# trash = protocol.load_trash_bin("A3")
print("Protocol control features demonstrated")
常见工作流
工作流 1:使用热循环仪进行 PCR 设置
目标:将预混液从试管架转移到热循环仪上的 PCR 板中,从单独的样本中加入模板 DNA,然后运行完整的 PCR 循环程序。
from opentrons import protocol_api
metadata = {
"protocolName": "PCR Setup and Run",
"author": "Lab Automation",
"apiLevel": "2.19",
}
def run(protocol: protocol_api.ProtocolContext):
# Hardware setup
tc_mod = protocol.load_module("thermocyclerModuleV2")
tc_plate = tc_mod.load_labware("nest_96_wellplate_100ul_pcr_full_skirt")
tips_300 = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
tips_20 = protocol.load_labware("opentrons_96_tiprack_20ul", "4")
reagents = protocol.load_labware("opentrons_24_tuberack_nest_1.5ml_snapcap", "2")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips_300])
p20 = protocol.load_instrument("p20_single_gen2", "right", tip_racks=[tips_20])
# Define liquids
master_mix = protocol.define_liquid("Master Mix", "2× PCR master mix", "#33BBEE")
template = protocol.define_liquid("Template", "gDNA 10 ng/µL", "#EE3377")
reagents["A1"].load_liquid(master_mix, volume=500)
for i in range(8):
reagents.wells()[i + 1].load_liquid(template, volume=50)
# Step 1: Open lid and distribute master mix (20 µL per well, 8 wells)
tc_mod.open_lid()
protocol.comment("Distributing master mix")
p300.distribute(
20,
reagents["A1"],
tc_plate.wells()[:8],
new_tip="once",
blow_out=True,
blowout_location="source well",
)
# Step 2: Add template DNA (5 µL per well, fresh tip each time)
protocol.comment("Adding template DNA")
for i in range(8):
p20.transfer(
5,
reagents.wells()[i + 1],
tc_plate.wells()[i],
new_tip="always",
mix_after=(2, 10),
)
# Step 3: Run PCR
tc_mod.close_lid()
tc_mod.set_lid_temperature(105)
tc_mod.set_block_temperature(95, hold_time_seconds=180) # initial denaturation
profile = [
{"temperature": 95, "hold_time_seconds": 15},
{"temperature": 60, "hold_time_seconds": 30},
{"temperature": 72, "hold_time_seconds": 30},
]
tc_mod.execute_profile(steps=profile, repetitions=35, block_max_volume=25)
tc_mod.set_block_temperature(72, hold_time_minutes=5) # final extension
tc_mod.set_block_temperature(4) # hold
tc_mod.deactivate_lid()
tc_mod.open_lid()
protocol.comment("PCR complete — 8 reactions in wells A1:H1")
工作流 2:使用多通道移液器进行 ELISA 系列稀释
目标:使用多通道移液器向第 2 至第 12 列加入稀释液,在整块板上进行 2 倍系列稀释,并一次性向所有孔中加入检测试剂。
from opentrons import protocol_api
metadata = {
"protocolName": "ELISA Serial Dilution",
"author": "Lab Automation",
"apiLevel": "2.19",
}
def run(protocol: protocol_api.ProtocolContext):
# Deck layout
tips_300 = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
tips_300b = protocol.load_labware("opentrons_96_tiprack_300ul", "4") # extra rack
reservoir = protocol.load_labware("nest_12_reservoir_15ml", "2")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
multi = protocol.load_instrument("p300_multi_gen2", "left",
tip_racks=[tips_300, tips_300b])
# Define liquids
diluent = protocol.define_liquid("Diluent", "PBS + 1% BSA", "#0077BB")
sample = protocol.define_liquid("Sample", "Serum 1:10", "#EE7733")
reservoir["A1"].load_liquid(diluent, volume=50000)
reservoir["A2"].load_liquid(sample, volume=5000)
# Step 1: Load column 1 with undiluted sample (all 8 rows at once)
protocol.comment("Loading undiluted sample into column 1")
multi.transfer(100, reservoir["A2"], plate.columns()[0], new_tip="once")
# Step 2: Add diluent to columns 2-12
protocol.comment("Adding diluent to columns 2-12")
multi.distribute(
100,
reservoir["A1"],
[col[0] for col in plate.columns()[1:]], # A2 through A12 (multi-channel reads full column)
new_tip="once",
disposal_volume=10,
)
# Step 3: Serial dilution — transfer 100 µL from each column to the next, mix
protocol.comment("Performing 2-fold serial dilution across columns 1→11")
multi.transfer(
100,
[col[0] for col in plate.columns()[:11]], # cols 1-11 as source
[col[0] for col in plate.columns()[1:]], # cols 2-12 as destination
mix_after=(5, 80), # mix 80 µL × 5 reps after each dispense
new_tip="always", # fresh tip per column to avoid carry-over
)
# Step 4: Remove 100 µL from column 12 to equalize volumes
multi.pick_up_tip()
multi.aspirate(100, plate.columns()[11][0])
multi.drop_tip()
protocol.comment("ELISA serial dilution complete — 11 dilution steps, 12 columns")
print("Protocol complete: 2-fold dilution series across 96-well plate")
关键参数
| 参数 | 模块 / 函数 | 默认值 | 范围 / 选项 | 作用 |
|---|---|---|---|---|
new_tip | transfer、distribute、consolidate | "always" | "always"、"once"、"never" | 控制更换吸头的策略;使用 "always" 可防止交叉污染 |
mix_after | transfer | None | (重复次数, 体积) 元组 | 每次分液后,在目标孔中吸液 / 排液,以使液体均匀 |
mix_before | transfer | None | (重复次数, 体积) 元组 | 每次吸液前,在源孔中吸液 / 排液 |
blow_out | transfer | False | True、False | 分液后排出残余体积;设置 blowout_location 可控制排出位置 |
air_gap | transfer | 0 | 0–移液器最大值 µL | 吸液后加入空气间隙,防止机器人移动时滴漏 |
disposal_volume | distribute | 0 | 0–移液器最大值 µL | 开始时额外吸取的体积,可提高分液精度;随后弃入废液槽 |
flow_rate.aspirate | 移液器属性 | 因型号而异 | 1–1000 µL/s | 吸液速度;对黏稠样本(甘油、蛋白质 > 5 mg/mL)应调低 |
flow_rate.dispense | 移液器属性 | 因型号而异 | 1–1000 µL/s | 分液速度;对于易起泡或脆弱的细胞悬液应调低 |
height_from_base | mag_mod.engage() | — | 0–20 mm | 磁铁尖端高于板底的高度;取决于磁珠 / 板的几何结构 |
repetitions | tc_mod.execute_profile() | — | 1–99 | PCR 热循环次数 |
最佳实践
-
在硬件上运行前务必先进行模拟:使用
opentrons_simulate protocol.py检查实验室耗材名称错误、吸头不足、体积溢出和槽位冲突,避免消耗耗材或占用机器人运行时间。opentrons_simulate my_pcr_setup.py # Output shows all commands; errors printed with line numbers -
优先使用复合操作,而非手动执行取吸头 / 吸液 / 排液 / 弃吸头序列:
transfer()、distribute()和consolidate()会自动处理吸头管理、空气间隙和吹出。仅在复合方法不支持的操作中使用底层调用。 -
运行前清点吸头:计算吸头总消耗量(每次
new_tip="always"转移会为每对孔消耗一个吸头)。如果吸头数量超过吸头架容量,请在tip_racks=[]中添加更多吸头架。n_transfers = len(source_wells) # one tip per transfer tips_per_rack = 96 racks_needed = -(-n_transfers // tips_per_rack) # ceiling division print(f"Need {racks_needed} tip rack(s) for {n_transfers} transfers") -
使用
define_liquid()和load_liquid()验证设置:Opentrons App 中的液体追踪会以颜色标记孔及其体积,便于在按下 Run 前确认试剂放置正确。 -
在协议文件中区分 OT-2 槽位与 Flex 槽位:OT-2 使用数字字符串(
"1"至"11"),而 Flex 使用网格坐标("A1"至"D3")。设置requirements = {"robotType": "Flex"}或"OT-2",以便在模拟期间检测槽位不匹配。 -
针对难处理的液体调整流速:黏稠溶液(≥20% 甘油、PEG、蛋白质 > 5 mg/mL)需要较低的吸液速度(25-50 µL/s)。对于易起泡的样本,应降低分液速度以避免产生气泡。
-
手动步骤使用
protocol.pause(),不要使用protocol.delay():pause()会停止机器人并通知操作员;运行会按需恢复。delay()用于定时等待(孵育、模块平衡),此时不需要人工操作。
常用操作配方
配方:板复制(96 孔板到 96 孔板)
适用情况:使用每孔更换新吸头的方式,将整块源板复制到目标板。
from opentrons import protocol_api
metadata = {"protocolName": "Plate Replication", "apiLevel": "2.19"}
def run(protocol: protocol_api.ProtocolContext):
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
source = protocol.load_labware("corning_96_wellplate_360ul_flat", "2")
dest = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
# Transfer all 96 wells in one call — pairwise source[i] → dest[i]
p300.transfer(100, source.wells(), dest.wells(), new_tip="always")
protocol.comment("Plate replicated: 96 wells transferred")
配方:使用多通道移液器逐列加液
适用场景:使用多通道移液器和一个吸头,将单一试剂逐列加入 96 孔板。
from opentrons import protocol_api
metadata = {"protocolName": "Multi-Channel Fill", "apiLevel": "2.19"}
def run(protocol: protocol_api.ProtocolContext):
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
reservoir = protocol.load_labware("nest_12_reservoir_15ml", "2")
plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
multi = protocol.load_instrument("p300_multi_gen2", "left", tip_racks=[tips])
# distribute() with multi-channel: one pick-up, 12 dispenses across all columns
multi.distribute(
100,
reservoir["A1"],
[plate.columns()[i][0] for i in range(12)],
new_tip="once",
disposal_volume=10,
)
protocol.comment("96-well plate filled: 100 µL per well, single tip")
配方:磁珠清洗循环
适用场景:进行基于磁珠的自动化纯化(DNA 提取、IP 检测),并重复执行清洗步骤。
from opentrons import protocol_api
metadata = {"protocolName": "Magnetic Bead Wash", "apiLevel": "2.19"}
def run(protocol: protocol_api.ProtocolContext):
mag_mod = protocol.load_module("magnetic module gen2", "4")
bead_plate = mag_mod.load_labware("nest_96_wellplate_100ul_pcr_full_skirt")
tips = protocol.load_labware("opentrons_96_tiprack_300ul", "1")
reservoir = protocol.load_labware("nest_12_reservoir_15ml", "2")
waste = protocol.load_labware("nest_12_reservoir_15ml", "5")
p300 = protocol.load_instrument("p300_single_gen2", "left", tip_racks=[tips])
# Engage magnets and remove supernatant
mag_mod.engage(height_from_base=6)
protocol.delay(seconds=120, msg="Beads pelleting on magnet")
p300.transfer(90, bead_plate["A1"].bottom(z=0.5), waste["A1"], new_tip="once")
# Wash loop (2 washes)
for wash_num in range(2):
mag_mod.disengage()
protocol.comment(f"Wash {wash_num + 1} of 2")
p300.transfer(100, reservoir["A1"], bead_plate["A1"],
mix_after=(5, 80), new_tip="always")
mag_mod.engage(height_from_base=6)
protocol.delay(seconds=90)
p300.transfer(100, bead_plate["A1"].bottom(z=0.5), waste["A2"], new_tip="always")
# Elute
mag_mod.disengage()
elution_plate = protocol.load_labware("corning_96_wellplate_360ul_flat", "3")
p300.transfer(50, reservoir["A2"], bead_plate["A1"],
mix_after=(10, 40), new_tip="always")
mag_mod.engage(height_from_base=6)
protocol.delay(seconds=120)
p300.transfer(45, bead_plate["A1"].bottom(z=0.5), elution_plate["A1"], new_tip="always")
mag_mod.disengage()
protocol.comment("Bead cleanup complete: eluate in elution_plate A1")
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
LabwareNotFoundError: [labware name] | 培养器皿 API 名称字符串不正确 | 在 labware.opentrons.com 查找确切名称;名称区分大小写(例如,"corning_96_wellplate_360ul_flat") |
运行期间出现 OutOfTipsError | 协议所需吸头数量超出吸头盒提供的数量 | 在 tip_racks=[] 中添加额外的吸头盒;或者在重新装入吸头盒后调用 pipette.reset_tipracks() |
| 体积超过移液器的最大容量 | 尝试吸取或分液的体积超过移液器容量 | 使用 distribute() 自动拆分大体积;大体积(最高 1000 µL)可换用 p1000_single_gen2 |
DeckConflictError | 培养器皿放置在相互重叠的槽位中 | 热循环仪会自动占用槽位 7-11;运行前检查模拟输出中的 protocol.deck |
模拟通过,但机器人因 ModuleNotAttachedError 而失败 | 模块未实际连接,或型号字符串错误 | 检查 USB 连接;使用确切的型号字符串:"temperature module gen2"、"magnetic module gen2"、"thermocyclerModuleV2"、"heaterShakerModuleV1" |
| 体积不准确,尤其是接近移液器最小量程时 | 移液器处于校准量程边缘,或液体黏稠 | 选择最佳量程覆盖所需体积的移液器;关键转移前使用 mix() 预润洗吸头;降低流速 |
transfer() 的孔列表长度不匹配时出现 TypeError | 源孔列表和目标孔列表长度不同 | 成对转移时确保源孔列表和目标孔列表长度相同;或者使用单一源孔搭配目标孔列表进行一对多转移 |
| 在 Flex 上使用槽位名称时出现 OT-2 协议错误 | 机器人类型不匹配(数字槽位与网格槽位) | 设置 requirements = {"robotType": "Flex"} 或 "OT-2",以强制指定槽位命名;Flex 槽位是 "A1" 这类字符串,OT-2 槽位是 "1"-"11" |
相关技能
- pylabrobot — 面向 Hamilton、Tecan、Beckman 等供应商的硬件无关 Python API;当协议必须在非 Opentrons 硬件上运行时使用
- protocolsio-integration — 搜索并检索 protocols.io 上发布的湿实验室协议,以便改编为 Opentrons Python 协议
- benchling-integration — 将协议执行与 Benchling ELN 条目和样本登记库连接起来
参考资料
- Opentrons Protocol API v2 Documentation — 官方 API 参考,涵盖所有上下文方法、培养器皿和模块
- Opentrons Labware Library — 可搜索的目录,列出所有受支持的培养器皿及其 API 名称字符串
- Protocol API Tutorial — 从元数据到硬件模块的分步指南
- Opentrons GitHub Repository — 源代码、协议示例和问题跟踪器
- Opentrons Community Forum — 面向协议调试和硬件问题的社区问答