opentrons-protocol-api について
opentrons-protocol-api は、OT-2 と Flex ロボット向けの液体処理プロトコルを Python で作成するスキルです。ピペット、ラボウェア、ハードウェアモジュールを操作でき、実機で実行する前にローカルでシミュレーションできます。
主な機能
- 01Python ファイルでプロトコルを作成
- 02ピペット移送とチップ使用を制御
- 03ラボウェアを読み込みウェルにアクセス
- 04温度・磁気モジュールを操作
- 05プロトコルをローカルでシミュレーション
活用シーン
- 01PCR 反応の準備とサイクル実行
- 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 を使うと、OT-2 または Flex ロボットで実行できる、液体ハンドリングプロトコルを通常の Python ファイルとして作成できます。各プロトコルでは、metadata 辞書、任意の requirements 辞書、および run(protocol) 関数を定義します。run() に渡される ProtocolContext オブジェクトを通じて、デッキのセットアップ、ピペット操作、モジュール制御、ユーティリティメソッドをすべて利用できます。プロトコルは、opentrons_simulate を使って任意のコンピューター上でシミュレーションしてから、Opentrons App または HTTP API 経由でロボットにアップロードできます。
使用する場面
- PCR 反応のセットアップ:チューブラックからマスターミックスをサーモサイクラープレートに分注し、個々のチューブから鋳型 DNA を加えた後、PCR プロファイルを自動で実行します。
- 段階希釈の実行:マルチチャンネルピペットをプログラムで 96 ウェルプレート上に移動させ、定義した希釈液量を使って 2 倍希釈または任意の希釈系列を作成します。
- ELISA プレートレイアウトの実施:各試薬の間でチップを交換しながら、ブロッキングバッファー、一次抗体、二次抗体、基質を指定したウェルに加えます。
- 磁気ビーズ精製の自動化:磁気モジュールを作動/解除し、上清を吸引し、エタノールで洗浄して溶出するまでの工程を、完全自動のループで実行します。
- プレートのリフォーマットとスタンピング:コマンド 1 つで 96 ウェルプレート全体を送液先のプレートに移し、チューブからプレートへのリフォーマットも行います。
- ハードウェアモジュールの連携:温度制御、シェイキング、液体ハンドリングの各工程を、正確なタイミングで 1 つのプロトコル内で連携させます。
- Opentrons 固有のハードウェアを使わずに 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:プロトコルのメタデータとデッキのセットアップ
すべてのプロトコルには、少なくとも apiLevel を指定する metadata 辞書が必要です。任意の 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 | (repetitions, volume) タプル | 各分注後に移送先ウェル内で吸引・吐出し、均質化します |
mix_before | transfer | None | (repetitions, volume) タプル | 各吸引前に移送元ウェル内で吸引・吐出します |
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"の場合、ウェルのペアごとに 1 本のチップを使用します)。必要なチップがラックの容量を超える場合は、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 の液体トラッキングでは、液量が色分けされたウェルに表示されます。実行ボタンを押す前に、試薬が正しく配置されているかを簡単に確認できます。 -
プロトコルファイルで 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.delay()ではなくprotocol.pause()を使う: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")
レシピ:マルチチャンネルで列ごとに分注
使用する場面:マルチチャンネルピペットを使い、1本のチップで96ウェルプレートに試薬を1列ずつ分注します。
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 が発生する | 分注元と分注先のリストの長さが異なります | ペアごとに分注する場合は分注元と分注先のリストの長さを同じにします。または、1 対多の分注では、分注元を 1 つにして分注先リストを使用します |
| スロット名を指定した OT-2 プロトコルで、Flex のエラーが発生する | ロボットの種類が一致していません(数値スロットとグリッドスロット) | 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 — プロトコルのデバッグやハードウェアに関する質問を投稿できるコミュニティ Q&A