摘要:通宵实验真正的风险不是泵推不准,是"推到一半出错没人知道"。这篇给出分段排程的时序模型、堵针的三级判据、断点续跑的状态机设计,以及一份可直接用的实验日志表结构。
0. 先说结论
泵的机械精度出厂就定了。程序能决定的是三件事:什么时候推、推多少、出错怎么办。
前两件是时序,第三件是容错。通宵实验的成败,几乎全在第三件上------因为一旦无人值守,任何"卡住"都不再有第二个人来救。
我们的做法:把一次实验拆成"段(segment)",每段有独立的速率、体积、间隔和超时;每段结束写一条日志;任何异常都走同一个状态机,绝不静默重试。
1. 时序模型:实验 = 段的序列
| 字段 | 含义 | 示例 |
|---|---|---|
seg_id |
段号 | 3 |
rate |
流速(带单位) | 5 μL/min |
volume |
本段体积 | 50 μL |
direction |
注入/回吸 | INF |
dwell_s |
段后停留 | 120 |
timeout_s |
本段超时 | 体积/速率 × 1.2 |
retry |
允许重试次数 | 0 |
timeout_s 是最容易被漏掉的一列。没有超时,程序会在"泵没动"的状态下一直等------等到天亮,才发现一夜白跑。
计算示例:50 μL ÷ 5 μL/min = 10 min = 600 s,超时设 720 s。超过就判异常,进异常处理。
注意 retry 默认是 0:推了一半的段不能重试,否则剂量会多打一次。这个错误在生物实验里通常是不可接受的。
2. 串口控制:状态机比指令更重要
PHD2000 走 RS-232,指令本身很简单(设直径、设流速、设体积、设方向、启动、停止、读状态)。真正麻烦的是回显、超时和状态不一致。
一个能跑通的调用顺序:
1. 打开串口 (常见 9600 8N1, 无流控) ← 以设备手册为准
2. 发一条查询,确认有回包(证明链路活着)
3. 设 DIA(注射器直径)——不设这个,后面所有体积都是错的
4. 设 RAT(流速)+ 单位
5. 设 VOL(体积)+ DIR(方向)
6. RUN,然后按 100~500 ms 轮询状态
7. 收到"到位"或到达 timeout,STP,写日志
第 3 步是新手最容易跳过的一步,也是后果最严重的一步:直径不设,流速算出来是错的,而且错得"看起来正常"------你会得到一份完整、平滑、但剂量全错的曲线。
必须做的三件事:
- 每条指令回读确认,不能"发完就当成功"。
- 区分回显与应答。有的固件会把指令原样回显,程序若按"收到任意字节=成功"判断,会误判。
- 指令之间留间隔(≥ 20--50 ms),老设备处理不过来会吞指令。
import serial, time
def cmd(ser, text, wait=0.15):
ser.reset_input_buffer()
ser.write((text + "\r").encode())
time.sleep(wait)
return ser.read_all().decode(errors="ignore").strip()
ser = serial.Serial("COM3", 9600, timeout=0.5)
print(cmd(ser, "DIA")) # 先读一次,确认链路
print(cmd(ser, "DIA 14.53")) # 设直径(按你的注射器规格填)
print(cmd(ser, "RAT 5.0 ul/min"))
print(cmd(ser, "VOL 50"))
print(cmd(ser, "DIR INF"))
print(cmd(ser, "RUN"))
指令拼写与单位支持随固件版本不同,以你手上那台的指令手册为准。不要照抄网上的指令表------版本差异足以让泵"接受指令但行为不符"。
3. 堵针检测:没有压力传感器怎么办
PHD2000 本身不带压力反馈,所以堵针只能间接判断。三级判据,从便宜到贵:
| 级别 | 判据 | 能发现什么 | 误报风险 |
|---|---|---|---|
| 一级 | 实际推进量 vs 理论推进量 | 泵根本没动(通信/机械故障) | 低 |
| 二级 | 段耗时 vs 理论耗时 | 阻力过大导致速率下降 | 中 |
| 三级 | 外接压力/力传感器 | 真实堵针 | 低,但要加硬件 |
一级 :段开始时记下累计推注量,段结束时再读一次,与 volume 比对,偏差超 5% 判异常。这一级几乎不增加成本。
二级 :记录 RUN 到"到位"的实际耗时,与 volume/rate 比。耗时明显偏长,往往是针头渐堵------流速已经低于设定值,但泵"以为"自己在正常推。
三级才是根治。如果实验对堵塞敏感(高浓度悬液、长时程、易沉降样品),加一个压力传感器比任何软件判据都可靠。软件判据只能发现"已经偏了",压力能发现"正在堵"。
4. 断点续跑:状态要落盘
通宵实验最怕"程序崩了,不知道推到哪"。
做法:每段结束立即写一行日志(不是最后统一写),崩溃重启后从最后一条完整记录续跑。
日志表结构(CSV,一行一段):
| 字段 | 说明 |
|---|---|
run_id |
实验批次 ID(重启不变) |
seg_id |
段号 |
plan_rate / plan_volume |
计划值 |
actual_volume |
实际推注量(回读) |
t_start / t_end |
起止时间 |
duration_s |
实际耗时 |
status |
OK / TIMEOUT / COMM_ERR / ABORT |
note |
异常描述 |
续跑逻辑:
启动
→ 读日志
→ 取 run_id 相同的最后一条 status=OK 的 seg_id
→ 从 seg_id+1 开始
→ 若最后一条不是 OK:该段标记"部分完成",人工确认后再决定是否重推
关键点还是那句:部分完成的段不能自动重推。程序不知道那一刻到底推了多少,只有人能判断。
run_id 这个字段看起来多余,实际上是多批次实验并存的保险------同一台泵跑过好几轮实验时,靠它区分日志属于哪一轮。
5. 异常处理表
| 异常 | 检测方式 | 动作 |
|---|---|---|
| 串口超时 | 读超时 500 ms | 重发 1 次;再失败 → COMM_ERR,停机 |
| 泵未到位 | timeout_s 到 |
STP,标记 TIMEOUT,停机 |
| 推进量偏差 > 5% | 段末回读比对 | 标记 ABNORMAL,停机 |
| 程序异常退出 | 重启读日志 | 从最后 OK 段续跑 |
| 电源中断 | 重启后时间跳变 | 标记 INTERRUPTED,人工确认 |
原则只有一条:任何异常都停机等人,不做"聪明"的自动恢复。
无人值守时,"停下来"比"猜着继续"安全------因为停下来损失的是时间,猜错了损失的是样品,有时候是整批。
6. 边界
这套做法解决"时序与容错",不解决"流体的物理行为"。
气泡、黏度变化、针头部分堵塞导致的流量偏差,程序看不见------除非加传感器。程序的职责是"保证执行与计划一致",不是"保证流体行为符合预期"。这两件事的边界要分清,否则会得出"程序很稳,但结果不对"的困惑。
7. 参考资料
- Harvard Apparatus PHD 2000 用户手册(指令集、串口参数、单位定义)
- LabVIEW 串口通信与状态机设计(生产者和消费者架构)
附:5 分钟自测,先回答这 5 个问题
- 每一段都有
timeout_s吗? - 注射器直径(
DIA)在每条指令链里都设了吗? - 堵针判据有几级?只有"泵没动"这一级吗?
- 日志是每段立即写一行,还是最后统一写?
- 崩溃重启后,"部分完成"的段是自动重推,还是停机等人?
第 5 条答错的代价最大:自动重推意味着多打一次剂量,而那一刻推了多少,程序并不知道。
通宵实验的可靠性,不在泵,在程序怎么处理"出错"。把停机条件想清楚,比把控制逻辑写漂亮更重要。