仿真实验闭环工作流开发教程6设备抽象层——用 PyLabRobot 与 SiLA 2 让一份配液任务跑遍所有液体处理机版本声明块工具/软件PyLabRobotpylabrobot0.2.2、SiLA 2 官方 Python 实现sila20.14.0、Hamilton VENUS Web API / Prep API、Tecan Freedom EVOware无公开 Python SDK、Beckman Biomek i-Series vWorks无公开官方 API 文档语言/环境Python 3.11PyLabRobot 全异步async/await本文目标把配液任务从某台机器里剥出来让闭环的 Build 环节换机器不改代码。一句话结论LiquidHandler(backendSTARBackend())与LiquidHandler(backendOpentronsOT2Backend(host…))用的是同一套pick_up_tips / aspirate / dispense / return_tips方法台面布局由Deck.load_from_json_file(…json)提供——换 backend 一行即换设备而 Tecan/Beckman 因根本没有公开 Python SDK铁律 9 的现实答案只能经 PyLabRobot 驱动层或 SiLA 2sila20.14.0标准接入绝不能再臆造import tecan之类的入口。〇、本篇要解决的认知问题Q1闭环的执行层为什么不能直接对着每台液体处理机写原生脚本非要加一层硬件无关抽象Q2PyLabRobot 的LiquidHandler是怎么做到同一份配液代码、换一行就切设备的真实模块路径是什么Q3Deck.load_from_json_file在这套抽象里承担什么角色为什么说它是先仿真后实机的落点Q4SiLA 2 到底是什么pip install sila2装的是标准还是某厂商的驱动官方仓库怎么起开发环境Q5Hamilton、Tecan、Beckman 三家的官方 API边界各在哪哪些是能给代码、哪些只能以官方文档为准/需向厂商索取一、机制解析抽象层到底抽象掉了什么第 04 篇我们把一台 Opentons 用 Protocol API v2 遥控明白了但那是一台机器。真实实验室里同时摆着 Hamilton STAR、Tecan Freedom EVO、Beckman Biomek闭环里一句把这 96 个孔按浓度梯度配出来的配液意图Build 环节如果绑死在某家原生脚本上换一台工作站就得推倒重来。更致命的现实是Tecan 与 Beckman 根本没有公开的 Python SDK详见本节末。这正是系列铁律 9不臆造厂商 API的落点。厂商锁定vendor lock-in的成本在闭环里会被放大数倍因为闭环强调先仿真后实机“同一意图反复重放”铁律 2、铁律 8一旦意图与某家私有脚本焊死换设备、加设备、把某台机器挪去做别的任务都要重写并重新验证整段 Build 逻辑——而验证成本恰恰是合规环境下最耗人的部分。把意图抬到硬件无关层本质是给未来要换的硬件和不该重写的逻辑之间买一份保险。于是执行层需要一个硬件无关层hardware-agnostic layer把三件事解耦┌─────────────────────────────────────────┐ 意图层 │ 配液任务 prep_task(lh) ← 只讲做什么 │ (backend无关) │ aspirate/dispense/pick_up_tips ... │ └───────────────┬─────────────────────────┘ │ 同一个 LiquidHandler 对象 ┌───────────────┴────────────────┐ 抽象层 │ PyLabRobot LiquidHandler │ 统一方法签名 │ Deck(台面布局来自 JSON) │ └───┬───────────┬────────────┬────┘ │ │ │ 驱动/backend STARBackend OpentronsOT2 ...酶标仪/泵/天平 │ Backend ───────────────┼───────────┼────────────────────────── 厂商私有协议线 Hamilton Opentrons VENUS/Prep Protocol API/HTTP:31950对照三种通道理解抽象层与原生通道的分工维度PyLabRobot 抽象层SiLA 2 标准厂商原生通道定位硬件无关 Python SDK直接写任务设备间通信开放标准消息/属性/命令机器自带的控制 API覆盖Hamilton STAR、OT-2 及酶标仪/泵/天平/加热振荡器任何实现了某 SiLA 接口的设备单厂商、单软件线是否给 Python 代码是pip install pylabrobot是pip install sila20.14.0见下文差异极大换设备改代码吗只改 backend 一行换接口实现、任务逻辑不动通常整段重写为什么需要抽象层的现实答案铁律 9厂商原生通道的边界非常不整齐——Hamilton官方开发者门户 developer.hamiltoncompany.com 明确分两条线。VENUS Web API能力门户原文是 camera 信息与录制、获取已注册设备、method 的export/import/validation、methodloading与executionPrep走 REST WebSocket 实时事件ws://[IP_address]/NimbusLite/instinctevents且OpenAPI 文档在设备本机。也就是说 Hamilton 至少有两套可对接的官方面但方法名/端点细节门户未逐条列全的仍以官方文档为准。Tecan Freedom EVOware官方页只有两句可核实原文——“Additional drivers can be created using the Freedom EVOware Developer Studio.” / “Freedom EVOware can be controlled by other software via its API…”。没有公开 Python SDK、没有公开 REST 文档。正确姿势是申请官方接口、或经 PyLabRobot/SiLA 2 抽象层集成绝不是import tecan查无此物。Beckman Biomek i-Series vWorks产品页可访问但 “Biomek NX API” 未找到官方页vWorks 的 Scripting InterfaceVBScript/JavaScript仅第三方记载无公开官方 API 文档。写需联系厂商获取。把这三家摆在一起就懂了闭环不该赌某家给不给 SDK而应把任务写成硬件无关的谁有驱动就接谁的 backend谁只有 SiLA 接口就走 SiLA。一个必须记住的生态洗牌教训早些年社区里流传的所谓PySciMe / PyLab_server / pylabserver.com这一整族名字如今 PyPI 全部 404、域名 NXDOMAIN、Crossref 零命中——查无此物。这不是文档难找是项目/名字本身就没了。教训有二① 教程里凡是某个万能 Python 驱动库的说法动手前先到 PyPI 与官方 GitHub 验活② 把任务写在与具体库解耦的抽象层上库下线时只换适配、不换意图。这也是本篇推荐 PyLabRobot有活的 GitHubPyLabRobot/pylabrobot与 docs.pylabrobot.org、PyPI 0.2.2 可核实而非任何魔法库的原因。二、完整代码与逐行剖析2.1 一份配液任务切换两种 backend下面的核心是意图prep_task只写一次backend 与 deck 决定它跑在哪台机器上。模块路径全部来自 PyLabRobot 官方 README/PyPI 逐字示例。importasyncio# 三个真实模块路径均来自 pylabrobot 0.2.2 官方说明勿改写成臆造名frompylabrobot.liquid_handlingimportLiquidHandlerfrompylabrobot.liquid_handling.backendsimportSTARBackend,OpentronsOT2Backendfrompylabrobot.resourcesimportDeck# 意图层只讲做什么完全不提是哪台机器——这是抽象层的全部价值asyncdefprep_task(lh:LiquidHandler):# setup() 让 backend 与 deck 建立连接、校验台面资源真机才会握手仿真只解析 JSONawaitlh.setup()# get_resource 按 JSON 里登记的名称取资源[A1] 取孔位。名称来自 layout 文件而非硬编码坐标# 这样换机器只要换 JSON不动这段逻辑tip_racklh.deck.get_resource(tip_rack)platelh.deck.get_resource(plate)awaitlh.pick_up_tips(tip_rack[A1])# 取枪头所有 LiquidHandler backend 同名方法awaitlh.aspirate(plate[A1],vols100)# 吸 100 µLvols 单位固定为 µL跨设备一致是抽象层的契约awaitlh.dispense(plate[A2],vols100)# Dispense 到 A2把从 A1 移到 A2这层意图与# 具体 Z 轴速度曲线解耦后者藏在 backend 里awaitlh.return_tips()# 退枪头收尾动作也统一避免各机器手尾不一致# backend 工厂换设备只改这一处prep_task 一行不动defbuild_handler(kind:str)-LiquidHandler:# Deck 由 JSON 描述台面布局——先把物理台画进文件任务就能对着这份虚拟台面编写与审查deckDeck.load_from_json_file(f{kind}-layout.json)ifkindstar:# Hamilton STAR走厂商驱动线VENUS/Prep 由 backend 内部消化任务层看不到returnLiquidHandler(backendSTARBackend(),deckdeck)ifkindot2:# Opentrons OT-2host 指向机器 IP同一套方法底层落到第 04 篇的 Protocol API/HTTP 面returnLiquidHandler(backendOpentronsOT2Backend(hostx.x.x.x),deckdeck)raiseValueError(f未知 backend{kind})asyncdefmain():forkin(star,ot2):lhbuild_handler(k)awaitprep_task(lh)# 同一份任务分别下发到两种设备抽象上# 与 setup() 配对的连接释放方法名素材未逐字登记以 pylabrobot 官方文档为准# 长期服务切勿只 setup 不释放否则会造成设备端会话占用呼应第 03 篇速率/会话思路asyncio.run(main())逐段剖析prep_task里没有一行提到 STAR 或 OT-2——这正是抽象层的检验标准如果你的意图代码里出现了具体型号或厂商方法名说明抽象漏了。aspirate/dispense的vols100跨设备都按 µL 解释这是 PyLabRobot 替你钉死的单位契约呼应铁律 1先定数据契约只是这里契约落在移液体积上。2.2Deck.load_from_json_file仿真与实机共用同一份台面“先仿真后实机”铁律 2在 PyLabRobot 这里的落点是Deck完全由 JSON 描述不接硬件也能把台面加载进内存、把get_resource(plate)[A1]这类寻址跑通。做法是开发期把布局写进star-layout.json用Deck.load_from_json_file校验资源名称/坐标是否合法Opentrons 侧再叠加第 04 篇的opentrons.execute本地模拟双重确认后才上真机、且真机首跑空载或走水。frompylabrobot.resourcesimportDeck# 布局文件长这样示意 JSON 结构名称、类型、坐标字段以 pylabrobot 文档为准# {resources: [# {name: tip_rack, type: hamilton_tiprack, x: 0, y: 0},# {name: plate, type: sbs_plate, x: 100, y: 0}]}deckDeck.load_from_json_file(star-layout.json)# 不连硬件即可加载台面# 用异常来暴露布局错误名称写错时 get_resource 直接抛错比在真机上撞机便宜得多assertdeck.get_resource(plate)isnotNone# 意图层依赖的资源是否都在先断言print(台面资源载入成功任务可在无硬件环境下先行审查)这里的关键判断get_resource名称是意图层与台面之间唯一的耦合点。把名称统一好换机器换一份 JSONprep_task零改动。2.3 SiLA 2跨厂商的另一条标准化通道SiLA 2Laboratory Automation 的互联标准不是某家驱动而是一套设备用什么消息/属性/命令互相说话的开放规范官方 Python 实现装在sila20.14.0PyPI 摘要逐字为 “Python implementation of the SiLA 2 standard for lab automation”源码在gitlab.com/sila2/sila_python。它和 PyLabRobot 是互补PyLabRobot 给任务级抽象SiLA 2 给设备级通信契约AnIML 官网首页还放了AnIML SiLA的组合叙事数据格式 设备通信说明官方社区认可这条组合。# 官方仓库的开发环境搭建PyPI 逐字给出的做法不是臆造gitclone --recurse-submodules https://gitlab.com/sila2/sila_python.gitcdsila_python pipinstall-e.[full]# [full] 装开发/示例全套依赖只跑最小客户端可退化为 pip install sila2设备一旦暴露某个 SiLA 2 接口如某厂商的 Liquid Handling 服务闭环里就能用统一的命令/属性去调用它不必为每台机器单写驱动。但具体某台设备实现了哪些 SiLA 接口须以该设备随附的 SiLA 定义XML/服务器发现为准——素材未登记任何一台机器的 SiLA 接口清单此处不编造方法名。三、常见报错与排查现象from pylabrobot.liquid_handling.backends import STARBackend报ModuleNotFoundError。根因装成了别的同名魔法库或没装全。解法只认pip install pylabrobot0.2.2 官方 GitHubPyLabRobot/pylabrobot若你搜到的是PySciMe/PyLab_server一族那是查无此物的死库立刻放弃。现象lh.aspirate(...)直接await却没反应或报协程未执行。根因PyLabRobot 全异步忘了asyncio.run(main())外层驱动。解法把任务包进async def用asyncio.run跑不要用同步脚本硬调。现象Deck.load_from_json_file能加载真机setup()却握手失败。根因JSON 台面合法但设备端连接参数如 OT-2 的hostIP、STAR 的驱动线不对。解法先只在内存里load_from_json_fileget_resource断言通过再逐台填真实 host这正是仿真/实机分离的价值。现象想给 Tecan 写import tecan或给 Beckman 找 “Biomek NX API”。根因臆造厂商 API违反铁律 9。解法Tecan 只有 “Developer Studio / API”无公开 Python SDKBeckman vWorks 仅第三方记载——两者一律走 PyLabRobot/SiLA 抽象或需向厂商索取不写死入口。现象Hamilton 对接时找不到稳定方法清单。根因VENUS/Prep 端点门户未逐条公开。解法OpenAPI 在设备本机Prep以本机文档为权威门户能核实的是能力范围method export/import/validation、loading/execution、ws://[IP]/NimbusLite/instinctevents方法细节以官方文档为准。四、动手练习台面先于硬件写一份含tip_rack与plate两个资源的 layout JSON用Deck.load_from_json_file加载并对get_resource(plate)[A1]做非空断言。判定标准不接任何硬件脚本能打印载入成功且断言通过。意图与 backend 解耦自查把prep_task分别传入STARBackend()与OpentronsOT2Backend(host…)构造的 handler后者无真机时只跑到构造不setup。判定标准grep -nE STAR|OT2|hamilton|opentrons prep_task 的函数体命中为 0——意图层不得出现任何型号字样。厂商边界表仅依据本篇填一张三行表Hamilton/Tecan/Beckman是否有公开 Python SDK、可核实的官方通道名、需要向厂商索取的项。判定标准Tecan/Beckman 两格明确写无公开 Python SDK/需向厂商索取Hamilton 写 VENUS/Prep 且注明 OpenAPI 在本机。五、小结与下一篇预告执行层不该赌某家给不给 Python SDKTecan、Beckman 的现实是没有公开 SDKHamilton 有 VENUS/Prep 两条原生线但细节要以官方文档为准。把配液意图写成硬件无关的prep_task(lh)用LiquidHandlerDeck.load_from_json_file把做什么与在哪台机器上做彻底分开换 backend 一行即切换设备跨厂商、跨数据格式时再上 SiLA 2 标准。生态里PySciMe一族整体消失正是把任务压在具体库上的反面教材。第 04 篇详述了 Opentrons 原生的 Protocol API v2 与 HTTP:31950面本篇则把它收纳成OpentronsOT2Backend下一第 07 篇把镜头从执行转到记录——仿真跑完、配液做完结果怎么自动、可审计地写回 LIMS/ELN。本篇认知问题回显FAQQ1闭环执行层为什么必须加一层硬件无关抽象而不是直接给每台液体处理机写原生脚本A因为 Tecan、Beckman 根本没有公开 Python SDKHamilton 也只给 VENUS/Prep 两条线且细节要查官方文档绑死原生脚本意味着换工作站就推倒重来。用 PyLabRobotLiquidHandler把意图与设备解耦换 backend 一行即切换才符合不臆造厂商 API 的铁律。Q2PyLabRobot 是怎么做到同一份配液代码换一行切设备的模块路径是什么Afrom pylabrobot.liquid_handling import LiquidHandler、from pylabrobot.liquid_handling.backends import STARBackend, OpentronsOT2Backend、from pylabrobot.resources import Deckaspirate/dispense/pick_up_tips/return_tips跨 backend 同名同义只换LiquidHandler(backend…)即换机器pylabrobot 0.2.2。Q3Deck.load_from_json_file在抽象层里承担什么角色A它把物理台面布局读成内存对象资源名→坐标不接硬件即可get_resource(plate)[A1]寻址并断言校验是先仿真后实机的落点资源名称是意图层与台面唯一的耦合点换机器只换 JSON。Q4pip install sila2装的是标准还是驱动官方仓库怎么起开发环境A装的是 SiLA 2 标准的官方 Python 实现sila2 0.14.0PyPI 自述 “Python implementation of the SiLA 2 standard for lab automation”源码gitlab.com/sila2/sila_python开发环境用git clone --recurse-submodules …后pip install -e .[full]。Q5Hamilton/Tecan/Beckman 的官方 API 边界各在哪哪些能直接写代码AHamilton 有 VENUS Web APImethod export/import/validation、loading/execution与 Prep APIREST WebSocketws://[IP]/NimbusLite/instincteventsOpenAPI 在设备本机Tecan Freedom EVOware 只有 Developer Studio/API、无公开 Python SDKBeckman vWorks Scripting 仅第三方记载。后两者须经 PyLabRobot/SiLA 抽象或向厂商索取绝不写import tecan。
